Skip to content

feat: add support for redelegating from permission contexts#217

Open
jeffsmale90 wants to merge 3 commits intomainfrom
cursor/redelegate-permission-context-1c3b
Open

feat: add support for redelegating from permission contexts#217
jeffsmale90 wants to merge 3 commits intomainfrom
cursor/redelegate-permission-context-1c3b

Conversation

@jeffsmale90
Copy link
Copy Markdown
Collaborator

📝 Description

This PR implements support for creating redelegations from ERC-7715 permission contexts, significantly improving the developer experience when working with delegation chains.

Previously, developers had to manually decode permission contexts, extract the leaf delegation, and rebuild the chain. This PR streamlines that workflow with new APIs and improved type safety.

🔄 What Changed?

Core API Enhancements

  • Added parentPermissionContext parameter to createDelegation and createOpenDelegation

    • Accepts PermissionContext (either Delegation[] or encoded Hex)
    • Automatically extracts the leaf delegation from the chain
    • Uses discriminated unions to prevent conflicting parent parameters
  • Made scope optional when a parent delegation exists

    • When redelegating, scope can be inherited from the parent
    • Scope constraints are enforced through the parent delegation in the chain
    • Optional explicit scope override still supported for adding new constraints

New redelegatePermissionContext Action

  • High-level convenience function for complete redelegation workflow:

    1. Extracts leaf delegation from permission context
    2. Creates new delegation inheriting from parent
    3. Signs the delegation
    4. Prepends it to the chain
    5. Returns encoded permission context ready to use
  • Supports both delegation types:

    • Specific delegate: pass delegate parameter
    • Open delegation: omit delegate (uses ANY_BENEFICIARY)
  • Client extension pattern: redelegatePermissionContextActions()

    • Follows existing SDK patterns (e.g., signDelegationActions)
    • Can be used standalone or extended onto wallet client
    • Automatically infers chainId from client when extended

Type Safety Improvements

  • Discriminated union types ensure compile-time safety
  • Only one of scope, parentDelegation, or parentPermissionContext can be provided
  • TypeScript enforces correct parameter combinations

🚀 Why?

Issue: #196

When creating a redelegation from an ERC-7715 response, developers previously had to:

// Before: Manual multi-step process
const decodedContext = decodeDelegations(erc7715Response.context);
const leafDelegation = decodedContext[0];

const newDelegation = createDelegation({
  environment,
  scope: erc20Scope,  // Must manually specify scope again
  to: charlie.address,
  from: bob.address,
  parentDelegation: leafDelegation,
  caveats: [timestampCaveat],
});

const newChain = [newDelegation, ...decodedContext];
const newContext = encodeDelegations(newChain);

After this PR, developers can:

// After: One-liner with redelegatePermissionContext
const { delegation, permissionContext } = await redelegatePermissionContext(
  walletClient,
  {
    environment,
    permissionContext: erc7715Response.context,
    delegationManager: environment.DelegationManager,
    chainId: 11155111,
    delegate: charlie.address,
    caveats: [timestampCaveat],
    // scope is inherited automatically!
  }
);

// Ready to use immediately
await sendUserOperationWithDelegation(client, {
  calls: [{ to, data, permissionContext, delegationManager }],
});

Or with the lower-level API:

// Using enhanced createDelegation with parentPermissionContext
const newDelegation = createDelegation({
  environment,
  to: charlie.address,
  from: bob.address,
  parentPermissionContext: erc7715Response.context,  // Direct usage!
  caveats: [timestampCaveat],
  // scope inherited from parent
});

🧪 How to Test?

Automated Tests

  • Automated tests added/updated

    • test/delegation.test.ts: Tests for parentPermissionContext parameter
    • test/actions/redelegatePermissionContext.test.ts: Comprehensive action tests
  • All existing tests pass

    • ✅ 60 delegation tests pass
    • ✅ 10 redelegation action tests pass

Manual Testing

