-
-
Notifications
You must be signed in to change notification settings - Fork 15
Improve declarative delegation interface #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Key Documentation Updates Needed:createDelegation and createOpenDelegation API Changes:
Native Token Scope Enhancements:
Breaking Changes Documentation:
External Documentation:
|
allowedCalldata.forEach((calldataConfig) => { | ||
caveatBuilder.addCaveat('allowedCalldata', calldataConfig); | ||
}); | ||
} else if (exactCalldata) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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) { ... }
There was a problem hiding this comment.
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.
There was a problem hiding this 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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
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) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
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) { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
📝 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:
🚀 Why?
Explain the motivation behind these changes:
🧪 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
List any breaking changes:
Updated native token scope type definitions to include optional calldata parameters
📋 Checklist
Check off completed items:
🔗 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)