@metamask/subscription-controller 14.0.0 → 14.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [14.0.2]
11
+
12
+ ### Changed
13
+
14
+ - Bump `@metamask/profile-sync-controller` from `^34.0.3` to `^35.0.0` ([#10905](https://github.com/MetaMask/core/pull/10905), [#10925](https://github.com/MetaMask/core/pull/10925))
15
+
16
+ ## [14.0.1]
17
+
18
+ ### Changed
19
+
20
+ - Bump `@metamask/transaction-controller` from `^72.2.0` to `^72.2.1` ([#10817](https://github.com/MetaMask/core/pull/10817))
21
+ - Bump `@metamask/base-controller` from `^10.0.0` to `^10.1.0` ([#10864](https://github.com/MetaMask/core/pull/10864))
22
+
23
+ ### Fixed
24
+
25
+ - Stop reusing a cash-subscription delegation whose period `startDate` is outside the Subscription API window (14 days before the checkout start date, 5 minutes after it; trial checkouts also reject a live start). A stale stored permission caused `POST /subscriptions/crypto` to fail with `E214`. ([#10870](https://github.com/MetaMask/core/pull/10870))
26
+
10
27
  ## [14.0.0]
11
28
 
12
29
  ### Changed
@@ -646,7 +663,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
646
663
  - Bump `@metamask/controller-utils` from `^11.12.0` to `^11.14.0` ([#6620](https://github.com/MetaMask/core/pull/6620), [#6629](https://github.com/MetaMask/core/pull/6629))
647
664
  - Bump `@metamask/utils` from `^11.4.2` to `^11.8.0` ([#6588](https://github.com/MetaMask/core/pull/6588))
648
665
 
649
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/subscription-controller@14.0.0...HEAD
666
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/subscription-controller@14.0.2...HEAD
667
+ [14.0.2]: https://github.com/MetaMask/core/compare/@metamask/subscription-controller@14.0.1...@metamask/subscription-controller@14.0.2
668
+ [14.0.1]: https://github.com/MetaMask/core/compare/@metamask/subscription-controller@14.0.0...@metamask/subscription-controller@14.0.1
650
669
  [14.0.0]: https://github.com/MetaMask/core/compare/@metamask/subscription-controller@13.0.0...@metamask/subscription-controller@14.0.0
651
670
  [13.0.0]: https://github.com/MetaMask/core/compare/@metamask/subscription-controller@12.0.1...@metamask/subscription-controller@13.0.0
652
671
  [12.0.1]: https://github.com/MetaMask/core/compare/@metamask/subscription-controller@12.0.0...@metamask/subscription-controller@12.0.1
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriptionDelegationService.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/SubscriptionDelegationService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,qDAAqD,EACrD,oDAAoD,EACrD,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EACV,kCAAkC,EAClC,wCAAwC,EACxC,qCAAqC,EACtC,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAC;AAGhG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,wDAAwD,EAAE,MAAM,yCAAyC,CAAC;AACxH,OAAO,KAAK,EAAE,sDAAsD,EAAE,MAAM,4CAA4C,CAAC;AAKzH,OAAO,KAAK,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AAS1G,OAAO,KAAK,EACV,sCAAsC,EACtC,4CAA4C,EAC5C,kDAAkD,EAClD,uDAAuD,EACxD,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,8BAA8B,CAAC;AAsBzF,OAAO,KAAK,EAAE,0CAA0C,EAAE,MAAM,wDAAwD,CAAC;AAKzH,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC9B,oCAAoC,EAEpC,8BAA8B,EAE9B,sCAAsC,EACtC,qCAAqC,EAGtC,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,eAAO,MAAM,WAAW,kCAAkC,CAAC;AAoC3D;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAC9C,0CAA0C,CAAC;AAE7C;;GAEG;AACH,KAAK,cAAc,GACf,oDAAoD,GACpD,qDAAqD,GACrD,qCAAqC,GACrC,kCAAkC,GAClC,wCAAwC,GACxC,wCAAwC,GACxC,sDAAsD,GACtD,wDAAwD,GACxD,yCAAyC,GACzC,oCAAoC,GACpC,sCAAsC,GACtC,4CAA4C,GAC5C,kDAAkD,GAClD,uDAAuD,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAExD,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,sCAAsC,GAAG,SAAS,CAC5D,OAAO,WAAW,EAClB,oCAAoC,GAAG,cAAc,EACrD,mCAAmC,GAAG,aAAa,CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAAG;IACjD,SAAS,EAAE,sCAAsC,CAAC;CACnD,CAAC;AAqBF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,6BAA6B;;IACxC,QAAQ,CAAC,IAAI,EAAE,OAAO,WAAW,CAAe;IAIhD,YAAY,OAAO,EAAE,oCAAoC,EAOxD;IAED;;;;;;OAMG;IACG,wBAAwB,CAC5B,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,8BAA8B,CAAC,CAMzC;IA+BD;;;;;;;;;;;;;;;;OAgBG;IACG,+BAA+B,CACnC,OAAO,EAAE,sCAAsC,GAC9C,OAAO,CAAC,qCAAqC,CAAC,CA+DhD;IA2PD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,iBAAiB,CACrB,OAAO,EAAE,oCAAoC,EAC7C,QAAQ,UAAQ,GACf,OAAO,CAAC,8BAA8B,CAAC,CAoKzC;CAiJF"}
1
+ {"version":3,"file":"SubscriptionDelegationService.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/SubscriptionDelegationService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,qDAAqD,EACrD,oDAAoD,EACrD,MAAM,sCAAsC,CAAC;AAC9C,OAAO,KAAK,EACV,kCAAkC,EAClC,wCAAwC,EACxC,qCAAqC,EACtC,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,wCAAwC,EAAE,MAAM,iCAAiC,CAAC;AAGhG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,wDAAwD,EAAE,MAAM,yCAAyC,CAAC;AACxH,OAAO,KAAK,EAAE,sDAAsD,EAAE,MAAM,4CAA4C,CAAC;AAKzH,OAAO,KAAK,EAAE,yCAAyC,EAAE,MAAM,0CAA0C,CAAC;AAS1G,OAAO,KAAK,EACV,sCAAsC,EACtC,4CAA4C,EAC5C,kDAAkD,EAClD,uDAAuD,EACxD,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,8BAA8B,CAAC;AAsBzF,OAAO,KAAK,EAAE,0CAA0C,EAAE,MAAM,wDAAwD,CAAC;AAKzH,OAAO,KAAK,EACV,+BAA+B,EAC/B,8BAA8B,EAC9B,oCAAoC,EAEpC,8BAA8B,EAE9B,sCAAsC,EACtC,qCAAqC,EAGtC,MAAM,YAAY,CAAC;AAGpB;;;GAGG;AACH,eAAO,MAAM,WAAW,kCAAkC,CAAC;AAoC3D;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAC9C,0CAA0C,CAAC;AAE7C;;GAEG;AACH,KAAK,cAAc,GACf,oDAAoD,GACpD,qDAAqD,GACrD,qCAAqC,GACrC,kCAAkC,GAClC,wCAAwC,GACxC,wCAAwC,GACxC,sDAAsD,GACtD,wDAAwD,GACxD,yCAAyC,GACzC,oCAAoC,GACpC,sCAAsC,GACtC,4CAA4C,GAC5C,kDAAkD,GAClD,uDAAuD,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,mCAAmC,GAAG,KAAK,CAAC;AAExD,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,sCAAsC,GAAG,SAAS,CAC5D,OAAO,WAAW,EAClB,oCAAoC,GAAG,cAAc,EACrD,mCAAmC,GAAG,aAAa,CACpD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAAG;IACjD,SAAS,EAAE,sCAAsC,CAAC;CACnD,CAAC;AAqBF;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,6BAA6B;;IACxC,QAAQ,CAAC,IAAI,EAAE,OAAO,WAAW,CAAe;IAIhD,YAAY,OAAO,EAAE,oCAAoC,EAOxD;IAED;;;;;;OAMG;IACG,wBAAwB,CAC5B,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,8BAA8B,CAAC,CAMzC;IA+BD;;;;;;;;;;;;;;;;OAgBG;IACG,+BAA+B,CACnC,OAAO,EAAE,sCAAsC,GAC9C,OAAO,CAAC,qCAAqC,CAAC,CA+DhD;IA2PD;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,iBAAiB,CACrB,OAAO,EAAE,oCAAoC,EAC7C,QAAQ,UAAQ,GACf,OAAO,CAAC,8BAA8B,CAAC,CAmKzC;CAiJF"}
@@ -176,7 +176,7 @@ export class SubscriptionDelegationService {
176
176
  periodAmount,
177
177
  periodDuration,
178
178
  nowSeconds,
179
- isTrialDeferred: startDate > nowSeconds,
179
+ expectedStartDate: startDate,
180
180
  enforcers: config.enforcers,
181
181
  });
182
182
  const reusable = pickLatestMatchingSubscriptionDelegation((await this.#messenger.call('AuthenticatedUserStorageService:listDelegations')).filter(matches), config.enforcers);
@@ -340,7 +340,6 @@ export class SubscriptionDelegationService {
340
340
  ? price.trialPeriodDays
341
341
  : undefined,
342
342
  });
343
- const isTrialDeferred = startDate > nowSeconds;
344
343
  const reusable = forceNew
345
344
  ? undefined
346
345
  : await this.#findReusableDelegation({
@@ -352,7 +351,7 @@ export class SubscriptionDelegationService {
352
351
  periodAmount,
353
352
  periodDuration,
354
353
  nowSeconds,
355
- isTrialDeferred,
354
+ expectedStartDate: startDate,
356
355
  enforcers,
357
356
  });
358
357
  if (reusable) {
@@ -430,7 +429,7 @@ export class SubscriptionDelegationService {
430
429
  disposition: 'created',
431
430
  };
432
431
  }
433
- async #findReusableDelegation({ request, chainId, delegateAddress, paymentAddress, tokenAddress, periodAmount, periodDuration, nowSeconds, isTrialDeferred, enforcers, }) {
432
+ async #findReusableDelegation({ request, chainId, delegateAddress, paymentAddress, tokenAddress, periodAmount, periodDuration, nowSeconds, expectedStartDate, enforcers, }) {
434
433
  const matches = makeMatchesSubscriptionDelegation({
435
434
  delegatorAddress: request.payerAddress,
436
435
  delegateAddress,
@@ -440,7 +439,7 @@ export class SubscriptionDelegationService {
440
439
  periodAmount,
441
440
  periodDuration,
442
441
  nowSeconds,
443
- isTrialDeferred,
442
+ expectedStartDate,
444
443
  enforcers,
445
444
  });
446
445
  const existingDelegations = await this.#messenger.call('AuthenticatedUserStorageService:listDelegations');
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriptionDelegationService.js","sourceRoot":"","sources":["../../src/subscription-delegation/SubscriptionDelegationService.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DelegationResponse,\n AuthenticatedUserStorageServiceCreateDelegationAction,\n AuthenticatedUserStorageServiceListDelegationsAction,\n} from '@metamask/authenticated-user-storage';\nimport type {\n ChompApiServiceCreateIntentsAction,\n ChompApiServiceGetIntentsByAddressAction,\n ChompApiServiceVerifyDelegationAction,\n} from '@metamask/chomp-api-service';\nimport type { DelegationControllerSignDelegationAction } from '@metamask/delegation-controller';\nimport { hashDelegation } from '@metamask/delegation-core';\nimport { DELEGATOR_CONTRACTS } from '@metamask/delegation-deployments';\nimport type { Messenger } from '@metamask/messenger';\nimport type { MoneyAccountBalanceServiceFetchBalanceWithFallbackAction } from '@metamask/money-account-balance-service';\nimport type { MoneyAccountUpgradeControllerForceUpgradeAccountAction } from '@metamask/money-account-upgrade-controller';\nimport {\n getMoneyAccountVaultConfig,\n MUSD_DECIMALS,\n} from '@metamask/money-account-utils';\nimport type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller';\nimport { add0x, hexToNumber } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n ACTIVE_SUBSCRIPTION_STATUSES,\n SubscriptionControllerErrorMessage,\n SubscriptionDelegationServiceErrorMessage,\n} from '../constants.js';\nimport type {\n SubscriptionControllerGetPricingAction,\n SubscriptionControllerGetSubscriptionsAction,\n SubscriptionControllerIsUserEligibleForTrialAction,\n SubscriptionControllerStartSubscriptionWithCryptoAction,\n} from '../SubscriptionController-method-action-types.js';\nimport type { SubscriptionControllerGetStateAction } from '../SubscriptionController.js';\nimport { CRYPTO_AUTH_METHODS, PAYMENT_TYPES, PRODUCT_TYPES } from '../types.js';\nimport type {\n ProductPrice,\n ProductType,\n PricingCryptoPaymentMethod,\n RecurringInterval,\n Subscription,\n TokenPaymentInfo,\n} from '../types.js';\nimport {\n assertPositiveInteger,\n calculatePeriodAmount,\n getDelegationStartDate,\n getPeriodDuration,\n} from './amount.js';\nimport { buildUnsignedSubscriptionDelegation } from './caveats.js';\nimport {\n equalsIgnoreCase,\n makeMatchesSubscriptionDelegation,\n pickLatestMatchingSubscriptionDelegation,\n} from './fingerprint.js';\nimport type { SubscriptionDelegationServiceMethodActions } from './SubscriptionDelegationService-method-action-types.js';\nimport {\n buildDelegationTypedData,\n decodeSubscriptionAuthority,\n} from './typed-data.js';\nimport type {\n MoneyAccountBalanceCheckRequest,\n MoneyAccountBalanceCheckResult,\n PrepareSubscriptionDelegationRequest,\n PreparedSubscriptionPermission,\n PreparedSubscriptionDelegation,\n SignedSubscriptionDelegation,\n StartSubscriptionWithDelegationRequest,\n StartSubscriptionWithDelegationResult,\n SubscriptionDelegationEnforcers,\n UnsignedSubscriptionDelegation,\n} from './types.js';\nimport { CASH_SUBSCRIPTION_DELEGATION_TYPE } from './types.js';\n\n/**\n * The name of the {@link SubscriptionDelegationService}, used to namespace the\n * service's actions and events.\n */\nexport const serviceName = 'SubscriptionDelegationService';\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'prepareDelegation',\n 'checkMoneyAccountBalance',\n 'startSubscriptionWithDelegation',\n] as const;\n\nconst DELEGATION_FRAMEWORK_VERSION = '1.3.0';\n\nfunction resolveEnforcers(chainId: Hex): SubscriptionDelegationEnforcers {\n const contracts =\n DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION]?.[hexToNumber(chainId)];\n\n if (\n !contracts?.ERC20PeriodTransferEnforcer ||\n !contracts.AllowedCalldataEnforcer\n ) {\n throw new Error(\n `${SubscriptionDelegationServiceErrorMessage.DelegationContractsNotFound}: ${chainId}`,\n );\n }\n\n return {\n erc20TokenPeriodTransfer: contracts.ERC20PeriodTransferEnforcer,\n allowedCalldata: contracts.AllowedCalldataEnforcer,\n };\n}\n\nfunction removeDelegationSignature(\n delegation: SignedSubscriptionDelegation,\n): UnsignedSubscriptionDelegation {\n const { signature: _signature, ...unsignedDelegation } = delegation;\n return unsignedDelegation;\n}\n\n/**\n * Actions that {@link SubscriptionDelegationService} exposes to other consumers.\n */\nexport type SubscriptionDelegationServiceActions =\n SubscriptionDelegationServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link SubscriptionDelegationServiceMessenger} calls.\n */\ntype AllowedActions =\n | AuthenticatedUserStorageServiceListDelegationsAction\n | AuthenticatedUserStorageServiceCreateDelegationAction\n | ChompApiServiceVerifyDelegationAction\n | ChompApiServiceCreateIntentsAction\n | ChompApiServiceGetIntentsByAddressAction\n | DelegationControllerSignDelegationAction\n | MoneyAccountUpgradeControllerForceUpgradeAccountAction\n | MoneyAccountBalanceServiceFetchBalanceWithFallbackAction\n | RemoteFeatureFlagControllerGetStateAction\n | SubscriptionControllerGetStateAction\n | SubscriptionControllerGetPricingAction\n | SubscriptionControllerGetSubscriptionsAction\n | SubscriptionControllerIsUserEligibleForTrialAction\n | SubscriptionControllerStartSubscriptionWithCryptoAction;\n\n/**\n * Events that {@link SubscriptionDelegationService} exposes to other consumers.\n */\nexport type SubscriptionDelegationServiceEvents = never;\n\ntype AllowedEvents = never;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link SubscriptionDelegationService}.\n */\nexport type SubscriptionDelegationServiceMessenger = Messenger<\n typeof serviceName,\n SubscriptionDelegationServiceActions | AllowedActions,\n SubscriptionDelegationServiceEvents | AllowedEvents\n>;\n\n/**\n * Options for constructing {@link SubscriptionDelegationService}.\n */\nexport type SubscriptionDelegationServiceOptions = {\n messenger: SubscriptionDelegationServiceMessenger;\n};\n\ntype SubscriptionIntentParams = {\n account: Hex;\n chainId: Hex;\n delegationHash: Hex;\n allowance: Hex;\n tokenSymbol: string;\n tokenAddress: Hex;\n};\n\ntype ResolvedSubscriptionDelegationConfig = {\n chainId: Hex;\n delegateAddress: Hex;\n delegationManager: Hex;\n paymentAddress: Hex;\n enforcers: SubscriptionDelegationEnforcers;\n price: ProductPrice;\n token: TokenPaymentInfo;\n};\n\n/**\n * Stateless orchestrator for cash-subscription delegation setup.\n *\n * Owns the workflow: size periodic caveats → sign → CHOMP verify → persist to\n * Authenticated User Storage → register CHOMP intent. Returns a verified\n * `delegationHash` for `SubscriptionController.startSubscriptionWithCrypto`.\n *\n * Pass `skipChompInteractions: true` to skip CHOMP verify and intent\n * registration and return a locally computed delegation hash.\n *\n * Each call resolves the Money Account chain from remote feature flags, then\n * resolves its price, payment token, and delegate from `SubscriptionController`\n * pricing. The pricing `delegateAddress` is used as the delegation `delegate`.\n *\n * Does not own subscription state; `SubscriptionController` does not depend on\n * this service. Only Money Account Plus is supported.\n */\nexport class SubscriptionDelegationService {\n readonly name: typeof serviceName = serviceName;\n\n readonly #messenger: SubscriptionDelegationServiceMessenger;\n\n constructor(options: SubscriptionDelegationServiceOptions) {\n this.#messenger = options.messenger;\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Checks whether the Money Account holds enough convertible mUSD value to\n * cover pricing `unitAmount × minBillingCyclesForBalance`.\n *\n * @param request - Payer address and pricing amount fields.\n * @returns Balance comparison in mUSD base units (6 decimals).\n */\n async checkMoneyAccountBalance(\n request: MoneyAccountBalanceCheckRequest,\n ): Promise<MoneyAccountBalanceCheckResult> {\n const { price } = await this.#resolveConfiguration(\n request.product,\n request.recurringInterval,\n );\n return this.#compareMoneyAccountBalance(request.payerAddress, price);\n }\n\n async #compareMoneyAccountBalance(\n payerAddress: Hex,\n price: ProductPrice,\n ): Promise<MoneyAccountBalanceCheckResult> {\n assertPositiveInteger(\n price.minBillingCyclesForBalance,\n SubscriptionDelegationServiceErrorMessage.InvalidMinimumFundingCycles,\n );\n\n const periodAmount = calculatePeriodAmount({\n unitAmount: price.unitAmount,\n unitDecimals: price.unitDecimals,\n tokenDecimals: MUSD_DECIMALS,\n });\n const requiredBalance =\n periodAmount * BigInt(price.minBillingCyclesForBalance);\n\n const { totalBalance } = await this.#messenger.call(\n 'MoneyAccountBalanceService:fetchBalanceWithFallback',\n payerAddress,\n );\n\n return {\n hasSufficientBalance: BigInt(totalBalance) >= requiredBalance,\n balance: totalBalance,\n requiredBalance: requiredBalance.toString(),\n };\n }\n\n /**\n * Runs the complete Money Account subscription checkout authorization flow.\n *\n * Subscriptions are refreshed first. An active subscription for the product\n * is rejected before Money Account upgrade, delegation signing, persistence,\n * or CHOMP registration.\n *\n * The caller must obtain user consent and initiate funding before calling.\n * `MoneyAccountUpgradeController` ensures the Money Account vault\n * delegations and CHOMP intents exist. The Subscription API validates those\n * delegations and the Money Account balance server-side. No payment\n * delegation signing, persistence, or intent mutation occurs before the\n * account is upgraded.\n *\n * @param request - Product selection and Money Account identity.\n * @returns The result from `SubscriptionController:startSubscriptionWithCrypto`.\n */\n async startSubscriptionWithDelegation(\n request: StartSubscriptionWithDelegationRequest,\n ): Promise<StartSubscriptionWithDelegationResult> {\n if (request.product !== PRODUCT_TYPES.MONEY_ACCOUNT_PLUS) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.UnsupportedProduct,\n );\n }\n\n const config = await this.#resolveConfiguration(\n request.product,\n request.recurringInterval,\n );\n if (!equalsIgnoreCase(request.chainId, config.chainId)) {\n throw new Error(SubscriptionDelegationServiceErrorMessage.ChainMismatch);\n }\n const subscriptions = await this.#messenger.call(\n 'SubscriptionController:getSubscriptions',\n );\n this.#assertUserNotSubscribed(subscriptions, request.product);\n const isEligibleForTrial = await this.#messenger.call(\n 'SubscriptionController:isUserEligibleForTrial',\n request.product,\n );\n const isTrialRequested =\n config.price.trialPeriodDays > 0 && isEligibleForTrial;\n\n await this.#messenger.call(\n 'MoneyAccountUpgradeController:forceUpgradeAccount',\n request.payerAddress,\n );\n\n const paymentPermission = await this.#preparePaymentPermission(\n request,\n config,\n isTrialRequested,\n );\n const paymentDelegation = await this.#signPaymentPermissionIfRequired(\n paymentPermission,\n config,\n );\n const paymentDelegationHash = await this.#commitPaymentPermission({\n request,\n paymentPermission,\n paymentDelegation,\n config,\n });\n return await this.#messenger.call(\n 'SubscriptionController:startSubscriptionWithCrypto',\n {\n products: [request.product],\n isTrialRequested,\n recurringInterval: request.recurringInterval,\n billingCycles: config.price.minBillingCycles,\n chainId: request.chainId,\n payerAddress: request.payerAddress,\n tokenSymbol: config.token.symbol,\n cryptoAuthMethod: CRYPTO_AUTH_METHODS.DELEGATION,\n delegationHash: paymentDelegationHash,\n // Reject if eligibility changed during checkout, because the signed\n // delegation start date was derived from the original trial state.\n // This local guard is removed before the backend request.\n assertTrialEligibility: true,\n },\n );\n }\n\n /**\n * Rejects when the refreshed list already contains an active subscription\n * for the product. Active includes `active`, `trialing`, and `provisional`.\n *\n * @param subscriptions - Subscriptions returned by the latest refresh.\n * @param product - Product the caller is trying to start.\n */\n #assertUserNotSubscribed(\n subscriptions: Subscription[],\n product: ProductType,\n ): void {\n const alreadySubscribed = subscriptions.some(\n (subscription) =>\n ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status) &&\n subscription.products.some((entry) => entry.name === product),\n );\n if (alreadySubscribed) {\n throw new Error(SubscriptionControllerErrorMessage.UserAlreadySubscribed);\n }\n }\n\n async #preparePaymentPermission(\n request: StartSubscriptionWithDelegationRequest,\n config: ResolvedSubscriptionDelegationConfig,\n isTrialRequested: boolean,\n ): Promise<PreparedSubscriptionPermission> {\n const periodAmount = calculatePeriodAmount({\n unitAmount: config.price.unitAmount,\n unitDecimals: config.price.unitDecimals,\n tokenDecimals: config.token.decimals,\n });\n const periodDuration = getPeriodDuration(request.recurringInterval);\n const nowSeconds = Math.floor(Date.now() / 1000);\n const startDate = getDelegationStartDate({\n nowSeconds,\n trialPeriodDays: isTrialRequested\n ? config.price.trialPeriodDays\n : undefined,\n });\n const matches = makeMatchesSubscriptionDelegation({\n delegatorAddress: request.payerAddress,\n delegateAddress: config.delegateAddress,\n chainId: request.chainId,\n recipientAddress: config.paymentAddress,\n tokenAddress: config.token.address,\n periodAmount,\n periodDuration,\n nowSeconds,\n isTrialDeferred: startDate > nowSeconds,\n enforcers: config.enforcers,\n });\n const reusable = pickLatestMatchingSubscriptionDelegation(\n (\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:listDelegations',\n )\n ).filter(matches),\n config.enforcers,\n );\n const delegation = reusable\n ? removeDelegationSignature(reusable.signedDelegation)\n : buildUnsignedSubscriptionDelegation({\n delegateAddress: config.delegateAddress,\n delegatorAddress: request.payerAddress,\n enforcers: config.enforcers,\n recipientAddress: config.paymentAddress,\n tokenAddress: config.token.address,\n periodAmount,\n periodDuration,\n startDate,\n });\n const typedData = buildDelegationTypedData({\n delegation,\n chainId: request.chainId,\n delegationManager: config.delegationManager,\n });\n\n return {\n id: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n owner: 'subscription',\n disposition: reusable ? 'reused' : 'new',\n delegation,\n typedData,\n decodedAuthority: decodeSubscriptionAuthority(\n delegation,\n config.enforcers,\n ),\n ...(reusable\n ? { existingDelegationHash: reusable.metadata.delegationHash }\n : {}),\n };\n }\n\n async #signPaymentPermissionIfRequired(\n permission: PreparedSubscriptionPermission,\n config: ResolvedSubscriptionDelegationConfig,\n ): Promise<SignedSubscriptionDelegation | undefined> {\n if (permission.disposition === 'reused') {\n return undefined;\n }\n const signature = (await this.#messenger.call(\n 'DelegationController:signDelegation',\n {\n delegation: permission.delegation,\n chainId: config.chainId,\n },\n )) as Hex;\n return { ...permission.delegation, signature };\n }\n\n async #commitPaymentPermission({\n request,\n paymentPermission,\n paymentDelegation,\n config,\n }: {\n request: StartSubscriptionWithDelegationRequest;\n paymentPermission: PreparedSubscriptionPermission;\n paymentDelegation?: SignedSubscriptionDelegation;\n config: ResolvedSubscriptionDelegationConfig;\n }): Promise<Hex> {\n if (paymentPermission.disposition === 'reused') {\n const delegationHash = paymentPermission.existingDelegationHash as Hex;\n const storedDelegation = (\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:listDelegations',\n )\n ).find(({ metadata }) =>\n equalsIgnoreCase(metadata.delegationHash, delegationHash),\n );\n if (!storedDelegation) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ReusableDelegationInvalid,\n );\n }\n await this.#verifySignedPaymentDelegation(\n storedDelegation.signedDelegation,\n delegationHash,\n request.chainId,\n );\n await this.#ensureIntent({\n account: request.payerAddress,\n chainId: request.chainId,\n delegationHash,\n allowance: add0x(\n BigInt(paymentPermission.decodedAuthority.periodAmount).toString(16),\n ),\n tokenSymbol: config.token.symbol,\n tokenAddress: config.token.address,\n });\n await this.#assertIntentActive(request.payerAddress, delegationHash);\n return delegationHash;\n }\n\n const signedDelegation = paymentDelegation as SignedSubscriptionDelegation;\n const delegationHash = hashDelegation({\n ...signedDelegation,\n salt: BigInt(signedDelegation.salt),\n });\n await this.#verifySignedPaymentDelegation(\n signedDelegation,\n delegationHash,\n request.chainId,\n );\n\n const allowance = add0x(\n BigInt(paymentPermission.decodedAuthority.periodAmount).toString(16),\n );\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:createDelegation',\n {\n signedDelegation,\n metadata: {\n delegationHash,\n chainIdHex: request.chainId,\n allowance,\n tokenSymbol: config.token.symbol,\n tokenAddress: config.token.address,\n type: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n },\n },\n );\n await this.#createIntent({\n account: request.payerAddress,\n chainId: request.chainId,\n delegationHash,\n allowance,\n tokenSymbol: config.token.symbol,\n tokenAddress: config.token.address,\n });\n await this.#assertIntentActive(request.payerAddress, delegationHash);\n return delegationHash;\n }\n\n async #assertIntentActive(account: Hex, delegationHash: Hex): Promise<void> {\n const intents = await this.#messenger.call(\n 'ChompApiService:getIntentsByAddress',\n account,\n );\n const intent = intents.find((candidate) =>\n equalsIgnoreCase(candidate.delegationHash, delegationHash),\n );\n if (intent?.status !== 'active') {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompIntentNotActive,\n );\n }\n }\n\n async #verifySignedPaymentDelegation(\n signedDelegation: SignedSubscriptionDelegation,\n expectedDelegationHash: Hex,\n chainId: Hex,\n ): Promise<void> {\n const localHash = hashDelegation({\n ...signedDelegation,\n salt: BigInt(signedDelegation.salt),\n });\n if (!equalsIgnoreCase(localHash, expectedDelegationHash)) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ReusableDelegationInvalid,\n );\n }\n\n const verifyResult = await this.#messenger.call(\n 'ChompApiService:verifyDelegation',\n { signedDelegation, chainId },\n );\n if (!verifyResult.valid) {\n throw new Error(\n `${SubscriptionDelegationServiceErrorMessage.ChompRejectedDelegation}: ${\n verifyResult.errors?.join(', ') ?? 'unknown error'\n }`,\n );\n }\n if (!verifyResult.delegationHash) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompMissingDelegationHash,\n );\n }\n if (\n !equalsIgnoreCase(verifyResult.delegationHash, expectedDelegationHash)\n ) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompDelegationHashMismatch,\n );\n }\n }\n\n /**\n * Prepares a cash-subscription delegation and returns its hash.\n *\n * Reuses a stored AUS delegation that matches the semantic fingerprint when\n * one exists (ensuring a CHOMP intent is active for its hash, unless\n * `skipChompInteractions` is true). Reuse classifies period `startDate` as\n * trial-deferred (`> now`) vs immediately redeemable, matching creation.\n * When several records match, the latest period `startDate` is reused\n * so a `forceNew` replacement is preferred over an older equivalent\n * permission.\n * If there is no match, builds, signs, optionally verifies with CHOMP,\n * persists, and optionally registers a new delegation.\n *\n * When `skipChompInteractions` is true, CHOMP verify and intent calls are\n * skipped; the returned hash is computed locally.\n *\n * @param request - Authoritative pricing and payer details for the delegation.\n * @param forceNew - Whether to create a replacement instead of reusing a\n * matching stored delegation.\n * @returns The delegation hash (CHOMP-verified unless skipped) and whether it\n * was created or reused.\n */\n async prepareDelegation(\n request: PrepareSubscriptionDelegationRequest,\n forceNew = false,\n ): Promise<PreparedSubscriptionDelegation> {\n if (request.product !== PRODUCT_TYPES.MONEY_ACCOUNT_PLUS) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.UnsupportedProduct,\n );\n }\n\n const skipChomp = Boolean(request.skipChompInteractions);\n\n const {\n chainId,\n delegateAddress,\n paymentAddress,\n enforcers,\n price,\n token,\n } = await this.#resolveConfiguration(\n request.product,\n request.recurringInterval,\n );\n\n if (request.checkBalance) {\n const { hasSufficientBalance } = await this.#compareMoneyAccountBalance(\n request.payerAddress,\n price,\n );\n if (!hasSufficientBalance) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.InsufficientBalance,\n );\n }\n }\n\n const periodAmount = calculatePeriodAmount({\n unitAmount: price.unitAmount,\n unitDecimals: price.unitDecimals,\n tokenDecimals: token.decimals,\n });\n const periodDuration = getPeriodDuration(request.recurringInterval);\n const nowSeconds = Math.floor(Date.now() / 1000);\n const startDate = getDelegationStartDate({\n nowSeconds,\n trialPeriodDays: request.isTrialRequested\n ? price.trialPeriodDays\n : undefined,\n });\n const isTrialDeferred = startDate > nowSeconds;\n\n const reusable = forceNew\n ? undefined\n : await this.#findReusableDelegation({\n request,\n chainId,\n delegateAddress,\n paymentAddress,\n tokenAddress: token.address,\n periodAmount,\n periodDuration,\n nowSeconds,\n isTrialDeferred,\n enforcers,\n });\n if (reusable) {\n if (!skipChomp) {\n await this.#ensureIntent({\n account: request.payerAddress,\n chainId,\n delegationHash: reusable.metadata.delegationHash,\n allowance: reusable.metadata.allowance,\n tokenSymbol: reusable.metadata.tokenSymbol,\n tokenAddress: reusable.metadata.tokenAddress,\n });\n }\n return {\n delegationHash: reusable.metadata.delegationHash,\n disposition: 'reused',\n };\n }\n\n const unsigned = buildUnsignedSubscriptionDelegation({\n delegateAddress,\n delegatorAddress: request.payerAddress,\n recipientAddress: paymentAddress,\n enforcers,\n tokenAddress: token.address,\n periodAmount,\n periodDuration,\n startDate,\n });\n\n const signature = (await this.#messenger.call(\n 'DelegationController:signDelegation',\n { delegation: unsigned, chainId },\n )) as Hex;\n\n const signedDelegation = { ...unsigned, signature };\n\n const delegationHash = hashDelegation({\n ...unsigned,\n salt: BigInt(unsigned.salt),\n signature,\n });\n\n if (!skipChomp) {\n const verifyResult = await this.#messenger.call(\n 'ChompApiService:verifyDelegation',\n {\n signedDelegation,\n chainId,\n },\n );\n\n if (!verifyResult.valid) {\n throw new Error(\n `${SubscriptionDelegationServiceErrorMessage.ChompRejectedDelegation}: ${\n verifyResult.errors?.join(', ') ?? 'unknown error'\n }`,\n );\n }\n\n if (!verifyResult.delegationHash) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompMissingDelegationHash,\n );\n }\n if (!equalsIgnoreCase(verifyResult.delegationHash, delegationHash)) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompDelegationHashMismatch,\n );\n }\n }\n\n const allowance: Hex = add0x(periodAmount.toString(16));\n\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:createDelegation',\n {\n signedDelegation,\n metadata: {\n delegationHash,\n chainIdHex: chainId,\n allowance,\n tokenSymbol: token.symbol,\n tokenAddress: token.address,\n type: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n },\n },\n );\n\n if (!skipChomp) {\n await this.#createIntent({\n account: request.payerAddress,\n chainId,\n delegationHash,\n allowance,\n tokenSymbol: token.symbol,\n tokenAddress: token.address,\n });\n }\n\n return {\n delegationHash,\n disposition: 'created',\n };\n }\n\n async #findReusableDelegation({\n request,\n chainId,\n delegateAddress,\n paymentAddress,\n tokenAddress,\n periodAmount,\n periodDuration,\n nowSeconds,\n isTrialDeferred,\n enforcers,\n }: {\n request: PrepareSubscriptionDelegationRequest;\n chainId: Hex;\n delegateAddress: Hex;\n paymentAddress: Hex;\n tokenAddress: Hex;\n periodAmount: bigint;\n periodDuration: number;\n nowSeconds: number;\n isTrialDeferred: boolean;\n enforcers: SubscriptionDelegationEnforcers;\n }): Promise<DelegationResponse | undefined> {\n const matches = makeMatchesSubscriptionDelegation({\n delegatorAddress: request.payerAddress,\n delegateAddress,\n recipientAddress: paymentAddress,\n chainId,\n tokenAddress,\n periodAmount,\n periodDuration,\n nowSeconds,\n isTrialDeferred,\n enforcers,\n });\n\n const existingDelegations = await this.#messenger.call(\n 'AuthenticatedUserStorageService:listDelegations',\n );\n\n return pickLatestMatchingSubscriptionDelegation(\n existingDelegations.filter(matches),\n enforcers,\n );\n }\n\n async #resolveConfiguration(\n product: ProductType,\n recurringInterval: RecurringInterval,\n ): Promise<ResolvedSubscriptionDelegationConfig> {\n const { remoteFeatureFlags } = this.#messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n const vaultConfig = getMoneyAccountVaultConfig(remoteFeatureFlags);\n if (!vaultConfig) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.MissingMoneyAccountVaultConfig,\n );\n }\n\n const { chainId } = vaultConfig;\n const enforcers = resolveEnforcers(chainId);\n const pricing = await this.#messenger.call(\n 'SubscriptionController:getPricing',\n );\n const price = pricing.products\n .find((entry) => entry.name === product)\n ?.prices?.find((entry) => entry.interval === recurringInterval);\n const paymentMethod = pricing.paymentMethods.find(\n (entry): entry is PricingCryptoPaymentMethod =>\n entry.type === PAYMENT_TYPES.byCrypto &&\n entry.cryptoAuthMethod === CRYPTO_AUTH_METHODS.DELEGATION &&\n entry.products?.includes(product) === true,\n );\n const chain = paymentMethod?.chains?.find(\n (entry) => entry.chainId === chainId,\n );\n const token = chain?.tokens[0];\n if (!price || !chain?.delegateAddress || !token) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.PricingConfigurationNotFound,\n );\n }\n\n return {\n chainId,\n delegateAddress: chain.delegateAddress,\n delegationManager:\n DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION][hexToNumber(chainId)]\n .DelegationManager,\n paymentAddress: chain.paymentAddress,\n enforcers,\n price,\n token,\n };\n }\n\n /**\n * Ensures an active CHOMP intent exists for the given delegation hash,\n * registering one when missing or revoked.\n *\n * @param params - Intent identity and metadata.\n * @param params.account - Delegator / payer address.\n * @param params.chainId - Chain ID of the delegation.\n * @param params.delegationHash - Hash of the stored delegation.\n * @param params.allowance - Period allowance stored with the delegation.\n * @param params.tokenSymbol - Payment token symbol.\n * @param params.tokenAddress - Payment token address.\n */\n async #ensureIntent(params: SubscriptionIntentParams): Promise<void> {\n const existingIntents = await this.#messenger.call(\n 'ChompApiService:getIntentsByAddress',\n params.account,\n );\n\n const hasActiveIntent = existingIntents.some(\n (intent) =>\n equalsIgnoreCase(intent.delegationHash, params.delegationHash) &&\n intent.status === 'active',\n );\n\n if (hasActiveIntent) {\n return;\n }\n\n await this.#createIntent(params);\n }\n\n async #createIntent(params: SubscriptionIntentParams): Promise<void> {\n await this.#messenger.call('ChompApiService:createIntents', [\n {\n account: params.account,\n delegationHash: params.delegationHash,\n chainId: params.chainId,\n metadata: {\n allowance: params.allowance,\n tokenSymbol: params.tokenSymbol,\n tokenAddress: params.tokenAddress,\n type: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n },\n },\n ]);\n }\n}\n"]}
1
+ {"version":3,"file":"SubscriptionDelegationService.js","sourceRoot":"","sources":["../../src/subscription-delegation/SubscriptionDelegationService.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n DelegationResponse,\n AuthenticatedUserStorageServiceCreateDelegationAction,\n AuthenticatedUserStorageServiceListDelegationsAction,\n} from '@metamask/authenticated-user-storage';\nimport type {\n ChompApiServiceCreateIntentsAction,\n ChompApiServiceGetIntentsByAddressAction,\n ChompApiServiceVerifyDelegationAction,\n} from '@metamask/chomp-api-service';\nimport type { DelegationControllerSignDelegationAction } from '@metamask/delegation-controller';\nimport { hashDelegation } from '@metamask/delegation-core';\nimport { DELEGATOR_CONTRACTS } from '@metamask/delegation-deployments';\nimport type { Messenger } from '@metamask/messenger';\nimport type { MoneyAccountBalanceServiceFetchBalanceWithFallbackAction } from '@metamask/money-account-balance-service';\nimport type { MoneyAccountUpgradeControllerForceUpgradeAccountAction } from '@metamask/money-account-upgrade-controller';\nimport {\n getMoneyAccountVaultConfig,\n MUSD_DECIMALS,\n} from '@metamask/money-account-utils';\nimport type { RemoteFeatureFlagControllerGetStateAction } from '@metamask/remote-feature-flag-controller';\nimport { add0x, hexToNumber } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n ACTIVE_SUBSCRIPTION_STATUSES,\n SubscriptionControllerErrorMessage,\n SubscriptionDelegationServiceErrorMessage,\n} from '../constants.js';\nimport type {\n SubscriptionControllerGetPricingAction,\n SubscriptionControllerGetSubscriptionsAction,\n SubscriptionControllerIsUserEligibleForTrialAction,\n SubscriptionControllerStartSubscriptionWithCryptoAction,\n} from '../SubscriptionController-method-action-types.js';\nimport type { SubscriptionControllerGetStateAction } from '../SubscriptionController.js';\nimport { CRYPTO_AUTH_METHODS, PAYMENT_TYPES, PRODUCT_TYPES } from '../types.js';\nimport type {\n ProductPrice,\n ProductType,\n PricingCryptoPaymentMethod,\n RecurringInterval,\n Subscription,\n TokenPaymentInfo,\n} from '../types.js';\nimport {\n assertPositiveInteger,\n calculatePeriodAmount,\n getDelegationStartDate,\n getPeriodDuration,\n} from './amount.js';\nimport { buildUnsignedSubscriptionDelegation } from './caveats.js';\nimport {\n equalsIgnoreCase,\n makeMatchesSubscriptionDelegation,\n pickLatestMatchingSubscriptionDelegation,\n} from './fingerprint.js';\nimport type { SubscriptionDelegationServiceMethodActions } from './SubscriptionDelegationService-method-action-types.js';\nimport {\n buildDelegationTypedData,\n decodeSubscriptionAuthority,\n} from './typed-data.js';\nimport type {\n MoneyAccountBalanceCheckRequest,\n MoneyAccountBalanceCheckResult,\n PrepareSubscriptionDelegationRequest,\n PreparedSubscriptionPermission,\n PreparedSubscriptionDelegation,\n SignedSubscriptionDelegation,\n StartSubscriptionWithDelegationRequest,\n StartSubscriptionWithDelegationResult,\n SubscriptionDelegationEnforcers,\n UnsignedSubscriptionDelegation,\n} from './types.js';\nimport { CASH_SUBSCRIPTION_DELEGATION_TYPE } from './types.js';\n\n/**\n * The name of the {@link SubscriptionDelegationService}, used to namespace the\n * service's actions and events.\n */\nexport const serviceName = 'SubscriptionDelegationService';\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'prepareDelegation',\n 'checkMoneyAccountBalance',\n 'startSubscriptionWithDelegation',\n] as const;\n\nconst DELEGATION_FRAMEWORK_VERSION = '1.3.0';\n\nfunction resolveEnforcers(chainId: Hex): SubscriptionDelegationEnforcers {\n const contracts =\n DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION]?.[hexToNumber(chainId)];\n\n if (\n !contracts?.ERC20PeriodTransferEnforcer ||\n !contracts.AllowedCalldataEnforcer\n ) {\n throw new Error(\n `${SubscriptionDelegationServiceErrorMessage.DelegationContractsNotFound}: ${chainId}`,\n );\n }\n\n return {\n erc20TokenPeriodTransfer: contracts.ERC20PeriodTransferEnforcer,\n allowedCalldata: contracts.AllowedCalldataEnforcer,\n };\n}\n\nfunction removeDelegationSignature(\n delegation: SignedSubscriptionDelegation,\n): UnsignedSubscriptionDelegation {\n const { signature: _signature, ...unsignedDelegation } = delegation;\n return unsignedDelegation;\n}\n\n/**\n * Actions that {@link SubscriptionDelegationService} exposes to other consumers.\n */\nexport type SubscriptionDelegationServiceActions =\n SubscriptionDelegationServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link SubscriptionDelegationServiceMessenger} calls.\n */\ntype AllowedActions =\n | AuthenticatedUserStorageServiceListDelegationsAction\n | AuthenticatedUserStorageServiceCreateDelegationAction\n | ChompApiServiceVerifyDelegationAction\n | ChompApiServiceCreateIntentsAction\n | ChompApiServiceGetIntentsByAddressAction\n | DelegationControllerSignDelegationAction\n | MoneyAccountUpgradeControllerForceUpgradeAccountAction\n | MoneyAccountBalanceServiceFetchBalanceWithFallbackAction\n | RemoteFeatureFlagControllerGetStateAction\n | SubscriptionControllerGetStateAction\n | SubscriptionControllerGetPricingAction\n | SubscriptionControllerGetSubscriptionsAction\n | SubscriptionControllerIsUserEligibleForTrialAction\n | SubscriptionControllerStartSubscriptionWithCryptoAction;\n\n/**\n * Events that {@link SubscriptionDelegationService} exposes to other consumers.\n */\nexport type SubscriptionDelegationServiceEvents = never;\n\ntype AllowedEvents = never;\n\n/**\n * The messenger which is restricted to actions and events accessed by\n * {@link SubscriptionDelegationService}.\n */\nexport type SubscriptionDelegationServiceMessenger = Messenger<\n typeof serviceName,\n SubscriptionDelegationServiceActions | AllowedActions,\n SubscriptionDelegationServiceEvents | AllowedEvents\n>;\n\n/**\n * Options for constructing {@link SubscriptionDelegationService}.\n */\nexport type SubscriptionDelegationServiceOptions = {\n messenger: SubscriptionDelegationServiceMessenger;\n};\n\ntype SubscriptionIntentParams = {\n account: Hex;\n chainId: Hex;\n delegationHash: Hex;\n allowance: Hex;\n tokenSymbol: string;\n tokenAddress: Hex;\n};\n\ntype ResolvedSubscriptionDelegationConfig = {\n chainId: Hex;\n delegateAddress: Hex;\n delegationManager: Hex;\n paymentAddress: Hex;\n enforcers: SubscriptionDelegationEnforcers;\n price: ProductPrice;\n token: TokenPaymentInfo;\n};\n\n/**\n * Stateless orchestrator for cash-subscription delegation setup.\n *\n * Owns the workflow: size periodic caveats → sign → CHOMP verify → persist to\n * Authenticated User Storage → register CHOMP intent. Returns a verified\n * `delegationHash` for `SubscriptionController.startSubscriptionWithCrypto`.\n *\n * Pass `skipChompInteractions: true` to skip CHOMP verify and intent\n * registration and return a locally computed delegation hash.\n *\n * Each call resolves the Money Account chain from remote feature flags, then\n * resolves its price, payment token, and delegate from `SubscriptionController`\n * pricing. The pricing `delegateAddress` is used as the delegation `delegate`.\n *\n * Does not own subscription state; `SubscriptionController` does not depend on\n * this service. Only Money Account Plus is supported.\n */\nexport class SubscriptionDelegationService {\n readonly name: typeof serviceName = serviceName;\n\n readonly #messenger: SubscriptionDelegationServiceMessenger;\n\n constructor(options: SubscriptionDelegationServiceOptions) {\n this.#messenger = options.messenger;\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Checks whether the Money Account holds enough convertible mUSD value to\n * cover pricing `unitAmount × minBillingCyclesForBalance`.\n *\n * @param request - Payer address and pricing amount fields.\n * @returns Balance comparison in mUSD base units (6 decimals).\n */\n async checkMoneyAccountBalance(\n request: MoneyAccountBalanceCheckRequest,\n ): Promise<MoneyAccountBalanceCheckResult> {\n const { price } = await this.#resolveConfiguration(\n request.product,\n request.recurringInterval,\n );\n return this.#compareMoneyAccountBalance(request.payerAddress, price);\n }\n\n async #compareMoneyAccountBalance(\n payerAddress: Hex,\n price: ProductPrice,\n ): Promise<MoneyAccountBalanceCheckResult> {\n assertPositiveInteger(\n price.minBillingCyclesForBalance,\n SubscriptionDelegationServiceErrorMessage.InvalidMinimumFundingCycles,\n );\n\n const periodAmount = calculatePeriodAmount({\n unitAmount: price.unitAmount,\n unitDecimals: price.unitDecimals,\n tokenDecimals: MUSD_DECIMALS,\n });\n const requiredBalance =\n periodAmount * BigInt(price.minBillingCyclesForBalance);\n\n const { totalBalance } = await this.#messenger.call(\n 'MoneyAccountBalanceService:fetchBalanceWithFallback',\n payerAddress,\n );\n\n return {\n hasSufficientBalance: BigInt(totalBalance) >= requiredBalance,\n balance: totalBalance,\n requiredBalance: requiredBalance.toString(),\n };\n }\n\n /**\n * Runs the complete Money Account subscription checkout authorization flow.\n *\n * Subscriptions are refreshed first. An active subscription for the product\n * is rejected before Money Account upgrade, delegation signing, persistence,\n * or CHOMP registration.\n *\n * The caller must obtain user consent and initiate funding before calling.\n * `MoneyAccountUpgradeController` ensures the Money Account vault\n * delegations and CHOMP intents exist. The Subscription API validates those\n * delegations and the Money Account balance server-side. No payment\n * delegation signing, persistence, or intent mutation occurs before the\n * account is upgraded.\n *\n * @param request - Product selection and Money Account identity.\n * @returns The result from `SubscriptionController:startSubscriptionWithCrypto`.\n */\n async startSubscriptionWithDelegation(\n request: StartSubscriptionWithDelegationRequest,\n ): Promise<StartSubscriptionWithDelegationResult> {\n if (request.product !== PRODUCT_TYPES.MONEY_ACCOUNT_PLUS) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.UnsupportedProduct,\n );\n }\n\n const config = await this.#resolveConfiguration(\n request.product,\n request.recurringInterval,\n );\n if (!equalsIgnoreCase(request.chainId, config.chainId)) {\n throw new Error(SubscriptionDelegationServiceErrorMessage.ChainMismatch);\n }\n const subscriptions = await this.#messenger.call(\n 'SubscriptionController:getSubscriptions',\n );\n this.#assertUserNotSubscribed(subscriptions, request.product);\n const isEligibleForTrial = await this.#messenger.call(\n 'SubscriptionController:isUserEligibleForTrial',\n request.product,\n );\n const isTrialRequested =\n config.price.trialPeriodDays > 0 && isEligibleForTrial;\n\n await this.#messenger.call(\n 'MoneyAccountUpgradeController:forceUpgradeAccount',\n request.payerAddress,\n );\n\n const paymentPermission = await this.#preparePaymentPermission(\n request,\n config,\n isTrialRequested,\n );\n const paymentDelegation = await this.#signPaymentPermissionIfRequired(\n paymentPermission,\n config,\n );\n const paymentDelegationHash = await this.#commitPaymentPermission({\n request,\n paymentPermission,\n paymentDelegation,\n config,\n });\n return await this.#messenger.call(\n 'SubscriptionController:startSubscriptionWithCrypto',\n {\n products: [request.product],\n isTrialRequested,\n recurringInterval: request.recurringInterval,\n billingCycles: config.price.minBillingCycles,\n chainId: request.chainId,\n payerAddress: request.payerAddress,\n tokenSymbol: config.token.symbol,\n cryptoAuthMethod: CRYPTO_AUTH_METHODS.DELEGATION,\n delegationHash: paymentDelegationHash,\n // Reject if eligibility changed during checkout, because the signed\n // delegation start date was derived from the original trial state.\n // This local guard is removed before the backend request.\n assertTrialEligibility: true,\n },\n );\n }\n\n /**\n * Rejects when the refreshed list already contains an active subscription\n * for the product. Active includes `active`, `trialing`, and `provisional`.\n *\n * @param subscriptions - Subscriptions returned by the latest refresh.\n * @param product - Product the caller is trying to start.\n */\n #assertUserNotSubscribed(\n subscriptions: Subscription[],\n product: ProductType,\n ): void {\n const alreadySubscribed = subscriptions.some(\n (subscription) =>\n ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status) &&\n subscription.products.some((entry) => entry.name === product),\n );\n if (alreadySubscribed) {\n throw new Error(SubscriptionControllerErrorMessage.UserAlreadySubscribed);\n }\n }\n\n async #preparePaymentPermission(\n request: StartSubscriptionWithDelegationRequest,\n config: ResolvedSubscriptionDelegationConfig,\n isTrialRequested: boolean,\n ): Promise<PreparedSubscriptionPermission> {\n const periodAmount = calculatePeriodAmount({\n unitAmount: config.price.unitAmount,\n unitDecimals: config.price.unitDecimals,\n tokenDecimals: config.token.decimals,\n });\n const periodDuration = getPeriodDuration(request.recurringInterval);\n const nowSeconds = Math.floor(Date.now() / 1000);\n const startDate = getDelegationStartDate({\n nowSeconds,\n trialPeriodDays: isTrialRequested\n ? config.price.trialPeriodDays\n : undefined,\n });\n const matches = makeMatchesSubscriptionDelegation({\n delegatorAddress: request.payerAddress,\n delegateAddress: config.delegateAddress,\n chainId: request.chainId,\n recipientAddress: config.paymentAddress,\n tokenAddress: config.token.address,\n periodAmount,\n periodDuration,\n nowSeconds,\n expectedStartDate: startDate,\n enforcers: config.enforcers,\n });\n const reusable = pickLatestMatchingSubscriptionDelegation(\n (\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:listDelegations',\n )\n ).filter(matches),\n config.enforcers,\n );\n const delegation = reusable\n ? removeDelegationSignature(reusable.signedDelegation)\n : buildUnsignedSubscriptionDelegation({\n delegateAddress: config.delegateAddress,\n delegatorAddress: request.payerAddress,\n enforcers: config.enforcers,\n recipientAddress: config.paymentAddress,\n tokenAddress: config.token.address,\n periodAmount,\n periodDuration,\n startDate,\n });\n const typedData = buildDelegationTypedData({\n delegation,\n chainId: request.chainId,\n delegationManager: config.delegationManager,\n });\n\n return {\n id: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n owner: 'subscription',\n disposition: reusable ? 'reused' : 'new',\n delegation,\n typedData,\n decodedAuthority: decodeSubscriptionAuthority(\n delegation,\n config.enforcers,\n ),\n ...(reusable\n ? { existingDelegationHash: reusable.metadata.delegationHash }\n : {}),\n };\n }\n\n async #signPaymentPermissionIfRequired(\n permission: PreparedSubscriptionPermission,\n config: ResolvedSubscriptionDelegationConfig,\n ): Promise<SignedSubscriptionDelegation | undefined> {\n if (permission.disposition === 'reused') {\n return undefined;\n }\n const signature = (await this.#messenger.call(\n 'DelegationController:signDelegation',\n {\n delegation: permission.delegation,\n chainId: config.chainId,\n },\n )) as Hex;\n return { ...permission.delegation, signature };\n }\n\n async #commitPaymentPermission({\n request,\n paymentPermission,\n paymentDelegation,\n config,\n }: {\n request: StartSubscriptionWithDelegationRequest;\n paymentPermission: PreparedSubscriptionPermission;\n paymentDelegation?: SignedSubscriptionDelegation;\n config: ResolvedSubscriptionDelegationConfig;\n }): Promise<Hex> {\n if (paymentPermission.disposition === 'reused') {\n const delegationHash = paymentPermission.existingDelegationHash as Hex;\n const storedDelegation = (\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:listDelegations',\n )\n ).find(({ metadata }) =>\n equalsIgnoreCase(metadata.delegationHash, delegationHash),\n );\n if (!storedDelegation) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ReusableDelegationInvalid,\n );\n }\n await this.#verifySignedPaymentDelegation(\n storedDelegation.signedDelegation,\n delegationHash,\n request.chainId,\n );\n await this.#ensureIntent({\n account: request.payerAddress,\n chainId: request.chainId,\n delegationHash,\n allowance: add0x(\n BigInt(paymentPermission.decodedAuthority.periodAmount).toString(16),\n ),\n tokenSymbol: config.token.symbol,\n tokenAddress: config.token.address,\n });\n await this.#assertIntentActive(request.payerAddress, delegationHash);\n return delegationHash;\n }\n\n const signedDelegation = paymentDelegation as SignedSubscriptionDelegation;\n const delegationHash = hashDelegation({\n ...signedDelegation,\n salt: BigInt(signedDelegation.salt),\n });\n await this.#verifySignedPaymentDelegation(\n signedDelegation,\n delegationHash,\n request.chainId,\n );\n\n const allowance = add0x(\n BigInt(paymentPermission.decodedAuthority.periodAmount).toString(16),\n );\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:createDelegation',\n {\n signedDelegation,\n metadata: {\n delegationHash,\n chainIdHex: request.chainId,\n allowance,\n tokenSymbol: config.token.symbol,\n tokenAddress: config.token.address,\n type: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n },\n },\n );\n await this.#createIntent({\n account: request.payerAddress,\n chainId: request.chainId,\n delegationHash,\n allowance,\n tokenSymbol: config.token.symbol,\n tokenAddress: config.token.address,\n });\n await this.#assertIntentActive(request.payerAddress, delegationHash);\n return delegationHash;\n }\n\n async #assertIntentActive(account: Hex, delegationHash: Hex): Promise<void> {\n const intents = await this.#messenger.call(\n 'ChompApiService:getIntentsByAddress',\n account,\n );\n const intent = intents.find((candidate) =>\n equalsIgnoreCase(candidate.delegationHash, delegationHash),\n );\n if (intent?.status !== 'active') {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompIntentNotActive,\n );\n }\n }\n\n async #verifySignedPaymentDelegation(\n signedDelegation: SignedSubscriptionDelegation,\n expectedDelegationHash: Hex,\n chainId: Hex,\n ): Promise<void> {\n const localHash = hashDelegation({\n ...signedDelegation,\n salt: BigInt(signedDelegation.salt),\n });\n if (!equalsIgnoreCase(localHash, expectedDelegationHash)) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ReusableDelegationInvalid,\n );\n }\n\n const verifyResult = await this.#messenger.call(\n 'ChompApiService:verifyDelegation',\n { signedDelegation, chainId },\n );\n if (!verifyResult.valid) {\n throw new Error(\n `${SubscriptionDelegationServiceErrorMessage.ChompRejectedDelegation}: ${\n verifyResult.errors?.join(', ') ?? 'unknown error'\n }`,\n );\n }\n if (!verifyResult.delegationHash) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompMissingDelegationHash,\n );\n }\n if (\n !equalsIgnoreCase(verifyResult.delegationHash, expectedDelegationHash)\n ) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompDelegationHashMismatch,\n );\n }\n }\n\n /**\n * Prepares a cash-subscription delegation and returns its hash.\n *\n * Reuses a stored AUS delegation that matches the semantic fingerprint when\n * one exists (ensuring a CHOMP intent is active for its hash, unless\n * `skipChompInteractions` is true). Reuse classifies period `startDate` as\n * trial-deferred (`> now`) vs immediately redeemable, matching creation.\n * When several records match, the latest period `startDate` is reused\n * so a `forceNew` replacement is preferred over an older equivalent\n * permission.\n * If there is no match, builds, signs, optionally verifies with CHOMP,\n * persists, and optionally registers a new delegation.\n *\n * When `skipChompInteractions` is true, CHOMP verify and intent calls are\n * skipped; the returned hash is computed locally.\n *\n * @param request - Authoritative pricing and payer details for the delegation.\n * @param forceNew - Whether to create a replacement instead of reusing a\n * matching stored delegation.\n * @returns The delegation hash (CHOMP-verified unless skipped) and whether it\n * was created or reused.\n */\n async prepareDelegation(\n request: PrepareSubscriptionDelegationRequest,\n forceNew = false,\n ): Promise<PreparedSubscriptionDelegation> {\n if (request.product !== PRODUCT_TYPES.MONEY_ACCOUNT_PLUS) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.UnsupportedProduct,\n );\n }\n\n const skipChomp = Boolean(request.skipChompInteractions);\n\n const {\n chainId,\n delegateAddress,\n paymentAddress,\n enforcers,\n price,\n token,\n } = await this.#resolveConfiguration(\n request.product,\n request.recurringInterval,\n );\n\n if (request.checkBalance) {\n const { hasSufficientBalance } = await this.#compareMoneyAccountBalance(\n request.payerAddress,\n price,\n );\n if (!hasSufficientBalance) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.InsufficientBalance,\n );\n }\n }\n\n const periodAmount = calculatePeriodAmount({\n unitAmount: price.unitAmount,\n unitDecimals: price.unitDecimals,\n tokenDecimals: token.decimals,\n });\n const periodDuration = getPeriodDuration(request.recurringInterval);\n const nowSeconds = Math.floor(Date.now() / 1000);\n const startDate = getDelegationStartDate({\n nowSeconds,\n trialPeriodDays: request.isTrialRequested\n ? price.trialPeriodDays\n : undefined,\n });\n\n const reusable = forceNew\n ? undefined\n : await this.#findReusableDelegation({\n request,\n chainId,\n delegateAddress,\n paymentAddress,\n tokenAddress: token.address,\n periodAmount,\n periodDuration,\n nowSeconds,\n expectedStartDate: startDate,\n enforcers,\n });\n if (reusable) {\n if (!skipChomp) {\n await this.#ensureIntent({\n account: request.payerAddress,\n chainId,\n delegationHash: reusable.metadata.delegationHash,\n allowance: reusable.metadata.allowance,\n tokenSymbol: reusable.metadata.tokenSymbol,\n tokenAddress: reusable.metadata.tokenAddress,\n });\n }\n return {\n delegationHash: reusable.metadata.delegationHash,\n disposition: 'reused',\n };\n }\n\n const unsigned = buildUnsignedSubscriptionDelegation({\n delegateAddress,\n delegatorAddress: request.payerAddress,\n recipientAddress: paymentAddress,\n enforcers,\n tokenAddress: token.address,\n periodAmount,\n periodDuration,\n startDate,\n });\n\n const signature = (await this.#messenger.call(\n 'DelegationController:signDelegation',\n { delegation: unsigned, chainId },\n )) as Hex;\n\n const signedDelegation = { ...unsigned, signature };\n\n const delegationHash = hashDelegation({\n ...unsigned,\n salt: BigInt(unsigned.salt),\n signature,\n });\n\n if (!skipChomp) {\n const verifyResult = await this.#messenger.call(\n 'ChompApiService:verifyDelegation',\n {\n signedDelegation,\n chainId,\n },\n );\n\n if (!verifyResult.valid) {\n throw new Error(\n `${SubscriptionDelegationServiceErrorMessage.ChompRejectedDelegation}: ${\n verifyResult.errors?.join(', ') ?? 'unknown error'\n }`,\n );\n }\n\n if (!verifyResult.delegationHash) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompMissingDelegationHash,\n );\n }\n if (!equalsIgnoreCase(verifyResult.delegationHash, delegationHash)) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.ChompDelegationHashMismatch,\n );\n }\n }\n\n const allowance: Hex = add0x(periodAmount.toString(16));\n\n await this.#messenger.call(\n 'AuthenticatedUserStorageService:createDelegation',\n {\n signedDelegation,\n metadata: {\n delegationHash,\n chainIdHex: chainId,\n allowance,\n tokenSymbol: token.symbol,\n tokenAddress: token.address,\n type: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n },\n },\n );\n\n if (!skipChomp) {\n await this.#createIntent({\n account: request.payerAddress,\n chainId,\n delegationHash,\n allowance,\n tokenSymbol: token.symbol,\n tokenAddress: token.address,\n });\n }\n\n return {\n delegationHash,\n disposition: 'created',\n };\n }\n\n async #findReusableDelegation({\n request,\n chainId,\n delegateAddress,\n paymentAddress,\n tokenAddress,\n periodAmount,\n periodDuration,\n nowSeconds,\n expectedStartDate,\n enforcers,\n }: {\n request: PrepareSubscriptionDelegationRequest;\n chainId: Hex;\n delegateAddress: Hex;\n paymentAddress: Hex;\n tokenAddress: Hex;\n periodAmount: bigint;\n periodDuration: number;\n nowSeconds: number;\n expectedStartDate: number;\n enforcers: SubscriptionDelegationEnforcers;\n }): Promise<DelegationResponse | undefined> {\n const matches = makeMatchesSubscriptionDelegation({\n delegatorAddress: request.payerAddress,\n delegateAddress,\n recipientAddress: paymentAddress,\n chainId,\n tokenAddress,\n periodAmount,\n periodDuration,\n nowSeconds,\n expectedStartDate,\n enforcers,\n });\n\n const existingDelegations = await this.#messenger.call(\n 'AuthenticatedUserStorageService:listDelegations',\n );\n\n return pickLatestMatchingSubscriptionDelegation(\n existingDelegations.filter(matches),\n enforcers,\n );\n }\n\n async #resolveConfiguration(\n product: ProductType,\n recurringInterval: RecurringInterval,\n ): Promise<ResolvedSubscriptionDelegationConfig> {\n const { remoteFeatureFlags } = this.#messenger.call(\n 'RemoteFeatureFlagController:getState',\n );\n const vaultConfig = getMoneyAccountVaultConfig(remoteFeatureFlags);\n if (!vaultConfig) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.MissingMoneyAccountVaultConfig,\n );\n }\n\n const { chainId } = vaultConfig;\n const enforcers = resolveEnforcers(chainId);\n const pricing = await this.#messenger.call(\n 'SubscriptionController:getPricing',\n );\n const price = pricing.products\n .find((entry) => entry.name === product)\n ?.prices?.find((entry) => entry.interval === recurringInterval);\n const paymentMethod = pricing.paymentMethods.find(\n (entry): entry is PricingCryptoPaymentMethod =>\n entry.type === PAYMENT_TYPES.byCrypto &&\n entry.cryptoAuthMethod === CRYPTO_AUTH_METHODS.DELEGATION &&\n entry.products?.includes(product) === true,\n );\n const chain = paymentMethod?.chains?.find(\n (entry) => entry.chainId === chainId,\n );\n const token = chain?.tokens[0];\n if (!price || !chain?.delegateAddress || !token) {\n throw new Error(\n SubscriptionDelegationServiceErrorMessage.PricingConfigurationNotFound,\n );\n }\n\n return {\n chainId,\n delegateAddress: chain.delegateAddress,\n delegationManager:\n DELEGATOR_CONTRACTS[DELEGATION_FRAMEWORK_VERSION][hexToNumber(chainId)]\n .DelegationManager,\n paymentAddress: chain.paymentAddress,\n enforcers,\n price,\n token,\n };\n }\n\n /**\n * Ensures an active CHOMP intent exists for the given delegation hash,\n * registering one when missing or revoked.\n *\n * @param params - Intent identity and metadata.\n * @param params.account - Delegator / payer address.\n * @param params.chainId - Chain ID of the delegation.\n * @param params.delegationHash - Hash of the stored delegation.\n * @param params.allowance - Period allowance stored with the delegation.\n * @param params.tokenSymbol - Payment token symbol.\n * @param params.tokenAddress - Payment token address.\n */\n async #ensureIntent(params: SubscriptionIntentParams): Promise<void> {\n const existingIntents = await this.#messenger.call(\n 'ChompApiService:getIntentsByAddress',\n params.account,\n );\n\n const hasActiveIntent = existingIntents.some(\n (intent) =>\n equalsIgnoreCase(intent.delegationHash, params.delegationHash) &&\n intent.status === 'active',\n );\n\n if (hasActiveIntent) {\n return;\n }\n\n await this.#createIntent(params);\n }\n\n async #createIntent(params: SubscriptionIntentParams): Promise<void> {\n await this.#messenger.call('ChompApiService:createIntents', [\n {\n account: params.account,\n delegationHash: params.delegationHash,\n chainId: params.chainId,\n metadata: {\n allowance: params.allowance,\n tokenSymbol: params.tokenSymbol,\n tokenAddress: params.tokenAddress,\n type: CASH_SUBSCRIPTION_DELEGATION_TYPE,\n },\n },\n ]);\n }\n}\n"]}
@@ -10,15 +10,16 @@ export type SubscriptionDelegationFingerprint = {
10
10
  periodAmount: bigint;
11
11
  periodDuration: number;
12
12
  /**
13
- * Current unix timestamp in seconds. Used to classify a stored period
14
- * `startDate` as immediately redeemable (`<= now`) vs trial-deferred (`> now`).
13
+ * Current unix timestamp in seconds. Used for the trial floor so a live
14
+ * permission cannot satisfy a checkout whose start date is still in the future.
15
15
  */
16
16
  nowSeconds: number;
17
17
  /**
18
- * When true, only a still-deferred period start (`> nowSeconds`) matches.
19
- * When false, only an immediately redeemable start (`<= nowSeconds`) matches.
18
+ * Unix start date this checkout would sign. A stored permission is reusable
19
+ * only when its period `startDate` is inside the Subscription API window
20
+ * around this value.
20
21
  */
21
- isTrialDeferred: boolean;
22
+ expectedStartDate: number;
22
23
  enforcers: SubscriptionDelegationEnforcers;
23
24
  };
24
25
  /**
@@ -32,9 +33,9 @@ export declare function equalsIgnoreCase(left: string, right: string): boolean;
32
33
  /**
33
34
  * Builds a predicate that matches a stored AUS delegation to the semantic
34
35
  * cash-subscription fingerprint. Salt is ignored so a previously signed
35
- * equivalent permission can be reused. Period `startDate` is compared only
36
- * as trial-deferred (`> nowSeconds`) vs immediately redeemable (`<= nowSeconds`)
37
- * so a positive-length trial cannot reuse a live permission and vice versa.
36
+ * equivalent permission can be reused. Period `startDate` must fall inside the
37
+ * Subscription API window around `expectedStartDate` so stored permissions are
38
+ * rejected when they are too old or too far in the future for the checkout.
38
39
  *
39
40
  * @param expected - Semantic fields that must match.
40
41
  * @returns Predicate over {@link DelegationResponse}.
@@ -1 +1 @@
1
- {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAK/E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAM3C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAGlE,MAAM,MAAM,iCAAiC,GAAG;IAC9C,gBAAgB,EAAE,GAAG,CAAC;IACtB,eAAe,EAAE,GAAG,CAAC;IACrB,gBAAgB,EAAE,GAAG,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC;IACb,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,+BAA+B,CAAC;CAC5C,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,iCAAiC,GAC1C,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CA+ExC;AA8BD;;;;;;;;;GASG;AACH,wBAAgB,wCAAwC,CACtD,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,SAAS,EAAE,+BAA+B,GACzC,kBAAkB,GAAG,SAAS,CAYhC"}
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAK/E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAM3C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAGlE,MAAM,MAAM,iCAAiC,GAAG;IAC9C,gBAAgB,EAAE,GAAG,CAAC;IACtB,eAAe,EAAE,GAAG,CAAC;IACrB,gBAAgB,EAAE,GAAG,CAAC;IACtB,OAAO,EAAE,GAAG,CAAC;IACb,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,+BAA+B,CAAC;CAC5C,CAAC;AA4BF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,QAAQ,EAAE,iCAAiC,GAC1C,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAoFxC;AA8BD;;;;;;;;;GASG;AACH,wBAAgB,wCAAwC,CACtD,OAAO,EAAE,SAAS,kBAAkB,EAAE,EACtC,SAAS,EAAE,+BAA+B,GACzC,kBAAkB,GAAG,SAAS,CAYhC"}
@@ -1,6 +1,20 @@
1
1
  import { decodeAllowedCalldataTerms, decodeERC20TokenPeriodTransferTerms, } from '@metamask/delegation-core';
2
2
  import { encodeTransferToCalldataPrefix, TRANSFER_CALLDATA_PREFIX_START_INDEX, } from './caveats.js';
3
3
  import { CASH_SUBSCRIPTION_DELEGATION_TYPE } from './types.js';
4
+ /** Must match va-mmcx-subscription-api START_DATE_PAST_SKEW_SECONDS. */
5
+ const START_DATE_PAST_SKEW_SECONDS = 14 * 24 * 60 * 60;
6
+ /** Must match va-mmcx-subscription-api START_DATE_FUTURE_SKEW_SECONDS. */
7
+ const START_DATE_FUTURE_SKEW_SECONDS = 5 * 60;
8
+ function isStartDateInsideApiWindow(storedStartDate, expectedStartDate, nowSeconds) {
9
+ if (storedStartDate <= 0) {
10
+ return false;
11
+ }
12
+ const retryMinimumStartDate = expectedStartDate - START_DATE_PAST_SKEW_SECONDS;
13
+ const trialLiveFloor = expectedStartDate > nowSeconds ? nowSeconds + 1 : 0;
14
+ const minimumStartDate = Math.max(retryMinimumStartDate, trialLiveFloor);
15
+ const maximumStartDate = expectedStartDate + START_DATE_FUTURE_SKEW_SECONDS;
16
+ return (storedStartDate >= minimumStartDate && storedStartDate <= maximumStartDate);
17
+ }
4
18
  /**
5
19
  * Case-insensitive hex equality.
6
20
  *
@@ -14,9 +28,9 @@ export function equalsIgnoreCase(left, right) {
14
28
  /**
15
29
  * Builds a predicate that matches a stored AUS delegation to the semantic
16
30
  * cash-subscription fingerprint. Salt is ignored so a previously signed
17
- * equivalent permission can be reused. Period `startDate` is compared only
18
- * as trial-deferred (`> nowSeconds`) vs immediately redeemable (`<= nowSeconds`)
19
- * so a positive-length trial cannot reuse a live permission and vice versa.
31
+ * equivalent permission can be reused. Period `startDate` must fall inside the
32
+ * Subscription API window around `expectedStartDate` so stored permissions are
33
+ * rejected when they are too old or too far in the future for the checkout.
20
34
  *
21
35
  * @param expected - Semantic fields that must match.
22
36
  * @returns Predicate over {@link DelegationResponse}.
@@ -57,8 +71,7 @@ export function makeMatchesSubscriptionDelegation(expected) {
57
71
  }
58
72
  const periodTerms = decodeERC20TokenPeriodTransferTerms(periodCaveat.terms);
59
73
  const storedStartDate = Number(periodTerms.startDate);
60
- const isStoredDeferred = storedStartDate > expected.nowSeconds;
61
- if (expected.isTrialDeferred !== isStoredDeferred) {
74
+ if (!isStartDateInsideApiWindow(storedStartDate, expected.expectedStartDate, expected.nowSeconds)) {
62
75
  return false;
63
76
  }
64
77
  return (equalsIgnoreCase(periodTerms.tokenAddress, expected.tokenAddress) &&
@@ -1 +1 @@
1
- {"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/subscription-delegation/fingerprint.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DelegationResponse } from '@metamask/authenticated-user-storage';\nimport {\n decodeAllowedCalldataTerms,\n decodeERC20TokenPeriodTransferTerms,\n} from '@metamask/delegation-core';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n encodeTransferToCalldataPrefix,\n TRANSFER_CALLDATA_PREFIX_START_INDEX,\n} from './caveats.js';\nimport type { SubscriptionDelegationEnforcers } from './types.js';\nimport { CASH_SUBSCRIPTION_DELEGATION_TYPE } from './types.js';\n\nexport type SubscriptionDelegationFingerprint = {\n delegatorAddress: Hex;\n delegateAddress: Hex;\n recipientAddress: Hex;\n chainId: Hex;\n tokenAddress: Hex;\n periodAmount: bigint;\n periodDuration: number;\n /**\n * Current unix timestamp in seconds. Used to classify a stored period\n * `startDate` as immediately redeemable (`<= now`) vs trial-deferred (`> now`).\n */\n nowSeconds: number;\n /**\n * When true, only a still-deferred period start (`> nowSeconds`) matches.\n * When false, only an immediately redeemable start (`<= nowSeconds`) matches.\n */\n isTrialDeferred: boolean;\n enforcers: SubscriptionDelegationEnforcers;\n};\n\n/**\n * Case-insensitive hex equality.\n *\n * @param left - First hex value.\n * @param right - Second hex value.\n * @returns Whether the values are equal ignoring case.\n */\nexport function equalsIgnoreCase(left: string, right: string): boolean {\n return left.toLowerCase() === right.toLowerCase();\n}\n\n/**\n * Builds a predicate that matches a stored AUS delegation to the semantic\n * cash-subscription fingerprint. Salt is ignored so a previously signed\n * equivalent permission can be reused. Period `startDate` is compared only\n * as trial-deferred (`> nowSeconds`) vs immediately redeemable (`<= nowSeconds`)\n * so a positive-length trial cannot reuse a live permission and vice versa.\n *\n * @param expected - Semantic fields that must match.\n * @returns Predicate over {@link DelegationResponse}.\n */\nexport function makeMatchesSubscriptionDelegation(\n expected: SubscriptionDelegationFingerprint,\n): (entry: DelegationResponse) => boolean {\n return (entry) => {\n if (entry.metadata.type !== CASH_SUBSCRIPTION_DELEGATION_TYPE) {\n return false;\n }\n if (\n !equalsIgnoreCase(\n entry.signedDelegation.delegator,\n expected.delegatorAddress,\n )\n ) {\n return false;\n }\n if (\n !equalsIgnoreCase(\n entry.signedDelegation.delegate,\n expected.delegateAddress,\n )\n ) {\n return false;\n }\n if (!equalsIgnoreCase(entry.metadata.chainIdHex, expected.chainId)) {\n return false;\n }\n if (!equalsIgnoreCase(entry.metadata.tokenAddress, expected.tokenAddress)) {\n return false;\n }\n\n // CHOMP requires exactly these two enforcers, so stored delegations with\n // extra caveats (e.g. a legacy `ValueLte`) cannot be reused.\n const { caveats } = entry.signedDelegation;\n if (caveats.length !== 2) {\n return false;\n }\n\n const periodCaveat = caveats.find((caveat) =>\n equalsIgnoreCase(\n caveat.enforcer,\n expected.enforcers.erc20TokenPeriodTransfer,\n ),\n );\n const allowedCalldataCaveat = caveats.find((caveat) =>\n equalsIgnoreCase(caveat.enforcer, expected.enforcers.allowedCalldata),\n );\n if (!periodCaveat || !allowedCalldataCaveat) {\n return false;\n }\n\n try {\n const calldataTerms = decodeAllowedCalldataTerms(\n allowedCalldataCaveat.terms,\n );\n if (\n calldataTerms.startIndex !== TRANSFER_CALLDATA_PREFIX_START_INDEX ||\n !equalsIgnoreCase(\n calldataTerms.value,\n encodeTransferToCalldataPrefix(expected.recipientAddress),\n )\n ) {\n return false;\n }\n\n const periodTerms = decodeERC20TokenPeriodTransferTerms(\n periodCaveat.terms,\n );\n const storedStartDate = Number(periodTerms.startDate);\n const isStoredDeferred = storedStartDate > expected.nowSeconds;\n if (expected.isTrialDeferred !== isStoredDeferred) {\n return false;\n }\n return (\n equalsIgnoreCase(periodTerms.tokenAddress, expected.tokenAddress) &&\n periodTerms.periodAmount === expected.periodAmount &&\n Number(periodTerms.periodDuration) === expected.periodDuration\n );\n } catch {\n return false;\n }\n };\n}\n\n/**\n * Reads the ERC-20 period-transfer `startDate` from a stored delegation.\n *\n * @param entry - Stored AUS delegation.\n * @param enforcers - Enforcer addresses used to locate the period caveat.\n * @returns Unix timestamp in seconds, or `undefined` when the caveat is\n * missing or malformed.\n */\nfunction getSubscriptionDelegationStartDate(\n entry: DelegationResponse,\n enforcers: SubscriptionDelegationEnforcers,\n): number | undefined {\n const periodCaveat = entry.signedDelegation.caveats.find((caveat) =>\n equalsIgnoreCase(caveat.enforcer, enforcers.erc20TokenPeriodTransfer),\n );\n if (!periodCaveat) {\n return undefined;\n }\n\n try {\n return Number(\n decodeERC20TokenPeriodTransferTerms(periodCaveat.terms).startDate,\n );\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns the fingerprint match with the latest period `startDate`.\n *\n * Equal `startDate`s keep list order. A delegation whose `startDate` cannot\n * be read sorts last.\n *\n * @param matches - Fingerprint-matching AUS delegations, in list order.\n * @param enforcers - Enforcer addresses used to read `startDate`.\n * @returns The preferred match, or `undefined` when `matches` is empty.\n */\nexport function pickLatestMatchingSubscriptionDelegation(\n matches: readonly DelegationResponse[],\n enforcers: SubscriptionDelegationEnforcers,\n): DelegationResponse | undefined {\n const byStartDateDescending = [...matches].sort((left, right) => {\n const leftStart =\n getSubscriptionDelegationStartDate(left, enforcers) ??\n Number.NEGATIVE_INFINITY;\n const rightStart =\n getSubscriptionDelegationStartDate(right, enforcers) ??\n Number.NEGATIVE_INFINITY;\n return rightStart - leftStart;\n });\n\n return byStartDateDescending[0];\n}\n"]}
1
+ {"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/subscription-delegation/fingerprint.ts"],"names":[],"mappings":"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","sourcesContent":["import type { DelegationResponse } from '@metamask/authenticated-user-storage';\nimport {\n decodeAllowedCalldataTerms,\n decodeERC20TokenPeriodTransferTerms,\n} from '@metamask/delegation-core';\nimport type { Hex } from '@metamask/utils';\n\nimport {\n encodeTransferToCalldataPrefix,\n TRANSFER_CALLDATA_PREFIX_START_INDEX,\n} from './caveats.js';\nimport type { SubscriptionDelegationEnforcers } from './types.js';\nimport { CASH_SUBSCRIPTION_DELEGATION_TYPE } from './types.js';\n\nexport type SubscriptionDelegationFingerprint = {\n delegatorAddress: Hex;\n delegateAddress: Hex;\n recipientAddress: Hex;\n chainId: Hex;\n tokenAddress: Hex;\n periodAmount: bigint;\n periodDuration: number;\n /**\n * Current unix timestamp in seconds. Used for the trial floor so a live\n * permission cannot satisfy a checkout whose start date is still in the future.\n */\n nowSeconds: number;\n /**\n * Unix start date this checkout would sign. A stored permission is reusable\n * only when its period `startDate` is inside the Subscription API window\n * around this value.\n */\n expectedStartDate: number;\n enforcers: SubscriptionDelegationEnforcers;\n};\n\n/** Must match va-mmcx-subscription-api START_DATE_PAST_SKEW_SECONDS. */\nconst START_DATE_PAST_SKEW_SECONDS = 14 * 24 * 60 * 60;\n\n/** Must match va-mmcx-subscription-api START_DATE_FUTURE_SKEW_SECONDS. */\nconst START_DATE_FUTURE_SKEW_SECONDS = 5 * 60;\n\nfunction isStartDateInsideApiWindow(\n storedStartDate: number,\n expectedStartDate: number,\n nowSeconds: number,\n): boolean {\n if (storedStartDate <= 0) {\n return false;\n }\n\n const retryMinimumStartDate =\n expectedStartDate - START_DATE_PAST_SKEW_SECONDS;\n const trialLiveFloor = expectedStartDate > nowSeconds ? nowSeconds + 1 : 0;\n const minimumStartDate = Math.max(retryMinimumStartDate, trialLiveFloor);\n const maximumStartDate = expectedStartDate + START_DATE_FUTURE_SKEW_SECONDS;\n\n return (\n storedStartDate >= minimumStartDate && storedStartDate <= maximumStartDate\n );\n}\n\n/**\n * Case-insensitive hex equality.\n *\n * @param left - First hex value.\n * @param right - Second hex value.\n * @returns Whether the values are equal ignoring case.\n */\nexport function equalsIgnoreCase(left: string, right: string): boolean {\n return left.toLowerCase() === right.toLowerCase();\n}\n\n/**\n * Builds a predicate that matches a stored AUS delegation to the semantic\n * cash-subscription fingerprint. Salt is ignored so a previously signed\n * equivalent permission can be reused. Period `startDate` must fall inside the\n * Subscription API window around `expectedStartDate` so stored permissions are\n * rejected when they are too old or too far in the future for the checkout.\n *\n * @param expected - Semantic fields that must match.\n * @returns Predicate over {@link DelegationResponse}.\n */\nexport function makeMatchesSubscriptionDelegation(\n expected: SubscriptionDelegationFingerprint,\n): (entry: DelegationResponse) => boolean {\n return (entry) => {\n if (entry.metadata.type !== CASH_SUBSCRIPTION_DELEGATION_TYPE) {\n return false;\n }\n if (\n !equalsIgnoreCase(\n entry.signedDelegation.delegator,\n expected.delegatorAddress,\n )\n ) {\n return false;\n }\n if (\n !equalsIgnoreCase(\n entry.signedDelegation.delegate,\n expected.delegateAddress,\n )\n ) {\n return false;\n }\n if (!equalsIgnoreCase(entry.metadata.chainIdHex, expected.chainId)) {\n return false;\n }\n if (!equalsIgnoreCase(entry.metadata.tokenAddress, expected.tokenAddress)) {\n return false;\n }\n\n // CHOMP requires exactly these two enforcers, so stored delegations with\n // extra caveats (e.g. a legacy `ValueLte`) cannot be reused.\n const { caveats } = entry.signedDelegation;\n if (caveats.length !== 2) {\n return false;\n }\n\n const periodCaveat = caveats.find((caveat) =>\n equalsIgnoreCase(\n caveat.enforcer,\n expected.enforcers.erc20TokenPeriodTransfer,\n ),\n );\n const allowedCalldataCaveat = caveats.find((caveat) =>\n equalsIgnoreCase(caveat.enforcer, expected.enforcers.allowedCalldata),\n );\n if (!periodCaveat || !allowedCalldataCaveat) {\n return false;\n }\n\n try {\n const calldataTerms = decodeAllowedCalldataTerms(\n allowedCalldataCaveat.terms,\n );\n if (\n calldataTerms.startIndex !== TRANSFER_CALLDATA_PREFIX_START_INDEX ||\n !equalsIgnoreCase(\n calldataTerms.value,\n encodeTransferToCalldataPrefix(expected.recipientAddress),\n )\n ) {\n return false;\n }\n\n const periodTerms = decodeERC20TokenPeriodTransferTerms(\n periodCaveat.terms,\n );\n const storedStartDate = Number(periodTerms.startDate);\n if (\n !isStartDateInsideApiWindow(\n storedStartDate,\n expected.expectedStartDate,\n expected.nowSeconds,\n )\n ) {\n return false;\n }\n return (\n equalsIgnoreCase(periodTerms.tokenAddress, expected.tokenAddress) &&\n periodTerms.periodAmount === expected.periodAmount &&\n Number(periodTerms.periodDuration) === expected.periodDuration\n );\n } catch {\n return false;\n }\n };\n}\n\n/**\n * Reads the ERC-20 period-transfer `startDate` from a stored delegation.\n *\n * @param entry - Stored AUS delegation.\n * @param enforcers - Enforcer addresses used to locate the period caveat.\n * @returns Unix timestamp in seconds, or `undefined` when the caveat is\n * missing or malformed.\n */\nfunction getSubscriptionDelegationStartDate(\n entry: DelegationResponse,\n enforcers: SubscriptionDelegationEnforcers,\n): number | undefined {\n const periodCaveat = entry.signedDelegation.caveats.find((caveat) =>\n equalsIgnoreCase(caveat.enforcer, enforcers.erc20TokenPeriodTransfer),\n );\n if (!periodCaveat) {\n return undefined;\n }\n\n try {\n return Number(\n decodeERC20TokenPeriodTransferTerms(periodCaveat.terms).startDate,\n );\n } catch {\n return undefined;\n }\n}\n\n/**\n * Returns the fingerprint match with the latest period `startDate`.\n *\n * Equal `startDate`s keep list order. A delegation whose `startDate` cannot\n * be read sorts last.\n *\n * @param matches - Fingerprint-matching AUS delegations, in list order.\n * @param enforcers - Enforcer addresses used to read `startDate`.\n * @returns The preferred match, or `undefined` when `matches` is empty.\n */\nexport function pickLatestMatchingSubscriptionDelegation(\n matches: readonly DelegationResponse[],\n enforcers: SubscriptionDelegationEnforcers,\n): DelegationResponse | undefined {\n const byStartDateDescending = [...matches].sort((left, right) => {\n const leftStart =\n getSubscriptionDelegationStartDate(left, enforcers) ??\n Number.NEGATIVE_INFINITY;\n const rightStart =\n getSubscriptionDelegationStartDate(right, enforcers) ??\n Number.NEGATIVE_INFINITY;\n return rightStart - leftStart;\n });\n\n return byStartDateDescending[0];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/subscription-controller",
3
- "version": "14.0.0",
3
+ "version": "14.0.2",
4
4
  "description": "Handle user subscription",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -44,15 +44,15 @@
44
44
  "messenger-action-types:check": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --check",
45
45
  "messenger-action-types:generate": "node --import ../../scripts/resolver/register.ts --experimental-transform-types ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --generate",
46
46
  "since-latest-release": "../../scripts/since-latest-release.sh",
47
- "test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
48
- "test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
49
- "test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
50
- "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
47
+ "test": "vitest",
48
+ "test:clean": "vitest --clearCache",
49
+ "test:verbose": "vitest --reporter=verbose",
50
+ "test:watch": "vitest --watch"
51
51
  },
52
52
  "dependencies": {
53
53
  "@ethersproject/abi": "^5.7.0",
54
54
  "@metamask/authenticated-user-storage": "^4.1.0",
55
- "@metamask/base-controller": "^10.0.0",
55
+ "@metamask/base-controller": "^10.1.0",
56
56
  "@metamask/base-data-service": "^2.1.0",
57
57
  "@metamask/chomp-api-service": "^6.0.1",
58
58
  "@metamask/controller-utils": "^13.0.0",
@@ -65,11 +65,11 @@
65
65
  "@metamask/money-account-upgrade-controller": "^5.1.0",
66
66
  "@metamask/money-account-utils": "^2.1.0",
67
67
  "@metamask/polling-controller": "^17.0.0",
68
- "@metamask/profile-sync-controller": "^34.0.3",
68
+ "@metamask/profile-sync-controller": "^35.0.0",
69
69
  "@metamask/remote-feature-flag-controller": "^7.0.0",
70
70
  "@metamask/seedless-onboarding-controller": "^12.0.0",
71
71
  "@metamask/superstruct": "^3.4.1",
72
- "@metamask/transaction-controller": "^72.2.0",
72
+ "@metamask/transaction-controller": "^72.2.1",
73
73
  "@metamask/utils": "^12.0.0",
74
74
  "@tanstack/query-core": "^5.103.2",
75
75
  "bignumber.js": "^9.1.2",
@@ -77,16 +77,12 @@
77
77
  },
78
78
  "devDependencies": {
79
79
  "@metamask/auto-changelog": "^6.2.1",
80
- "@types/jest": "^30.0.0",
81
80
  "@typescript/native": "npm:typescript@^7.0.2",
82
- "deepmerge": "^4.3.1",
83
- "jest": "^30.5.2",
84
- "jest-environment-node": "^30.5.2",
85
81
  "rimraf": "^6.1.3",
86
- "ts-jest": "^29.4.14",
87
82
  "typedoc": "^0.28.20",
88
83
  "typedoc-plugin-missing-exports": "^4.1.4",
89
- "typescript": "npm:@typescript/typescript6@^6.0.2"
84
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
85
+ "vitest": "^5.0.0"
90
86
  },
91
87
  "engines": {
92
88
  "node": "^22.14.0 || ^24"