Test scenarios covered:

  1. Redelegate with specific delegate

    • Create root delegation → encode → redelegate to new address
    • Verify signature is valid and permission context is updated
  2. Create open redelegation

    • Redelegate without specifying delegate
    • Verify delegate is set to ANY_BENEFICIARY
  3. Inherit scope from parent

    • Redelegate without providing scope
    • Verify child delegation has minimal caveats (parent enforces scope)
  4. Override parent scope

    • Redelegate with explicit scope
    • Verify new scope caveats are applied
  5. Client extension pattern

    • Extend wallet client with redelegatePermissionContextActions()
    • Verify chainId inference works correctly

⚠️ Breaking Changes

  • No breaking changes
    • All existing APIs remain unchanged
    • New parameters are optional
    • Fully backward compatible

📋 Checklist

  • Code follows the project's coding standards
  • Self-review completed
  • Documentation updated (JSDoc comments added)
  • Tests added/updated (70 total tests added/updated)
  • Changelog updated (if needed)
  • All CI checks pass (local tests passing, awaiting CI)

🔗 Related Issues

Closes #196

📚 Additional Notes

Design Decisions

  1. Why discriminated union for parent parameters?

    • Prevents accidentally passing both parentDelegation and parentPermissionContext
    • Provides better TypeScript error messages
    • Enforces mutually exclusive parameters at compile time
  2. Why make scope optional?

    • Scope constraints are inherently enforced through the parent delegation chain
    • When redeeming, ALL delegations in the chain are validated
    • Child delegations typically add additional constraints (caveats), not redefine scope
    • Reduces boilerplate for common redelegation patterns
  3. Why return both delegation and permissionContext?

    • Gives developers flexibility to use either format
    • delegation: for inspection or further manipulation
    • permissionContext: ready to use in transactions immediately
    • Avoids forcing developers to re-encode if they need the delegation object

Future Considerations

  • Could add helper to extract specific delegation by index from permission context
  • Could add validation that parent delegation authorizes the delegator
  • Could add convenience methods for common redelegation patterns (time-limited, amount-limited, etc.)

Examples

Full working examples are included in the JSDoc comments of the new functions.

Slack Thread

Open in Web Open in Cursor 

cursoragent and others added 2 commits April 29, 2026 01:45
This implements support for creating redelegations from ERC-7715 permission
contexts, improving the developer experience when working with delegation chains.

Key changes:

1. **Enhanced createDelegation/createOpenDelegation**:
   - Added  parameter to accept PermissionContext directly
   - Made  optional when a parent delegation exists (scope is inherited)
   - Supports both Delegation[] and encoded Hex formats

2. **New redelegatePermissionContext action**:
   - High-level convenience function for the complete redelegation workflow
   - Automatically extracts leaf delegation from permission context
   - Signs the new delegation and returns updated permission context
   - Supports both specific delegate and open delegation (ANY_BENEFICIARY)
   - Can be used standalone or as a client extension via redelegatePermissionContextActions

3. **Improved type safety**:
   - Uses discriminated unions to ensure only one parent source is provided
   - Prevents conflicting parentDelegation and parentPermissionContext parameters

4. **Comprehensive test coverage**:
   - Tests for parentPermissionContext with Delegation arrays and encoded Hex
   - Tests for scope inheritance when no scope is provided
   - Tests for scope override with explicit scope parameter
   - Tests for both standalone and client extension usage patterns

Breaking changes: None. All existing APIs remain unchanged and backward compatible.

Addresses: #196

Co-authored-by: jeffsmale90 <jeffsmale90@users.noreply.github.com>
Co-authored-by: jeffsmale90 <jeffsmale90@users.noreply.github.com>
@cursor cursor Bot marked this pull request as ready for review April 29, 2026 01:47
@cursor cursor Bot requested a review from a team as a code owner April 29, 2026 01:47
- Fix import order in redelegatePermissionContext.ts and test files
- Remove forbidden non-null assertion, use explicit check instead
- Apply auto-formatting fixes

Co-authored-by: jeffsmale90 <jeffsmale90@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redelegation from PermissionContext

2 participants