@metamask/gator-permissions-controller 1.1.0 → 1.1.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,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.1.2]
11
+
12
+ ### Fixed
13
+
14
+ - Bump `@metamask/transaction-controller` from `^62.10.0` to `^62.11.0` to resolve mismatching `WebSocketState` enum export in `@metamask/core-backend` transient dependency ([#7760](https://github.com/MetaMask/core/pull/7760))
15
+
16
+ ## [1.1.1]
17
+
18
+ ### Changed
19
+
20
+ - Bump `@metamask/transaction-controller` from `^62.9.2` to `^62.10.0` ([#7737](https://github.com/MetaMask/core/pull/7737))
21
+
22
+ ### Fixed
23
+
24
+ - Correctly validate `erc20-token-revocation` terms when decoding permission. ([#7729](https://github.com/MetaMask/core/pull/7729))
25
+
10
26
  ## [1.1.0]
11
27
 
12
28
  ### Changed
@@ -134,7 +150,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
134
150
 
135
151
  - Initial release ([#6033](https://github.com/MetaMask/core/pull/6033))
136
152
 
137
- [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@1.1.0...HEAD
153
+ [Unreleased]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@1.1.2...HEAD
154
+ [1.1.2]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@1.1.1...@metamask/gator-permissions-controller@1.1.2
155
+ [1.1.1]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@1.1.0...@metamask/gator-permissions-controller@1.1.1
138
156
  [1.1.0]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@1.0.0...@metamask/gator-permissions-controller@1.1.0
139
157
  [1.0.0]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@0.8.0...@metamask/gator-permissions-controller@1.0.0
140
158
  [0.8.0]: https://github.com/MetaMask/core/compare/@metamask/gator-permissions-controller@0.7.0...@metamask/gator-permissions-controller@0.8.0
@@ -117,7 +117,7 @@ const getPermissionDataAndExpiry = ({ contracts, caveats, permissionType, }) =>
117
117
  ...caveat,
118
118
  enforcer: (0, utils_1.getChecksumAddress)(caveat.enforcer),
119
119
  }));
120
- const { erc20StreamingEnforcer, erc20PeriodicEnforcer, nativeTokenStreamingEnforcer, nativeTokenPeriodicEnforcer, timestampEnforcer, } = (0, utils_2.getChecksumEnforcersByChainId)(contracts);
120
+ const { erc20StreamingEnforcer, erc20PeriodicEnforcer, nativeTokenStreamingEnforcer, nativeTokenPeriodicEnforcer, timestampEnforcer, allowedCalldataEnforcer, valueLteEnforcer, } = (0, utils_2.getChecksumEnforcersByChainId)(contracts);
121
121
  const expiryTerms = (0, utils_2.getTermsByEnforcer)({
122
122
  caveats: checksumCaveats,
123
123
  enforcer: timestampEnforcer,
@@ -186,6 +186,26 @@ const getPermissionDataAndExpiry = ({ contracts, caveats, permissionType, }) =>
186
186
  break;
187
187
  }
188
188
  case 'erc20-token-revocation': {
189
+ // 0 value for ValueLteEnforcer
190
+ const ZERO_32_BYTES = '0x0000000000000000000000000000000000000000000000000000000000000000';
191
+ // Approve() 4byte selector starting at index 0
192
+ const ERC20_APPROVE_SELECTOR_TERMS = '0x0000000000000000000000000000000000000000000000000000000000000000095ea7b3';
193
+ // 0 amount starting at index 24
194
+ const ERC20_APPROVE_ZERO_AMOUNT_TERMS = '0x00000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000';
195
+ const allowedCalldataCaveats = checksumCaveats.filter((caveat) => caveat.enforcer === allowedCalldataEnforcer);
196
+ const allowedCalldataTerms = allowedCalldataCaveats.map((caveat) => caveat.terms.toLowerCase());
197
+ const hasApproveSelector = allowedCalldataTerms.includes(ERC20_APPROVE_SELECTOR_TERMS);
198
+ const hasZeroAmount = allowedCalldataTerms.includes(ERC20_APPROVE_ZERO_AMOUNT_TERMS);
199
+ if (!hasApproveSelector || !hasZeroAmount) {
200
+ throw new Error('Invalid erc20-token-revocation terms: expected approve selector and zero amount constraints');
201
+ }
202
+ const valueLteTerms = (0, utils_2.getTermsByEnforcer)({
203
+ caveats: checksumCaveats,
204
+ enforcer: valueLteEnforcer,
205
+ });
206
+ if (valueLteTerms !== ZERO_32_BYTES) {
207
+ throw new Error('Invalid ValueLteEnforcer terms: maxValue must be 0');
208
+ }
189
209
  data = {};
190
210
  break;
191
211
  }
@@ -1 +1 @@
1
- {"version":3,"file":"decodePermission.cjs","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":";;;AACA,+DAA2D;AAC3D,2CAA+E;AAO/E,uCAKiB;AAEjB;;;;;;;;;;;;;;;;;GAiBG;AACI,MAAM,6BAA6B,GAAG,CAAC,EAC5C,SAAS,EACT,SAAS,GAIV,EAAkB,EAAE;IACnB,kEAAkE;IAClE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAe,CAAC;IACtC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,GAAG,CAAC,0BAAkB,CAAC,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAE5C,MAAM,eAAe,GAAG,IAAA,uCAA+B,EAAC,SAAS,CAAC,CAAC;IAEnE,IAAI,sBAAsB,GAA0B,IAAI,CAAC;IAEzD,KAAK,MAAM,EACT,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GACf,IAAI,eAAe,EAAE,CAAC;QACrB,0EAA0E;QAC1E,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAM;YACpC,GAAG,iBAAiB;YACpB,GAAG,iBAAiB,CAAC,IAAI,EAAE;SAC5B,CAAC,CAAC;QAEH,IAAI,qBAAqB,GAAG,KAAK,CAAC;QAElC,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAClC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,qBAAqB,GAAG,IAAI,CAAC;gBAC7B,MAAM;YACR,CAAC;QACH,CAAC;QAED,kDAAkD;QAClD,IAAI,mBAAmB,GAAG,IAAI,CAAC;QAC/B,KAAK,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,IAAI,iBAAiB,CAAC,OAAO,EAAE,EAAE,CAAC;YAChE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,aAAa,EAAE,CAAC;gBAC9C,mBAAmB,GAAG,KAAK,CAAC;gBAC5B,MAAM;YACR,CAAC;QACH,CAAC;QAED,IAAI,mBAAmB,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAClD,IAAI,sBAAsB,EAAE,CAAC;gBAC3B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACrD,CAAC;YACD,sBAAsB,GAAG,cAAc,CAAC;QAC1C,CAAC;IACH,CAAC;IAED,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,sBAAsB,CAAC;AAChC,CAAC,CAAC;AA5DW,QAAA,6BAA6B,iCA4DxC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,4BAA4B,GAAG,CAAC,KAAU,EAAU,EAAE;IAC1D,0FAA0F;IAC1F,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,qFAAqF,KAAK,CAAC,MAAM,EAAE,CACpG,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,IAAA,gBAAQ,EAAC,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAElD,IAAI,IAAA,mBAAW,EAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,mBAAW,EAAC,MAAM,CAAC,CAAC;IAEnC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,iEAAiE,CAClE,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACI,MAAM,0BAA0B,GAAG,CAAC,EACzC,SAAS,EACT,OAAO,EACP,cAAc,GAKf,EAGC,EAAE;IACF,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC/C,GAAG,MAAM;QACT,QAAQ,EAAE,IAAA,0BAAkB,EAAC,MAAM,CAAC,QAAQ,CAAC;KAC9C,CAAC,CAAC,CAAC;IAEJ,MAAM,EACJ,sBAAsB,EACtB,qBAAqB,EACrB,4BAA4B,EAC5B,2BAA2B,EAC3B,iBAAiB,GAClB,GAAG,IAAA,qCAA6B,EAAC,SAAS,CAAC,CAAC;IAE7C,MAAM,WAAW,GAAG,IAAA,0BAAkB,EAAC;QACrC,OAAO,EAAE,eAAe;QACxB,QAAQ,EAAE,iBAAiB;QAC3B,eAAe,EAAE,KAAK;KACvB,CAAC,CAAC;IAEH,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,GAAG,4BAA4B,CAAC,WAAW,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,IAA6C,CAAC;IAElD,QAAQ,cAAc,EAAE,CAAC;QACvB,KAAK,oBAAoB,CAAC,CAAC,CAAC;YAC1B,MAAM,mBAAmB,GAAG,IAAA,0BAAkB,EAAC;gBAC7C,OAAO,EAAE,eAAe;gBACxB,QAAQ,EAAE,sBAAsB;aACjC,CAAC,CAAC;YAEH,MAAM,CACJ,YAAY,EACZ,aAAa,EACb,SAAS,EACT,eAAe,EACf,YAAY,EACb,GAAG,IAAA,gBAAQ,EAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAExD,IAAI,GAAG;gBACL,YAAY;gBACZ,aAAa;gBACb,SAAS;gBACT,eAAe;gBACf,SAAS,EAAE,IAAA,mBAAW,EAAC,YAAY,CAAC;aACrC,CAAC;YACF,MAAM;QACR,CAAC;QACD,KAAK,sBAAsB,CAAC,CAAC,CAAC;YAC5B,MAAM,kBAAkB,GAAG,IAAA,0BAAkB,EAAC;gBAC5C,OAAO,EAAE,eAAe;gBACxB,QAAQ,EAAE,qBAAqB;aAChC,CAAC,CAAC;YAEH,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,CAAC,GACjE,IAAA,gBAAQ,EAAC,kBAAkB,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAEjD,IAAI,GAAG;gBACL,YAAY;gBACZ,YAAY;gBACZ,cAAc,EAAE,IAAA,mBAAW,EAAC,iBAAiB,CAAC;gBAC9C,SAAS,EAAE,IAAA,mBAAW,EAAC,YAAY,CAAC;aACrC,CAAC;YACF,MAAM;QACR,CAAC;QAED,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAC3B,MAAM,yBAAyB,GAAG,IAAA,0BAAkB,EAAC;gBACnD,OAAO,EAAE,eAAe;gBACxB,QAAQ,EAAE,4BAA4B;aACvC,CAAC,CAAC;YAEH,MAAM,CAAC,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,CAAC,GAC7D,IAAA,gBAAQ,EAAC,yBAAyB,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAExD,IAAI,GAAG;gBACL,aAAa;gBACb,SAAS;gBACT,eAAe;gBACf,SAAS,EAAE,IAAA,mBAAW,EAAC,YAAY,CAAC;aACrC,CAAC;YACF,MAAM;QACR,CAAC;QACD,KAAK,uBAAuB,CAAC,CAAC,CAAC;YAC7B,MAAM,wBAAwB,GAAG,IAAA,0BAAkB,EAAC;gBAClD,OAAO,EAAE,eAAe;gBACxB,QAAQ,EAAE,2BAA2B;aACtC,CAAC,CAAC;YAEH,MAAM,CAAC,YAAY,EAAE,iBAAiB,EAAE,YAAY,CAAC,GAAG,IAAA,gBAAQ,EAC9D,wBAAwB,EACxB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CACb,CAAC;YAEF,IAAI,GAAG;gBACL,YAAY;gBACZ,cAAc,EAAE,IAAA,mBAAW,EAAC,iBAAiB,CAAC;gBAC9C,SAAS,EAAE,IAAA,mBAAW,EAAC,YAAY,CAAC;aACrC,CAAC;YACF,MAAM;QACR,CAAC;QACD,KAAK,wBAAwB,CAAC,CAAC,CAAC;YAC9B,IAAI,GAAG,EAAE,CAAC;YACV,MAAM;QACR,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC,CAAC;AA5HW,QAAA,0BAA0B,8BA4HrC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACI,MAAM,4BAA4B,GAAG,CAAC,EAC3C,OAAO,EACP,cAAc,EACd,SAAS,EACT,QAAQ,EACR,SAAS,EACT,MAAM,EACN,IAAI,EACJ,aAAa,EACb,eAAe,GAWhB,EAAqB,EAAE;IACtB,IAAI,SAAS,KAAK,gCAAc,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,UAAU,GAAsB;QACpC,OAAO,EAAE,IAAA,mBAAW,EAAC,OAAO,CAAC;QAC7B,IAAI,EAAE,SAAS;QACf,EAAE,EAAE,QAAQ;QACZ,UAAU,EAAE;YACV,IAAI,EAAE,cAAc;YACpB,IAAI;YACJ,aAAa;SACd;QACD,MAAM;QACN,MAAM,EAAE,eAAe;KACxB,CAAC;IAEF,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC;AAvCW,QAAA,4BAA4B,gCAuCvC","sourcesContent":["import type { Caveat, Hex } from '@metamask/delegation-core';\nimport { ROOT_AUTHORITY } from '@metamask/delegation-core';\nimport { getChecksumAddress, hexToNumber, numberToHex } from '@metamask/utils';\n\nimport type {\n DecodedPermission,\n DeployedContractsByName,\n PermissionType,\n} from './types';\nimport {\n createPermissionRulesForChainId,\n getChecksumEnforcersByChainId,\n getTermsByEnforcer,\n splitHex,\n} from './utils';\n\n/**\n * Identifies the unique permission type that matches a given set of enforcer\n * contract addresses for a specific chain.\n *\n * A permission type matches when:\n * - All of its required enforcers are present in the provided list; and\n * - No provided enforcer falls outside the union of the type's required and\n * optional enforcers (currently only `TimestampEnforcer` is allowed extra).\n *\n * If exactly one permission type matches, its identifier is returned.\n *\n * @param args - The arguments to this function.\n * @param args.enforcers - List of enforcer contract addresses (hex strings).\n *\n * @param args.contracts - The deployed contracts for the chain.\n * @returns The identifier of the matching permission type.\n * @throws If no permission type matches, or if more than one permission type matches.\n */\nexport const identifyPermissionByEnforcers = ({\n enforcers,\n contracts,\n}: {\n enforcers: Hex[];\n contracts: DeployedContractsByName;\n}): PermissionType => {\n // Build frequency map for enforcers (using checksummed addresses)\n const counts = new Map<Hex, number>();\n for (const addr of enforcers.map(getChecksumAddress)) {\n counts.set(addr, (counts.get(addr) ?? 0) + 1);\n }\n const enforcersSet = new Set(counts.keys());\n\n const permissionRules = createPermissionRulesForChainId(contracts);\n\n let matchingPermissionType: PermissionType | null = null;\n\n for (const {\n optionalEnforcers,\n requiredEnforcers,\n permissionType,\n } of permissionRules) {\n // union of optional + required enforcers. Any other address is forbidden.\n const allowedEnforcers = new Set<Hex>([\n ...optionalEnforcers,\n ...requiredEnforcers.keys(),\n ]);\n\n let hasForbiddenEnforcers = false;\n\n for (const caveat of enforcersSet) {\n if (!allowedEnforcers.has(caveat)) {\n hasForbiddenEnforcers = true;\n break;\n }\n }\n\n // exact multiplicity match for required enforcers\n let meetsRequiredCounts = true;\n for (const [addr, requiredCount] of requiredEnforcers.entries()) {\n if ((counts.get(addr) ?? 0) !== requiredCount) {\n meetsRequiredCounts = false;\n break;\n }\n }\n\n if (meetsRequiredCounts && !hasForbiddenEnforcers) {\n if (matchingPermissionType) {\n throw new Error('Multiple permission types match');\n }\n matchingPermissionType = permissionType;\n }\n }\n\n if (!matchingPermissionType) {\n throw new Error('Unable to identify permission type');\n }\n\n return matchingPermissionType;\n};\n\n/**\n * Extracts the expiry timestamp from TimestampEnforcer caveat terms.\n *\n * Based on the TimestampEnforcer contract encoding:\n * - Terms are 32 bytes total (64 hex characters without '0x')\n * - First 16 bytes (32 hex chars): timestampAfterThreshold (uint128) - must be 0\n * - Last 16 bytes (32 hex chars): timestampBeforeThreshold (uint128) - this is the expiry\n *\n * @param terms - The hex-encoded terms from a TimestampEnforcer caveat\n * @returns The expiry timestamp in seconds\n * @throws If the terms are not exactly 32 bytes, if the timestampAfterThreshold is non-zero,\n * or if the timestampBeforeThreshold is zero\n */\nconst extractExpiryFromCaveatTerms = (terms: Hex): number => {\n // Validate terms length: must be exactly 32 bytes (64 hex chars + '0x' prefix = 66 chars)\n if (terms.length !== 66) {\n throw new Error(\n `Invalid TimestampEnforcer terms length: expected 66 characters (0x + 64 hex), got ${terms.length}`,\n );\n }\n\n const [after, before] = splitHex(terms, [16, 16]);\n\n if (hexToNumber(after) !== 0) {\n throw new Error('Invalid expiry: timestampAfterThreshold must be 0');\n }\n\n const expiry = hexToNumber(before);\n\n if (expiry === 0) {\n throw new Error(\n 'Invalid expiry: timestampBeforeThreshold must be greater than 0',\n );\n }\n\n return expiry;\n};\n\n/**\n * Extracts the permission-specific data payload and the expiry timestamp from\n * the provided caveats for a given permission type.\n *\n * This function locates the relevant caveat enforcer for the `permissionType`,\n * interprets its `terms` by splitting the hex string into byte-sized segments,\n * and converts each segment into the appropriate numeric or address shape.\n *\n * The expiry timestamp is derived from the `TimestampEnforcer` terms and must\n * have a zero `timestampAfterThreshold` and a positive `timestampBeforeThreshold`.\n *\n * @param args - The arguments to this function.\n * @param args.contracts - The deployed contracts for the chain.\n * @param args.caveats - Caveats decoded from the permission context.\n * @param args.permissionType - The previously identified permission type.\n *\n * @returns An object containing the `expiry` timestamp and the decoded `data` payload.\n * @throws If the caveats are malformed, missing, or the terms fail to decode.\n */\nexport const getPermissionDataAndExpiry = ({\n contracts,\n caveats,\n permissionType,\n}: {\n contracts: DeployedContractsByName;\n caveats: Caveat<Hex>[];\n permissionType: PermissionType;\n}): {\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n} => {\n const checksumCaveats = caveats.map((caveat) => ({\n ...caveat,\n enforcer: getChecksumAddress(caveat.enforcer),\n }));\n\n const {\n erc20StreamingEnforcer,\n erc20PeriodicEnforcer,\n nativeTokenStreamingEnforcer,\n nativeTokenPeriodicEnforcer,\n timestampEnforcer,\n } = getChecksumEnforcersByChainId(contracts);\n\n const expiryTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: timestampEnforcer,\n throwIfNotFound: false,\n });\n\n let expiry: number | null = null;\n if (expiryTerms) {\n expiry = extractExpiryFromCaveatTerms(expiryTerms);\n }\n\n let data: DecodedPermission['permission']['data'];\n\n switch (permissionType) {\n case 'erc20-token-stream': {\n const erc20StreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20StreamingEnforcer,\n });\n\n const [\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTimeRaw,\n ] = splitHex(erc20StreamingTerms, [20, 32, 32, 32, 32]);\n\n data = {\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-periodic': {\n const erc20PeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20PeriodicEnforcer,\n });\n\n const [tokenAddress, periodAmount, periodDurationRaw, startTimeRaw] =\n splitHex(erc20PeriodicTerms, [20, 32, 32, 32]);\n\n data = {\n tokenAddress,\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n\n case 'native-token-stream': {\n const nativeTokenStreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenStreamingEnforcer,\n });\n\n const [initialAmount, maxAmount, amountPerSecond, startTimeRaw] =\n splitHex(nativeTokenStreamingTerms, [32, 32, 32, 32]);\n\n data = {\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'native-token-periodic': {\n const nativeTokenPeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenPeriodicEnforcer,\n });\n\n const [periodAmount, periodDurationRaw, startTimeRaw] = splitHex(\n nativeTokenPeriodicTerms,\n [32, 32, 32],\n );\n\n data = {\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-revocation': {\n data = {};\n break;\n }\n default:\n throw new Error('Invalid permission type');\n }\n\n return { expiry, data };\n};\n\n/**\n * Reconstructs a {@link DecodedPermission} object from primitive values\n * obtained while decoding a permission context.\n *\n * The resulting object contains:\n * - `chainId` encoded as hex (`0x…`)\n * - `address` set to the delegator (user account)\n * - `signer` set to an account signer with the delegate address\n * - `permission` with the identified type and decoded data\n * - `expiry` timestamp (or null)\n *\n * @param args - The arguments to this function.\n * @param args.chainId - Chain ID.\n * @param args.permissionType - Identified permission type.\n * @param args.delegator - Address of the account delegating permission.\n * @param args.delegate - Address that will act under the granted permission.\n * @param args.authority - Authority identifier; must be ROOT_AUTHORITY.\n * @param args.expiry - Expiry timestamp (unix seconds) or null if unbounded.\n * @param args.data - Permission-specific decoded data payload.\n * @param args.justification - Human-readable justification for the permission.\n * @param args.specifiedOrigin - The origin reported in the request metadata.\n *\n * @returns The reconstructed {@link DecodedPermission}.\n */\nexport const reconstructDecodedPermission = ({\n chainId,\n permissionType,\n delegator,\n delegate,\n authority,\n expiry,\n data,\n justification,\n specifiedOrigin,\n}: {\n chainId: number;\n permissionType: PermissionType;\n delegator: Hex;\n delegate: Hex;\n authority: Hex;\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n justification: string;\n specifiedOrigin: string;\n}): DecodedPermission => {\n if (authority !== ROOT_AUTHORITY) {\n throw new Error('Invalid authority');\n }\n\n const permission: DecodedPermission = {\n chainId: numberToHex(chainId),\n from: delegator,\n to: delegate,\n permission: {\n type: permissionType,\n data,\n justification,\n },\n expiry,\n origin: specifiedOrigin,\n };\n\n return permission;\n};\n"]}
1
+ {"version":3,"file":"decodePermission.cjs","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Caveat, Hex } from '@metamask/delegation-core';\nimport { ROOT_AUTHORITY } from '@metamask/delegation-core';\nimport { getChecksumAddress, hexToNumber, numberToHex } from '@metamask/utils';\n\nimport type {\n DecodedPermission,\n DeployedContractsByName,\n PermissionType,\n} from './types';\nimport {\n createPermissionRulesForChainId,\n getChecksumEnforcersByChainId,\n getTermsByEnforcer,\n splitHex,\n} from './utils';\n\n/**\n * Identifies the unique permission type that matches a given set of enforcer\n * contract addresses for a specific chain.\n *\n * A permission type matches when:\n * - All of its required enforcers are present in the provided list; and\n * - No provided enforcer falls outside the union of the type's required and\n * optional enforcers (currently only `TimestampEnforcer` is allowed extra).\n *\n * If exactly one permission type matches, its identifier is returned.\n *\n * @param args - The arguments to this function.\n * @param args.enforcers - List of enforcer contract addresses (hex strings).\n *\n * @param args.contracts - The deployed contracts for the chain.\n * @returns The identifier of the matching permission type.\n * @throws If no permission type matches, or if more than one permission type matches.\n */\nexport const identifyPermissionByEnforcers = ({\n enforcers,\n contracts,\n}: {\n enforcers: Hex[];\n contracts: DeployedContractsByName;\n}): PermissionType => {\n // Build frequency map for enforcers (using checksummed addresses)\n const counts = new Map<Hex, number>();\n for (const addr of enforcers.map(getChecksumAddress)) {\n counts.set(addr, (counts.get(addr) ?? 0) + 1);\n }\n const enforcersSet = new Set(counts.keys());\n\n const permissionRules = createPermissionRulesForChainId(contracts);\n\n let matchingPermissionType: PermissionType | null = null;\n\n for (const {\n optionalEnforcers,\n requiredEnforcers,\n permissionType,\n } of permissionRules) {\n // union of optional + required enforcers. Any other address is forbidden.\n const allowedEnforcers = new Set<Hex>([\n ...optionalEnforcers,\n ...requiredEnforcers.keys(),\n ]);\n\n let hasForbiddenEnforcers = false;\n\n for (const caveat of enforcersSet) {\n if (!allowedEnforcers.has(caveat)) {\n hasForbiddenEnforcers = true;\n break;\n }\n }\n\n // exact multiplicity match for required enforcers\n let meetsRequiredCounts = true;\n for (const [addr, requiredCount] of requiredEnforcers.entries()) {\n if ((counts.get(addr) ?? 0) !== requiredCount) {\n meetsRequiredCounts = false;\n break;\n }\n }\n\n if (meetsRequiredCounts && !hasForbiddenEnforcers) {\n if (matchingPermissionType) {\n throw new Error('Multiple permission types match');\n }\n matchingPermissionType = permissionType;\n }\n }\n\n if (!matchingPermissionType) {\n throw new Error('Unable to identify permission type');\n }\n\n return matchingPermissionType;\n};\n\n/**\n * Extracts the expiry timestamp from TimestampEnforcer caveat terms.\n *\n * Based on the TimestampEnforcer contract encoding:\n * - Terms are 32 bytes total (64 hex characters without '0x')\n * - First 16 bytes (32 hex chars): timestampAfterThreshold (uint128) - must be 0\n * - Last 16 bytes (32 hex chars): timestampBeforeThreshold (uint128) - this is the expiry\n *\n * @param terms - The hex-encoded terms from a TimestampEnforcer caveat\n * @returns The expiry timestamp in seconds\n * @throws If the terms are not exactly 32 bytes, if the timestampAfterThreshold is non-zero,\n * or if the timestampBeforeThreshold is zero\n */\nconst extractExpiryFromCaveatTerms = (terms: Hex): number => {\n // Validate terms length: must be exactly 32 bytes (64 hex chars + '0x' prefix = 66 chars)\n if (terms.length !== 66) {\n throw new Error(\n `Invalid TimestampEnforcer terms length: expected 66 characters (0x + 64 hex), got ${terms.length}`,\n );\n }\n\n const [after, before] = splitHex(terms, [16, 16]);\n\n if (hexToNumber(after) !== 0) {\n throw new Error('Invalid expiry: timestampAfterThreshold must be 0');\n }\n\n const expiry = hexToNumber(before);\n\n if (expiry === 0) {\n throw new Error(\n 'Invalid expiry: timestampBeforeThreshold must be greater than 0',\n );\n }\n\n return expiry;\n};\n\n/**\n * Extracts the permission-specific data payload and the expiry timestamp from\n * the provided caveats for a given permission type.\n *\n * This function locates the relevant caveat enforcer for the `permissionType`,\n * interprets its `terms` by splitting the hex string into byte-sized segments,\n * and converts each segment into the appropriate numeric or address shape.\n *\n * The expiry timestamp is derived from the `TimestampEnforcer` terms and must\n * have a zero `timestampAfterThreshold` and a positive `timestampBeforeThreshold`.\n *\n * @param args - The arguments to this function.\n * @param args.contracts - The deployed contracts for the chain.\n * @param args.caveats - Caveats decoded from the permission context.\n * @param args.permissionType - The previously identified permission type.\n *\n * @returns An object containing the `expiry` timestamp and the decoded `data` payload.\n * @throws If the caveats are malformed, missing, or the terms fail to decode.\n */\nexport const getPermissionDataAndExpiry = ({\n contracts,\n caveats,\n permissionType,\n}: {\n contracts: DeployedContractsByName;\n caveats: Caveat<Hex>[];\n permissionType: PermissionType;\n}): {\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n} => {\n const checksumCaveats = caveats.map((caveat) => ({\n ...caveat,\n enforcer: getChecksumAddress(caveat.enforcer),\n }));\n\n const {\n erc20StreamingEnforcer,\n erc20PeriodicEnforcer,\n nativeTokenStreamingEnforcer,\n nativeTokenPeriodicEnforcer,\n timestampEnforcer,\n allowedCalldataEnforcer,\n valueLteEnforcer,\n } = getChecksumEnforcersByChainId(contracts);\n\n const expiryTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: timestampEnforcer,\n throwIfNotFound: false,\n });\n\n let expiry: number | null = null;\n if (expiryTerms) {\n expiry = extractExpiryFromCaveatTerms(expiryTerms);\n }\n\n let data: DecodedPermission['permission']['data'];\n\n switch (permissionType) {\n case 'erc20-token-stream': {\n const erc20StreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20StreamingEnforcer,\n });\n\n const [\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTimeRaw,\n ] = splitHex(erc20StreamingTerms, [20, 32, 32, 32, 32]);\n\n data = {\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-periodic': {\n const erc20PeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20PeriodicEnforcer,\n });\n\n const [tokenAddress, periodAmount, periodDurationRaw, startTimeRaw] =\n splitHex(erc20PeriodicTerms, [20, 32, 32, 32]);\n\n data = {\n tokenAddress,\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n\n case 'native-token-stream': {\n const nativeTokenStreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenStreamingEnforcer,\n });\n\n const [initialAmount, maxAmount, amountPerSecond, startTimeRaw] =\n splitHex(nativeTokenStreamingTerms, [32, 32, 32, 32]);\n\n data = {\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'native-token-periodic': {\n const nativeTokenPeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenPeriodicEnforcer,\n });\n\n const [periodAmount, periodDurationRaw, startTimeRaw] = splitHex(\n nativeTokenPeriodicTerms,\n [32, 32, 32],\n );\n\n data = {\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-revocation': {\n // 0 value for ValueLteEnforcer\n const ZERO_32_BYTES =\n '0x0000000000000000000000000000000000000000000000000000000000000000' as const;\n\n // Approve() 4byte selector starting at index 0\n const ERC20_APPROVE_SELECTOR_TERMS =\n '0x0000000000000000000000000000000000000000000000000000000000000000095ea7b3' as const;\n\n // 0 amount starting at index 24\n const ERC20_APPROVE_ZERO_AMOUNT_TERMS =\n '0x00000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000' as const;\n\n const allowedCalldataCaveats = checksumCaveats.filter(\n (caveat) => caveat.enforcer === allowedCalldataEnforcer,\n );\n\n const allowedCalldataTerms = allowedCalldataCaveats.map((caveat) =>\n caveat.terms.toLowerCase(),\n );\n\n const hasApproveSelector = allowedCalldataTerms.includes(\n ERC20_APPROVE_SELECTOR_TERMS,\n );\n\n const hasZeroAmount = allowedCalldataTerms.includes(\n ERC20_APPROVE_ZERO_AMOUNT_TERMS,\n );\n\n if (!hasApproveSelector || !hasZeroAmount) {\n throw new Error(\n 'Invalid erc20-token-revocation terms: expected approve selector and zero amount constraints',\n );\n }\n\n const valueLteTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: valueLteEnforcer,\n });\n\n if (valueLteTerms !== ZERO_32_BYTES) {\n throw new Error('Invalid ValueLteEnforcer terms: maxValue must be 0');\n }\n\n data = {};\n break;\n }\n default:\n throw new Error('Invalid permission type');\n }\n\n return { expiry, data };\n};\n\n/**\n * Reconstructs a {@link DecodedPermission} object from primitive values\n * obtained while decoding a permission context.\n *\n * The resulting object contains:\n * - `chainId` encoded as hex (`0x…`)\n * - `address` set to the delegator (user account)\n * - `signer` set to an account signer with the delegate address\n * - `permission` with the identified type and decoded data\n * - `expiry` timestamp (or null)\n *\n * @param args - The arguments to this function.\n * @param args.chainId - Chain ID.\n * @param args.permissionType - Identified permission type.\n * @param args.delegator - Address of the account delegating permission.\n * @param args.delegate - Address that will act under the granted permission.\n * @param args.authority - Authority identifier; must be ROOT_AUTHORITY.\n * @param args.expiry - Expiry timestamp (unix seconds) or null if unbounded.\n * @param args.data - Permission-specific decoded data payload.\n * @param args.justification - Human-readable justification for the permission.\n * @param args.specifiedOrigin - The origin reported in the request metadata.\n *\n * @returns The reconstructed {@link DecodedPermission}.\n */\nexport const reconstructDecodedPermission = ({\n chainId,\n permissionType,\n delegator,\n delegate,\n authority,\n expiry,\n data,\n justification,\n specifiedOrigin,\n}: {\n chainId: number;\n permissionType: PermissionType;\n delegator: Hex;\n delegate: Hex;\n authority: Hex;\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n justification: string;\n specifiedOrigin: string;\n}): DecodedPermission => {\n if (authority !== ROOT_AUTHORITY) {\n throw new Error('Invalid authority');\n }\n\n const permission: DecodedPermission = {\n chainId: numberToHex(chainId),\n from: delegator,\n to: delegate,\n permission: {\n type: permissionType,\n data,\n justification,\n },\n expiry,\n origin: specifiedOrigin,\n };\n\n return permission;\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"decodePermission.d.cts","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,kCAAkC;AAI7D,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACf,oBAAgB;AAQjB;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,6BAA6B;eAI7B,GAAG,EAAE;eACL,uBAAuB;MAChC,cAsDH,CAAC;AAwCF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B;eAK1B,uBAAuB;aACzB,OAAO,GAAG,CAAC,EAAE;oBACN,cAAc;;YAEtB,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;CAkH9C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,4BAA4B;aAW9B,MAAM;oBACC,cAAc;eACnB,GAAG;cACJ,GAAG;eACF,GAAG;YACN,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;mBAC9B,MAAM;qBACJ,MAAM;MACrB,iBAmBH,CAAC"}
1
+ {"version":3,"file":"decodePermission.d.cts","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,kCAAkC;AAI7D,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACf,oBAAgB;AAQjB;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,6BAA6B;eAI7B,GAAG,EAAE;eACL,uBAAuB;MAChC,cAsDH,CAAC;AAwCF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B;eAK1B,uBAAuB;aACzB,OAAO,GAAG,CAAC,EAAE;oBACN,cAAc;;YAEtB,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;CA+J9C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,4BAA4B;aAW9B,MAAM;oBACC,cAAc;eACnB,GAAG;cACJ,GAAG;eACF,GAAG;YACN,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;mBAC9B,MAAM;qBACJ,MAAM;MACrB,iBAmBH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"decodePermission.d.mts","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,kCAAkC;AAI7D,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACf,oBAAgB;AAQjB;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,6BAA6B;eAI7B,GAAG,EAAE;eACL,uBAAuB;MAChC,cAsDH,CAAC;AAwCF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B;eAK1B,uBAAuB;aACzB,OAAO,GAAG,CAAC,EAAE;oBACN,cAAc;;YAEtB,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;CAkH9C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,4BAA4B;aAW9B,MAAM;oBACC,cAAc;eACnB,GAAG;cACJ,GAAG;eACF,GAAG;YACN,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;mBAC9B,MAAM;qBACJ,MAAM;MACrB,iBAmBH,CAAC"}
1
+ {"version":3,"file":"decodePermission.d.mts","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,kCAAkC;AAI7D,OAAO,KAAK,EACV,iBAAiB,EACjB,uBAAuB,EACvB,cAAc,EACf,oBAAgB;AAQjB;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,6BAA6B;eAI7B,GAAG,EAAE;eACL,uBAAuB;MAChC,cAsDH,CAAC;AAwCF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B;eAK1B,uBAAuB;aACzB,OAAO,GAAG,CAAC,EAAE;oBACN,cAAc;;YAEtB,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;CA+J9C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,4BAA4B;aAW9B,MAAM;oBACC,cAAc;eACnB,GAAG;cACJ,GAAG;eACF,GAAG;YACN,MAAM,GAAG,IAAI;UACf,iBAAiB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;mBAC9B,MAAM;qBACJ,MAAM;MACrB,iBAmBH,CAAC"}
@@ -113,7 +113,7 @@ export const getPermissionDataAndExpiry = ({ contracts, caveats, permissionType,
113
113
  ...caveat,
114
114
  enforcer: getChecksumAddress(caveat.enforcer),
115
115
  }));
116
- const { erc20StreamingEnforcer, erc20PeriodicEnforcer, nativeTokenStreamingEnforcer, nativeTokenPeriodicEnforcer, timestampEnforcer, } = getChecksumEnforcersByChainId(contracts);
116
+ const { erc20StreamingEnforcer, erc20PeriodicEnforcer, nativeTokenStreamingEnforcer, nativeTokenPeriodicEnforcer, timestampEnforcer, allowedCalldataEnforcer, valueLteEnforcer, } = getChecksumEnforcersByChainId(contracts);
117
117
  const expiryTerms = getTermsByEnforcer({
118
118
  caveats: checksumCaveats,
119
119
  enforcer: timestampEnforcer,
@@ -182,6 +182,26 @@ export const getPermissionDataAndExpiry = ({ contracts, caveats, permissionType,
182
182
  break;
183
183
  }
184
184
  case 'erc20-token-revocation': {
185
+ // 0 value for ValueLteEnforcer
186
+ const ZERO_32_BYTES = '0x0000000000000000000000000000000000000000000000000000000000000000';
187
+ // Approve() 4byte selector starting at index 0
188
+ const ERC20_APPROVE_SELECTOR_TERMS = '0x0000000000000000000000000000000000000000000000000000000000000000095ea7b3';
189
+ // 0 amount starting at index 24
190
+ const ERC20_APPROVE_ZERO_AMOUNT_TERMS = '0x00000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000';
191
+ const allowedCalldataCaveats = checksumCaveats.filter((caveat) => caveat.enforcer === allowedCalldataEnforcer);
192
+ const allowedCalldataTerms = allowedCalldataCaveats.map((caveat) => caveat.terms.toLowerCase());
193
+ const hasApproveSelector = allowedCalldataTerms.includes(ERC20_APPROVE_SELECTOR_TERMS);
194
+ const hasZeroAmount = allowedCalldataTerms.includes(ERC20_APPROVE_ZERO_AMOUNT_TERMS);
195
+ if (!hasApproveSelector || !hasZeroAmount) {
196
+ throw new Error('Invalid erc20-token-revocation terms: expected approve selector and zero amount constraints');
197
+ }
198
+ const valueLteTerms = getTermsByEnforcer({
199
+ caveats: checksumCaveats,
200
+ enforcer: valueLteEnforcer,
201
+ });
202
+ if (valueLteTerms !== ZERO_32_BYTES) {
203
+ throw new Error('Invalid ValueLteEnforcer terms: maxValue must be 0');
204
+ }
185
205
  data = {};
186
206
  break;
187
207
  }
@@ -1 +1 @@
1
- {"version":3,"file":"decodePermission.mjs","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Caveat, Hex } from '@metamask/delegation-core';\nimport { ROOT_AUTHORITY } from '@metamask/delegation-core';\nimport { getChecksumAddress, hexToNumber, numberToHex } from '@metamask/utils';\n\nimport type {\n DecodedPermission,\n DeployedContractsByName,\n PermissionType,\n} from './types';\nimport {\n createPermissionRulesForChainId,\n getChecksumEnforcersByChainId,\n getTermsByEnforcer,\n splitHex,\n} from './utils';\n\n/**\n * Identifies the unique permission type that matches a given set of enforcer\n * contract addresses for a specific chain.\n *\n * A permission type matches when:\n * - All of its required enforcers are present in the provided list; and\n * - No provided enforcer falls outside the union of the type's required and\n * optional enforcers (currently only `TimestampEnforcer` is allowed extra).\n *\n * If exactly one permission type matches, its identifier is returned.\n *\n * @param args - The arguments to this function.\n * @param args.enforcers - List of enforcer contract addresses (hex strings).\n *\n * @param args.contracts - The deployed contracts for the chain.\n * @returns The identifier of the matching permission type.\n * @throws If no permission type matches, or if more than one permission type matches.\n */\nexport const identifyPermissionByEnforcers = ({\n enforcers,\n contracts,\n}: {\n enforcers: Hex[];\n contracts: DeployedContractsByName;\n}): PermissionType => {\n // Build frequency map for enforcers (using checksummed addresses)\n const counts = new Map<Hex, number>();\n for (const addr of enforcers.map(getChecksumAddress)) {\n counts.set(addr, (counts.get(addr) ?? 0) + 1);\n }\n const enforcersSet = new Set(counts.keys());\n\n const permissionRules = createPermissionRulesForChainId(contracts);\n\n let matchingPermissionType: PermissionType | null = null;\n\n for (const {\n optionalEnforcers,\n requiredEnforcers,\n permissionType,\n } of permissionRules) {\n // union of optional + required enforcers. Any other address is forbidden.\n const allowedEnforcers = new Set<Hex>([\n ...optionalEnforcers,\n ...requiredEnforcers.keys(),\n ]);\n\n let hasForbiddenEnforcers = false;\n\n for (const caveat of enforcersSet) {\n if (!allowedEnforcers.has(caveat)) {\n hasForbiddenEnforcers = true;\n break;\n }\n }\n\n // exact multiplicity match for required enforcers\n let meetsRequiredCounts = true;\n for (const [addr, requiredCount] of requiredEnforcers.entries()) {\n if ((counts.get(addr) ?? 0) !== requiredCount) {\n meetsRequiredCounts = false;\n break;\n }\n }\n\n if (meetsRequiredCounts && !hasForbiddenEnforcers) {\n if (matchingPermissionType) {\n throw new Error('Multiple permission types match');\n }\n matchingPermissionType = permissionType;\n }\n }\n\n if (!matchingPermissionType) {\n throw new Error('Unable to identify permission type');\n }\n\n return matchingPermissionType;\n};\n\n/**\n * Extracts the expiry timestamp from TimestampEnforcer caveat terms.\n *\n * Based on the TimestampEnforcer contract encoding:\n * - Terms are 32 bytes total (64 hex characters without '0x')\n * - First 16 bytes (32 hex chars): timestampAfterThreshold (uint128) - must be 0\n * - Last 16 bytes (32 hex chars): timestampBeforeThreshold (uint128) - this is the expiry\n *\n * @param terms - The hex-encoded terms from a TimestampEnforcer caveat\n * @returns The expiry timestamp in seconds\n * @throws If the terms are not exactly 32 bytes, if the timestampAfterThreshold is non-zero,\n * or if the timestampBeforeThreshold is zero\n */\nconst extractExpiryFromCaveatTerms = (terms: Hex): number => {\n // Validate terms length: must be exactly 32 bytes (64 hex chars + '0x' prefix = 66 chars)\n if (terms.length !== 66) {\n throw new Error(\n `Invalid TimestampEnforcer terms length: expected 66 characters (0x + 64 hex), got ${terms.length}`,\n );\n }\n\n const [after, before] = splitHex(terms, [16, 16]);\n\n if (hexToNumber(after) !== 0) {\n throw new Error('Invalid expiry: timestampAfterThreshold must be 0');\n }\n\n const expiry = hexToNumber(before);\n\n if (expiry === 0) {\n throw new Error(\n 'Invalid expiry: timestampBeforeThreshold must be greater than 0',\n );\n }\n\n return expiry;\n};\n\n/**\n * Extracts the permission-specific data payload and the expiry timestamp from\n * the provided caveats for a given permission type.\n *\n * This function locates the relevant caveat enforcer for the `permissionType`,\n * interprets its `terms` by splitting the hex string into byte-sized segments,\n * and converts each segment into the appropriate numeric or address shape.\n *\n * The expiry timestamp is derived from the `TimestampEnforcer` terms and must\n * have a zero `timestampAfterThreshold` and a positive `timestampBeforeThreshold`.\n *\n * @param args - The arguments to this function.\n * @param args.contracts - The deployed contracts for the chain.\n * @param args.caveats - Caveats decoded from the permission context.\n * @param args.permissionType - The previously identified permission type.\n *\n * @returns An object containing the `expiry` timestamp and the decoded `data` payload.\n * @throws If the caveats are malformed, missing, or the terms fail to decode.\n */\nexport const getPermissionDataAndExpiry = ({\n contracts,\n caveats,\n permissionType,\n}: {\n contracts: DeployedContractsByName;\n caveats: Caveat<Hex>[];\n permissionType: PermissionType;\n}): {\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n} => {\n const checksumCaveats = caveats.map((caveat) => ({\n ...caveat,\n enforcer: getChecksumAddress(caveat.enforcer),\n }));\n\n const {\n erc20StreamingEnforcer,\n erc20PeriodicEnforcer,\n nativeTokenStreamingEnforcer,\n nativeTokenPeriodicEnforcer,\n timestampEnforcer,\n } = getChecksumEnforcersByChainId(contracts);\n\n const expiryTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: timestampEnforcer,\n throwIfNotFound: false,\n });\n\n let expiry: number | null = null;\n if (expiryTerms) {\n expiry = extractExpiryFromCaveatTerms(expiryTerms);\n }\n\n let data: DecodedPermission['permission']['data'];\n\n switch (permissionType) {\n case 'erc20-token-stream': {\n const erc20StreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20StreamingEnforcer,\n });\n\n const [\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTimeRaw,\n ] = splitHex(erc20StreamingTerms, [20, 32, 32, 32, 32]);\n\n data = {\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-periodic': {\n const erc20PeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20PeriodicEnforcer,\n });\n\n const [tokenAddress, periodAmount, periodDurationRaw, startTimeRaw] =\n splitHex(erc20PeriodicTerms, [20, 32, 32, 32]);\n\n data = {\n tokenAddress,\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n\n case 'native-token-stream': {\n const nativeTokenStreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenStreamingEnforcer,\n });\n\n const [initialAmount, maxAmount, amountPerSecond, startTimeRaw] =\n splitHex(nativeTokenStreamingTerms, [32, 32, 32, 32]);\n\n data = {\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'native-token-periodic': {\n const nativeTokenPeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenPeriodicEnforcer,\n });\n\n const [periodAmount, periodDurationRaw, startTimeRaw] = splitHex(\n nativeTokenPeriodicTerms,\n [32, 32, 32],\n );\n\n data = {\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-revocation': {\n data = {};\n break;\n }\n default:\n throw new Error('Invalid permission type');\n }\n\n return { expiry, data };\n};\n\n/**\n * Reconstructs a {@link DecodedPermission} object from primitive values\n * obtained while decoding a permission context.\n *\n * The resulting object contains:\n * - `chainId` encoded as hex (`0x…`)\n * - `address` set to the delegator (user account)\n * - `signer` set to an account signer with the delegate address\n * - `permission` with the identified type and decoded data\n * - `expiry` timestamp (or null)\n *\n * @param args - The arguments to this function.\n * @param args.chainId - Chain ID.\n * @param args.permissionType - Identified permission type.\n * @param args.delegator - Address of the account delegating permission.\n * @param args.delegate - Address that will act under the granted permission.\n * @param args.authority - Authority identifier; must be ROOT_AUTHORITY.\n * @param args.expiry - Expiry timestamp (unix seconds) or null if unbounded.\n * @param args.data - Permission-specific decoded data payload.\n * @param args.justification - Human-readable justification for the permission.\n * @param args.specifiedOrigin - The origin reported in the request metadata.\n *\n * @returns The reconstructed {@link DecodedPermission}.\n */\nexport const reconstructDecodedPermission = ({\n chainId,\n permissionType,\n delegator,\n delegate,\n authority,\n expiry,\n data,\n justification,\n specifiedOrigin,\n}: {\n chainId: number;\n permissionType: PermissionType;\n delegator: Hex;\n delegate: Hex;\n authority: Hex;\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n justification: string;\n specifiedOrigin: string;\n}): DecodedPermission => {\n if (authority !== ROOT_AUTHORITY) {\n throw new Error('Invalid authority');\n }\n\n const permission: DecodedPermission = {\n chainId: numberToHex(chainId),\n from: delegator,\n to: delegate,\n permission: {\n type: permissionType,\n data,\n justification,\n },\n expiry,\n origin: specifiedOrigin,\n };\n\n return permission;\n};\n"]}
1
+ {"version":3,"file":"decodePermission.mjs","sourceRoot":"","sources":["../../src/decodePermission/decodePermission.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Caveat, Hex } from '@metamask/delegation-core';\nimport { ROOT_AUTHORITY } from '@metamask/delegation-core';\nimport { getChecksumAddress, hexToNumber, numberToHex } from '@metamask/utils';\n\nimport type {\n DecodedPermission,\n DeployedContractsByName,\n PermissionType,\n} from './types';\nimport {\n createPermissionRulesForChainId,\n getChecksumEnforcersByChainId,\n getTermsByEnforcer,\n splitHex,\n} from './utils';\n\n/**\n * Identifies the unique permission type that matches a given set of enforcer\n * contract addresses for a specific chain.\n *\n * A permission type matches when:\n * - All of its required enforcers are present in the provided list; and\n * - No provided enforcer falls outside the union of the type's required and\n * optional enforcers (currently only `TimestampEnforcer` is allowed extra).\n *\n * If exactly one permission type matches, its identifier is returned.\n *\n * @param args - The arguments to this function.\n * @param args.enforcers - List of enforcer contract addresses (hex strings).\n *\n * @param args.contracts - The deployed contracts for the chain.\n * @returns The identifier of the matching permission type.\n * @throws If no permission type matches, or if more than one permission type matches.\n */\nexport const identifyPermissionByEnforcers = ({\n enforcers,\n contracts,\n}: {\n enforcers: Hex[];\n contracts: DeployedContractsByName;\n}): PermissionType => {\n // Build frequency map for enforcers (using checksummed addresses)\n const counts = new Map<Hex, number>();\n for (const addr of enforcers.map(getChecksumAddress)) {\n counts.set(addr, (counts.get(addr) ?? 0) + 1);\n }\n const enforcersSet = new Set(counts.keys());\n\n const permissionRules = createPermissionRulesForChainId(contracts);\n\n let matchingPermissionType: PermissionType | null = null;\n\n for (const {\n optionalEnforcers,\n requiredEnforcers,\n permissionType,\n } of permissionRules) {\n // union of optional + required enforcers. Any other address is forbidden.\n const allowedEnforcers = new Set<Hex>([\n ...optionalEnforcers,\n ...requiredEnforcers.keys(),\n ]);\n\n let hasForbiddenEnforcers = false;\n\n for (const caveat of enforcersSet) {\n if (!allowedEnforcers.has(caveat)) {\n hasForbiddenEnforcers = true;\n break;\n }\n }\n\n // exact multiplicity match for required enforcers\n let meetsRequiredCounts = true;\n for (const [addr, requiredCount] of requiredEnforcers.entries()) {\n if ((counts.get(addr) ?? 0) !== requiredCount) {\n meetsRequiredCounts = false;\n break;\n }\n }\n\n if (meetsRequiredCounts && !hasForbiddenEnforcers) {\n if (matchingPermissionType) {\n throw new Error('Multiple permission types match');\n }\n matchingPermissionType = permissionType;\n }\n }\n\n if (!matchingPermissionType) {\n throw new Error('Unable to identify permission type');\n }\n\n return matchingPermissionType;\n};\n\n/**\n * Extracts the expiry timestamp from TimestampEnforcer caveat terms.\n *\n * Based on the TimestampEnforcer contract encoding:\n * - Terms are 32 bytes total (64 hex characters without '0x')\n * - First 16 bytes (32 hex chars): timestampAfterThreshold (uint128) - must be 0\n * - Last 16 bytes (32 hex chars): timestampBeforeThreshold (uint128) - this is the expiry\n *\n * @param terms - The hex-encoded terms from a TimestampEnforcer caveat\n * @returns The expiry timestamp in seconds\n * @throws If the terms are not exactly 32 bytes, if the timestampAfterThreshold is non-zero,\n * or if the timestampBeforeThreshold is zero\n */\nconst extractExpiryFromCaveatTerms = (terms: Hex): number => {\n // Validate terms length: must be exactly 32 bytes (64 hex chars + '0x' prefix = 66 chars)\n if (terms.length !== 66) {\n throw new Error(\n `Invalid TimestampEnforcer terms length: expected 66 characters (0x + 64 hex), got ${terms.length}`,\n );\n }\n\n const [after, before] = splitHex(terms, [16, 16]);\n\n if (hexToNumber(after) !== 0) {\n throw new Error('Invalid expiry: timestampAfterThreshold must be 0');\n }\n\n const expiry = hexToNumber(before);\n\n if (expiry === 0) {\n throw new Error(\n 'Invalid expiry: timestampBeforeThreshold must be greater than 0',\n );\n }\n\n return expiry;\n};\n\n/**\n * Extracts the permission-specific data payload and the expiry timestamp from\n * the provided caveats for a given permission type.\n *\n * This function locates the relevant caveat enforcer for the `permissionType`,\n * interprets its `terms` by splitting the hex string into byte-sized segments,\n * and converts each segment into the appropriate numeric or address shape.\n *\n * The expiry timestamp is derived from the `TimestampEnforcer` terms and must\n * have a zero `timestampAfterThreshold` and a positive `timestampBeforeThreshold`.\n *\n * @param args - The arguments to this function.\n * @param args.contracts - The deployed contracts for the chain.\n * @param args.caveats - Caveats decoded from the permission context.\n * @param args.permissionType - The previously identified permission type.\n *\n * @returns An object containing the `expiry` timestamp and the decoded `data` payload.\n * @throws If the caveats are malformed, missing, or the terms fail to decode.\n */\nexport const getPermissionDataAndExpiry = ({\n contracts,\n caveats,\n permissionType,\n}: {\n contracts: DeployedContractsByName;\n caveats: Caveat<Hex>[];\n permissionType: PermissionType;\n}): {\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n} => {\n const checksumCaveats = caveats.map((caveat) => ({\n ...caveat,\n enforcer: getChecksumAddress(caveat.enforcer),\n }));\n\n const {\n erc20StreamingEnforcer,\n erc20PeriodicEnforcer,\n nativeTokenStreamingEnforcer,\n nativeTokenPeriodicEnforcer,\n timestampEnforcer,\n allowedCalldataEnforcer,\n valueLteEnforcer,\n } = getChecksumEnforcersByChainId(contracts);\n\n const expiryTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: timestampEnforcer,\n throwIfNotFound: false,\n });\n\n let expiry: number | null = null;\n if (expiryTerms) {\n expiry = extractExpiryFromCaveatTerms(expiryTerms);\n }\n\n let data: DecodedPermission['permission']['data'];\n\n switch (permissionType) {\n case 'erc20-token-stream': {\n const erc20StreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20StreamingEnforcer,\n });\n\n const [\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTimeRaw,\n ] = splitHex(erc20StreamingTerms, [20, 32, 32, 32, 32]);\n\n data = {\n tokenAddress,\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-periodic': {\n const erc20PeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: erc20PeriodicEnforcer,\n });\n\n const [tokenAddress, periodAmount, periodDurationRaw, startTimeRaw] =\n splitHex(erc20PeriodicTerms, [20, 32, 32, 32]);\n\n data = {\n tokenAddress,\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n\n case 'native-token-stream': {\n const nativeTokenStreamingTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenStreamingEnforcer,\n });\n\n const [initialAmount, maxAmount, amountPerSecond, startTimeRaw] =\n splitHex(nativeTokenStreamingTerms, [32, 32, 32, 32]);\n\n data = {\n initialAmount,\n maxAmount,\n amountPerSecond,\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'native-token-periodic': {\n const nativeTokenPeriodicTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: nativeTokenPeriodicEnforcer,\n });\n\n const [periodAmount, periodDurationRaw, startTimeRaw] = splitHex(\n nativeTokenPeriodicTerms,\n [32, 32, 32],\n );\n\n data = {\n periodAmount,\n periodDuration: hexToNumber(periodDurationRaw),\n startTime: hexToNumber(startTimeRaw),\n };\n break;\n }\n case 'erc20-token-revocation': {\n // 0 value for ValueLteEnforcer\n const ZERO_32_BYTES =\n '0x0000000000000000000000000000000000000000000000000000000000000000' as const;\n\n // Approve() 4byte selector starting at index 0\n const ERC20_APPROVE_SELECTOR_TERMS =\n '0x0000000000000000000000000000000000000000000000000000000000000000095ea7b3' as const;\n\n // 0 amount starting at index 24\n const ERC20_APPROVE_ZERO_AMOUNT_TERMS =\n '0x00000000000000000000000000000000000000000000000000000000000000240000000000000000000000000000000000000000000000000000000000000000' as const;\n\n const allowedCalldataCaveats = checksumCaveats.filter(\n (caveat) => caveat.enforcer === allowedCalldataEnforcer,\n );\n\n const allowedCalldataTerms = allowedCalldataCaveats.map((caveat) =>\n caveat.terms.toLowerCase(),\n );\n\n const hasApproveSelector = allowedCalldataTerms.includes(\n ERC20_APPROVE_SELECTOR_TERMS,\n );\n\n const hasZeroAmount = allowedCalldataTerms.includes(\n ERC20_APPROVE_ZERO_AMOUNT_TERMS,\n );\n\n if (!hasApproveSelector || !hasZeroAmount) {\n throw new Error(\n 'Invalid erc20-token-revocation terms: expected approve selector and zero amount constraints',\n );\n }\n\n const valueLteTerms = getTermsByEnforcer({\n caveats: checksumCaveats,\n enforcer: valueLteEnforcer,\n });\n\n if (valueLteTerms !== ZERO_32_BYTES) {\n throw new Error('Invalid ValueLteEnforcer terms: maxValue must be 0');\n }\n\n data = {};\n break;\n }\n default:\n throw new Error('Invalid permission type');\n }\n\n return { expiry, data };\n};\n\n/**\n * Reconstructs a {@link DecodedPermission} object from primitive values\n * obtained while decoding a permission context.\n *\n * The resulting object contains:\n * - `chainId` encoded as hex (`0x…`)\n * - `address` set to the delegator (user account)\n * - `signer` set to an account signer with the delegate address\n * - `permission` with the identified type and decoded data\n * - `expiry` timestamp (or null)\n *\n * @param args - The arguments to this function.\n * @param args.chainId - Chain ID.\n * @param args.permissionType - Identified permission type.\n * @param args.delegator - Address of the account delegating permission.\n * @param args.delegate - Address that will act under the granted permission.\n * @param args.authority - Authority identifier; must be ROOT_AUTHORITY.\n * @param args.expiry - Expiry timestamp (unix seconds) or null if unbounded.\n * @param args.data - Permission-specific decoded data payload.\n * @param args.justification - Human-readable justification for the permission.\n * @param args.specifiedOrigin - The origin reported in the request metadata.\n *\n * @returns The reconstructed {@link DecodedPermission}.\n */\nexport const reconstructDecodedPermission = ({\n chainId,\n permissionType,\n delegator,\n delegate,\n authority,\n expiry,\n data,\n justification,\n specifiedOrigin,\n}: {\n chainId: number;\n permissionType: PermissionType;\n delegator: Hex;\n delegate: Hex;\n authority: Hex;\n expiry: number | null;\n data: DecodedPermission['permission']['data'];\n justification: string;\n specifiedOrigin: string;\n}): DecodedPermission => {\n if (authority !== ROOT_AUTHORITY) {\n throw new Error('Invalid authority');\n }\n\n const permission: DecodedPermission = {\n chainId: numberToHex(chainId),\n from: delegator,\n to: delegate,\n permission: {\n type: permissionType,\n data,\n justification,\n },\n expiry,\n origin: specifiedOrigin,\n };\n\n return permission;\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask/gator-permissions-controller",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Controller for managing gator permissions with profile sync integration",
5
5
  "keywords": [
6
6
  "MetaMask",
@@ -56,7 +56,7 @@
56
56
  "@metamask/snaps-controllers": "^17.2.0",
57
57
  "@metamask/snaps-sdk": "^10.3.0",
58
58
  "@metamask/snaps-utils": "^11.7.0",
59
- "@metamask/transaction-controller": "^62.9.2",
59
+ "@metamask/transaction-controller": "^62.11.0",
60
60
  "@metamask/utils": "^11.9.0"
61
61
  },
62
62
  "devDependencies": {