8000 RFC: Enforced recurring splits for Tempo subscriptions · Issue #920 · wevm/mppx · GitHub
[go: up one dir, main page]

Skip to content

RFC: Enforced recurring splits for Tempo subscriptions #920

Open

Description

@EfeDurmaz16

Following up on mppx discussion #905: could Tempo subscriptions support fixed splits enforced on-chain?

Use case

The author needs creator/platform fee splits for one-time payments and subscriptions. They reported a partial browser-flow patch in the MPP Telegram group and want official support, without the marketplace collecting creator funds before distributing them.

A simple example: a $10 membership pays $8 to the creator and $2 to the platform each period.

tempo.charge supports splits; tempo.subscription authorizes one recipient. Initial scope: one payer, one TIP-20 token, fixed amounts, and existing subscription cadence and expiry rules.

Expected behavior

  • Enforce every approved recipient and amount; the platform cannot alter or omit a share.
  • Credit all shares atomically or revert the entire payment, including any held funds. Transaction fees may still apply.
  • Charge each period at most once, including retries and concurrent attempts.
  • Respect cancellation, revocation, and expiry. One subscriber's failure must not block others.

Tradeoff: one recipient's receive restriction blocks that membership's renewal until resolved.

Why this needs more than a recipient list

The current authorization checks bind one recipient and a total period limit. An expanded allowlist alone could permit all $10 to go to one address.

Tempo receive policies can hold funds while the transfer call succeeds. Receipt checks afterward cannot undo partial distribution; recipient-credit checks must trigger rollback during execution.

Suggested starting point

For discussion:

  • Authorization: Bind approval to the network, settlement mechanism, token, recipients, amounts, schedule, and expiry. Require fresh approval for changed terms and provide on-chain revocation.
  • Enforcement: Explore a minimal contract checking actual credits, with its own period limits and revocation. Allowance alone is insufficient. Keep a native mechanism open as an alternative.
  • Compatibility: Require explicit support; unsupported clients reject instead of silently falling back to single-recipient settlement.
  • Lifecycle: Track subscription and period. Reverted attempts remain retryable; settled periods cannot be charged again. On-chain revocation blocks later collections without reversing settled payments.

Does this model fit Tempo subscriptions, and how should it integrate with existing authorization?

Feasibility check

A Tempo v1.15.0 local dev-chain prototype demonstrated exact allocation, rollback of a held second share, retry, duplicate-period rejection, cancellation, and expiry. Removing the credit check reproduced partial distribution despite transaction success.

The prototype uses payer-deployed immutable terms and allowance. It establishes settlement feasibility; access-key integration, wallet approval, and live-network compatibility remain unverified.

Activity

  1. horn111 commented on Sep 30, 2026

    @horn111

    Hi @EfeDurmaz16, I'm exploring a testnet demo for the marketplace use case in #905. You proved the local settlement behavior; I'd want to use that work as a starting point.
    Have you started testing wallet approval and unattended renewals on testnet?

  2. EfeDurmaz16 commented on Sep 30, 2026

    @EfeDurmaz16
    ContributorAuthor

    Wallet approval and unattended renewals on testnet are still unverified. Which wallet and renewal flow are you planning to use for the demo?

  3. horn111 commented on Oct 1, 2026

    @horn111

    I haven't chosen a wallet yet; I'm checking @nofluffdev's current setup.
    The target flow is one approval for fixed creator/platform shares, then backend triggered renewals and onchain cancellation.
    Could you share the prototype code and any wallet/access key constraints you've identified?

  4. EfeDurmaz16 commented on Oct 1, 2026

    @EfeDurmaz16
    ContributorAuthor

    Here’s the TypeScript prototype.

    Local access-key tests passed for activation, backend renewal and onchain revocation. The key still permits 10/0 or an omitted share, and a receive-policy block can hold the platform share while consuming the period budget.

    Wallet approval, live-network compatibility and unattended testnet renewals remain untested. This is a verifier experiment with a custom renewal callback, not complete SDK support.

  5. horn111 commented on Oct 1, 2026

    @horn111

    Thanks for the code. I'm considering the earlier contract and allowance path for a first testnet demo.
    Do you see a way to connect it to mppx's subscription lifecycle, or would it need a separate authorization flow?

  6. EfeDurmaz16 commented on Oct 1, 2026

    @EfeDurmaz16
    ContributorAuthor

    The contract prototype already demonstrated exact 8/2 settlement, rollback, period checks and cancellation locally. Separately, we tested a custom renewal callback through SubscriptionStore.renew.

    The activate/renew hooks look like possible adapter points, but we haven't connected the contract path end to end. It would still need its own contract/allowance authorization; the current Tempo flow verifies access-key credentials before custom activation.

    I'm contributing experiments here. Whether this should become a supported subscription path is a question for the maintainers.

  7. nofluffdev commented on Oct 2, 2026

    @nofluffdev

    @EfeDurmaz16

    As the README notes, the main integration gap is that the default subscription payment handler still sends the full amount to the primary recipient. Before exposing this as SDK split support, I’d expect:

    • Split terms persisted in subscription records and included in binding checks.
    • Both activation and renewal to construct the split transfers, including sponsored transactions.
    • Receipt verification for every expected payment leg.
    • Tests through the normal subscription lifecycle, including concurrent renewals and recovery after successful submission followed by a persistence failure.

    The counterexamples also clarify the limits of this approach: recipient scopes and an aggregate spending cap do not enforce a fixed allocation or require every recipient to be paid together. The challenge and witness bind the approved terms for SDK verification, but native-key execution still relies on the backend constructing the intended transfers.
    The receive-policy case deserves explicit recovery coverage. A successful transaction with an uncredited leg cannot safely be treated as an unpaid attempt and retried in full. Receipt checks can detect that outcome, but cannot reverse funds already transferred.

    This seems like a sound starting point for SDK-managed splits, with those guarantees documented explicitly. Integrating the split handling into the default activation and renewal paths would be the next useful step.

  8. EfeDurmaz16 commented on Oct 2, 2026

    @EfeDurmaz16
    ContributorAuthor

    @nofluffdev

    I’ve now wired splits into the default activation and renewal paths, including sponsorship, stored terms, binding checks and verification of every payment leg. Local lifecycle and concurrent-renewal tests pass.

    The 8/2 allocation is still SDK-managed. Receive-policy holds keep their normal behavior; receipt checks cannot undo transferred funds. Recovery after a successful payment followed by a persistence failure remains an open gap.

  9. EfeDurmaz16 commented on Oct 2, 2026

    @EfeDurmaz16
    ContributorAuthor

    @nofluffdev Here’s the SDK lifecycle diff. It includes stored split terms and binding checks, the shared activation/renewal batch, and per-leg verification with tests.

  10. nofluffdev commented on Oct 2, 2026

    @nofluffdev

    @EfeDurmaz16 nice work, lgtm!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0