@metamask/subscription-controller 10.0.1 → 11.0.0

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.
Files changed (51) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/dist/SubscriptionController.d.ts.map +1 -1
  3. package/dist/SubscriptionController.js +11 -5
  4. package/dist/SubscriptionController.js.map +1 -1
  5. package/dist/SubscriptionService-method-action-types.d.ts +2 -1
  6. package/dist/SubscriptionService-method-action-types.d.ts.map +1 -1
  7. package/dist/SubscriptionService-method-action-types.js.map +1 -1
  8. package/dist/SubscriptionService-structs.d.ts +33 -47
  9. package/dist/SubscriptionService-structs.d.ts.map +1 -1
  10. package/dist/SubscriptionService-structs.js +2 -6
  11. package/dist/SubscriptionService-structs.js.map +1 -1
  12. package/dist/SubscriptionService.d.ts +2 -1
  13. package/dist/SubscriptionService.d.ts.map +1 -1
  14. package/dist/SubscriptionService.js +4 -3
  15. package/dist/SubscriptionService.js.map +1 -1
  16. package/dist/constants.d.ts +8 -1
  17. package/dist/constants.d.ts.map +1 -1
  18. package/dist/constants.js +7 -0
  19. package/dist/constants.js.map +1 -1
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/subscription-delegation/SubscriptionDelegationService-method-action-types.d.ts +29 -5
  25. package/dist/subscription-delegation/SubscriptionDelegationService-method-action-types.d.ts.map +1 -1
  26. package/dist/subscription-delegation/SubscriptionDelegationService-method-action-types.js.map +1 -1
  27. package/dist/subscription-delegation/SubscriptionDelegationService.d.ts +33 -11
  28. package/dist/subscription-delegation/SubscriptionDelegationService.d.ts.map +1 -1
  29. package/dist/subscription-delegation/SubscriptionDelegationService.js +305 -22
  30. package/dist/subscription-delegation/SubscriptionDelegationService.js.map +1 -1
  31. package/dist/subscription-delegation/caveats.d.ts +31 -2
  32. package/dist/subscription-delegation/caveats.d.ts.map +1 -1
  33. package/dist/subscription-delegation/caveats.js +49 -9
  34. package/dist/subscription-delegation/caveats.js.map +1 -1
  35. package/dist/subscription-delegation/fingerprint.d.ts +1 -0
  36. package/dist/subscription-delegation/fingerprint.d.ts.map +1 -1
  37. package/dist/subscription-delegation/fingerprint.js +10 -6
  38. package/dist/subscription-delegation/fingerprint.js.map +1 -1
  39. package/dist/subscription-delegation/typed-data.d.ts +23 -0
  40. package/dist/subscription-delegation/typed-data.d.ts.map +1 -0
  41. package/dist/subscription-delegation/typed-data.js +75 -0
  42. package/dist/subscription-delegation/typed-data.js.map +1 -0
  43. package/dist/subscription-delegation/types.d.ts +105 -12
  44. package/dist/subscription-delegation/types.d.ts.map +1 -1
  45. package/dist/subscription-delegation/types.js +3 -4
  46. package/dist/subscription-delegation/types.js.map +1 -1
  47. package/dist/types.d.ts +15 -6
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/types.js +2 -0
  50. package/dist/types.js.map +1 -1
  51. package/package.json +16 -13
@@ -1,10 +1,29 @@
1
1
  import type { SignedDelegation } from '@metamask/authenticated-user-storage';
2
2
  import type { Hex } from '@metamask/utils';
3
3
  import type { SubscriptionDelegationEnforcers } from './types.js';
4
+ /**
5
+ * Calldata offset of the `AllowedCalldata` prefix. Must match CHOMP's
6
+ * expected terms, which pin the selector together with the recipient.
7
+ * ref: https://github.com/consensys-vertical-apps/va-mmcx-chomp-api/blob/28b72ff16cb5d1b89a7a8ebeb0049a53015a516b/src/intent/delegation-validation.service.ts#L244
8
+ */
9
+ export declare const TRANSFER_CALLDATA_PREFIX_START_INDEX = 0;
10
+ /**
11
+ * Encodes the ERC-20 `transfer(address,uint256)` selector followed by the
12
+ * ABI-encoded recipient (36 bytes), i.e. calldata up to the amount argument.
13
+ *
14
+ * @param recipient - Transfer recipient address.
15
+ * @returns The lowercased hex calldata prefix.
16
+ */
17
+ export declare function encodeTransferToCalldataPrefix(recipient: Hex): Hex;
4
18
  export type UnsignedSubscriptionDelegation = Omit<SignedDelegation, 'signature'>;
