Skip to content

escrow contract is completely undocumented — absent from README and architecture docs #22

Description

@abayomicornelius

Problem (spike / open-ended)

Subscription (stellar_send/src/subscription.rs lines 26-41) has no expiry, max_executions, or remaining_executions field. Once created via create_subscription, a subscription with active: true can be executed by anyone (it's explicitly keeper/permissionless, per the module doc comment) indefinitely, forever, as long as the payer's token allowance (set separately via token.approve) keeps being large enough — the only way to stop it is cancel_subscription, which the payer must remember to call.

Why this is worth investigating

The module doc comment is explicit that execute_subscription is designed to run "with no interactive signature from the payer at execution time," relying entirely on a pre-set allowance. This is a reasonable pull-payment design, but combined with no built-in expiry/execution cap, it means a payer who sets a large, long-expiration allowance (as they'd naturally need to, so the keeper doesn't get blocked prematurely) and then forgets about the subscription has no automatic stop — the subscription runs until the allowance is exhausted or manually cancelled, potentially far longer than the payer intended. This is exactly the kind of "silent recurring charge no one remembers to cancel" problem well known in traditional card-network subscriptions, translated into an on-chain, permissionless-execution context where there's arguably less friction to notice it than a bank statement line item.

Open questions

  • Should subscriptions gain an optional max_executions: Option<u32> or expiry: Option<u64> (or both) set at creation time, enforced in execute_subscription by checking a remaining_executions/now > expiry condition before transferring?
  • Should this be opt-in (payer chooses at creation, default unbounded for backward compatibility) or should unbounded subscriptions be disallowed entirely going forward (breaking change, but arguably the safer default)?
  • How does an execution cap interact with cancel_subscription's existing "cancelled is permanent, no re-activation" rule (line 39-40 comment) — should hitting max_executions naturally flip active to false and emit the same emit_subscription_cancelled event, or a distinct "expired" event, so downstream consumers can distinguish payer-initiated cancellation from natural completion?

Investigation steps

Survey whether integrators building on stellar_send subscriptions actually want unbounded recurring payments (e.g. an ongoing SaaS-style charge) versus fixed-term ones (e.g. a 12-month installment plan) to determine whether max_executions/expiry should be mandatory, optional, or omitted per the answer.

Testing strategy

Once a design is settled: test_subscription_stops_after_max_executions, test_subscription_expiry_blocks_execution_after_deadline, and a test confirming get_subscription correctly reflects active: false once the cap/expiry is reached without requiring an explicit cancel_subscription call.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial Campaign | FWC26Campaign: Official Campaign | FWC26documentationImprovements or additions to documentationvery hardVery difficult / senior-level bounty issue

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions