@@ -93,19 +93,24 @@ type DelegationCreationContext = {
9393 createDelegationConfig : Parameters < typeof createOpenDelegation > [ 0 ] ;
9494} ;
9595
96- type ResolveX402DelegationCaveatsParams = {
96+ type Resolvex402DelegationCaveatsParams = {
9797 environment : SmartAccountsEnvironment ;
9898 caveatsConfig : Caveats | undefined ;
9999 existingDelegations : Delegation [ ] ;
100100 facilitatorAddresses : Hex [ ] | undefined ;
101101} ;
102102
103- const resolveX402DelegationCaveats = ( {
103+ const normalizeAddress = ( address : Hex ) : string => address . toLowerCase ( ) ;
104+
105+ const isSubset = ( subset : string [ ] , superset : string [ ] ) : boolean =>
106+ subset . every ( ( item ) => superset . includes ( item ) ) ;
107+
108+ const resolvex402DelegationCaveats = ( {
104109 environment,
105110 caveatsConfig,
106111 existingDelegations,
107112 facilitatorAddresses,
108- } : ResolveX402DelegationCaveatsParams ) : Caveat [ ] => {
113+ } : Resolvex402DelegationCaveatsParams ) : Caveat [ ] => {
109114 const {
110115 caveatEnforcers : { RedeemerEnforcer : redeemerEnforcer } ,
111116 } = environment ;
@@ -114,28 +119,28 @@ const resolveX402DelegationCaveats = ({
114119 throw new Error ( 'RedeemerEnforcer not found in environment' ) ;
115120 }
116121
117- const redeemerAddressLowerCase = redeemerEnforcer . toLowerCase ( ) ;
122+ const redeemerAddressNormalized = normalizeAddress ( redeemerEnforcer ) ;
118123
119124 const caveats = resolveCaveats ( {
120125 environment,
121126 caveats : caveatsConfig ,
122- // we need to resolve the caveats so that we can add more, the scope is added in the createDelegation call
127+ // Resolve caveats first so we can append a redeemer caveat when needed.
128+ // Scope is still attached later during delegation creation.
123129 isScopeOptional : true ,
124130 } ) ;
125131
126- const existingRedeemerCaveats = [
132+ const redeemerCaveats = [
127133 ...caveats ,
128- ...existingDelegations . flatMap ( ( enforcer ) => enforcer . caveats ) ,
134+ ...existingDelegations . flatMap ( ( delegation ) => delegation . caveats ) ,
129135 ] . filter (
130- ( { enforcer } ) => enforcer . toLowerCase ( ) === redeemerAddressLowerCase ,
136+ ( { enforcer } ) => normalizeAddress ( enforcer ) === redeemerAddressNormalized ,
131137 ) ;
132138
133- const hasRedeemerCaveatInChainOrSpecifiedCaveats =
134- existingRedeemerCaveats . length > 0 ;
139+ const hasExistingRedeemerConstraint = redeemerCaveats . length > 0 ;
135140
136141 if ( ! facilitatorAddresses || facilitatorAddresses . length === 0 ) {
137- // if no facilitators are specified, we must at least have some constraints on allowed redeemer
138- if ( ! hasRedeemerCaveatInChainOrSpecifiedCaveats ) {
142+ // Without facilitators, a redeemer constraint must already exist.
143+ if ( ! hasExistingRedeemerConstraint ) {
139144 throw new Error (
140145 'Redeemer must be constrained, either in the specified `caveats`, `parentPermissionContext`, or the `PaymentRequirements` as `extra.facilitatorAddresses`.' ,
141146 ) ;
@@ -144,20 +149,17 @@ const resolveX402DelegationCaveats = ({
144149 return caveats ;
145150 }
146151
147- const facilitatorAddressesLowerCase = facilitatorAddresses . map ( ( address ) =>
148- address . toLowerCase ( ) ,
149- ) ;
152+ const facilitatorAddressesLowerCase =
153+ facilitatorAddresses . map ( normalizeAddress ) ;
150154
151- // if any of the existing redeemer caveats only specify facilitator addresses, we don't need to add a new caveat
152- const hasSufficientlyConstrainedRedeemerCaveat = existingRedeemerCaveats . some (
155+ // If an existing redeemer caveat is already within facilitator bounds, no new caveat is needed.
156+ const hasSufficientlyConstrainedRedeemerCaveat = redeemerCaveats . some (
153157 ( caveat ) => {
154158 const allowedRedeemerAddresses = decodeRedeemerTerms (
155159 caveat . terms ,
156- ) . redeemers . map ( ( redeemer ) => redeemer . toLowerCase ( ) ) ;
160+ ) . redeemers . map ( normalizeAddress ) ;
157161
158- return allowedRedeemerAddresses . every ( ( allowedRedeemerAddress ) =>
159- facilitatorAddressesLowerCase . includes ( allowedRedeemerAddress ) ,
160- ) ;
162+ return isSubset ( allowedRedeemerAddresses , facilitatorAddressesLowerCase ) ;
161163 } ,
162164 ) ;
163165
@@ -206,7 +208,7 @@ const resolveDelegationCreationContext = async (
206208 : [ ] ;
207209
208210 const { DelegationManager : delegationManager } = config . environment ;
209- const caveats = resolveX402DelegationCaveats ( {
211+ const caveats = resolvex402DelegationCaveats ( {
210212 environment : config . environment ,
211213 caveatsConfig,
212214 existingDelegations,
0 commit comments