Skip to content
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

Spec update 6 #18

Merged
merged 27 commits into from
Dec 1, 2023
Merged

Spec update 6 #18

merged 27 commits into from
Dec 1, 2023

Conversation

fangting-alchemy
Copy link
Collaborator

Update reference implementation to be fully compliant with updated ERC-6900 with some clarifications and bug fixes.

adamegyed and others added 27 commits November 28, 2023 16:00
Rename Execution to Call, and update IStandardExecutor
update IPlugin, IPluginManager, IAccountLoupe, BaseModularAccount to match spec update 6
Remove extraneous install/uninstall field checking
…or_faster_test_runs

perf: precompile contracts for faster test runs
@fangting-alchemy fangting-alchemy merged commit 0cb269d into main Dec 1, 2023
3 checks passed
@fangting-alchemy fangting-alchemy deleted the spec-update-6 branch December 1, 2023 20:19
@@ -227,6 +224,11 @@ contract UpgradeableModularAccount is
bytes4 selector = bytes4(data);
AccountStorage storage _storage = getAccountStorage();

// Make sure plugin is allowed to spend native token.
if (value > 0 && value > msg.value && !_storage.pluginData[msg.sender].canSpendNativeToken) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the reasoning behind value > msg.value?
Does it mean canSpendNativeToken will be ignored if value <= msg.value?

Copy link
Contributor

Choose a reason for hiding this comment

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

This check basically ensures that the plugin is only spending from the account's native token balance if it has permission to do so. If the plugin is managing its own native token balance (outside of the account), and covers the amount sent via executeFromPluginExternal, then it isn't deducting anything from the user's balance and therefore can send a call with value.

PostExecToRun[] memory postPermittedCallHooks =
_doPrePermittedCallHooks(IPluginExecutor.executeFromPluginExternal.selector, msg.sender);
PostExecToRun[] memory postPermittedCallHooks = _doPrePermittedCallHooks(
getPermittedCallKey(msg.sender, IPluginExecutor.executeFromPluginExternal.selector), msg.data
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious why we use msg.data instead of data here

Copy link
Contributor

Choose a reason for hiding this comment

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

The hooks applied to executeFromPluginExternal are tied to the function selector on the account, rather than on the target contract (i.e. you can see in this line that it uses IPluginExecutor.executeFromPluginExternal.selector. ) So, when it is attached to that function, it makes sense to send the calldata for that function call to the hook, rather than the data for the inner call.

By doing this instead of using the inner data field, the hook can be certain that the incoming calldata will be in a predictable format, which is the abi-encoded call to executeFromPluginExternal. It will also get the value to be sent, and which address the account is making the call to, which would not be visible if we just sent data because it is a separate parameter.

Copy link
Contributor

@huaweigu huaweigu Dec 12, 2023

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, the difference with executeFromPlugin is that it emulates a call to the fallback, so it needs to use the inner data field. Example:

  • Plugin A defines a function foo(uint256)
  • Plugin B adds a pre-exec hook over foo(...) that reads the uint256 from calldata and performs a check
  • Plugin C requests permission to use foo(...) via executeFromPlugin.

When plugin C uses executeFromPlugin for foo(...), plugin B should get the data for foo(uint256), rather than executeFromPlugin(abi.encodeCall(foo, (...)). If it were to get the second format for the data, then every hook would need two mechanisms to decode calldata rather than just one. By sending the inner data rather than the entire msg.data, it makes it more straightforward to implement hooks.


// Run any pre exec hooks for this selector
PostExecToRun[] memory postExecHooks = _doPreExecHooks(IPluginExecutor.executeFromPluginExternal.selector);
PostExecToRun[] memory postExecHooks =
_doPreExecHooks(IPluginExecutor.executeFromPluginExternal.selector, msg.data);
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto on the usage of msg.data

Copy link
Contributor

Choose a reason for hiding this comment

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

Same reasoning as above for permitted call hooks - the hook function benefits from the standardized format, and gets access to the target address and the value, which are otherwise not visible if we just send data.

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.

5 participants