1- import { encodeSingle , decodeSingle } from '@metamask/abi-utils' ;
1+ import { encode , encodeSingle , decodeSingle } from '@metamask/abi-utils' ;
22import { type BytesLike } from '@metamask/utils' ;
3+ import { keccak_256 } from '@noble/hashes/sha3' ;
34
45import {
56 bytesLikeToBytes ,
@@ -9,14 +10,35 @@ import {
910 type EncodingOptions ,
1011 type ResultValue ,
1112} from './returns' ;
12- import type { Delegation , Hex } from './types' ;
13+ import type { CaveatStruct , DelegationStruct , Hex } from './types' ;
14+
15+ /**
16+ * To be used in a delegation to allow any beneficiary to redeem the delegation.
17+ */
18+ export const ANY_BENEFICIARY = '0x0000000000000000000000000000000000000a11' ;
1319
1420/**
1521 * To be used on a delegation as the root authority.
1622 */
1723export const ROOT_AUTHORITY =
1824 '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' ;
1925
26+ /**
27+ * The typehash for a delegation, used when generating a delegation hash.
28+ *
29+ * keccak('Delegation(address delegate,address delegator,bytes32 authority,Caveat[] caveats,uint256 salt)')
30+ */
31+ export const DELEGATION_TYPEHASH =
32+ '0x88c1d2ecf185adf710588203a5f263f0ff61be0d33da39792cde19ba9aa4331e' ;
33+
34+ /**
35+ * The typehash for a caveat, used when generating a caveat hash.
36+ *
37+ * keccak('Caveat(address enforcer,bytes terms)')
38+ */
39+ export const CAVEAT_TYPEHASH =
40+ '0x80ad7e1b04ee6d994a125f4714ca0720908bd80ed16063ec8aee4b88e9253e2d' ;
41+
2042/**
2143 * The ABI types for an array of delegations.
2244 */
@@ -30,11 +52,11 @@ const DELEGATION_ARRAY_ABI_TYPES =
3052 * @returns The encoded delegations as a hex string (default) or Uint8Array.
3153 */
3254export function encodeDelegations (
33- delegations : Delegation [ ] ,
55+ delegations : DelegationStruct [ ] ,
3456 options ?: EncodingOptions < 'hex' > ,
3557) : Hex ;
3658export function encodeDelegations (
37- delegations : Delegation [ ] ,
59+ delegations : DelegationStruct [ ] ,
3860 options : EncodingOptions < 'bytes' > ,
3961) : Uint8Array ;
4062/**
@@ -44,7 +66,7 @@ export function encodeDelegations(
4466 * @returns The encoded delegations as a hex string (default) or Uint8Array.
4567 */
4668export function encodeDelegations (
47- delegations : Delegation [ ] ,
69+ delegations : DelegationStruct [ ] ,
4870 options : EncodingOptions < ResultValue > = defaultOptions ,
4971) : Hex | Uint8Array {
5072 let result : Uint8Array ;
@@ -83,7 +105,7 @@ export function encodeDelegations(
83105const delegationFromDecodedDelegation = < TEncoding extends BytesLike > (
84106 decodedDelegation : DecodedDelegation ,
85107 convertFn : ( value : BytesLike ) => TEncoding ,
86- ) : Delegation < TEncoding > => {
108+ ) : DelegationStruct < TEncoding > => {
87109 const [ delegate , delegator , authority , caveats , salt , signature ] =
88110 decodedDelegation ;
89111
@@ -124,11 +146,11 @@ type DecodedDelegation = [
124146export function decodeDelegations (
125147 encoded : BytesLike ,
126148 options ?: EncodingOptions < 'hex' > ,
127- ) : Delegation < Hex > [ ] ;
149+ ) : DelegationStruct < Hex > [ ] ;
128150export function decodeDelegations (
129151 encoded : BytesLike ,
130152 options : EncodingOptions < 'bytes' > ,
131- ) : Delegation < Uint8Array > [ ] ;
153+ ) : DelegationStruct < Uint8Array > [ ] ;
132154/**
133155 * Decodes an encoded permission context back into an array of delegations.
134156 * @param encoded - The encoded delegations as a hex string or Uint8Array.
@@ -138,7 +160,7 @@ export function decodeDelegations(
138160export function decodeDelegations (
139161 encoded : BytesLike ,
140162 options : EncodingOptions < ResultValue > = defaultOptions ,
141- ) : Delegation < Hex > [ ] | Delegation < Uint8Array > [ ] {
163+ ) : DelegationStruct < Hex > [ ] | DelegationStruct < Uint8Array > [ ] {
142164 // it's possible to short circuit for empty delegations, but due to the
143165 // complexity of the input type, and the relative infrequency of empty delegations,
144166 // it's not worthwhile.
@@ -158,3 +180,86 @@ export function decodeDelegations(
158180 delegationFromDecodedDelegation ( struct , bytesLikeToHex ) ,
159181 ) ;
160182}
183+
184+ /**
185+ * Calculates the hash of a delegation for signing purposes.
186+ * The hash is computed by encoding the delegation parameters with the delegation typehash
187+ * and then applying keccak256 hashing.
188+ *
189+ * @param delegation - The delegation to hash.
190+ * @param options - Encoding options. Defaults to { out: 'hex' }.
191+ * @returns The keccak256 hash of the encoded delegation as a hex string (default) or Uint8Array.
192+ */
193+ export function getDelegationHash (
194+ delegation : DelegationStruct ,
195+ options ?: EncodingOptions < 'hex' > ,
196+ ) : Hex ;
197+ export function getDelegationHash (
198+ delegation : DelegationStruct ,
199+ options : EncodingOptions < 'bytes' > ,
200+ ) : Uint8Array ;
201+ /**
202+ * Calculates the hash of a delegation for signing purposes.
203+ * The hash is computed by encoding the delegation parameters with the delegation typehash
204+ * and then applying keccak256 hashing.
205+ *
206+ * @param delegation - The delegation to hash.
207+ * @param options - Encoding options. Defaults to { out: 'hex' }.
208+ * @returns The keccak256 hash of the encoded delegation as a hex string (default) or Uint8Array.
209+ */
210+ export function getDelegationHash (
211+ delegation : DelegationStruct ,
212+ options : EncodingOptions < ResultValue > = defaultOptions ,
213+ ) : Hex | Uint8Array {
214+ const encoded = encode (
215+ [ 'bytes32' , 'address' , 'address' , 'bytes32' , 'bytes32' , 'uint256' ] ,
216+ [
217+ DELEGATION_TYPEHASH ,
218+ delegation . delegate ,
219+ delegation . delegator ,
220+ delegation . authority ,
221+ getCaveatsArrayHash ( delegation . caveats ) ,
222+ delegation . salt ,
223+ ] ,
224+ ) ;
225+ const hash = keccak_256 ( encoded ) ;
226+ return prepareResult ( hash , options ) ;
227+ }
228+
229+ /**
230+ * Calculates the hash of an array of caveats. The caveats are individually abi
231+ * encoded and hashed, and concatenated. The resulting byte array is then
232+ * hashed to produce the CaveatsArrayHash.
233+ *
234+ * @param caveats - The array of caveats to hash.
235+ * @returns The keccak256 hash of the encoded caveat array.
236+ */
237+ function getCaveatsArrayHash ( caveats : CaveatStruct [ ] ) : Uint8Array {
238+ const byteLength = 32 * caveats . length ;
239+ const encoded = new Uint8Array ( byteLength ) ;
240+
241+ for ( let i = 0 ; i < caveats . length ; i ++ ) {
242+ const caveat = caveats [ i ] ;
243+ if ( ! caveat ) {
244+ throw new Error ( `Caveat was undefined at index ${ i } ` ) ;
245+ }
246+ const caveatHash = getCaveatHash ( caveat ) ;
247+ encoded . set ( caveatHash , i * 32 ) ;
248+ }
249+
250+ return keccak_256 ( encoded ) ;
251+ }
252+
253+ /**
254+ * Calculates the hash of a single caveat.
255+ * @param caveat - The caveat to hash.
256+ * @returns The keccak256 hash of the encoded caveat.
257+ */
258+ function getCaveatHash ( caveat : CaveatStruct ) : Uint8Array {
259+ const encoded = encode (
260+ [ 'bytes32' , 'address' , 'bytes32' ] ,
261+ [ CAVEAT_TYPEHASH , caveat . enforcer , caveat . terms ] ,
262+ ) ;
263+ const hash = keccak_256 ( encoded ) ;
264+ return hash ;
265+ }
0 commit comments