Skip to content

Feat: subscription delegation - #10130

Draft
tuna1207 wants to merge 10 commits into
mainfrom
feat/subscription-delegation
Draft

Feat: subscription delegation#10130
tuna1207 wants to merge 10 commits into
mainfrom
feat/subscription-delegation

Conversation

@tuna1207

@tuna1207 tuna1207 commented Sep 7, 2026

Copy link
Copy Markdown
Member

Explanation

Money Account Plus crypto subscribe needs a subscription-payment delegation (periodic ERC-20 transfer caveats + CHOMP intent), not the Shield-style ERC-20 approval path. That workflow was split across clients and backend services with no single Core orchestrator.

This PR adds a stateless SubscriptionDelegationService that owns setup end-to-end and returns a verified delegationHash for SubscriptionController.startSubscriptionWithCrypto. The controller does not depend on this service; clients call SubscriptionDelegationService:prepareDelegation first, then start the subscription with the hash.

prepareDelegation flow

  1. Resolve chain from moneyAccountVaultConfig (remote feature flags), the temporary delegate from CHOMP autoDepositDelegate, and Delegation Framework v1.3.0 enforcers from @metamask/delegation-deployments.
  2. Size periodic caveats from authoritative pricing (unitAmount / unitDecimals / token decimals.
  3. Reuse a stored Authenticated User Storage delegation when the semantic fingerprint matches (and ensure a CHOMP intent is active); otherwise sign, CHOMP-verify, persist, and register a new intent.
  4. Return { delegationHash, disposition: 'created' | 'reused' }.

Only MONEY_ACCOUNT_PLUS is supported; Shield continues to use ERC-20 approval.

Package split

Package Change
@metamask/subscription-controller New SubscriptionDelegationService, helpers (amount, caveats, fingerprint), messenger action SubscriptionDelegationService:prepareDelegation.
@metamask/chomp-api-service Accept 'subscription-payment' as an intent / metadata type (with exported ChompIntentType).
@metamask/wallet Wire the service into default initialization and delegate AUS, CHOMP, DelegationController:signDelegation, and RemoteFeatureFlagController:getState.

Hosts must register AuthenticatedUserStorageService, ChompApiService, and DelegationController on the root messenger before calling prepareDelegation. RemoteFeatureFlagController is already initialized by the default wallet factory.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Comment thread packages/chomp-api-service/CHANGELOG.md Outdated

### Changed

- Accept `'subscription-payment'` as a CHOMP intent / delegation metadata type alongside `'cash-deposit'` and `'cash-withdrawal'`. ([#10130](https://github.com/MetaMask/core/pull/10130))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const ChompIntentTypeStruct = enums([
'cash-deposit',
'cash-withdrawal',
'subscription-payment',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename pls

Comment thread packages/chomp-api-service/src/types.ts Outdated
export type ChompIntentType =
| 'cash-deposit'
| 'cash-withdrawal'
| 'subscription-payment';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above.
Note that when we support premium vault, we may need add more intent and delegations: cash-deposit-premium, cash-withdrawal-premium

InvalidDecimals = 'Subscription delegation decimals must be a non-negative integer',
LossyAmountScale = 'Subscription delegation amount cannot be scaled to token decimals without remainder',
UnsupportedRecurringInterval = 'Unsupported subscription recurring interval',
UnsupportedProduct = 'Subscription delegation is only supported for Money Account Plus',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will we show this error message to end users? We may use MM Orange? Is it possible to have translations instead?

/**
* Storage / CHOMP metadata type for subscription-payment delegations.
*/
export const SUBSCRIPTION_PAYMENT_DELEGATION_TYPE = 'subscription-payment';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may need to rename to cash-subscription

periodDuration,
startDate,
}),
args: '0x',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need redeemerCaveat just like the other delegations "cash-deposit", "cash-withdrawal"?

tokenAddress: request.tokenAddress,
periodAmount,
periodDuration,
startDate,

@tanguyenvn tanguyenvn Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we'll need to add the recipient address which is subscription payment address?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants