Skip to content

Conversation

hanzel98
Copy link
Contributor

@hanzel98 hanzel98 commented Aug 20, 2025

📝 Description

Improves the declarative delegation interface developer experience by making caveats optional, removing unnecessary parameters, and adding calldata support to native token scopes.

🔄 What Changed?

List the specific changes made:

  • Made caveats parameter optional in createDelegation and createOpenDelegation functions
  • Removed allowInsecureFullAuthorizationDelegation parameter from createDelegation and createOpenDelegation (kept in signDelegation and CaveatBuilder)
  • Added calldata support to native token scopes (nativeTokenTransfer, nativeTokenStreaming, nativeTokenPeriodic)
  • Added exactCalldata and allowedCalldata parameters to native token scope configurations
  • Updated resolveCaveats function to handle optional caveats parameter
  • Created comprehensive E2E tests for calldata functionality with new PayableReceiver smart contract
  • Added helper functions for deploying and interacting with test contracts

🚀 Why?

Explain the motivation behind these changes:

  • Better Developer Experience: Developers no longer need to pass empty arrays when using scopes without additional caveats
  • Cleaner API: Removed redundant parameter that's no longer relevant since delegations always have caveats from scopes
  • Enhanced Flexibility: Developers can now specify calldata restrictions when transferring native tokens, enabling more precise control over delegated operations
  • Improved Testing: E2E tests now use real contract interactions instead of hardcoded calldata, providing more realistic validation

🧪 How to Test?

Describe how to test these changes:

  • Manual testing steps:

  • Create a delegation using createDelegation without specifying caveats parameter

  • Create a delegation with native token scope including exactCalldata or allowedCalldata

  • Verify delegation creation works with scope-only caveats

  • Automated tests added/updated

  • All existing tests pass

⚠️ Breaking Changes

List any breaking changes:

  • Breaking changes (describe below):
  • Removed allowInsecureFullAuthorizationDelegation parameter from createDelegation and createOpenDelegation functions

Updated native token scope type definitions to include optional calldata parameters

📋 Checklist

Check off completed items:

  • Code follows the project's coding standards
  • Self-review completed
  • Documentation updated (if needed)
  • Tests added/updated
  • Changelog updated (if needed)
  • All CI checks pass

🔗 Related Issues

Closes # https://app.zenhub.com/workspaces/readable-permissions-67982ce51eb4360029b2c1a1/issues/gh/metamask/delegator-readable-permissions/345

📚 Additional Notes

The PayableReceiver.sol contract was created specifically for testing calldata restrictions in E2E tests, providing a realistic target for delegated contract calls
Helper functions in test/utils/helpers.ts now return Promise directly to avoid type casting in tests
All native token scope tests now perform proper end-to-end validation by creating, signing, and redeeming delegations on-chain
Error message assertions updated to match actual smart contract revert messages (invalid-calldata instead of previous placeholder messages)

@hanzel98 hanzel98 requested a review from a team as a code owner August 20, 2025 20:17
cursor[bot]

This comment was marked as outdated.

@hanzel98
Copy link
Contributor Author

Key Documentation Updates Needed:

createDelegation and createOpenDelegation API Changes:

  • Update examples to show caveats parameter is now optional
  • Remove references to allowInsecureFullAuthorizationDelegation parameter
  • Add examples showing scope-only delegations

Native Token Scope Enhancements:

  • Document new exactCalldata and allowedCalldata parameters for native token scopes
  • Update examples in delegation-core/README.md to show calldata usage
  • Add examples of using calldata restrictions with native token transfers

Breaking Changes Documentation:

  • Document the removal of allowInsecureFullAuthorizationDelegation from creation functions
  • Update any migration guides or changelog entries

External Documentation:

  • The external docs sites (docs.gator.metamask.io, docs.metamask.io/delegation-toolkit/) will need updates to reflect:
    • New optional caveats parameter
    • Calldata support in native token scopes
    • Updated API signatures

@hanzel98 hanzel98 requested a review from jeffsmale90 August 20, 2025 20:54
@hanzel98 hanzel98 self-assigned this Aug 20, 2025
allowedCalldata.forEach((calldataConfig) => {
caveatBuilder.addCaveat('allowedCalldata', calldataConfig);
});
} else if (exactCalldata) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here the user can specify either allowedCalldata or exactCalldata. But the function accepts both - this could confuse the developer. I would set it to either:

calldataRestriction?: AllowedCalldataBuilderConfig[] |  ExactCalldataBuilderConfig

Or make it that if both parameters are defined and error is thrown to protect the user from unforeseen behavior.

Same with other scopes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure implemented the protection when both are passed

@hanzel98 hanzel98 requested a review from MoMannn August 27, 2025 20:33
MoMannn
MoMannn previously approved these changes Aug 28, 2025
Comment on lines +46 to +56
if (allowedCalldata && allowedCalldata.length > 0 && exactCalldata) {
throw new Error(
'Cannot specify both allowedCalldata and exactCalldata. Please use only one calldata restriction type.',
);
}

const caveatBuilder = createCaveatBuilder(environment)
.addCaveat('allowedTargets', { targets })
.addCaveat('allowedMethods', { selectors });

if (allowedCalldata) {
if (allowedCalldata && allowedCalldata.length > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: these two allowedCalldata && allowedCalldata.length > 0 checks could be simplified to allowedCalldata?.length > 0 because this will never be true if allowedCalldata is undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I change it I get this issue

image

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah my bad - because the operator is not an equality operator, this doesn't work (can't do > with undefined).

You could coalesce and keep the optional chaining, or you could change to an equality operator. Or you could leave it as it is.

if (allowedCalldata?.length !== 0) { ... } // actually, you'd probably want to switch the two cases and make this an `===` comparison 
if (allowedCalldata?.length ?? 0 > 0) { ... }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, I tried to change it using those cool ideas but I got lint errors.

jeffsmale90
jeffsmale90 previously approved these changes Sep 1, 2025
Copy link
Collaborator

@jeffsmale90 jeffsmale90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! one minor nit, duplicated across a couple files.

I don't know if it's worthwhile deduplicating the scope logic now, might be worthwhile deferring to a separate change.

exactCalldata,
} = config;

if (allowedCalldata && allowedCalldata.length > 0 && exactCalldata) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same simplification as above - allowedCalldata?.length > 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get an issue if I change it

const caveatBuilder = createCaveatBuilder(environment);

// Add calldata restrictions
if (allowedCalldata && allowedCalldata.length > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same simplification as above - allowedCalldata?.length > 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get an issue if I change it

Comment on lines +40 to +49
if (allowedCalldata && allowedCalldata.length > 0 && exactCalldata) {
throw new Error(
'Cannot specify both allowedCalldata and exactCalldata. Please use only one calldata restriction type.',
);
}

const caveatBuilder = createCaveatBuilder(environment);

// Add calldata restrictions
if (allowedCalldata && allowedCalldata.length > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same simplification as above - allowedCalldata?.length > 0

I wonder if there's value in deduplicating this logic. Maybe a concern for a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get an issue if I change it

Comment on lines +33 to +42
if (allowedCalldata && allowedCalldata.length > 0 && exactCalldata) {
throw new Error(
'Cannot specify both allowedCalldata and exactCalldata. Please use only one calldata restriction type.',
);
}

const caveatBuilder = createCaveatBuilder(environment);

// Add calldata restrictions
if (allowedCalldata && allowedCalldata.length > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: same simplification as above - allowedCalldata?.length > 0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get an issue if I change it

@hanzel98 hanzel98 dismissed stale reviews from jeffsmale90 and MoMannn via 3a8ab8b September 4, 2025 16:27
@hanzel98 hanzel98 merged commit c70e131 into main Sep 8, 2025
17 checks passed
@hanzel98 hanzel98 deleted the refactor/improve-delegation-devex branch September 8, 2025 20:34
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.

3 participants