-
Notifications
You must be signed in to change notification settings - Fork 25
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
Add samples
directory and custom session key plugins
#22
Conversation
feat: Add Modular Session Key Plugin to `samples` directory
Updated some parts of the implementation with new |
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.
Took a pass, thanks for your patience here! Some ERC updates in flight too, so I imagine we'll need to work through some rebases.
Addressed all feedbacks you provided, with small fixes to maintain compatibility with recent changes in the spec. |
address(ownerPlugin), (uint8(ISingleOwnerPlugin.FunctionId.USER_OP_VALIDATION_OWNER)) | ||
); | ||
modularSessionDependency[1] = FunctionReferenceLib.pack( | ||
address(ownerPlugin), (uint8(ISingleOwnerPlugin.FunctionId.RUNTIME_VALIDATION_OWNER_OR_SELF)) |
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.
address(ownerPlugin), (uint8(ISingleOwnerPlugin.FunctionId.RUNTIME_VALIDATION_OWNER_OR_SELF)) | |
address(ownerPlugin), uint8(ISingleOwnerPlugin.FunctionId.RUNTIME_VALIDATION_OWNER_OR_SELF) |
} | ||
|
||
/// @inheritdoc BasePlugin | ||
function onUninstall(bytes calldata data) external override { |
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.
Area of improvement here would be to be able to clean up all of an account's session keys just based on msg.sender
, without relying on what is passed into data
. Ideally, the responsibility of proper cleanup lies with the plugin.
This is probably a larger change though. Feel free to tackle this later.
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.
Though it will add some gas overhead, this sounds necessary to keep the consistency in the storage of plugin. I added an additional mapping that stores the information of session key and allowed selector to implement this.
if (validUntil != 0) { | ||
return _packValidationData(false, validUntil, validAfter); | ||
} | ||
return _SIG_VALIDATION_FAILED; |
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.
Delaying SIG_VALIDATION_FAILED like this helps with gas estimation from bundlers when using dummy signatures.
if (validUntil != 0) { | |
return _packValidationData(false, validUntil, validAfter); | |
} | |
return _SIG_VALIDATION_FAILED; | |
return _packValidationData(validUntil == 0, validUntil, validAfter); |
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.
That seems valid. Fixed it.
returns (uint256) | ||
{ | ||
if (functionId == uint8(FunctionId.USER_OP_VALIDATION_TEMPORARY_OWNER)) { | ||
(address signer,) = userOpHash.toEthSignedMessageHash().tryRecover(userOp.signature); |
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.
Check the result of tryRecover, and if err != ECDSA.RecoverError.NoError
, revert with InvalidSignature()
.
{ | ||
if (functionId == uint8(FunctionId.RUNTIME_VALIDATION_TEMPORARY_OWNER)) { | ||
bytes4 selector = bytes4(data[0:4]); | ||
bytes memory key = address(msg.sender).allocateAssociatedStorageKey(0, 1); |
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
bytes memory key = address(msg.sender).allocateAssociatedStorageKey(0, 1); | |
bytes memory key = msg.sender.allocateAssociatedStorageKey(0, 1); |
@jaypaik Addressed all the comments you suggested! |
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.
Looks good! I left a few optional suggestions, feel free to either adopt them or not.
function _castToBytes32(address addr, bytes4 b4) internal pure returns (bytes32 res) { | ||
assembly { | ||
res := or(shl(96, addr), b4) | ||
} | ||
} | ||
|
||
function _castToAddressAndBytes4(bytes32 b32) internal pure returns (address addr, bytes4 b4) { | ||
assembly { | ||
addr := shr(96, b32) | ||
b4 := and(b32, 0xFFFFFFFF) | ||
} | ||
} |
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.
These work as-is, but you could reduce the shift amount down to 32 (from 96) if you wanted to make it compact.
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.
Yep that's valid! Fixed it.
/// @param _untils The times until which the owners are valid. | ||
event SessionKeysAdded( | ||
address indexed account, | ||
address[] indexed sessionKeys, |
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.
Just a heads up, indexing an array will result in the hash of the entire array, rather than indexing on the elements within it: https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#encoding-of-indexed-event-parameters.
This is OK to leave as-is, just flagging that it may behave differently than expected. An alternative is to not index this field, or to emit a SessionKeyAdded
event for each key, though that is expensive in gas.
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 also think that this will lead to an unexpected behavior, so I just removed the indexed field at the arrays at the both events.
/// @param account The account whose session keys are updated. | ||
/// @param sessionKeys The addresses of the session keys. | ||
/// @param selectors The selectors of the functions that the session keys are allowed to call. | ||
event SessionKeysRemoved(address indexed account, address[] indexed sessionKeys, bytes4[] selectors); |
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.
Same here as above ^
@adam-alchemy Thanks for the feedbacks! Addressed to all of them. |
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.
Some final nits!
Also noticed you use unchecked { ++i; }
in some places but not others. Would prefer those be consistent too!
uint48 _validAfter, | ||
uint48 _validUntil |
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:
uint48 _validAfter, | |
uint48 _validUntil | |
uint48 validAfter, | |
uint48 validUntil |
sessionInfo.validAfter = _validAfter; | ||
sessionInfo.validUntil = _validUntil; |
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.
sessionInfo.validAfter = _validAfter; | |
sessionInfo.validUntil = _validUntil; | |
sessionInfo.validAfter = validAfter; | |
sessionInfo.validUntil = validUntil; |
for (uint256 i = 0; i < manifest.dependencyInterfaceIds.length;) { | ||
manifest.dependencyInterfaceIds[i] = type(IModularSessionKeyPlugin).interfaceId; | ||
unchecked { | ||
++i; | ||
} | ||
} |
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.
Why not just:
manifest.dependencyInterfaceIds[0] = ...
manifest.dependencyInterfaceIds[1] = ...
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.
That's more straightforward, fixed it.
/// @param _afters The times after which the owners are valid. | ||
/// @param _untils The times until which the owners are valid. | ||
event SessionKeysAdded( | ||
address indexed account, | ||
address[] sessionKeys, | ||
bytes4[] selectors, | ||
uint48[] _afters, | ||
uint48[] _untils |
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.
/// @param _afters The times after which the owners are valid. | |
/// @param _untils The times until which the owners are valid. | |
event SessionKeysAdded( | |
address indexed account, | |
address[] sessionKeys, | |
bytes4[] selectors, | |
uint48[] _afters, | |
uint48[] _untils | |
/// @param afters The times after which the owners are valid. | |
/// @param untils The times until which the owners are valid. | |
event SessionKeysAdded( | |
address indexed account, | |
address[] sessionKeys, | |
bytes4[] selectors, | |
uint48[] afters, | |
uint48[] untils |
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.
Making _after
to after
leads to the following error
Expected ',' but got reserved keyword 'after'
So I changed all of it to validAfter
, and same with all until
s.
/// @param _after The time after which the owner is valid. | ||
/// @param _until The time until which the owner is valid. | ||
event SessionKeyAdded( | ||
address indexed account, address indexed sessionKey, bytes4 selector, uint48 _after, uint48 _until | ||
); |
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.
/// @param _after The time after which the owner is valid. | |
/// @param _until The time until which the owner is valid. | |
event SessionKeyAdded( | |
address indexed account, address indexed sessionKey, bytes4 selector, uint48 _after, uint48 _until | |
); | |
/// @param after The time after which the owner is valid. | |
/// @param until The time until which the owner is valid. | |
event SessionKeyAdded( | |
address indexed account, address indexed sessionKey, bytes4 selector, uint48 after, uint48 until | |
); |
/// @param _after The time after which the owner is valid. | ||
/// @param _until The time until which the owner is valid. | ||
function addSessionKey(address sessionKey, bytes4 allowedSelector, uint48 _after, uint48 _until) external; |
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.
/// @param _after The time after which the owner is valid. | |
/// @param _until The time until which the owner is valid. | |
function addSessionKey(address sessionKey, bytes4 allowedSelector, uint48 _after, uint48 _until) external; | |
/// @param after The time after which the owner is valid. | |
/// @param until The time until which the owner is valid. | |
function addSessionKey(address sessionKey, bytes4 allowedSelector, uint48 after, uint48 until) external; |
/// @param _afters The times after which the owners are valid. | ||
/// @param _untils The times until which the owners are valid. | ||
function addSessionKeyBatch( | ||
address[] calldata sessionKeys, | ||
bytes4[] calldata allowedSelectors, | ||
uint48[] calldata _afters, | ||
uint48[] calldata _untils |
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.
/// @param _afters The times after which the owners are valid. | |
/// @param _untils The times until which the owners are valid. | |
function addSessionKeyBatch( | |
address[] calldata sessionKeys, | |
bytes4[] calldata allowedSelectors, | |
uint48[] calldata _afters, | |
uint48[] calldata _untils | |
/// @param afters The times after which the owners are valid. | |
/// @param untils The times until which the owners are valid. | |
function addSessionKeyBatch( | |
address[] calldata sessionKeys, | |
bytes4[] calldata allowedSelectors, | |
uint48[] calldata afters, | |
uint48[] calldata untils |
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.
🥳
Looks like lint failed, can you run forge fmt? |
Forgot it, just committed :) @jaypaik |
Motivation
While there are basic plugins like
SingleOwnerPlugin
insrc/plugins
folder, it is not sufficient to show the different practices of plugins and the functionalities of ERC-6900. To enhance the understanding and utilization of ERC-6900 among plugin developers, establishing directories for various plugins would be beneficial.Solution
This involves creating two new directories:
src/samples/plugins
andtest/samples/plugins
. These directories would host a range of plugins, showcasing the different capabilities and practices within the ERC-6900 framework.Also add a custom session key plugin inside them, which demonstrates the usage of dependency and
executeFromPluginExternal
.