5
19
  export type BuildSubscriptionCaveatsParams = {
6
20
  enforcers: SubscriptionDelegationEnforcers;
7
21
  delegateAddress: Hex;
22
+ /**
23
+ * Subscription treasury (pricing chain `paymentAddress`); the only allowed
24
+ * ERC-20 `transfer` recipient.
25
+ */
26
+ recipientAddress: Hex;
8
27
  tokenAddress: Hex;
9
28
  periodAmount: bigint;
10
29
  periodDuration: number;
@@ -12,18 +31,28 @@ export type BuildSubscriptionCaveatsParams = {
12
31
  };
13
32
  /**
14
33
  * Builds the caveat list for a cash-subscription delegation:
15
- * `ValueLte(0)` then `ERC20TokenPeriodTransfer(...)`.
34
+ * `ERC20TokenPeriodTransfer(...)` then `AllowedCalldata` pinning the
35
+ * `transfer` recipient to the subscription treasury.
36
+ *
37
+ * `ValueLte(0)` is intentionally omitted: CHOMP rejects cash-subscription
38
+ * delegations whose enforcers are not exactly `ERC20PeriodTransferEnforcer`
39
+ * and `AllowedCalldataEnforcer`. Neither enforcer restricts native value, so
40
+ * this relies on the settlement token's `transfer` being non-payable. Re-add
41
+ * `ValueLte(0)` once CHOMP accepts it.
42
+ * ref: https://github.com/consensys-vertical-apps/va-mmcx-chomp-api/blob/28b72ff16cb5d1b89a7a8ebeb0049a53015a516b/src/intent/delegation-validation.service.ts#L174
16
43
  *
17
44
  * @param params - Enforcer addresses, parties, and period terms.
18
45
  * @param params.enforcers - Delegation Framework enforcer addresses.
46
+ * @param params.recipientAddress - Subscription treasury address.
19
47
  * @param params.tokenAddress - Subscription settlement token.
20
48
  * @param params.periodAmount - Maximum token amount per period.
21
49
  * @param params.periodDuration - Period length in seconds.
22
50
  * @param params.startDate - Unix timestamp when transfers may begin.
23
51
  * @returns Caveats in enforcer order.
24
52
  */
25
- export declare function buildSubscriptionCaveats({ enforcers, tokenAddress, periodAmount, periodDuration, startDate, }: BuildSubscriptionCaveatsParams): SignedDelegation['caveats'];
53
+ export declare function buildSubscriptionCaveats({ enforcers, recipientAddress, tokenAddress, periodAmount, periodDuration, startDate, }: BuildSubscriptionCaveatsParams): SignedDelegation['caveats'];
26
54
  export type BuildUnsignedSubscriptionDelegationParams = BuildSubscriptionCaveatsParams & {
55
+ delegateAddress: Hex;
27
56
  delegatorAddress: Hex;
28
57
  /**
29
58
  * Optional salt for tests. When omitted, a random 32-byte salt is generated.
@@ -1 +1 @@
1
- {"version":3,"file":"caveats.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/caveats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAO7E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAElE,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAC/C,gBAAgB,EAChB,WAAW,CACZ,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,SAAS,EAAE,+BAA+B,CAAC;IAC3C,eAAe,EAAE,GAAG,CAAC;IACrB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,SAAS,GACV,EAAE,8BAA8B,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAkB9D;AAED,MAAM,MAAM,yCAAyC,GACnD,8BAA8B,GAAG;IAC/B,gBAAgB,EAAE,GAAG,CAAC;IACtB;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAC;AAEJ;;;;;GAKG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,yCAAyC,GAChD,8BAA8B,CAYhC"}
1
+ {"version":3,"file":"caveats.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/caveats.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAO7E,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,YAAY,CAAC;AAMlE;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,IAAI,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG,CAWlE;AAED,MAAM,MAAM,8BAA8B,GAAG,IAAI,CAC/C,gBAAgB,EAChB,WAAW,CACZ,CAAC;AAEF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,SAAS,EAAE,+BAA+B,CAAC;IAC3C,eAAe,EAAE,GAAG,CAAC;IACrB;;;OAGG;IACH,gBAAgB,EAAE,GAAG,CAAC;IACtB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,SAAS,GACV,EAAE,8BAA8B,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAqB9D;AAED,MAAM,MAAM,yCAAyC,GACnD,8BAA8B,GAAG;IAC/B,eAAe,EAAE,GAAG,CAAC;IACrB,gBAAgB,EAAE,GAAG,CAAC;IACtB;;OAEG;IACH,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAC;AAEJ;;;;;GAKG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,yCAAyC,GAChD,8BAA8B,CAYhC"}
@@ -1,24 +1,56 @@
1
- import { ROOT_AUTHORITY, createERC20TokenPeriodTransferTerms, createValueLteTerms, } from '@metamask/delegation-core';
2
- import { bytesToHex } from '@metamask/utils';
1
+ import { defaultAbiCoder, Interface } from '@ethersproject/abi';
2
+ import { ROOT_AUTHORITY, createAllowedCalldataTerms, createERC20TokenPeriodTransferTerms, } from '@metamask/delegation-core';
3
+ import { bytesToHex, remove0x } from '@metamask/utils';
4
+ import { SubscriptionDelegationServiceErrorMessage } from '../constants.js';
5
+ const ERC20_INTERFACE = new Interface([
6
+ 'function transfer(address to, uint256 amount)',
7
+ ]);
8
+ /**
9
+ * Calldata offset of the `AllowedCalldata` prefix. Must match CHOMP's
10
+ * expected terms, which pin the selector together with the recipient.
11
+ * ref: https://github.com/consensys-vertical-apps/va-mmcx-chomp-api/blob/28b72ff16cb5d1b89a7a8ebeb0049a53015a516b/src/intent/delegation-validation.service.ts#L244
12
+ */
13
+ export const TRANSFER_CALLDATA_PREFIX_START_INDEX = 0;
14
+ /**
15
+ * Encodes the ERC-20 `transfer(address,uint256)` selector followed by the
16
+ * ABI-encoded recipient (36 bytes), i.e. calldata up to the amount argument.
17
+ *
18
+ * @param recipient - Transfer recipient address.
19
+ * @returns The lowercased hex calldata prefix.
20
+ */
21
+ export function encodeTransferToCalldataPrefix(recipient) {
22
+ let encodedRecipient;
23
+ try {
24
+ encodedRecipient = defaultAbiCoder.encode(['address'], [recipient]);
25
+ }
26
+ catch {
27
+ throw new Error(`${SubscriptionDelegationServiceErrorMessage.InvalidRecipientAddress}: ${recipient}`);
28
+ }
29
+ return `${ERC20_INTERFACE.getSighash('transfer')}${remove0x(encodedRecipient)}`;
30
+ }
3
31
  /**
4
32
  * Builds the caveat list for a cash-subscription delegation:
5
- * `ValueLte(0)` then `ERC20TokenPeriodTransfer(...)`.
33
+ * `ERC20TokenPeriodTransfer(...)` then `AllowedCalldata` pinning the
34
+ * `transfer` recipient to the subscription treasury.
35
+ *
36
+ * `ValueLte(0)` is intentionally omitted: CHOMP rejects cash-subscription
37
+ * delegations whose enforcers are not exactly `ERC20PeriodTransferEnforcer`
38
+ * and `AllowedCalldataEnforcer`. Neither enforcer restricts native value, so
39
+ * this relies on the settlement token's `transfer` being non-payable. Re-add
40
+ * `ValueLte(0)` once CHOMP accepts it.
41
+ * ref: https://github.com/consensys-vertical-apps/va-mmcx-chomp-api/blob/28b72ff16cb5d1b89a7a8ebeb0049a53015a516b/src/intent/delegation-validation.service.ts#L174
6
42
  *
7
43
  * @param params - Enforcer addresses, parties, and period terms.
8
44
  * @param params.enforcers - Delegation Framework enforcer addresses.
45
+ * @param params.recipientAddress - Subscription treasury address.
9
46
  * @param params.tokenAddress - Subscription settlement token.
10
47
  * @param params.periodAmount - Maximum token amount per period.
11
48
  * @param params.periodDuration - Period length in seconds.
12
49
  * @param params.startDate - Unix timestamp when transfers may begin.
13
50
  * @returns Caveats in enforcer order.
14
51
  */
15
- export function buildSubscriptionCaveats({ enforcers, tokenAddress, periodAmount, periodDuration, startDate, }) {
52
+ export function buildSubscriptionCaveats({ enforcers, recipientAddress, tokenAddress, periodAmount, periodDuration, startDate, }) {
16
53
  return [
17
- {
18
- enforcer: enforcers.valueLte,
19
- terms: createValueLteTerms({ maxValue: 0n }),
20
- args: '0x',
21
- },
22
54
  {
23
55
  enforcer: enforcers.erc20TokenPeriodTransfer,
24
56
  terms: createERC20TokenPeriodTransferTerms({
@@ -29,6 +61,14 @@ export function buildSubscriptionCaveats({ enforcers, tokenAddress, periodAmount
29
61
  }),
30
62
  args: '0x',
31
63
  },
64
+ {
65
+ enforcer: enforcers.allowedCalldata,
66
+ terms: createAllowedCalldataTerms({
67
+ startIndex: TRANSFER_CALLDATA_PREFIX_START_INDEX,
68
+ value: encodeTransferToCalldataPrefix(recipientAddress),
69
+ }),
70
+ args: '0x',
71
+ },
32
72
  ];
33
73
  }
34
74
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"caveats.js","sourceRoot":"","sources":["../../src/subscription-delegation/caveats.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,mCAAmC,EACnC,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAmB7C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CAAC,EACvC,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,SAAS,GACsB;IAC/B,OAAO;QACL;YACE,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,KAAK,EAAE,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAC5C,IAAI,EAAE,IAAI;SACX;QACD;YACE,QAAQ,EAAE,SAAS,CAAC,wBAAwB;YAC5C,KAAK,EAAE,mCAAmC,CAAC;gBACzC,YAAY;gBACZ,YAAY;gBACZ,cAAc;gBACd,SAAS;aACV,CAAC;YACF,IAAI,EAAE,IAAI;SACX;KACF,CAAC;AACJ,CAAC;AAWD;;;;;GAKG;AACH,MAAM,UAAU,mCAAmC,CACjD,MAAiD;IAEjD,MAAM,IAAI,GACR,MAAM,CAAC,IAAI;QACX,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpE,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,eAAe;QAChC,SAAS,EAAE,MAAM,CAAC,gBAAgB;QAClC,SAAS,EAAE,cAAc;QACzB,OAAO,EAAE,wBAAwB,CAAC,MAAM,CAAC;QACzC,IAAI;KACL,CAAC;AACJ,CAAC","sourcesContent":["import type { SignedDelegation } from '@metamask/authenticated-user-storage';\nimport {\n ROOT_AUTHORITY,\n createERC20TokenPeriodTransferTerms,\n createValueLteTerms,\n} from '@metamask/delegation-core';\nimport { bytesToHex } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport type { SubscriptionDelegationEnforcers } from './types.js';\n\nexport type UnsignedSubscriptionDelegation = Omit<\n SignedDelegation,\n 'signature'\n>;\n\nexport type BuildSubscriptionCaveatsParams = {\n enforcers: SubscriptionDelegationEnforcers;\n delegateAddress: Hex;\n tokenAddress: Hex;\n periodAmount: bigint;\n periodDuration: number;\n startDate: number;\n};\n\n/**\n * Builds the caveat list for a cash-subscription delegation:\n * `ValueLte(0)` then `ERC20TokenPeriodTransfer(...)`.\n *\n * @param params - Enforcer addresses, parties, and period terms.\n * @param params.enforcers - Delegation Framework enforcer addresses.\n * @param params.tokenAddress - Subscription settlement token.\n * @param params.periodAmount - Maximum token amount per period.\n * @param params.periodDuration - Period length in seconds.\n * @param params.startDate - Unix timestamp when transfers may begin.\n * @returns Caveats in enforcer order.\n */\nexport function buildSubscriptionCaveats({\n enforcers,\n tokenAddress,\n periodAmount,\n periodDuration,\n startDate,\n}: BuildSubscriptionCaveatsParams): SignedDelegation['caveats'] {\n return [\n {\n enforcer: enforcers.valueLte,\n terms: createValueLteTerms({ maxValue: 0n }),\n args: '0x',\n },\n {\n enforcer: enforcers.erc20TokenPeriodTransfer,\n terms: createERC20TokenPeriodTransferTerms({\n tokenAddress,\n periodAmount,\n periodDuration,\n startDate,\n }),\n args: '0x',\n },\n ];\n}\n\nexport type BuildUnsignedSubscriptionDelegationParams =\n BuildSubscriptionCaveatsParams & {\n delegatorAddress: Hex;\n /**\n * Optional salt for tests. When omitted, a random 32-byte salt is generated.\n */\n salt?: Hex;\n };\n\n/**\n * Builds an unsigned root cash-subscription delegation.\n *\n * @param params - Delegation parties, enforcers, and period terms.\n * @returns An unsigned delegation ready for signing.\n */\nexport function buildUnsignedSubscriptionDelegation(\n params: BuildUnsignedSubscriptionDelegationParams,\n): UnsignedSubscriptionDelegation {\n const salt =\n params.salt ??\n bytesToHex(globalThis.crypto.getRandomValues(new Uint8Array(32)));\n\n return {\n delegate: params.delegateAddress,\n delegator: params.delegatorAddress,\n authority: ROOT_AUTHORITY,\n caveats: buildSubscriptionCaveats(params),\n salt,\n };\n}\n"]}
1
+ {"version":3,"file":"caveats.js","sourceRoot":"","sources":["../../src/subscription-delegation/caveats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEhE,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,mCAAmC,GACpC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAGvD,OAAO,EAAE,yCAAyC,EAAE,MAAM,iBAAiB,CAAC;AAG5E,MAAM,eAAe,GAAG,IAAI,SAAS,CAAC;IACpC,+CAA+C;CAChD,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,oCAAoC,GAAG,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,UAAU,8BAA8B,CAAC,SAAc;IAC3D,IAAI,gBAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,gBAAgB,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,GAAG,yCAAyC,CAAC,uBAAuB,KAAK,SAAS,EAAE,CACrF,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,EAAS,CAAC;AACzF,CAAC;AAqBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,wBAAwB,CAAC,EACvC,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,SAAS,GACsB;IAC/B,OAAO;QACL;YACE,QAAQ,EAAE,SAAS,CAAC,wBAAwB;YAC5C,KAAK,EAAE,mCAAmC,CAAC;gBACzC,YAAY;gBACZ,YAAY;gBACZ,cAAc;gBACd,SAAS;aACV,CAAC;YACF,IAAI,EAAE,IAAI;SACX;QACD;YACE,QAAQ,EAAE,SAAS,CAAC,eAAe;YACnC,KAAK,EAAE,0BAA0B,CAAC;gBAChC,UAAU,EAAE,oCAAoC;gBAChD,KAAK,EAAE,8BAA8B,CAAC,gBAAgB,CAAC;aACxD,CAAC;YACF,IAAI,EAAE,IAAI;SACX;KACF,CAAC;AACJ,CAAC;AAYD;;;;;GAKG;AACH,MAAM,UAAU,mCAAmC,CACjD,MAAiD;IAEjD,MAAM,IAAI,GACR,MAAM,CAAC,IAAI;QACX,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpE,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,eAAe;QAChC,SAAS,EAAE,MAAM,CAAC,gBAAgB;QAClC,SAAS,EAAE,cAAc;QACzB,OAAO,EAAE,wBAAwB,CAAC,MAAM,CAAC;QACzC,IAAI;KACL,CAAC;AACJ,CAAC","sourcesContent":["import { defaultAbiCoder, Interface } from '@ethersproject/abi';\nimport type { SignedDelegation } from '@metamask/authenticated-user-storage';\nimport {\n ROOT_AUTHORITY,\n createAllowedCalldataTerms,\n createERC20TokenPeriodTransferTerms,\n} from '@metamask/delegation-core';\nimport { bytesToHex, remove0x } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport { SubscriptionDelegationServiceErrorMessage } from '../constants.js';\nimport type { SubscriptionDelegationEnforcers } from './types.js';\n\nconst ERC20_INTERFACE = new Interface([\n 'function transfer(address to, uint256 amount)',\n]);\n\n/**\n * Calldata offset of the `AllowedCalldata` prefix. Must match CHOMP's\n * expected terms, which pin the selector together with the recipient.\n * ref: https://github.com/consensys-vertical-apps/va-mmcx-chomp-api/blob/28b72ff16cb5d1b89a7a8ebeb0049a53015a516b/src/intent/delegation-validation.service.ts#L244\n */\nexport const TRANSFER_CALLDATA_PREFIX_START_INDEX = 0;\n\n/**\n * Encodes the ERC-20 `transfer(address,uint256)` selector followed by the\n * ABI-encoded recipient (36 bytes), i.e. calldata up to the amount argument.\n *\n * @param recipient - Transfer recipient address.\n * @returns The lowercased hex calldata prefix.\n */\nexport function encodeTransferToCalldataPrefix(recipient: Hex): Hex {\n let encodedRecipient: string;\n try {\n encodedRecipient = defaultAbiCoder.encode(['address'], [recipient]);\n } catch {\n throw new Error(\n `${SubscriptionDelegationServiceErrorMessage.InvalidRecipientAddress}: ${recipient}`,\n );\n }\n\n return `${ERC20_INTERFACE.getSighash('transfer')}${remove0x(encodedRecipient)}` as Hex;\n}\n\nexport type UnsignedSubscriptionDelegation = Omit<\n SignedDelegation,\n 'signature'\n>;\n\nexport type BuildSubscriptionCaveatsParams = {\n enforcers: SubscriptionDelegationEnforcers;\n delegateAddress: Hex;\n /**\n * Subscription treasury (pricing chain `paymentAddress`); the only allowed\n * ERC-20 `transfer` recipient.\n */\n recipientAddress: Hex;\n tokenAddress: Hex;\n periodAmount: bigint;\n periodDuration: number;\n startDate: number;\n};\n\n/**\n * Builds the caveat list for a cash-subscription delegation:\n * `ERC20TokenPeriodTransfer(...)` then `AllowedCalldata` pinning the\n * `transfer` recipient to the subscription treasury.\n *\n * `ValueLte(0)` is intentionally omitted: CHOMP rejects cash-subscription\n * delegations whose enforcers are not exactly `ERC20PeriodTransferEnforcer`\n * and `AllowedCalldataEnforcer`. Neither enforcer restricts native value, so\n * this relies on the settlement token's `transfer` being non-payable. Re-add\n * `ValueLte(0)` once CHOMP accepts it.\n * ref: https://github.com/consensys-vertical-apps/va-mmcx-chomp-api/blob/28b72ff16cb5d1b89a7a8ebeb0049a53015a516b/src/intent/delegation-validation.service.ts#L174\n *\n * @param params - Enforcer addresses, parties, and period terms.\n * @param params.enforcers - Delegation Framework enforcer addresses.\n * @param params.recipientAddress - Subscription treasury address.\n * @param params.tokenAddress - Subscription settlement token.\n * @param params.periodAmount - Maximum token amount per period.\n * @param params.periodDuration - Period length in seconds.\n * @param params.startDate - Unix timestamp when transfers may begin.\n * @returns Caveats in enforcer order.\n */\nexport function buildSubscriptionCaveats({\n enforcers,\n recipientAddress,\n tokenAddress,\n periodAmount,\n periodDuration,\n startDate,\n}: BuildSubscriptionCaveatsParams): SignedDelegation['caveats'] {\n return [\n {\n enforcer: enforcers.erc20TokenPeriodTransfer,\n terms: createERC20TokenPeriodTransferTerms({\n tokenAddress,\n periodAmount,\n periodDuration,\n startDate,\n }),\n args: '0x',\n },\n {\n enforcer: enforcers.allowedCalldata,\n terms: createAllowedCalldataTerms({\n startIndex: TRANSFER_CALLDATA_PREFIX_START_INDEX,\n value: encodeTransferToCalldataPrefix(recipientAddress),\n }),\n args: '0x',\n },\n ];\n}\n\nexport type BuildUnsignedSubscriptionDelegationParams =\n BuildSubscriptionCaveatsParams & {\n delegateAddress: Hex;\n delegatorAddress: Hex;\n /**\n * Optional salt for tests. When omitted, a random 32-byte salt is generated.\n */\n salt?: Hex;\n };\n\n/**\n * Builds an unsigned root cash-subscription delegation.\n *\n * @param params - Delegation parties, enforcers, and period terms.\n * @returns An unsigned delegation ready for signing.\n */\nexport function buildUnsignedSubscriptionDelegation(\n params: BuildUnsignedSubscriptionDelegationParams,\n): UnsignedSubscriptionDelegation {\n const salt =\n params.salt ??\n bytesToHex(globalThis.crypto.getRandomValues(new Uint8Array(32)));\n\n return {\n delegate: params.delegateAddress,\n delegator: params.delegatorAddress,\n authority: ROOT_AUTHORITY,\n caveats: buildSubscriptionCaveats(params),\n salt,\n };\n}\n"]}
@@ -4,6 +4,7 @@ import type { SubscriptionDelegationEnforcers } from './types.js';
4
4
  export type SubscriptionDelegationFingerprint = {
5
5
  delegatorAddress: Hex;
6
6
  delegateAddress: Hex;
7
+ recipientAddress: Hex;
7
8
  chainId: Hex;
8
9
  tokenAddress: Hex;
9
10
  periodAmount: bigint;
@@ -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;AAE3C,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,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,CAsExC;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;;;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,4 +1,5 @@
1
- import { decodeERC20TokenPeriodTransferTerms, decodeValueLteTerms, } from '@metamask/delegation-core';
1
+ import { decodeAllowedCalldataTerms, decodeERC20TokenPeriodTransferTerms, } from '@metamask/delegation-core';
2
+ import { encodeTransferToCalldataPrefix, TRANSFER_CALLDATA_PREFIX_START_INDEX, } from './caveats.js';
2
3
  import { CASH_SUBSCRIPTION_DELEGATION_TYPE } from './types.js';
3
4
  /**
4
5
  * Case-insensitive hex equality.
@@ -37,18 +38,21 @@ export function makeMatchesSubscriptionDelegation(expected) {
37
38
  if (!equalsIgnoreCase(entry.metadata.tokenAddress, expected.tokenAddress)) {
38
39
  return false;
39
40
  }
41
+ // CHOMP requires exactly these two enforcers, so stored delegations with
42
+ // extra caveats (e.g. a legacy `ValueLte`) cannot be reused.
40
43
  const { caveats } = entry.signedDelegation;
41
- if (caveats.length < 2) {
44
+ if (caveats.length !== 2) {
42
45
  return false;
43
46
  }
44
- const valueLteCaveat = caveats.find((caveat) => equalsIgnoreCase(caveat.enforcer, expected.enforcers.valueLte));
45
47
  const periodCaveat = caveats.find((caveat) => equalsIgnoreCase(caveat.enforcer, expected.enforcers.erc20TokenPeriodTransfer));
46
- if (!valueLteCaveat || !periodCaveat) {
48
+ const allowedCalldataCaveat = caveats.find((caveat) => equalsIgnoreCase(caveat.enforcer, expected.enforcers.allowedCalldata));
49
+ if (!periodCaveat || !allowedCalldataCaveat) {
47
50
  return false;
48
51
  }
49
52
  try {
50
- const valueTerms = decodeValueLteTerms(valueLteCaveat.terms);
51
- if (valueTerms.maxValue !== 0n) {
53
+ const calldataTerms = decodeAllowedCalldataTerms(allowedCalldataCaveat.terms);
54
+ if (calldataTerms.startIndex !== TRANSFER_CALLDATA_PREFIX_START_INDEX ||
55
+ !equalsIgnoreCase(calldataTerms.value, encodeTransferToCalldataPrefix(expected.recipientAddress))) {
52
56
  return false;
53
57
  }
54
58
  const periodTerms = decodeERC20TokenPeriodTransferTerms(periodCaveat.terms);
@@ -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 decodeERC20TokenPeriodTransferTerms,\n decodeValueLteTerms,\n} from '@metamask/delegation-core';\nimport type { Hex } from '@metamask/utils';\n\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 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 const { caveats } = entry.signedDelegation;\n if (caveats.length < 2) {\n return false;\n }\n\n const valueLteCaveat = caveats.find((caveat) =>\n equalsIgnoreCase(caveat.enforcer, expected.enforcers.valueLte),\n );\n const periodCaveat = caveats.find((caveat) =>\n equalsIgnoreCase(\n caveat.enforcer,\n expected.enforcers.erc20TokenPeriodTransfer,\n ),\n );\n if (!valueLteCaveat || !periodCaveat) {\n return false;\n }\n\n try {\n const valueTerms = decodeValueLteTerms(valueLteCaveat.terms);\n if (valueTerms.maxValue !== 0n) {\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\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":"AACA,OAAO,EACL,0BAA0B,EAC1B,mCAAmC,GACpC,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,8BAA8B,EAC9B,oCAAoC,GACrC,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iCAAiC,EAAE,MAAM,YAAY,CAAC;AAuB/D;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,KAAa;IAC1D,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC;AACpD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iCAAiC,CAC/C,QAA2C;IAE3C,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,iCAAiC,EAAE,CAAC;YAC9D,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IACE,CAAC,gBAAgB,CACf,KAAK,CAAC,gBAAgB,CAAC,SAAS,EAChC,QAAQ,CAAC,gBAAgB,CAC1B,EACD,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IACE,CAAC,gBAAgB,CACf,KAAK,CAAC,gBAAgB,CAAC,QAAQ,EAC/B,QAAQ,CAAC,eAAe,CACzB,EACD,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACnE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1E,OAAO,KAAK,CAAC;QACf,CAAC;QAED,yEAAyE;QACzE,6DAA6D;QAC7D,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,gBAAgB,CAAC;QAC3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAC3C,gBAAgB,CACd,MAAM,CAAC,QAAQ,EACf,QAAQ,CAAC,SAAS,CAAC,wBAAwB,CAC5C,CACF,CAAC;QACF,MAAM,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CACpD,gBAAgB,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,eAAe,CAAC,CACtE,CAAC;QACF,IAAI,CAAC,YAAY,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,0BAA0B,CAC9C,qBAAqB,CAAC,KAAK,CAC5B,CAAC;YACF,IACE,aAAa,CAAC,UAAU,KAAK,oCAAoC;gBACjE,CAAC,gBAAgB,CACf,aAAa,CAAC,KAAK,EACnB,8BAA8B,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAC1D,EACD,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,WAAW,GAAG,mCAAmC,CACrD,YAAY,CAAC,KAAK,CACnB,CAAC;YACF,MAAM,eAAe,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YACtD,MAAM,gBAAgB,GAAG,eAAe,GAAG,QAAQ,CAAC,UAAU,CAAC;YAC/D,IAAI,QAAQ,CAAC,eAAe,KAAK,gBAAgB,EAAE,CAAC;gBAClD,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,CACL,gBAAgB,CAAC,WAAW,CAAC,YAAY,EAAE,QAAQ,CAAC,YAAY,CAAC;gBACjE,WAAW,CAAC,YAAY,KAAK,QAAQ,CAAC,YAAY;gBAClD,MAAM,CAAC,WAAW,CAAC,cAAc,CAAC,KAAK,QAAQ,CAAC,cAAc,CAC/D,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,kCAAkC,CACzC,KAAyB,EACzB,SAA0C;IAE1C,MAAM,YAAY,GAAG,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAClE,gBAAgB,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,wBAAwB,CAAC,CACtE,CAAC;IACF,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,CACX,mCAAmC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,SAAS,CAClE,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wCAAwC,CACtD,OAAsC,EACtC,SAA0C;IAE1C,MAAM,qBAAqB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC9D,MAAM,SAAS,GACb,kCAAkC,CAAC,IAAI,EAAE,SAAS,CAAC;YACnD,MAAM,CAAC,iBAAiB,CAAC;QAC3B,MAAM,UAAU,GACd,kCAAkC,CAAC,KAAK,EAAE,SAAS,CAAC;YACpD,MAAM,CAAC,iBAAiB,CAAC;QAC3B,OAAO,UAAU,GAAG,SAAS,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC","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"]}
@@ -0,0 +1,23 @@
1
+ import type { Hex } from '@metamask/utils';
2
+ import type { DecodedPermission, SubscriptionDelegationEnforcers, SubscriptionDelegationTypedData, UnsignedSubscriptionDelegation } from './types.js';
3
+ export declare function buildDelegationTypedData({ delegation, chainId, delegationManager, }: {
4
+ delegation: UnsignedSubscriptionDelegation;
5
+ chainId: Hex;
6
+ delegationManager: Hex;
7
+ }): SubscriptionDelegationTypedData;
8
+ /**
9
+ * Decodes the effective permission granted by a cash-subscription delegation
10
+ * for display in confirmations.
11
+ *
12
+ * The delegation must carry exactly the two caveats CHOMP accepts:
13
+ * `ERC20TokenPeriodTransfer` (token, amount, period, start) and
14
+ * `AllowedCalldata` (pinning the `transfer` recipient). No `ValueLte` caveat
15
+ * is present; `maxNativeValue` is reported as `'0'` because the settlement
16
+ * token's `transfer` is non-payable (see `buildSubscriptionCaveats`).
17
+ *
18
+ * @param delegation - The unsigned subscription delegation.
19
+ * @param enforcers - Delegation Framework enforcer addresses for the chain.
20
+ * @returns The decoded permission.
21
+ */
22
+ export declare function decodeSubscriptionAuthority(delegation: UnsignedSubscriptionDelegation, enforcers: SubscriptionDelegationEnforcers): DecodedPermission;
23
+ //# sourceMappingURL=typed-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-data.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/typed-data.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,KAAK,EACV,iBAAiB,EACjB,+BAA+B,EAC/B,+BAA+B,EAC/B,8BAA8B,EAC/B,MAAM,YAAY,CAAC;AAsBpB,wBAAgB,wBAAwB,CAAC,EACvC,UAAU,EACV,OAAO,EACP,iBAAiB,GAClB,EAAE;IACD,UAAU,EAAE,8BAA8B,CAAC;IAC3C,OAAO,EAAE,GAAG,CAAC;IACb,iBAAiB,EAAE,GAAG,CAAC;CACxB,GAAG,+BAA+B,CAqBlC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,8BAA8B,EAC1C,SAAS,EAAE,+BAA+B,GACzC,iBAAiB,CAqBnB"}
@@ -0,0 +1,75 @@
1
+ import { decodeERC20TokenPeriodTransferTerms } from '@metamask/delegation-core';
2
+ import { getChecksumAddress, hexToNumber } from '@metamask/utils';
3
+ import { equalsIgnoreCase } from './fingerprint.js';
4
+ const SIGNABLE_DELEGATION_TYPES = {
5
+ EIP712Domain: [
6
+ { name: 'name', type: 'string' },
7
+ { name: 'version', type: 'string' },
8
+ { name: 'chainId', type: 'uint256' },
9
+ { name: 'verifyingContract', type: 'address' },
10
+ ],
11
+ Caveat: [
12
+ { name: 'enforcer', type: 'address' },
13
+ { name: 'terms', type: 'bytes' },
14
+ ],
15
+ Delegation: [
16
+ { name: 'delegate', type: 'address' },
17
+ { name: 'delegator', type: 'address' },
18
+ { name: 'authority', type: 'bytes32' },
19
+ { name: 'caveats', type: 'Caveat[]' },
20
+ { name: 'salt', type: 'uint256' },
21
+ ],
22
+ };
23
+ export function buildDelegationTypedData({ delegation, chainId, delegationManager, }) {
24
+ return {
25
+ types: SIGNABLE_DELEGATION_TYPES,
26
+ primaryType: 'Delegation',
27
+ domain: {
28
+ chainId: hexToNumber(chainId),
29
+ name: 'DelegationManager',
30
+ version: '1',
31
+ verifyingContract: delegationManager,
32
+ },
33
+ message: {
34
+ delegate: getChecksumAddress(delegation.delegate),
35
+ delegator: getChecksumAddress(delegation.delegator),
36
+ authority: delegation.authority,
37
+ caveats: delegation.caveats.map(({ enforcer, terms }) => ({
38
+ enforcer: getChecksumAddress(enforcer),
39
+ terms,
40
+ })),
41
+ salt: delegation.salt,
42
+ },
43
+ };
44
+ }
45
+ /**
46
+ * Decodes the effective permission granted by a cash-subscription delegation
47
+ * for display in confirmations.
48
+ *
49
+ * The delegation must carry exactly the two caveats CHOMP accepts:
50
+ * `ERC20TokenPeriodTransfer` (token, amount, period, start) and
51
+ * `AllowedCalldata` (pinning the `transfer` recipient). No `ValueLte` caveat
52
+ * is present; `maxNativeValue` is reported as `'0'` because the settlement
53
+ * token's `transfer` is non-payable (see `buildSubscriptionCaveats`).
54
+ *
55
+ * @param delegation - The unsigned subscription delegation.
56
+ * @param enforcers - Delegation Framework enforcer addresses for the chain.
57
+ * @returns The decoded permission.
58
+ */
59
+ export function decodeSubscriptionAuthority(delegation, enforcers) {
60
+ const periodTransfer = delegation.caveats.find(({ enforcer }) => equalsIgnoreCase(enforcer, enforcers.erc20TokenPeriodTransfer));
61
+ const allowedCalldata = delegation.caveats.find(({ enforcer }) => equalsIgnoreCase(enforcer, enforcers.allowedCalldata));
62
+ if (!periodTransfer || !allowedCalldata) {
63
+ throw new Error('Subscription delegation is missing required caveats');
64
+ }
65
+ const periodTerms = decodeERC20TokenPeriodTransferTerms(periodTransfer.terms);
66
+ return {
67
+ tokenAddress: periodTerms.tokenAddress,
68
+ delegateAddress: delegation.delegate,
69
+ periodAmount: periodTerms.periodAmount.toString(),
70
+ periodDuration: Number(periodTerms.periodDuration),
71
+ startDate: Number(periodTerms.startDate),
72
+ maxNativeValue: '0',
73
+ };
74
+ }
75
+ //# sourceMappingURL=typed-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typed-data.js","sourceRoot":"","sources":["../../src/subscription-delegation/typed-data.ts"],"names":[],"mappings":"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","sourcesContent":["import { decodeERC20TokenPeriodTransferTerms } from '@metamask/delegation-core';\nimport { getChecksumAddress, hexToNumber } from '@metamask/utils';\nimport type { Hex } from '@metamask/utils';\n\nimport { equalsIgnoreCase } from './fingerprint.js';\nimport type {\n DecodedPermission,\n SubscriptionDelegationEnforcers,\n SubscriptionDelegationTypedData,\n UnsignedSubscriptionDelegation,\n} from './types.js';\n\nconst SIGNABLE_DELEGATION_TYPES = {\n EIP712Domain: [\n { name: 'name', type: 'string' },\n { name: 'version', type: 'string' },\n { name: 'chainId', type: 'uint256' },\n { name: 'verifyingContract', type: 'address' },\n ],\n Caveat: [\n { name: 'enforcer', type: 'address' },\n { name: 'terms', type: 'bytes' },\n ],\n Delegation: [\n { name: 'delegate', type: 'address' },\n { name: 'delegator', type: 'address' },\n { name: 'authority', type: 'bytes32' },\n { name: 'caveats', type: 'Caveat[]' },\n { name: 'salt', type: 'uint256' },\n ],\n};\n\nexport function buildDelegationTypedData({\n delegation,\n chainId,\n delegationManager,\n}: {\n delegation: UnsignedSubscriptionDelegation;\n chainId: Hex;\n delegationManager: Hex;\n}): SubscriptionDelegationTypedData {\n return {\n types: SIGNABLE_DELEGATION_TYPES,\n primaryType: 'Delegation',\n domain: {\n chainId: hexToNumber(chainId),\n name: 'DelegationManager',\n version: '1',\n verifyingContract: delegationManager,\n },\n message: {\n delegate: getChecksumAddress(delegation.delegate),\n delegator: getChecksumAddress(delegation.delegator),\n authority: delegation.authority,\n caveats: delegation.caveats.map(({ enforcer, terms }) => ({\n enforcer: getChecksumAddress(enforcer),\n terms,\n })),\n salt: delegation.salt,\n },\n };\n}\n\n/**\n * Decodes the effective permission granted by a cash-subscription delegation\n * for display in confirmations.\n *\n * The delegation must carry exactly the two caveats CHOMP accepts:\n * `ERC20TokenPeriodTransfer` (token, amount, period, start) and\n * `AllowedCalldata` (pinning the `transfer` recipient). No `ValueLte` caveat\n * is present; `maxNativeValue` is reported as `'0'` because the settlement\n * token's `transfer` is non-payable (see `buildSubscriptionCaveats`).\n *\n * @param delegation - The unsigned subscription delegation.\n * @param enforcers - Delegation Framework enforcer addresses for the chain.\n * @returns The decoded permission.\n */\nexport function decodeSubscriptionAuthority(\n delegation: UnsignedSubscriptionDelegation,\n enforcers: SubscriptionDelegationEnforcers,\n): DecodedPermission {\n const periodTransfer = delegation.caveats.find(({ enforcer }) =>\n equalsIgnoreCase(enforcer, enforcers.erc20TokenPeriodTransfer),\n );\n const allowedCalldata = delegation.caveats.find(({ enforcer }) =>\n equalsIgnoreCase(enforcer, enforcers.allowedCalldata),\n );\n if (!periodTransfer || !allowedCalldata) {\n throw new Error('Subscription delegation is missing required caveats');\n }\n\n const periodTerms = decodeERC20TokenPeriodTransferTerms(periodTransfer.terms);\n\n return {\n tokenAddress: periodTerms.tokenAddress,\n delegateAddress: delegation.delegate,\n periodAmount: periodTerms.periodAmount.toString(),\n periodDuration: Number(periodTerms.periodDuration),\n startDate: Number(periodTerms.startDate),\n maxNativeValue: '0',\n };\n}\n"]}
@@ -1,15 +1,110 @@
1
- import type { Hex } from '@metamask/utils';
1
+ import type { ChompIntentType } from '@metamask/chomp-api-service';
2
+ import type { Hex, Json } from '@metamask/utils';
2
3
  import { PRODUCT_TYPES } from '../types.js';
3
- import type { RecurringInterval } from '../types.js';
4
+ import type { ProductType, RecurringInterval, StartCryptoSubscriptionResponse } from '../types.js';
5
+ export type { ChompIntentType };
4
6
  /**
5
7
  * Storage / CHOMP metadata type for cash-subscription delegations.
6
8
  *
7
- * Defined locally so this package does not depend on an unreleased
8
- * `@metamask/chomp-api-service` intent type. Production CHOMP intent
9
- * registration still requires a follow-up chomp-api-service release that
10
- * accepts `'cash-subscription'`.
9
+ * The `'cash-subscription'` member of {@link ChompIntentType}.
11
10
  */
12
- export declare const CASH_SUBSCRIPTION_DELEGATION_TYPE: 'cash-subscription';
11
+ export declare const CASH_SUBSCRIPTION_DELEGATION_TYPE: "cash-subscription";
12
+ export declare const SUBSCRIPTION_DELEGATION_APPROVAL_TYPE: 'subscription_delegation';
13
+ export declare const SUBSCRIPTION_DELEGATION_POLICY_VERSION: '1';
14
+ export type SubscriptionPermissionId = ChompIntentType;
15
+ export type MoneyAccountAuthorizationReason = 'controller-not-ready' | 'address-not-associated' | 'eip7702-not-active' | 'monitoring-list-missing' | 'configuration-changed';
16
+ export type UnsignedSubscriptionDelegation = {
17
+ delegate: Hex;
18
+ delegator: Hex;
19
+ authority: Hex;
20
+ caveats: {
21
+ enforcer: Hex;
22
+ terms: Hex;
23
+ args: Hex;
24
+ }[];
25
+ salt: Hex;
26
+ };
27
+ export type SignedSubscriptionDelegation = UnsignedSubscriptionDelegation & {
28
+ signature: Hex;
29
+ };
30
+ export type SubscriptionDelegationTypedData = {
31
+ types: Record<string, {
32
+ name: string;
33
+ type: string;
34
+ }[]>;
35
+ primaryType: string;
36
+ domain: {
37
+ chainId: number;
38
+ name: string;
39
+ version: string;
40
+ verifyingContract: Hex;
41
+ };
42
+ message: Json;
43
+ };
44
+ export type DecodedPermission = {
45
+ tokenAddress: Hex;
46
+ delegateAddress: Hex;
47
+ periodAmount: string;
48
+ periodDuration: number;
49
+ startDate: number;
50
+ maxNativeValue: '0';
51
+ };
52
+ export type PreparedSubscriptionPermission = {
53
+ id: SubscriptionPermissionId;
54
+ owner: 'money-account' | 'subscription';
55
+ disposition: 'new' | 'reused';
56
+ delegation: UnsignedSubscriptionDelegation;
57
+ typedData: SubscriptionDelegationTypedData;
58
+ decodedAuthority: DecodedPermission;
59
+ existingDelegationHash?: Hex;
60
+ };
61
+ export type PreparedSubscriptionDelegationBundle = {
62
+ policyVersion: string;
63
+ account: Hex;
64
+ chainId: Hex;
65
+ permissions: PreparedSubscriptionPermission[];
66
+ };
67
+ export type SubscriptionFundingRequest = {
68
+ useCase: 'subscription';
69
+ destinationAccount: Hex;
70
+ chainId: Hex;
71
+ targetToken: {
72
+ symbol: 'mUSD';
73
+ address: Hex;
74
+ };
75
+ targetAmount: string;
76
+ };
77
+ export type SubscriptionDelegationApprovalResult = {
78
+ fundingTransactionHash: Hex;
79
+ };
80
+ export type StartSubscriptionWithDelegationRequest = {
81
+ product: ProductType;
82
+ recurringInterval: RecurringInterval;
83
+ chainId: Hex;
84
+ payerAddress: Hex;
85
+ /**
86
+ * When `true`, skips the `ApprovalController:addRequest` consent and
87
+ * funding step and proceeds directly to signing, committing, and starting
88
+ * the subscription. Callers must have already obtained user consent and
89
+ * ensured the Money Account is funded.
90
+ */
91
+ skipApproval?: boolean;
92
+ };
93
+ export type PrepareAuthorizationBundleResult = {
94
+ status: 'prepared';
95
+ bundle: PreparedSubscriptionDelegationBundle;
96
+ } | {
97
+ status: 'money-account-authorization-required';
98
+ reasons: MoneyAccountAuthorizationReason[];
99
+ };
100
+ export type CommitAuthorizationBundleRequest = {
101
+ bundle: PreparedSubscriptionDelegationBundle;
102
+ signedPaymentDelegation: SignedSubscriptionDelegation;
103
+ };
104
+ export type CommitAuthorizationBundleResult = {
105
+ paymentDelegationHash: Hex;
106
+ };
107
+ export type StartSubscriptionWithDelegationResult = StartCryptoSubscriptionResponse;
13
108
  /**
14
109
  * Request to prepare a cash-subscription delegation.
15
110
  *
@@ -34,10 +129,8 @@ export type PrepareSubscriptionDelegationRequest = {
34
129
  */
35
130
  checkBalance?: boolean;
36
131
  /**
37
- * When true, skips CHOMP verify/intent interactions. Required for alpha
38
- * until `@metamask/chomp-api-service` accepts `'cash-subscription'` intent
39
- * metadata. Defaults to false (production path; unsupported until that
40
- * follow-up release).
132
+ * When true, skips CHOMP verify and intent registration. The returned
133
+ * delegation hash is computed locally. Defaults to false.
41
134
  */
42
135
  skipChompInteractions?: boolean;
43
136
  };
@@ -66,7 +159,7 @@ export type MoneyAccountBalanceCheckResult = {
66
159
  * Delegation Framework enforcers used by cash-subscription delegations.
67
160
  */
68
161
  export type SubscriptionDelegationEnforcers = {
69
- valueLte: Hex;
70
162
  erc20TokenPeriodTransfer: Hex;
163
+ allowedCalldata: Hex;
71
164
  };
72
165
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,EAAG,mBAA4B,CAAC;AAE9E;;;;;;;;GAQG;AACH,MAAM,MAAM,oCAAoC,GAAG;IACjD,OAAO,EAAE,OAAO,aAAa,CAAC,kBAAkB,CAAC;IACjD,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,YAAY,EAAE,GAAG,CAAC;IAClB;;;OAGG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,cAAc,EAAE,GAAG,CAAC;IACpB,WAAW,EAAE,SAAS,GAAG,QAAQ,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,oCAAoC,EACpC,SAAS,GAAG,mBAAmB,GAAG,cAAc,CACjD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,8BAA8B,GAAG;IAC3C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,EAAE,GAAG,CAAC;IACd,wBAAwB,EAAE,GAAG,CAAC;CAC/B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/subscription-delegation/types.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Storage / CHOMP metadata type for cash-subscription delegations.
3
3
  *
4
- * Defined locally so this package does not depend on an unreleased
5
- * `@metamask/chomp-api-service` intent type. Production CHOMP intent
6
- * registration still requires a follow-up chomp-api-service release that
7
- * accepts `'cash-subscription'`.
4
+ * The `'cash-subscription'` member of {@link ChompIntentType}.
8
5
  */
9
6
  export const CASH_SUBSCRIPTION_DELEGATION_TYPE = 'cash-subscription';
7
+ export const SUBSCRIPTION_DELEGATION_APPROVAL_TYPE = 'subscription_delegation';
8
+ export const SUBSCRIPTION_DELEGATION_POLICY_VERSION = '1';
10
9
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/subscription-delegation/types.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,mBAA4B,CAAC","sourcesContent":["import type { Hex } from '@metamask/utils';\n\nimport { PRODUCT_TYPES } from '../types.js';\nimport type { RecurringInterval } from '../types.js';\n\n/**\n * Storage / CHOMP metadata type for cash-subscription delegations.\n *\n * Defined locally so this package does not depend on an unreleased\n * `@metamask/chomp-api-service` intent type. Production CHOMP intent\n * registration still requires a follow-up chomp-api-service release that\n * accepts `'cash-subscription'`.\n */\nexport const CASH_SUBSCRIPTION_DELEGATION_TYPE = 'cash-subscription' as const;\n\n/**\n * Request to prepare a cash-subscription delegation.\n *\n * The service resolves all amount, token, delegate, and trial-duration fields\n * from authoritative pricing held by `SubscriptionController`.\n *\n * Only Money Account Plus is supported; Shield continues to use ERC-20\n * approval rather than delegation.\n */\nexport type PrepareSubscriptionDelegationRequest = {\n product: typeof PRODUCT_TYPES.MONEY_ACCOUNT_PLUS;\n recurringInterval: RecurringInterval;\n payerAddress: Hex;\n /**\n * Whether the user selected the pricing trial. Pricing `trialPeriodDays`\n * only affects the delegation start date when this is true.\n */\n isTrialRequested: boolean;\n /**\n * When true, gates preparation on a sufficient Money Account balance\n * (`unitAmount × minBillingCyclesForBalance` in mUSD) before side effects.\n */\n checkBalance?: boolean;\n /**\n * When true, skips CHOMP verify/intent interactions. Required for alpha\n * until `@metamask/chomp-api-service` accepts `'cash-subscription'` intent\n * metadata. Defaults to false (production path; unsupported until that\n * follow-up release).\n */\n skipChompInteractions?: boolean;\n};\n\n/**\n * Result of {@link SubscriptionDelegationService.prepareDelegation}.\n */\nexport type PreparedSubscriptionDelegation = {\n delegationHash: Hex;\n disposition: 'created' | 'reused';\n};\n\n/**\n * Request to check whether a Money Account holds enough convertible mUSD value\n * to cover the subscription funding requirement.\n */\nexport type MoneyAccountBalanceCheckRequest = Pick<\n PrepareSubscriptionDelegationRequest,\n 'product' | 'recurringInterval' | 'payerAddress'\n>;\n\n/**\n * Result of {@link SubscriptionDelegationService.checkMoneyAccountBalance}.\n * `balance` and `requiredBalance` are mUSD base units (6 decimals).\n */\nexport type MoneyAccountBalanceCheckResult = {\n hasSufficientBalance: boolean;\n balance: string;\n requiredBalance: string;\n};\n\n/**\n * Delegation Framework enforcers used by cash-subscription delegations.\n */\nexport type SubscriptionDelegationEnforcers = {\n valueLte: Hex;\n erc20TokenPeriodTransfer: Hex;\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/subscription-delegation/types.ts"],"names":[],"mappings":"AAYA;;;;GAIG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAC5C,mBAAsD,CAAC;AAEzD,MAAM,CAAC,MAAM,qCAAqC,GAChD,yBAAkC,CAAC;AAErC,MAAM,CAAC,MAAM,sCAAsC,GAAG,GAAY,CAAC","sourcesContent":["import type { ChompIntentType } from '@metamask/chomp-api-service';\nimport type { Hex, Json } from '@metamask/utils';\n\nimport { PRODUCT_TYPES } from '../types.js';\nimport type {\n ProductType,\n RecurringInterval,\n StartCryptoSubscriptionResponse,\n} from '../types.js';\n\nexport type { ChompIntentType };\n\n/**\n * Storage / CHOMP metadata type for cash-subscription delegations.\n *\n * The `'cash-subscription'` member of {@link ChompIntentType}.\n */\nexport const CASH_SUBSCRIPTION_DELEGATION_TYPE =\n 'cash-subscription' as const satisfies ChompIntentType;\n\nexport const SUBSCRIPTION_DELEGATION_APPROVAL_TYPE =\n 'subscription_delegation' as const;\n\nexport const SUBSCRIPTION_DELEGATION_POLICY_VERSION = '1' as const;\n\nexport type SubscriptionPermissionId = ChompIntentType;\n\nexport type MoneyAccountAuthorizationReason =\n | 'controller-not-ready'\n | 'address-not-associated'\n | 'eip7702-not-active'\n | 'monitoring-list-missing'\n | 'configuration-changed';\n\nexport type UnsignedSubscriptionDelegation = {\n delegate: Hex;\n delegator: Hex;\n authority: Hex;\n caveats: {\n enforcer: Hex;\n terms: Hex;\n args: Hex;\n }[];\n salt: Hex;\n};\n\nexport type SignedSubscriptionDelegation = UnsignedSubscriptionDelegation & {\n signature: Hex;\n};\n\nexport type SubscriptionDelegationTypedData = {\n types: Record<string, { name: string; type: string }[]>;\n primaryType: string;\n domain: {\n chainId: number;\n name: string;\n version: string;\n verifyingContract: Hex;\n };\n message: Json;\n};\n\nexport type DecodedPermission = {\n tokenAddress: Hex;\n delegateAddress: Hex;\n periodAmount: string;\n periodDuration: number;\n startDate: number;\n maxNativeValue: '0';\n};\n\nexport type PreparedSubscriptionPermission = {\n id: SubscriptionPermissionId;\n owner: 'money-account' | 'subscription';\n disposition: 'new' | 'reused';\n delegation: UnsignedSubscriptionDelegation;\n typedData: SubscriptionDelegationTypedData;\n decodedAuthority: DecodedPermission;\n existingDelegationHash?: Hex;\n};\n\nexport type PreparedSubscriptionDelegationBundle = {\n policyVersion: string;\n account: Hex;\n chainId: Hex;\n permissions: PreparedSubscriptionPermission[];\n};\n\nexport type SubscriptionFundingRequest = {\n useCase: 'subscription';\n destinationAccount: Hex;\n chainId: Hex;\n targetToken: {\n symbol: 'mUSD';\n address: Hex;\n };\n targetAmount: string;\n};\n\nexport type SubscriptionDelegationApprovalResult = {\n fundingTransactionHash: Hex;\n};\n\nexport type StartSubscriptionWithDelegationRequest = {\n product: ProductType;\n recurringInterval: RecurringInterval;\n chainId: Hex;\n payerAddress: Hex;\n /**\n * When `true`, skips the `ApprovalController:addRequest` consent and\n * funding step and proceeds directly to signing, committing, and starting\n * the subscription. Callers must have already obtained user consent and\n * ensured the Money Account is funded.\n */\n skipApproval?: boolean;\n};\n\nexport type PrepareAuthorizationBundleResult =\n | {\n status: 'prepared';\n bundle: PreparedSubscriptionDelegationBundle;\n }\n | {\n status: 'money-account-authorization-required';\n reasons: MoneyAccountAuthorizationReason[];\n };\n\nexport type CommitAuthorizationBundleRequest = {\n bundle: PreparedSubscriptionDelegationBundle;\n signedPaymentDelegation: SignedSubscriptionDelegation;\n};\n\nexport type CommitAuthorizationBundleResult = {\n paymentDelegationHash: Hex;\n};\n\nexport type StartSubscriptionWithDelegationResult =\n StartCryptoSubscriptionResponse;\n\n/**\n * Request to prepare a cash-subscription delegation.\n *\n * The service resolves all amount, token, delegate, and trial-duration fields\n * from authoritative pricing held by `SubscriptionController`.\n *\n * Only Money Account Plus is supported; Shield continues to use ERC-20\n * approval rather than delegation.\n */\nexport type PrepareSubscriptionDelegationRequest = {\n product: typeof PRODUCT_TYPES.MONEY_ACCOUNT_PLUS;\n recurringInterval: RecurringInterval;\n payerAddress: Hex;\n /**\n * Whether the user selected the pricing trial. Pricing `trialPeriodDays`\n * only affects the delegation start date when this is true.\n */\n isTrialRequested: boolean;\n /**\n * When true, gates preparation on a sufficient Money Account balance\n * (`unitAmount × minBillingCyclesForBalance` in mUSD) before side effects.\n */\n checkBalance?: boolean;\n /**\n * When true, skips CHOMP verify and intent registration. The returned\n * delegation hash is computed locally. Defaults to false.\n */\n skipChompInteractions?: boolean;\n};\n\n/**\n * Result of {@link SubscriptionDelegationService.prepareDelegation}.\n */\nexport type PreparedSubscriptionDelegation = {\n delegationHash: Hex;\n disposition: 'created' | 'reused';\n};\n\n/**\n * Request to check whether a Money Account holds enough convertible mUSD value\n * to cover the subscription funding requirement.\n */\nexport type MoneyAccountBalanceCheckRequest = Pick<\n PrepareSubscriptionDelegationRequest,\n 'product' | 'recurringInterval' | 'payerAddress'\n>;\n\n/**\n * Result of {@link SubscriptionDelegationService.checkMoneyAccountBalance}.\n * `balance` and `requiredBalance` are mUSD base units (6 decimals).\n */\nexport type MoneyAccountBalanceCheckResult = {\n hasSufficientBalance: boolean;\n balance: string;\n requiredBalance: string;\n};\n\n/**\n * Delegation Framework enforcers used by cash-subscription delegations.\n */\nexport type SubscriptionDelegationEnforcers = {\n erc20TokenPeriodTransfer: Hex;\n allowedCalldata: Hex;\n};\n"]}