1- import { makeErc20TokenAllowanceDecoderConfig } from './erc20TokenAllowance' ;
2- import { makeErc20TokenPeriodicDecoderConfig } from './erc20TokenPeriodic' ;
3- import { makeErc20TokenRevocationDecoderConfig } from './erc20TokenRevocation' ;
4- import { makeErc20TokenStreamDecoderConfig } from './erc20TokenStream' ;
5- import { makeNativeTokenAllowanceDecoderConfig } from './nativeTokenAllowance' ;
6- import { makeNativeTokenPeriodicDecoderConfig } from './nativeTokenPeriodic' ;
7- import { makeNativeTokenStreamDecoderConfig } from './nativeTokenStream' ;
8- import { makeTokenApprovalRevocationDecoderConfig } from './tokenApprovalRevocation' ;
9- import type { DeployedContractsByName , PermissionDecoderConfig } from './types' ;
1+ import { getChecksumAddress } from '@metamask/utils' ;
2+
3+ import { makeErc20TokenAllowanceDecoderConfig } from './caveats/erc20TokenAllowance' ;
4+ import { makeErc20TokenPeriodicDecoderConfig } from './caveats/erc20TokenPeriodic' ;
5+ import { makeErc20TokenRevocationDecoderConfig } from './caveats/erc20TokenRevocation' ;
6+ import { makeErc20TokenStreamDecoderConfig } from './caveats/erc20TokenStream' ;
7+ import { makeNativeTokenAllowanceDecoderConfig } from './caveats/nativeTokenAllowance' ;
8+ import { makeNativeTokenPeriodicDecoderConfig } from './caveats/nativeTokenPeriodic' ;
9+ import { makeNativeTokenStreamDecoderConfig } from './caveats/nativeTokenStream' ;
10+ import { makeTokenApprovalRevocationDecoderConfig } from './caveats/tokenApprovalRevocation' ;
11+ import type {
12+ Caveat ,
13+ DeployedContractsByName ,
14+ PermissionDecoder ,
15+ PermissionDecoderConfig ,
16+ ValidateAndDecodeResult ,
17+ } from './types' ;
18+ import type { Hex } from '../types' ;
1019import { getChecksumEnforcersByChainId } from './utils' ;
1120
1221export type {
@@ -16,8 +25,40 @@ export type {
1625 DeployedContractsByName ,
1726 DecodedPermission ,
1827 MakePermissionDecoderConfig ,
28+ PermissionDecoder ,
29+ PermissionDecoderConfig ,
30+ PermissionDecoderSpec ,
31+ PermissionType ,
32+ RuleDecoder ,
33+ ValidateAndDecodeResult ,
1934} from './types' ;
2035
36+ export type { ExpiryRule } from './rules/expiry' ;
37+ export type { PayeeRule } from './rules/payee' ;
38+ export type { RedeemerRule } from './rules/redeemer' ;
39+
40+ export {
41+ EXECUTION_PERMISSION_EXPIRY_RULE_TYPE ,
42+ expiryRule ,
43+ } from './rules/expiry' ;
44+ export {
45+ EXECUTION_PERMISSION_PAYEE_RULE_TYPE ,
46+ erc20PayeeRuleDecoder ,
47+ nativePayeeRuleDecoder ,
48+ } from './rules/payee' ;
49+ export {
50+ EXECUTION_PERMISSION_REDEEMER_RULE_TYPE ,
51+ redeemerRuleDecoder ,
52+ } from './rules/redeemer' ;
53+ export { makeNativeTokenStreamDecoderConfig } from './caveats/nativeTokenStream' ;
54+ export { makeNativeTokenPeriodicDecoderConfig } from './caveats/nativeTokenPeriodic' ;
55+ export { makeNativeTokenAllowanceDecoderConfig } from './caveats/nativeTokenAllowance' ;
56+ export { makeErc20TokenStreamDecoderConfig } from './caveats/erc20TokenStream' ;
57+ export { makeErc20TokenPeriodicDecoderConfig } from './caveats/erc20TokenPeriodic' ;
58+ export { makeErc20TokenAllowanceDecoderConfig } from './caveats/erc20TokenAllowance' ;
59+ export { makeErc20TokenRevocationDecoderConfig } from './caveats/erc20TokenRevocation' ;
60+ export { makeTokenApprovalRevocationDecoderConfig } from './caveats/tokenApprovalRevocation' ;
61+
2162/**
2263 * Builds the canonical set of permission decoders for a chain.
2364 *
@@ -40,3 +81,103 @@ export const makePermissionDecoderConfigs = (
4081 makeTokenApprovalRevocationDecoderConfig ( contractAddresses ) ,
4182 ] ;
4283} ;
84+
85+ /**
86+ * Creates a runtime decoder from one permission decoder configuration.
87+ *
88+ * @param config - Permission decoder configuration.
89+ * @returns Permission decoder.
90+ */
91+ export const makePermissionDecoder = (
92+ config : PermissionDecoderConfig ,
93+ ) : PermissionDecoder => {
94+ const requiredEnforcers = new Map < Hex , number > (
95+ Object . entries ( config . requiredEnforcers ) as [ Hex , number ] [ ] ,
96+ ) ;
97+ const optionalEnforcers = new Set ( config . optionalEnforcers ) ;
98+
99+ const caveatAddressesMatch = ( caveatAddresses : Hex [ ] ) : boolean => {
100+ const counts = new Map < Hex , number > ( ) ;
101+
102+ for ( const address of caveatAddresses ) {
103+ counts . set ( address , ( counts . get ( address ) ?? 0 ) + 1 ) ;
104+ }
105+ for ( const [ address , count ] of counts ) {
106+ const maxAllowedCount =
107+ requiredEnforcers . get ( address ) ??
108+ ( optionalEnforcers . has ( address ) ? 1 : 0 ) ;
109+ if ( maxAllowedCount === 0 || count > maxAllowedCount ) {
110+ return false ;
111+ }
112+ }
113+
114+ return true ;
115+ } ;
116+
117+ const validateAndDecodePermission = (
118+ caveats : Caveat [ ] ,
119+ ) : ValidateAndDecodeResult => {
120+ try {
121+ const normalizedCaveats = caveats . map ( ( caveat ) => ( {
122+ ...caveat ,
123+ enforcer : getChecksumAddress ( caveat . enforcer ) ,
124+ } ) ) ;
125+
126+ const caveatAddresses = normalizedCaveats . map (
127+ ( caveat ) => caveat . enforcer ,
128+ ) ;
129+
130+ if ( ! caveatAddressesMatch ( caveatAddresses ) ) {
131+ throw new Error ( 'Invalid caveats' ) ;
132+ }
133+
134+ const data = config . validateAndDecodeData (
135+ normalizedCaveats ,
136+ config . contractAddresses ,
137+ ) ;
138+ const rules = config . rules
139+ . map ( ( decodeRule ) =>
140+ decodeRule ( {
141+ contractAddresses : config . contractAddresses ,
142+ caveats : normalizedCaveats ,
143+ requiredEnforcers,
144+ } ) ,
145+ )
146+ . filter ( ( rule ) => rule !== null ) ;
147+ const expiryRule = rules . find ( ( rule ) => rule . type === 'expiry' ) ;
148+
149+ return {
150+ isValid : true ,
151+ expiry :
152+ expiryRule ?. type === 'expiry' ? expiryRule . data . timestamp : null ,
153+ data,
154+ rules : rules . length > 0 ? rules : undefined ,
155+ } ;
156+ } catch ( error ) {
157+ return {
158+ isValid : false ,
159+ error : error instanceof Error ? error : new Error ( 'Invalid caveats' ) ,
160+ } ;
161+ }
162+ } ;
163+
164+ return {
165+ permissionType : config . permissionType ,
166+ requiredEnforcers,
167+ optionalEnforcers,
168+ caveatAddressesMatch,
169+ validateAndDecodePermission,
170+ } ;
171+ } ;
172+
173+ /**
174+ * Creates permission decoders for all supported permission types.
175+ *
176+ * @param contracts - Deployed delegation framework contracts for one chain.
177+ * @returns Runtime permission decoders.
178+ */
179+ export const createPermissionDecodersForContracts = (
180+ contracts : DeployedContractsByName ,
181+ ) : PermissionDecoder [ ] => {
182+ return makePermissionDecoderConfigs ( contracts ) . map ( makePermissionDecoder ) ;
183+ } ;
0 commit comments