@@ -34,6 +34,7 @@ type EnsureRedeemerSufficientlyConstrainedParams = {
3434 redeemerEnforcer : Hex ;
3535 caveats : Caveat [ ] ;
3636 existingDelegations : Delegation [ ] ;
37+ facilitatorAddresses : Address [ ] | undefined ;
3738 redeemerAddresses : Address [ ] | undefined ;
3839 requireRedeemers : boolean ;
3940} ;
@@ -61,10 +62,10 @@ type EnsureExpirySufficientlyConstrainedParams = {
6162/**
6263 * Inputs for resolving allowed redeemer addresses.
6364 */
64- type ResolveRedeemerAddressesParams = {
65- facilitatorAddresses ?: Address [ ] ;
66- redeemerAddresses ?: Address [ ] ;
67- } ;
65+ type ResolveRedeemerAddressesParams = Pick <
66+ EnsureRedeemerSufficientlyConstrainedParams ,
67+ 'facilitatorAddresses' | 'redeemerAddresses'
68+ > ;
6869
6970/**
7071 * Resolved context required to build and sign an x402 delegation.
@@ -245,13 +246,16 @@ export const ensureExpirySufficientlyConstrained = ({
245246} ;
246247
247248/**
248- * Resolves the allowed redeemer addresses from the union of facilitator specified and caller specified redeemer addresses.
249- * If either are undefined, the other is returned.
249+ * Resolves allowed redeemer addresses from facilitator and configured redeemer inputs.
250+ *
251+ * If both inputs are provided, returns their intersection.
252+ * If only one input is provided, returns that input.
253+ * If neither input is provided, returns undefined.
250254 *
251255 * @param options - Redeemer address inputs.
252256 * @param options.facilitatorAddresses - Optional facilitator addresses from the PaymentRequirements.
253257 * @param options.redeemerAddresses - Optional redeemer addresses from the RedeemersConfig.
254- * @returns The allowed redeemer addresses, or undefined if none are specified .
258+ * @returns Resolved allowed redeemer addresses, or undefined when no input is provided .
255259 */
256260const resolveRedeemerAddresses = ( {
257261 facilitatorAddresses,
@@ -288,40 +292,44 @@ const resolveRedeemerAddresses = ({
288292 * Returns the caveat list unchanged when an existing redeemer caveat is already
289293 * strict enough, or appends a new redeemer caveat scoped to facilitator addresses.
290294 *
291- * @param options0 - Redeemer constraint evaluation inputs.
292- * @param options0 .redeemerEnforcer - Address of the redeemer enforcer caveat contract.
293- * @param options0 .caveats - Currently resolved caveats for the delegation being created.
294- * @param options0 .existingDelegations - Existing parent-chain delegations to inspect for inherited constraints.
295- * @param options0 .redeemerAddresses - Optional addresses to which redemption should be constrained.
296- * @param options0 .requireRedeemers - Whether at least one redeemer constraint must exist when no redeemer addresses are supplied .
295+ * @param config - Redeemer constraint evaluation inputs.
296+ * @param config .redeemerEnforcer - Address of the redeemer enforcer caveat contract.
297+ * @param config .caveats - Currently resolved caveats for the delegation being created.
298+ * @param config .existingDelegations - Existing parent-chain delegations to inspect for inherited constraints.
299+ * @param config .redeemerAddresses - Optional addresses to which redemption should be constrained.
300+ * @param config .requireRedeemers - Whether at least one redeemer constraint must exist.
297301 * @returns The original caveats when sufficiently constrained, otherwise caveats with a redeemer caveat appended.
298- * @throws If no facilitator addresses are provided and no redeemer constraint exists.
302+ * @throws If either facilitatorAddresses and/or redeemerAddresses are provided, but the intersection is empty.
303+ * @throws If requireRedeemers is true, but no valid redeemer addresses are provided, and no existing redeemer caveat is found in the existingDelegations.
299304 */
300- export const ensureRedeemerSufficientlyConstrained = ( {
301- redeemerEnforcer ,
302- caveats ,
303- existingDelegations ,
304- redeemerAddresses ,
305- requireRedeemers ,
306- } : EnsureRedeemerSufficientlyConstrainedParams ) : Caveat [ ] => {
307- const redeemerEnforcerNormalized = normalizeAddress ( redeemerEnforcer ) ;
308-
305+ export const ensureRedeemerSufficientlyConstrained = (
306+ config : EnsureRedeemerSufficientlyConstrainedParams ,
307+ ) : Caveat [ ] => {
308+ // `redeemerAddresses` is the intersection of `facilitatorAddresses` and `redeemerAddresses`.
309+ // If either is undefined, it implies no constraint is specified by that value, and the other is returned.
310+ // If both are undefined, it implies that no constraint is specified overall.
311+ const redeemerAddresses = resolveRedeemerAddresses ( config ) ;
312+
313+ // If the result is defined, but zero-length, it implies no redeemers are allowed, which is a non-satisfiable constraint, so an error is thrown.
309314 if ( redeemerAddresses ?. length === 0 ) {
310315 throw new Error (
311- 'No valid redeemer addresses were resolved. The intersection of `redeemers.addresses` and `extra.facilitatorAddresses` must be non-empty .' ,
316+ 'No valid redeemer addresses were resolved. If both `redeemers.addresses` and `extra.facilitatorAddresses` are provided, they must overlap. If only one is provided, it must include at least one address .' ,
312317 ) ;
313318 }
314319
320+ const redeemerEnforcer = normalizeAddress ( config . redeemerEnforcer ) ;
321+
322+ const { caveats, existingDelegations } = config ;
323+
315324 if ( ! redeemerAddresses ) {
316- if ( ! requireRedeemers ) {
325+ if ( ! config . requireRedeemers ) {
317326 return caveats ;
318327 }
319328
320329 const hasExistingRedeemerCaveat = hasMatchingCaveats (
321330 caveats ,
322331 existingDelegations ,
323- ( { enforcer } ) =>
324- normalizeAddress ( enforcer ) === redeemerEnforcerNormalized ,
332+ ( { enforcer } ) => normalizeAddress ( enforcer ) === redeemerEnforcer ,
325333 ) ;
326334
327335 if ( ! hasExistingRedeemerCaveat ) {
@@ -333,22 +341,21 @@ export const ensureRedeemerSufficientlyConstrained = ({
333341 return caveats ;
334342 }
335343
336- const redeemerAddressesNormalized = redeemerAddresses . map ( normalizeAddress ) ;
337- const redeemerAddressesSet = new Set ( redeemerAddressesNormalized ) ;
344+ const redeemerAddressesSet = new Set ( redeemerAddresses . map ( normalizeAddress ) ) ;
338345
339346 const hasSupersedingRedeemerCaveat = hasMatchingCaveats (
340347 caveats ,
341348 existingDelegations ,
342349 ( caveat ) => {
343- if ( normalizeAddress ( caveat . enforcer ) !== redeemerEnforcerNormalized ) {
350+ if ( normalizeAddress ( caveat . enforcer ) !== redeemerEnforcer ) {
344351 return false ;
345352 }
346353
347354 const allowedRedeemerAddresses = decodeRedeemerTerms (
348355 caveat . terms ,
349356 ) . redeemers . map ( normalizeAddress ) ;
350357
351- // If this redeemer caveat only allows facilitator addresses, it is sufficiently constrained.
358+ // If this redeemer caveat only allows redeemer addresses, it is sufficiently constrained.
352359 return allowedRedeemerAddresses . every ( ( item ) =>
353360 redeemerAddressesSet . has ( item ) ,
354361 ) ;
@@ -472,16 +479,12 @@ export const resolvex402DelegationCaveats = ({
472479 isScopeOptional : true ,
473480 } ) ;
474481
475- const resolvedRedeemerAddresses = resolveRedeemerAddresses ( {
476- facilitatorAddresses,
477- redeemerAddresses,
478- } ) ;
479-
480482 const caveatsWithRedeemer = ensureRedeemerSufficientlyConstrained ( {
481483 redeemerEnforcer,
484+ facilitatorAddresses,
485+ redeemerAddresses,
482486 caveats : initialCaveats ,
483487 existingDelegations,
484- redeemerAddresses : resolvedRedeemerAddresses ,
485488 requireRedeemers,
486489 } ) ;
487490
0 commit comments