From afb5a03f57358fae117122acee1c4d9720113fb9 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sat, 6 Jul 2024 00:05:22 +0530 Subject: [PATCH 01/11] feat:add support for delegate calltype --- contracts/Nexus.sol | 25 +++++++++- contracts/base/ExecutionHelper.sol | 46 +++++++++++++++++++ ...AccountExecution_ExecuteFromExecutor.t.sol | 20 -------- .../smart-account/Nexus.Basics.specs.ts | 14 +++--- test/hardhat/utils/erc7579Utils.ts | 2 +- 5 files changed, 77 insertions(+), 30 deletions(-) diff --git a/contracts/Nexus.sol b/contracts/Nexus.sol index 40563e36..b43f99e9 100644 --- a/contracts/Nexus.sol +++ b/contracts/Nexus.sol @@ -25,7 +25,7 @@ import { ModuleManager } from "./base/ModuleManager.sol"; import { ExecutionHelper } from "./base/ExecutionHelper.sol"; import { IValidator } from "./interfaces/modules/IValidator.sol"; import { MODULE_TYPE_VALIDATOR, MODULE_TYPE_EXECUTOR, MODULE_TYPE_FALLBACK, MODULE_TYPE_HOOK, VALIDATION_FAILED } from "./types/Constants.sol"; -import { ModeLib, ExecutionMode, ExecType, CallType, CALLTYPE_BATCH, CALLTYPE_SINGLE, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "./lib/ModeLib.sol"; +import { ModeLib, ExecutionMode, ExecType, CallType, CALLTYPE_BATCH, CALLTYPE_SINGLE, CALLTYPE_DELEGATECALL, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "./lib/ModeLib.sol"; /// @title Nexus - Smart Account /// @notice This contract integrates various functionalities to handle modular smart accounts compliant with ERC-7579 and ERC-4337 standards. @@ -102,6 +102,8 @@ contract Nexus is INexus, EIP712, BaseAccount, ExecutionHelper, ModuleManager, U _handleSingleExecution(executionCalldata, execType); } else if (callType == CALLTYPE_BATCH) { _handleBatchExecution(executionCalldata, execType); + } else if (callType == CALLTYPE_DELEGATECALL) { + _handleDelegateCallExecution(executionCalldata, execType); } else { revert UnsupportedCallType(callType); } @@ -140,6 +142,14 @@ contract Nexus is INexus, EIP712, BaseAccount, ExecutionHelper, ModuleManager, U if (execType == EXECTYPE_DEFAULT) returnData = _executeBatch(executions); else if (execType == EXECTYPE_TRY) returnData = _tryExecuteBatch(executions); else revert UnsupportedExecType(execType); + } else if (callType == CALLTYPE_DELEGATECALL) { + // destructure executionCallData according to single exec + address delegate = address(uint160(bytes20(executionCalldata[0:20]))); + bytes calldata callData = executionCalldata[20:]; + // check if execType is revert or try + if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData); + else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData); + else revert UnsupportedExecType(execType); } else { revert UnsupportedCallType(callType); } @@ -282,7 +292,7 @@ contract Nexus is INexus, EIP712, BaseAccount, ExecutionHelper, ModuleManager, U (CallType callType, ExecType execType) = mode.decodeBasic(); // Return true if both the call type and execution type are supported. - return (callType == CALLTYPE_SINGLE || callType == CALLTYPE_BATCH) && (execType == EXECTYPE_DEFAULT || execType == EXECTYPE_TRY); + return (callType == CALLTYPE_SINGLE || callType == CALLTYPE_BATCH || callType == CALLTYPE_DELEGATECALL) && (execType == EXECTYPE_DEFAULT || execType == EXECTYPE_TRY); } /// @notice Determines whether a module is installed on the smart account. @@ -551,6 +561,17 @@ contract Nexus is INexus, EIP712, BaseAccount, ExecutionHelper, ModuleManager, U else revert UnsupportedExecType(execType); } + /// @dev Executes a single transaction based on the specified execution type. + /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). + /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). + function _handleDelegateCallExecution(bytes calldata executionCalldata, ExecType execType) private { + address delegate = address(uint160(bytes20(executionCalldata[0:20]))); + bytes calldata callData = executionCalldata[20:]; + if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData); + else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData); + else revert UnsupportedExecType(execType); + } + /// @notice Checks if a module is installed on the smart account. /// @param moduleTypeId The module type ID. /// @param module The module address. diff --git a/contracts/base/ExecutionHelper.sol b/contracts/base/ExecutionHelper.sol index ac8a2a21..4ddace0f 100644 --- a/contracts/base/ExecutionHelper.sol +++ b/contracts/base/ExecutionHelper.sol @@ -95,4 +95,50 @@ contract ExecutionHelper is IExecutionHelperEventsAndErrors { if (!success) emit TryExecuteUnsuccessful(i, result[i]); } } + + /// @dev Execute a delegatecall with `delegate` on this account. + function _executeDelegatecall( + address delegate, + bytes calldata callData + ) + internal + returns (bytes memory result) + { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + calldatacopy(result, callData.offset, callData.length) + // Forwards the `data` to `delegate` via delegatecall. + if iszero(delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00)) { + // Bubble up the revert if the call reverts. + returndatacopy(result, 0x00, returndatasize()) + revert(result, returndatasize()) + } + mstore(result, returndatasize()) // Store the length. + let o := add(result, 0x20) + returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. + mstore(0x40, add(o, returndatasize())) // Allocate the memory. + } + } + + /// @dev Execute a delegatecall with `delegate` on this account and catch reverts. + function _tryExecuteDelegatecall( + address delegate, + bytes calldata callData + ) + internal + returns (bool success, bytes memory result) + { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + calldatacopy(result, callData.offset, callData.length) + // Forwards the `data` to `delegate` via delegatecall. + success := delegatecall(gas(), delegate, result, callData.length, codesize(), 0x00) + mstore(result, returndatasize()) // Store the length. + let o := add(result, 0x20) + returndatacopy(o, 0x00, returndatasize()) // Copy the returndata. + mstore(0x40, add(o, returndatasize())) // Allocate the memory. + } + } } diff --git a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol index 527e3395..810e5778 100644 --- a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol +++ b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol @@ -150,26 +150,6 @@ contract TestAccountExecution_ExecuteFromExecutor is TestAccountExecution_Base { assertEq(balanceRecipient, amount, "Recipient should have received 0 tokens"); } - /// @notice Tests execution with an unsupported call type via MockExecutor - function test_RevertIf_ExecuteFromExecutor_UnsupportedCallType() public { - ExecutionMode unsupportedMode = ExecutionMode.wrap(bytes32(abi.encodePacked(bytes1(0xff), bytes1(0x00), bytes4(0), bytes22(0)))); - bytes memory executionCalldata = abi.encodePacked(address(counter), uint256(0), abi.encodeWithSelector(Counter.incrementNumber.selector)); - - (CallType callType, , , ) = ModeLib.decode(unsupportedMode); - Execution[] memory execution = new Execution[](1); - execution[0] = Execution(address(mockExecutor), 0, executionCalldata); - - vm.expectRevert(abi.encodeWithSelector(UnsupportedCallType.selector, callType)); - - mockExecutor.customExecuteViaAccount( - unsupportedMode, - BOB_ACCOUNT, - address(counter), - 0, - abi.encodeWithSelector(Counter.incrementNumber.selector) - ); - } - /// @notice Tests single execution with an unsupported execution type via MockExecutor function test_RevertIf_ExecuteFromExecutor_UnsupportedExecType_Single() public { // Create an unsupported execution mode with an invalid execution type diff --git a/test/hardhat/smart-account/Nexus.Basics.specs.ts b/test/hardhat/smart-account/Nexus.Basics.specs.ts index a2b6048e..5fd25073 100644 --- a/test/hardhat/smart-account/Nexus.Basics.specs.ts +++ b/test/hardhat/smart-account/Nexus.Basics.specs.ts @@ -31,7 +31,7 @@ import { CALLTYPE_BATCH, CALLTYPE_SINGLE, EXECTYPE_DEFAULT, - EXECTYPE_DELEGATE, + CALLTYPE_DELEGATE, EXECTYPE_TRY, MODE_DEFAULT, MODE_PAYLOAD, @@ -247,14 +247,14 @@ describe("Nexus Basic Specs", function () { expect( await smartAccount.supportsExecutionMode( ethers.concat([ - ethers.zeroPadValue(toBeHex(EXECTYPE_DELEGATE), 1), - ethers.zeroPadValue(toBeHex(CALLTYPE_SINGLE), 1), + ethers.zeroPadValue(toBeHex(CALLTYPE_DELEGATE), 1), + ethers.zeroPadValue(toBeHex(EXECTYPE_DEFAULT), 1), ethers.zeroPadValue(toBeHex(UNUSED), 4), ethers.zeroPadValue(toBeHex(MODE_DEFAULT), 4), ethers.zeroPadValue(toBeHex(MODE_PAYLOAD), 22), ]), ), - ).to.be.false; + ).to.be.true; }); it("Should verify unsupported execution modes", async function () { @@ -262,14 +262,14 @@ describe("Nexus Basic Specs", function () { expect( await smartAccount.supportsExecutionMode( ethers.concat([ - ethers.zeroPadValue(toBeHex(EXECTYPE_DELEGATE), 1), - ethers.zeroPadValue(toBeHex(CALLTYPE_SINGLE), 1), + ethers.zeroPadValue(toBeHex(CALLTYPE_DELEGATE), 1), + ethers.zeroPadValue(toBeHex(EXECTYPE_DEFAULT), 1), ethers.zeroPadValue(toBeHex(UNUSED), 4), ethers.zeroPadValue(toBeHex("0x00"), 4), ethers.zeroPadValue(toBeHex(MODE_PAYLOAD), 22), ]), ), - ).to.be.false; + ).to.be.true; }); it("Should return false for unsupported execution mode", async function () { diff --git a/test/hardhat/utils/erc7579Utils.ts b/test/hardhat/utils/erc7579Utils.ts index a4b65528..38acf014 100644 --- a/test/hardhat/utils/erc7579Utils.ts +++ b/test/hardhat/utils/erc7579Utils.ts @@ -7,7 +7,7 @@ export const CALLTYPE_SINGLE = "0x00"; // 1 byte export const CALLTYPE_BATCH = "0x01"; // 1 byte export const EXECTYPE_DEFAULT = "0x00"; // 1 byte export const EXECTYPE_TRY = "0x01"; // 1 byte -export const EXECTYPE_DELEGATE = "0xFF"; // 1 byte +export const CALLTYPE_DELEGATE = "0xFF"; // 1 byte export const MODE_DEFAULT = "0x00000000"; // 4 bytes export const UNUSED = "0x00000000"; // 4 bytes export const MODE_PAYLOAD = "0x00000000000000000000000000000000000000000000"; // 22 bytes From 349f69b4c9cf94d3cf5883f390928562f9d73880 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sat, 6 Jul 2024 15:07:16 +0530 Subject: [PATCH 02/11] lint fixes --- .solhint.json | 4 ++-- contracts/Nexus.sol | 4 +++- contracts/base/ExecutionHelper.sol | 16 ++-------------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/.solhint.json b/.solhint.json index 0e9fc477..e8ce84a1 100644 --- a/.solhint.json +++ b/.solhint.json @@ -18,9 +18,9 @@ "imports-on-top": "error", "ordering": "error", "visibility-modifier-order": "error", - "code-complexity": ["error", 9], + "code-complexity": ["error", 11], "function-max-lines": ["error", 80], - "max-line-length": ["error", 150], + "max-line-length": ["error", 170], "no-empty-blocks": "off", "no-unused-vars": "error", "payable-fallback": "off", diff --git a/contracts/Nexus.sol b/contracts/Nexus.sol index b43f99e9..91bd5640 100644 --- a/contracts/Nexus.sol +++ b/contracts/Nexus.sol @@ -292,7 +292,9 @@ contract Nexus is INexus, EIP712, BaseAccount, ExecutionHelper, ModuleManager, U (CallType callType, ExecType execType) = mode.decodeBasic(); // Return true if both the call type and execution type are supported. - return (callType == CALLTYPE_SINGLE || callType == CALLTYPE_BATCH || callType == CALLTYPE_DELEGATECALL) && (execType == EXECTYPE_DEFAULT || execType == EXECTYPE_TRY); + return + (callType == CALLTYPE_SINGLE || callType == CALLTYPE_BATCH || callType == CALLTYPE_DELEGATECALL) && + (execType == EXECTYPE_DEFAULT || execType == EXECTYPE_TRY); } /// @notice Determines whether a module is installed on the smart account. diff --git a/contracts/base/ExecutionHelper.sol b/contracts/base/ExecutionHelper.sol index 4ddace0f..652a4f20 100644 --- a/contracts/base/ExecutionHelper.sol +++ b/contracts/base/ExecutionHelper.sol @@ -97,13 +97,7 @@ contract ExecutionHelper is IExecutionHelperEventsAndErrors { } /// @dev Execute a delegatecall with `delegate` on this account. - function _executeDelegatecall( - address delegate, - bytes calldata callData - ) - internal - returns (bytes memory result) - { + function _executeDelegatecall(address delegate, bytes calldata callData) internal returns (bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) @@ -122,13 +116,7 @@ contract ExecutionHelper is IExecutionHelperEventsAndErrors { } /// @dev Execute a delegatecall with `delegate` on this account and catch reverts. - function _tryExecuteDelegatecall( - address delegate, - bytes calldata callData - ) - internal - returns (bool success, bytes memory result) - { + function _tryExecuteDelegatecall(address delegate, bytes calldata callData) internal returns (bool success, bytes memory result) { /// @solidity memory-safe-assembly assembly { result := mload(0x40) From e7f179fbf25ba72adc5a50f57d8fc2995cb4ffad Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:11:36 +0530 Subject: [PATCH 03/11] respond to PR comments --- .solhint.json | 4 ++-- contracts/Nexus.sol | 14 ++++--------- contracts/lib/ExecLib.sol | 6 ++++++ ...AccountExecution_ExecuteFromExecutor.t.sol | 20 +++++++++++++++++++ 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/.solhint.json b/.solhint.json index e8ce84a1..0e9fc477 100644 --- a/.solhint.json +++ b/.solhint.json @@ -18,9 +18,9 @@ "imports-on-top": "error", "ordering": "error", "visibility-modifier-order": "error", - "code-complexity": ["error", 11], + "code-complexity": ["error", 9], "function-max-lines": ["error", 80], - "max-line-length": ["error", 170], + "max-line-length": ["error", 150], "no-empty-blocks": "off", "no-unused-vars": "error", "payable-fallback": "off", diff --git a/contracts/Nexus.sol b/contracts/Nexus.sol index 91bd5640..653b5704 100644 --- a/contracts/Nexus.sol +++ b/contracts/Nexus.sol @@ -25,7 +25,8 @@ import { ModuleManager } from "./base/ModuleManager.sol"; import { ExecutionHelper } from "./base/ExecutionHelper.sol"; import { IValidator } from "./interfaces/modules/IValidator.sol"; import { MODULE_TYPE_VALIDATOR, MODULE_TYPE_EXECUTOR, MODULE_TYPE_FALLBACK, MODULE_TYPE_HOOK, VALIDATION_FAILED } from "./types/Constants.sol"; -import { ModeLib, ExecutionMode, ExecType, CallType, CALLTYPE_BATCH, CALLTYPE_SINGLE, CALLTYPE_DELEGATECALL, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "./lib/ModeLib.sol"; +import { ModeLib, ExecutionMode, ExecType, CallType, CALLTYPE_BATCH, CALLTYPE_SINGLE, CALLTYPE_DELEGATECALL, EXECTYPE_DEFAULT, +EXECTYPE_TRY } from "./lib/ModeLib.sol"; /// @title Nexus - Smart Account /// @notice This contract integrates various functionalities to handle modular smart accounts compliant with ERC-7579 and ERC-4337 standards. @@ -143,13 +144,7 @@ contract Nexus is INexus, EIP712, BaseAccount, ExecutionHelper, ModuleManager, U else if (execType == EXECTYPE_TRY) returnData = _tryExecuteBatch(executions); else revert UnsupportedExecType(execType); } else if (callType == CALLTYPE_DELEGATECALL) { - // destructure executionCallData according to single exec - address delegate = address(uint160(bytes20(executionCalldata[0:20]))); - bytes calldata callData = executionCalldata[20:]; - // check if execType is revert or try - if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData); - else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData); - else revert UnsupportedExecType(execType); + _handleDelegateCallExecution(executionCalldata, execType); } else { revert UnsupportedCallType(callType); } @@ -567,8 +562,7 @@ contract Nexus is INexus, EIP712, BaseAccount, ExecutionHelper, ModuleManager, U /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). function _handleDelegateCallExecution(bytes calldata executionCalldata, ExecType execType) private { - address delegate = address(uint160(bytes20(executionCalldata[0:20]))); - bytes calldata callData = executionCalldata[20:]; + (address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall(); if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData); else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData); else revert UnsupportedExecType(execType); diff --git a/contracts/lib/ExecLib.sol b/contracts/lib/ExecLib.sol index b19ebc35..45fe0922 100644 --- a/contracts/lib/ExecLib.sol +++ b/contracts/lib/ExecLib.sol @@ -35,6 +35,12 @@ library ExecLib { callData = executionCalldata[52:]; } + function decodeDelegateCall(bytes calldata executionCalldata) internal pure returns (address delegate, bytes calldata callData) { + // destructure executionCallData according to single exec + delegate = address(uint160(bytes20(executionCalldata[0:20]))); + callData = executionCalldata[20:]; + } + function encodeSingle(address target, uint256 value, bytes memory callData) internal pure returns (bytes memory userOpCalldata) { userOpCalldata = abi.encodePacked(target, value, callData); } diff --git a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol index 810e5778..e74a6482 100644 --- a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol +++ b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol @@ -174,6 +174,26 @@ contract TestAccountExecution_ExecuteFromExecutor is TestAccountExecution_Base { ); } + /// @notice Tests execution with an unsupported call type via MockExecutor + function test_RevertIf_ExecuteFromExecutor_UnsupportedCallType() public { + ExecutionMode unsupportedMode = ExecutionMode.wrap(bytes32(abi.encodePacked(bytes1(0xee), bytes1(0x00), bytes4(0), bytes22(0)))); + bytes memory executionCalldata = abi.encodePacked(address(counter), uint256(0), abi.encodeWithSelector(Counter.incrementNumber.selector)); + + (CallType callType, , , ) = ModeLib.decode(unsupportedMode); + Execution[] memory execution = new Execution[](1); + execution[0] = Execution(address(mockExecutor), 0, executionCalldata); + + vm.expectRevert(abi.encodeWithSelector(UnsupportedCallType.selector, callType)); + + mockExecutor.customExecuteViaAccount( + unsupportedMode, + BOB_ACCOUNT, + address(counter), + 0, + abi.encodeWithSelector(Counter.incrementNumber.selector) + ); + } + /// @notice Tests batch execution with an unsupported execution type via MockExecutor function test_RevertIf_ExecuteFromExecutor_UnsupportedExecType_Batch() public { // Create an unsupported execution mode with an invalid execution type From ee8a7ac0cdd7ee00bbcfca757add8bf7ab129278 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 14:17:16 +0530 Subject: [PATCH 04/11] fix contract size blocker for hardhat tests --- hardhat.config.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hardhat.config.ts b/hardhat.config.ts index ee6766e7..2390389d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -20,6 +20,11 @@ const config: HardhatUserConfig = { }, }, }, + networks: { + hardhat: { + allowUnlimitedContractSize: true + }, + }, docgen: { projectName: "Nexus", projectDescription: "Nexus - Biconomy Modular Smart Account - ERC-7579", From ef1949ac8344ff0d24b5e2d5333042321cca9c35 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 21:18:36 +0530 Subject: [PATCH 05/11] respond to PR comments + add tests --- contracts/Nexus.sol | 54 +--------------- contracts/base/ExecutionHelper.sol | 62 +++++++++++++++++++ .../interfaces/INexusEventsAndErrors.sol | 4 -- .../base/IExecutionHelperEventsAndErrors.sol | 7 +++ contracts/mocks/MockDelegateTarget.sol | 8 +++ .../TestAccountExecution_DelegateCall.t.sol | 59 ++++++++++++++++++ test/foundry/utils/TestHelper.t.sol | 1 + 7 files changed, 140 insertions(+), 55 deletions(-) create mode 100644 contracts/mocks/MockDelegateTarget.sol create mode 100644 test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol diff --git a/contracts/Nexus.sol b/contracts/Nexus.sol index 2f1713ac..67726db2 100644 --- a/contracts/Nexus.sol +++ b/contracts/Nexus.sol @@ -140,29 +140,11 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra bytes calldata executionCalldata ) external payable onlyExecutorModule withHook withRegistry(msg.sender, MODULE_TYPE_EXECUTOR) returns (bytes[] memory returnData) { (CallType callType, ExecType execType) = mode.decodeBasic(); - - // check if calltype is batch or single + // check if calltype is batch or single or delegate call if (callType == CALLTYPE_SINGLE) { - // destructure executionCallData according to single exec - (address target, uint256 value, bytes calldata callData) = executionCalldata.decodeSingle(); - returnData = new bytes[](1); - bool success; - // check if execType is revert(default) or try - if (execType == EXECTYPE_DEFAULT) { - returnData[0] = _execute(target, value, callData); - } else if (execType == EXECTYPE_TRY) { - (success, returnData[0]) = _tryExecute(target, value, callData); - if (!success) emit TryExecuteUnsuccessful(0, returnData[0]); - } else { - revert UnsupportedExecType(execType); - } + returnData = _handleSingleExecutionAndReturnData(executionCalldata, execType); } else if (callType == CALLTYPE_BATCH) { - // destructure executionCallData according to batched exec - Execution[] calldata executions = executionCalldata.decodeBatch(); - // check if execType is revert or try - if (execType == EXECTYPE_DEFAULT) returnData = _executeBatch(executions); - else if (execType == EXECTYPE_TRY) returnData = _tryExecuteBatch(executions); - else revert UnsupportedExecType(execType); + returnData = _handleBatchExecutionAndReturnData(executionCalldata, execType); } else if (callType == CALLTYPE_DELEGATECALL) { _handleDelegateCallExecution(executionCalldata, execType); } else { @@ -543,36 +525,6 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra version = "1.0.0-beta"; } - /// @dev Executes a single transaction based on the specified execution type. - /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). - /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). - function _handleSingleExecution(bytes calldata executionCalldata, ExecType execType) private { - (address target, uint256 value, bytes calldata callData) = executionCalldata.decodeSingle(); - if (execType == EXECTYPE_DEFAULT) _execute(target, value, callData); - else if (execType == EXECTYPE_TRY) _tryExecute(target, value, callData); - else revert UnsupportedExecType(execType); - } - - /// @dev Executes a batch of transactions based on the specified execution type. - /// @param executionCalldata The calldata for a batch of transactions. - /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). - function _handleBatchExecution(bytes calldata executionCalldata, ExecType execType) private { - Execution[] calldata executions = executionCalldata.decodeBatch(); - if (execType == EXECTYPE_DEFAULT) _executeBatch(executions); - else if (execType == EXECTYPE_TRY) _tryExecuteBatch(executions); - else revert UnsupportedExecType(execType); - } - - /// @dev Executes a single transaction based on the specified execution type. - /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). - /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). - function _handleDelegateCallExecution(bytes calldata executionCalldata, ExecType execType) private { - (address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall(); - if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData); - else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData); - else revert UnsupportedExecType(execType); - } - /// @dev For use in `_erc1271HashForIsValidSignatureViaNestedEIP712`, function _typedDataSignFields() private view returns (bytes32 m) { ( diff --git a/contracts/base/ExecutionHelper.sol b/contracts/base/ExecutionHelper.sol index 652a4f20..2b821020 100644 --- a/contracts/base/ExecutionHelper.sol +++ b/contracts/base/ExecutionHelper.sol @@ -14,6 +14,8 @@ pragma solidity ^0.8.26; import { Execution } from "../types/DataTypes.sol"; import { IExecutionHelperEventsAndErrors } from "../interfaces/base/IExecutionHelper.sol"; +import { ExecType, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "../lib/ModeLib.sol"; +import { ExecLib } from "../lib/ExecLib.sol"; /// @title Nexus - ExecutionHelper /// @notice Implements execution management within the Nexus suite, facilitating transaction execution strategies and @@ -26,6 +28,8 @@ import { IExecutionHelperEventsAndErrors } from "../interfaces/base/IExecutionHe /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady contract ExecutionHelper is IExecutionHelperEventsAndErrors { + using ExecLib for bytes; + /// @notice Executes a call to a target address with specified value and data. /// @param target The address to execute the call on. /// @param value The amount of wei to send with the call. @@ -129,4 +133,62 @@ contract ExecutionHelper is IExecutionHelperEventsAndErrors { mstore(0x40, add(o, returndatasize())) // Allocate the memory. } } + + /// @dev Executes a single transaction based on the specified execution type. + /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). + /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). + function _handleSingleExecution(bytes calldata executionCalldata, ExecType execType) internal { + (address target, uint256 value, bytes calldata callData) = executionCalldata.decodeSingle(); + if (execType == EXECTYPE_DEFAULT) _execute(target, value, callData); + else if (execType == EXECTYPE_TRY) _tryExecute(target, value, callData); + else revert UnsupportedExecType(execType); + } + + /// @dev Executes a batch of transactions based on the specified execution type. + /// @param executionCalldata The calldata for a batch of transactions. + /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). + function _handleBatchExecution(bytes calldata executionCalldata, ExecType execType) internal { + Execution[] calldata executions = executionCalldata.decodeBatch(); + if (execType == EXECTYPE_DEFAULT) _executeBatch(executions); + else if (execType == EXECTYPE_TRY) _tryExecuteBatch(executions); + else revert UnsupportedExecType(execType); + } + + /// @dev Executes a single transaction based on the specified execution type. + /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). + /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). + function _handleDelegateCallExecution(bytes calldata executionCalldata, ExecType execType) internal { + (address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall(); + if (execType == EXECTYPE_DEFAULT) _executeDelegatecall(delegate, callData); + else if (execType == EXECTYPE_TRY) _tryExecuteDelegatecall(delegate, callData); + else revert UnsupportedExecType(execType); + } + + /// @dev Executes a single transaction based on the specified execution type. + /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). + /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). + function _handleSingleExecutionAndReturnData(bytes calldata executionCalldata, ExecType execType) internal returns(bytes[] memory returnData) { + (address target, uint256 value, bytes calldata callData) = executionCalldata.decodeSingle(); + returnData = new bytes[](1); + bool success; + // check if execType is revert(default) or try + if (execType == EXECTYPE_DEFAULT) { + returnData[0] = _execute(target, value, callData); + } else if (execType == EXECTYPE_TRY) { + (success, returnData[0]) = _tryExecute(target, value, callData); + if (!success) emit TryExecuteUnsuccessful(0, returnData[0]); + } else { + revert UnsupportedExecType(execType); + } + } + + /// @dev Executes a batch of transactions based on the specified execution type. + /// @param executionCalldata The calldata for a batch of transactions. + /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). + function _handleBatchExecutionAndReturnData(bytes calldata executionCalldata, ExecType execType) internal returns(bytes[] memory returnData){ + Execution[] calldata executions = executionCalldata.decodeBatch(); + if (execType == EXECTYPE_DEFAULT) returnData = _executeBatch(executions); + else if (execType == EXECTYPE_TRY) returnData = _tryExecuteBatch(executions); + else revert UnsupportedExecType(execType); + } } diff --git a/contracts/interfaces/INexusEventsAndErrors.sol b/contracts/interfaces/INexusEventsAndErrors.sol index 6e2ccaf9..dd8875fa 100644 --- a/contracts/interfaces/INexusEventsAndErrors.sol +++ b/contracts/interfaces/INexusEventsAndErrors.sol @@ -36,10 +36,6 @@ interface INexusEventsAndErrors { /// @param callType The unsupported call type. error UnsupportedCallType(CallType callType); - /// @notice Error thrown when an execution with an unsupported ExecType was made. - /// @param execType The unsupported execution type. - error UnsupportedExecType(ExecType execType); - /// @notice Error thrown on failed execution. error ExecutionFailed(); diff --git a/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol b/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol index 7e076563..ab334e15 100644 --- a/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol +++ b/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol @@ -21,7 +21,14 @@ pragma solidity ^0.8.26; /// @author @filmakarov | Biconomy | filipp.makarov@biconomy.io /// @author @zeroknots | Rhinestone.wtf | zeroknots.eth /// Special thanks to the Solady team for foundational contributions: https://github.com/Vectorized/solady + +import { ExecType } from "../../lib/ModeLib.sol"; + interface IExecutionHelperEventsAndErrors { /// @notice Event emitted when a transaction fails to execute successfully. event TryExecuteUnsuccessful(uint256 batchExecutionindex, bytes result); + + /// @notice Error thrown when an execution with an unsupported ExecType was made. + /// @param execType The unsupported execution type. + error UnsupportedExecType(ExecType execType); } diff --git a/contracts/mocks/MockDelegateTarget.sol b/contracts/mocks/MockDelegateTarget.sol new file mode 100644 index 00000000..ff46b43d --- /dev/null +++ b/contracts/mocks/MockDelegateTarget.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +contract MockDelegateTarget { + function sendValue(address target, uint256 _value) public { + target.call{ value: _value }(""); + } +} \ No newline at end of file diff --git a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol new file mode 100644 index 00000000..904627ba --- /dev/null +++ b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; + +import "../../../shared/TestAccountExecution_Base.t.sol"; + +/// @title TestAccountExecution_TryExecuteSingle +/// @notice This contract tests single execution attempts using the try method in the account execution system. +contract TestAccountExecution_TryExecuteSingle is TestAccountExecution_Base { + MockDelegateTarget delegateTarget; + /// @notice Sets up the testing environment. + function setUp() public { + setUpTestAccountExecution_Base(); + delegateTarget = new MockDelegateTarget(); + } + + /// @notice Tests successful execution of a single operation. + function test_ExecuteDelegateCall_Success() public { + + (bool res, ) = payable(address(BOB_ACCOUNT)).call{ value: 2 ether}(""); // Fund BOB_ACCOUNT + assertEq(res, true, "Funding BOB_ACCOUNT should succeed"); + + // Initial state assertion + assertEq(counter.getNumber(), 0, "Counter should start at 0"); + // Create calldata for the account to execute + address valueTarget = makeAddr("valueTarget"); + uint256 value = 1 ether; + + bytes memory sendValue = + abi.encodeWithSelector(MockDelegateTarget.sendValue.selector, valueTarget, value); + + // placeholder + Execution[] memory execution = new Execution[](1); + execution[0] = Execution(address(counter), 0, abi.encodeWithSelector(Counter.incrementNumber.selector)); + + + // Build UserOperation for single execution + PackedUserOperation[] memory userOps = buildPackedUserOperation(BOB, BOB_ACCOUNT, EXECTYPE_TRY, execution, address(VALIDATOR_MODULE)); + + bytes memory userOpCalldata = abi.encodeCall( + Nexus.execute, + ( + ModeLib.encode( + CALLTYPE_DELEGATECALL, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00) + ), + abi.encodePacked(address(delegateTarget), sendValue) + ) + ); + + userOps[0].callData = userOpCalldata; + + // Sign the operation + bytes32 userOpHash = ENTRYPOINT.getUserOpHash(userOps[0]); + userOps[0].signature = signMessage(BOB, userOpHash); + + ENTRYPOINT.handleOps(userOps, payable(address(BOB.addr))); + // Assert that the value was set ie that execution was successful + assertTrue(valueTarget.balance == value); + } +} diff --git a/test/foundry/utils/TestHelper.t.sol b/test/foundry/utils/TestHelper.t.sol index 7bc720e2..6889f490 100644 --- a/test/foundry/utils/TestHelper.t.sol +++ b/test/foundry/utils/TestHelper.t.sol @@ -15,6 +15,7 @@ import { Nexus } from "../../../contracts/Nexus.sol"; import { MockHook } from "../../../contracts/mocks/MockHook.sol"; import { MockHandler } from "../../../contracts/mocks/MockHandler.sol"; import { MockExecutor } from "../../../contracts/mocks/MockExecutor.sol"; +import { MockDelegateTarget } from "../../../contracts/mocks/MockDelegateTarget.sol"; import { MockValidator } from "../../../contracts/mocks/MockValidator.sol"; import { MockMultiModule } from "contracts/mocks/MockMultiModule.sol"; import { MockPaymaster } from "./../../../contracts/mocks/MockPaymaster.sol"; From 3197d8051f326cf4c91d11a1ef5f5b2be16cfe7c Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 21:41:04 +0530 Subject: [PATCH 06/11] chore:delegate call test execFromExecutor --- contracts/mocks/MockExecutor.sol | 30 ++++++++++++++++++- ...AccountExecution_ExecuteFromExecutor.t.sol | 17 +++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/contracts/mocks/MockExecutor.sol b/contracts/mocks/MockExecutor.sol index 9bc97033..36df5e1a 100644 --- a/contracts/mocks/MockExecutor.sol +++ b/contracts/mocks/MockExecutor.sol @@ -5,8 +5,19 @@ import { IModule } from "contracts/interfaces/modules/IModule.sol"; import { EncodedModuleTypes } from "contracts/lib/ModuleTypeLib.sol"; import { INexus } from "contracts/interfaces/INexus.sol"; import { MODULE_TYPE_EXECUTOR } from "contracts/types/Constants.sol"; -import { ModeLib, ExecutionMode, ExecType, CallType, CALLTYPE_BATCH, CALLTYPE_SINGLE, EXECTYPE_DEFAULT, EXECTYPE_TRY } from "contracts/lib/ModeLib.sol"; +import { + ModeLib, + ExecutionMode, + ExecType, + CallType, + CALLTYPE_BATCH, + CALLTYPE_SINGLE, + CALLTYPE_DELEGATECALL, + EXECTYPE_DEFAULT, + EXECTYPE_TRY +} from "contracts/lib/ModeLib.sol"; import { ExecLib } from "contracts/lib/ExecLib.sol"; +import { MODE_DEFAULT, ModePayload } from "contracts/lib/ModeLib.sol"; import { IExecutor } from "../../contracts/interfaces/modules/IExecutor.sol"; import "../../contracts/types/DataTypes.sol"; @@ -27,6 +38,21 @@ contract MockExecutor is IExecutor { return account.executeFromExecutor(ModeLib.encodeSimpleSingle(), ExecLib.encodeSingle(target, value, callData)); } + function execDelegatecall( + INexus account, + bytes calldata callData + ) + external + returns (bytes[] memory returnData) + { + return account.executeFromExecutor( + ModeLib.encode( + CALLTYPE_DELEGATECALL, EXECTYPE_DEFAULT, MODE_DEFAULT, ModePayload.wrap(0x00) + ), + callData + ); + } + function executeBatchViaAccount(INexus account, Execution[] calldata execs) external returns (bytes[] memory returnData) { return account.executeFromExecutor(ModeLib.encodeSimpleBatch(), ExecLib.encodeBatch(execs)); } @@ -72,4 +98,6 @@ contract MockExecutor is IExecutor { function isInitialized(address) external pure override returns (bool) { return false; } + + receive() external payable {} } diff --git a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol index e74a6482..883ba134 100644 --- a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol +++ b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol @@ -7,6 +7,7 @@ import "../../../shared/TestAccountExecution_Base.t.sol"; contract TestAccountExecution_ExecuteFromExecutor is TestAccountExecution_Base { MockExecutor public mockExecutor; + MockDelegateTarget delegateTarget; /// @notice Sets up the testing environment and installs the MockExecutor module function setUp() public { @@ -14,6 +15,7 @@ contract TestAccountExecution_ExecuteFromExecutor is TestAccountExecution_Base { mockExecutor = new MockExecutor(); counter = new Counter(); + delegateTarget = new MockDelegateTarget(); // Install MockExecutor as executor module on BOB_ACCOUNT bytes memory callDataInstall = abi.encodeWithSelector(IModuleManager.installModule.selector, uint256(2), address(mockExecutor), ""); @@ -48,6 +50,21 @@ contract TestAccountExecution_ExecuteFromExecutor is TestAccountExecution_Base { ENTRYPOINT.handleOps(userOpsExec, payable(address(BOB.addr))); assertEq(counter.getNumber(), 1, "Counter should have incremented"); } + + /// @notice Tests delegate call execution via MockExecutor + // function test_ExecuteDelegateCallFromExecutor_Success() public { + + // (bool res, ) = payable(address(BOB_ACCOUNT)).call{ value: 2 ether}(""); // Fund BOB_ACCOUNT + // assertEq(res, true, "Funding BOB_ACCOUNT should succeed"); + + // address valueTarget = makeAddr("valueTarget"); + // uint256 value = 1 ether; + // bytes memory sendValueCallData = + // abi.encodeWithSelector(MockDelegateTarget.sendValue.selector, valueTarget, value); + // mockExecutor.execDelegatecall(BOB_ACCOUNT, sendValueCallData); + // // Assert that the value was set ie that execution was successful + // assertTrue(valueTarget.balance == value); + // } /// @notice Tests batch execution via MockExecutor function test_ExecBatchFromExecutor_Success() public { From 2826033da58e5a9520f52ffd72d2805a1bd2488b Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 21:55:44 +0530 Subject: [PATCH 07/11] refactor execution helper --- contracts/Nexus.sol | 2 +- contracts/base/ExecutionHelper.sol | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/contracts/Nexus.sol b/contracts/Nexus.sol index 67726db2..b22eb1a1 100644 --- a/contracts/Nexus.sol +++ b/contracts/Nexus.sol @@ -146,7 +146,7 @@ contract Nexus is INexus, BaseAccount, ExecutionHelper, ModuleManager, UUPSUpgra } else if (callType == CALLTYPE_BATCH) { returnData = _handleBatchExecutionAndReturnData(executionCalldata, execType); } else if (callType == CALLTYPE_DELEGATECALL) { - _handleDelegateCallExecution(executionCalldata, execType); + returnData = _handleDelegateCallExecutionAndReturnData(executionCalldata, execType); } else { revert UnsupportedCallType(callType); } diff --git a/contracts/base/ExecutionHelper.sol b/contracts/base/ExecutionHelper.sol index 2b821020..3beaa70a 100644 --- a/contracts/base/ExecutionHelper.sol +++ b/contracts/base/ExecutionHelper.sol @@ -191,4 +191,18 @@ contract ExecutionHelper is IExecutionHelperEventsAndErrors { else if (execType == EXECTYPE_TRY) returnData = _tryExecuteBatch(executions); else revert UnsupportedExecType(execType); } + + /// @dev Executes a single transaction based on the specified execution type. + /// @param executionCalldata The calldata containing the transaction details (target address, value, and data). + /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). + function _handleDelegateCallExecutionAndReturnData(bytes calldata executionCalldata, ExecType execType) internal returns(bytes[] memory returnData) { + (address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall(); + if (execType == EXECTYPE_DEFAULT) { + returnData[0] = _executeDelegatecall(delegate, callData); + } + else if (execType == EXECTYPE_TRY) { + (, returnData[0]) = _tryExecuteDelegatecall(delegate, callData); + } + else revert UnsupportedExecType(execType); + } } From 7b3c243f0910f79b9f041e337f37a582715be597 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 21:58:18 +0530 Subject: [PATCH 08/11] gas reports --- .github/gas_report.json | 196 ++++++++++++++++++++-------------------- GAS_REPORT.md | 110 +++++++++++----------- 2 files changed, 153 insertions(+), 153 deletions(-) diff --git a/.github/gas_report.json b/.github/gas_report.json index 9bbb9be3..e7a72ec5 100644 --- a/.github/gas_report.json +++ b/.github/gas_report.json @@ -8,7 +8,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", "GAS_USED": 49374, - "GAS_DIFFERENCE": "🥳 -55" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 2, @@ -19,7 +19,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", "GAS_USED": 24674, - "GAS_DIFFERENCE": "🥳 -55" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 3, @@ -29,8 +29,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 91854, - "GAS_DIFFERENCE": "🥵 +183" + "GAS_USED": 91863, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 4, @@ -40,8 +40,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 71954, - "GAS_DIFFERENCE": "🥵 +183" + "GAS_USED": 71963, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 5, @@ -51,8 +51,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 360544, - "GAS_DIFFERENCE": "🥵 +32364" + "GAS_USED": 360553, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 6, @@ -62,8 +62,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 340644, - "GAS_DIFFERENCE": "🥵 +32364" + "GAS_USED": 340653, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 7, @@ -73,8 +73,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 345310, - "GAS_DIFFERENCE": "🥵 +32262" + "GAS_USED": 345319, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 8, @@ -84,8 +84,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 325409, - "GAS_DIFFERENCE": "🥵 +32261" + "GAS_USED": 325418, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 9, @@ -95,8 +95,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 393336, - "GAS_DIFFERENCE": "🥵 +32261" + "GAS_USED": 393345, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 10, @@ -106,8 +106,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 373436, - "GAS_DIFFERENCE": "🥵 +32261" + "GAS_USED": 373445, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 11, @@ -117,8 +117,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 106650, - "GAS_DIFFERENCE": "🥵 +196" + "GAS_USED": 106659, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 12, @@ -128,8 +128,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 86749, - "GAS_DIFFERENCE": "🥵 +195" + "GAS_USED": 86758, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 13, @@ -140,7 +140,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", "GAS_USED": 47585, - "GAS_DIFFERENCE": "🥳 -47" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 14, @@ -151,7 +151,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", "GAS_USED": 27685, - "GAS_DIFFERENCE": "🥳 -47" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 15, @@ -161,8 +161,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 95181, - "GAS_DIFFERENCE": "🥵 +183" + "GAS_USED": 95190, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 16, @@ -172,8 +172,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 75281, - "GAS_DIFFERENCE": "🥵 +183" + "GAS_USED": 75290, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 17, @@ -183,8 +183,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 359067, - "GAS_DIFFERENCE": "🥵 +32351" + "GAS_USED": 359076, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 18, @@ -194,8 +194,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 339167, - "GAS_DIFFERENCE": "🥵 +32351" + "GAS_USED": 339176, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 19, @@ -205,8 +205,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 343843, - "GAS_DIFFERENCE": "🥵 +32261" + "GAS_USED": 343852, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 20, @@ -216,8 +216,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 323943, - "GAS_DIFFERENCE": "🥵 +32261" + "GAS_USED": 323952, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 21, @@ -227,8 +227,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 391870, - "GAS_DIFFERENCE": "🥵 +32262" + "GAS_USED": 391879, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 22, @@ -238,8 +238,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 371970, - "GAS_DIFFERENCE": "🥵 +32262" + "GAS_USED": 371979, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 23, @@ -249,8 +249,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 109986, - "GAS_DIFFERENCE": "🥵 +172" + "GAS_USED": 109995, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 24, @@ -260,8 +260,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 90086, - "GAS_DIFFERENCE": "🥵 +172" + "GAS_USED": 90095, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 25, @@ -272,7 +272,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", "GAS_USED": 52862, - "GAS_DIFFERENCE": "🥳 -20" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 26, @@ -283,7 +283,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", "GAS_USED": 27862, - "GAS_DIFFERENCE": "🥳 -20" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 27, @@ -294,7 +294,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", "GAS_USED": 52926, - "GAS_DIFFERENCE": "🥳 -20" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 28, @@ -305,7 +305,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", "GAS_USED": 27926, - "GAS_DIFFERENCE": "🥳 -20" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 29, @@ -316,7 +316,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", "GAS_USED": 52926, - "GAS_DIFFERENCE": "🥳 -29" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 30, @@ -327,7 +327,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", "GAS_USED": 27926, - "GAS_DIFFERENCE": "🥳 -20" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 31, @@ -337,8 +337,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 99949, - "GAS_DIFFERENCE": "🥵 +183" + "GAS_USED": 99958, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 32, @@ -348,8 +348,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 74949, - "GAS_DIFFERENCE": "🥵 +183" + "GAS_USED": 74958, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 33, @@ -359,8 +359,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 363793, - "GAS_DIFFERENCE": "🥵 +32352" + "GAS_USED": 363802, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 34, @@ -370,8 +370,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 338793, - "GAS_DIFFERENCE": "🥵 +32352" + "GAS_USED": 338802, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 35, @@ -381,8 +381,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 348616, - "GAS_DIFFERENCE": "🥵 +32285" + "GAS_USED": 348625, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 36, @@ -392,8 +392,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 323616, - "GAS_DIFFERENCE": "🥵 +32285" + "GAS_USED": 323625, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 37, @@ -403,8 +403,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 396642, - "GAS_DIFFERENCE": "🥵 +32284" + "GAS_USED": 396651, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 38, @@ -414,8 +414,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 371642, - "GAS_DIFFERENCE": "🥵 +32284" + "GAS_USED": 371651, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 39, @@ -425,8 +425,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🧊 ColdAccess", - "GAS_USED": 114690, - "GAS_DIFFERENCE": "🥵 +170" + "GAS_USED": 114699, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 40, @@ -436,8 +436,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "🔥 WarmAccess", - "GAS_USED": 89690, - "GAS_DIFFERENCE": "🥵 +170" + "GAS_USED": 89699, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 41, @@ -448,7 +448,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", "GAS_USED": 148619, - "GAS_DIFFERENCE": "🥳 -47" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 42, @@ -458,8 +458,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 196548, - "GAS_DIFFERENCE": "🥵 +170" + "GAS_USED": 196557, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 43, @@ -469,8 +469,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 460558, - "GAS_DIFFERENCE": "🥵 +32364" + "GAS_USED": 460567, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 44, @@ -480,8 +480,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 445249, - "GAS_DIFFERENCE": "🥵 +32287" + "GAS_USED": 445258, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 45, @@ -491,8 +491,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 493276, - "GAS_DIFFERENCE": "🥵 +32288" + "GAS_USED": 493285, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 46, @@ -502,8 +502,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 211453, - "GAS_DIFFERENCE": "🥵 +209" + "GAS_USED": 211462, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 47, @@ -514,7 +514,7 @@ "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", "GAS_USED": 117563, - "GAS_DIFFERENCE": "🥳 -27" + "GAS_DIFFERENCE": "0" }, { "NUMBER": 48, @@ -524,8 +524,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 165537, - "GAS_DIFFERENCE": "🥵 +182" + "GAS_USED": 165546, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 49, @@ -535,8 +535,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 429527, - "GAS_DIFFERENCE": "🥵 +32353" + "GAS_USED": 429536, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 50, @@ -546,8 +546,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 414214, - "GAS_DIFFERENCE": "🥵 +32286" + "GAS_USED": 414223, + "GAS_DIFFERENCE": "🥵 +9" }, { "NUMBER": 51, @@ -557,8 +557,8 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 198069, - "GAS_DIFFERENCE": "🥵 +173" + "GAS_USED": 198111, + "GAS_DIFFERENCE": "🥵 +42" }, { "NUMBER": 52, @@ -568,8 +568,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 462317, - "GAS_DIFFERENCE": "🥵 +32358" + "GAS_USED": 462359, + "GAS_DIFFERENCE": "🥵 +42" }, { "NUMBER": 53, @@ -579,8 +579,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 446772, - "GAS_DIFFERENCE": "🥵 +32279" + "GAS_USED": 446814, + "GAS_DIFFERENCE": "🥵 +42" }, { "NUMBER": 54, @@ -590,8 +590,8 @@ "IS_DEPLOYED": "False", "WITH_PAYMASTER": "False", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 494799, - "GAS_DIFFERENCE": "🥵 +32280" + "GAS_USED": 494841, + "GAS_DIFFERENCE": "🥵 +42" }, { "NUMBER": 55, @@ -601,7 +601,7 @@ "IS_DEPLOYED": "True", "WITH_PAYMASTER": "True", "RECEIVER_ACCESS": "N/A", - "GAS_USED": 180434, - "GAS_DIFFERENCE": "🥵 +196" + "GAS_USED": 180443, + "GAS_DIFFERENCE": "🥵 +9" } ] \ No newline at end of file diff --git a/GAS_REPORT.md b/GAS_REPORT.md index 07425bfc..218c23a0 100644 --- a/GAS_REPORT.md +++ b/GAS_REPORT.md @@ -2,58 +2,58 @@ | **Protocol** | **Actions / Function** | **Account Type** | **Is Deployed** | **With Paymaster?** | **Receiver Access** | **Gas Used** | **Gas Difference** | | :----------: | :------------------------------: | :--------------: | :-------------: | :-----------------: | :-----------------: | :----------: | :----------------: | -| ERC20 | transfer | EOA | False | False | 🧊 ColdAccess | 49374 | 🥳 -55 | -| ERC20 | transfer | EOA | False | False | 🔥 WarmAccess | 24674 | 🥳 -55 | -| ERC20 | transfer | Smart Account | True | False | 🧊 ColdAccess | 91854 | 🥵 +183 | -| ERC20 | transfer | Smart Account | True | False | 🔥 WarmAccess | 71954 | 🥵 +183 | -| ERC20 | transfer | Smart Account | False | True | 🧊 ColdAccess | 360544 | 🥵 +32364 | -| ERC20 | transfer | Smart Account | False | True | 🔥 WarmAccess | 340644 | 🥵 +32364 | -| ERC20 | transfer | Smart Account | False | False | 🧊 ColdAccess | 345310 | 🥵 +32262 | -| ERC20 | transfer | Smart Account | False | False | 🔥 WarmAccess | 325409 | 🥵 +32261 | -| ERC20 | transfer | Smart Account | False | False | 🧊 ColdAccess | 393336 | 🥵 +32261 | -| ERC20 | transfer | Smart Account | False | False | 🔥 WarmAccess | 373436 | 🥵 +32261 | -| ERC20 | transfer | Smart Account | True | True | 🧊 ColdAccess | 106650 | 🥵 +196 | -| ERC20 | transfer | Smart Account | True | True | 🔥 WarmAccess | 86749 | 🥵 +195 | -| ERC721 | transferFrom | EOA | False | False | 🧊 ColdAccess | 47585 | 🥳 -47 | -| ERC721 | transferFrom | EOA | False | False | 🔥 WarmAccess | 27685 | 🥳 -47 | -| ERC721 | transferFrom | Smart Account | True | False | 🧊 ColdAccess | 95181 | 🥵 +183 | -| ERC721 | transferFrom | Smart Account | True | False | 🔥 WarmAccess | 75281 | 🥵 +183 | -| ERC721 | transferFrom | Smart Account | False | True | 🧊 ColdAccess | 359067 | 🥵 +32351 | -| ERC721 | transferFrom | Smart Account | False | True | 🔥 WarmAccess | 339167 | 🥵 +32351 | -| ERC721 | transferFrom | Smart Account | False | False | 🧊 ColdAccess | 343843 | 🥵 +32261 | -| ERC721 | transferFrom | Smart Account | False | False | 🔥 WarmAccess | 323943 | 🥵 +32261 | -| ERC721 | transferFrom | Smart Account | False | False | 🧊 ColdAccess | 391870 | 🥵 +32262 | -| ERC721 | transferFrom | Smart Account | False | False | 🔥 WarmAccess | 371970 | 🥵 +32262 | -| ERC721 | transferFrom | Smart Account | True | True | 🧊 ColdAccess | 109986 | 🥵 +172 | -| ERC721 | transferFrom | Smart Account | True | True | 🔥 WarmAccess | 90086 | 🥵 +172 | -| ETH | transfer | EOA | False | False | 🧊 ColdAccess | 52862 | 🥳 -20 | -| ETH | transfer | EOA | False | False | 🔥 WarmAccess | 27862 | 🥳 -20 | -| ETH | call | EOA | False | False | 🧊 ColdAccess | 52926 | 🥳 -20 | -| ETH | call | EOA | False | False | 🔥 WarmAccess | 27926 | 🥳 -20 | -| ETH | send | EOA | False | False | 🧊 ColdAccess | 52926 | 🥳 -29 | -| ETH | send | EOA | False | False | 🔥 WarmAccess | 27926 | 🥳 -20 | -| ETH | transfer | Smart Account | True | False | 🧊 ColdAccess | 99949 | 🥵 +183 | -| ETH | transfer | Smart Account | True | False | 🔥 WarmAccess | 74949 | 🥵 +183 | -| ETH | transfer | Smart Account | False | True | 🧊 ColdAccess | 363793 | 🥵 +32352 | -| ETH | transfer | Smart Account | False | True | 🔥 WarmAccess | 338793 | 🥵 +32352 | -| ETH | transfer | Smart Account | False | False | 🧊 ColdAccess | 348616 | 🥵 +32285 | -| ETH | transfer | Smart Account | False | False | 🔥 WarmAccess | 323616 | 🥵 +32285 | -| ETH | transfer | Smart Account | False | False | 🧊 ColdAccess | 396642 | 🥵 +32284 | -| ETH | transfer | Smart Account | False | False | 🔥 WarmAccess | 371642 | 🥵 +32284 | -| ETH | transfer | Smart Account | True | True | 🧊 ColdAccess | 114690 | 🥵 +170 | -| ETH | transfer | Smart Account | True | True | 🔥 WarmAccess | 89690 | 🥵 +170 | -| UniswapV2 | swapExactETHForTokens | EOA | False | False | N/A | 148619 | 🥳 -47 | -| UniswapV2 | swapExactETHForTokens | Smart Account | True | False | N/A | 196548 | 🥵 +170 | -| UniswapV2 | swapExactETHForTokens | Smart Account | False | True | N/A | 460558 | 🥵 +32364 | -| UniswapV2 | swapExactETHForTokens | Smart Account | False | False | N/A | 445249 | 🥵 +32287 | -| UniswapV2 | swapExactETHForTokens | Smart Account | False | False | N/A | 493276 | 🥵 +32288 | -| UniswapV2 | swapExactETHForTokens | Smart Account | True | True | N/A | 211453 | 🥵 +209 | -| UniswapV2 | swapExactTokensForTokens | EOA | False | False | N/A | 117563 | 🥳 -27 | -| UniswapV2 | swapExactTokensForTokens | Smart Account | True | False | N/A | 165537 | 🥵 +182 | -| UniswapV2 | swapExactTokensForTokens | Smart Account | False | True | N/A | 429527 | 🥵 +32353 | -| UniswapV2 | swapExactTokensForTokens | Smart Account | False | False | N/A | 414214 | 🥵 +32286 | -| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | True | False | N/A | 198069 | 🥵 +173 | -| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | False | True | N/A | 462317 | 🥵 +32358 | -| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | False | False | N/A | 446772 | 🥵 +32279 | -| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | False | False | N/A | 494799 | 🥵 +32280 | -| UniswapV2 | swapExactTokensForTokens | Smart Account | True | True | N/A | 180434 | 🥵 +196 | +| ERC20 | transfer | EOA | False | False | 🧊 ColdAccess | 49374 | 0 | +| ERC20 | transfer | EOA | False | False | 🔥 WarmAccess | 24674 | 0 | +| ERC20 | transfer | Smart Account | True | False | 🧊 ColdAccess | 91863 | 🥵 +9 | +| ERC20 | transfer | Smart Account | True | False | 🔥 WarmAccess | 71963 | 🥵 +9 | +| ERC20 | transfer | Smart Account | False | True | 🧊 ColdAccess | 360553 | 🥵 +9 | +| ERC20 | transfer | Smart Account | False | True | 🔥 WarmAccess | 340653 | 🥵 +9 | +| ERC20 | transfer | Smart Account | False | False | 🧊 ColdAccess | 345319 | 🥵 +9 | +| ERC20 | transfer | Smart Account | False | False | 🔥 WarmAccess | 325418 | 🥵 +9 | +| ERC20 | transfer | Smart Account | False | False | 🧊 ColdAccess | 393345 | 🥵 +9 | +| ERC20 | transfer | Smart Account | False | False | 🔥 WarmAccess | 373445 | 🥵 +9 | +| ERC20 | transfer | Smart Account | True | True | 🧊 ColdAccess | 106659 | 🥵 +9 | +| ERC20 | transfer | Smart Account | True | True | 🔥 WarmAccess | 86758 | 🥵 +9 | +| ERC721 | transferFrom | EOA | False | False | 🧊 ColdAccess | 47585 | 0 | +| ERC721 | transferFrom | EOA | False | False | 🔥 WarmAccess | 27685 | 0 | +| ERC721 | transferFrom | Smart Account | True | False | 🧊 ColdAccess | 95190 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | True | False | 🔥 WarmAccess | 75290 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | False | True | 🧊 ColdAccess | 359076 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | False | True | 🔥 WarmAccess | 339176 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | False | False | 🧊 ColdAccess | 343852 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | False | False | 🔥 WarmAccess | 323952 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | False | False | 🧊 ColdAccess | 391879 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | False | False | 🔥 WarmAccess | 371979 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | True | True | 🧊 ColdAccess | 109995 | 🥵 +9 | +| ERC721 | transferFrom | Smart Account | True | True | 🔥 WarmAccess | 90095 | 🥵 +9 | +| ETH | transfer | EOA | False | False | 🧊 ColdAccess | 52862 | 0 | +| ETH | transfer | EOA | False | False | 🔥 WarmAccess | 27862 | 0 | +| ETH | call | EOA | False | False | 🧊 ColdAccess | 52926 | 0 | +| ETH | call | EOA | False | False | 🔥 WarmAccess | 27926 | 0 | +| ETH | send | EOA | False | False | 🧊 ColdAccess | 52926 | 0 | +| ETH | send | EOA | False | False | 🔥 WarmAccess | 27926 | 0 | +| ETH | transfer | Smart Account | True | False | 🧊 ColdAccess | 99958 | 🥵 +9 | +| ETH | transfer | Smart Account | True | False | 🔥 WarmAccess | 74958 | 🥵 +9 | +| ETH | transfer | Smart Account | False | True | 🧊 ColdAccess | 363802 | 🥵 +9 | +| ETH | transfer | Smart Account | False | True | 🔥 WarmAccess | 338802 | 🥵 +9 | +| ETH | transfer | Smart Account | False | False | 🧊 ColdAccess | 348625 | 🥵 +9 | +| ETH | transfer | Smart Account | False | False | 🔥 WarmAccess | 323625 | 🥵 +9 | +| ETH | transfer | Smart Account | False | False | 🧊 ColdAccess | 396651 | 🥵 +9 | +| ETH | transfer | Smart Account | False | False | 🔥 WarmAccess | 371651 | 🥵 +9 | +| ETH | transfer | Smart Account | True | True | 🧊 ColdAccess | 114699 | 🥵 +9 | +| ETH | transfer | Smart Account | True | True | 🔥 WarmAccess | 89699 | 🥵 +9 | +| UniswapV2 | swapExactETHForTokens | EOA | False | False | N/A | 148619 | 0 | +| UniswapV2 | swapExactETHForTokens | Smart Account | True | False | N/A | 196557 | 🥵 +9 | +| UniswapV2 | swapExactETHForTokens | Smart Account | False | True | N/A | 460567 | 🥵 +9 | +| UniswapV2 | swapExactETHForTokens | Smart Account | False | False | N/A | 445258 | 🥵 +9 | +| UniswapV2 | swapExactETHForTokens | Smart Account | False | False | N/A | 493285 | 🥵 +9 | +| UniswapV2 | swapExactETHForTokens | Smart Account | True | True | N/A | 211462 | 🥵 +9 | +| UniswapV2 | swapExactTokensForTokens | EOA | False | False | N/A | 117563 | 0 | +| UniswapV2 | swapExactTokensForTokens | Smart Account | True | False | N/A | 165546 | 🥵 +9 | +| UniswapV2 | swapExactTokensForTokens | Smart Account | False | True | N/A | 429536 | 🥵 +9 | +| UniswapV2 | swapExactTokensForTokens | Smart Account | False | False | N/A | 414223 | 🥵 +9 | +| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | True | False | N/A | 198111 | 🥵 +42 | +| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | False | True | N/A | 462359 | 🥵 +42 | +| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | False | False | N/A | 446814 | 🥵 +42 | +| UniswapV2 | approve+swapExactTokensForTokens | Smart Account | False | False | N/A | 494841 | 🥵 +42 | +| UniswapV2 | swapExactTokensForTokens | Smart Account | True | True | N/A | 180443 | 🥵 +9 | From f0593e2adf3bc3883fe4e32669d4f15bbaa50507 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:04:06 +0530 Subject: [PATCH 09/11] add test for try execute delegate call --- .../TestAccountExecution_DelegateCall.t.sol | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol index 904627ba..b3229a63 100644 --- a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol +++ b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_DelegateCall.t.sol @@ -34,7 +34,7 @@ contract TestAccountExecution_TryExecuteSingle is TestAccountExecution_Base { // Build UserOperation for single execution - PackedUserOperation[] memory userOps = buildPackedUserOperation(BOB, BOB_ACCOUNT, EXECTYPE_TRY, execution, address(VALIDATOR_MODULE)); + PackedUserOperation[] memory userOps = buildPackedUserOperation(BOB, BOB_ACCOUNT, EXECTYPE_DEFAULT, execution, address(VALIDATOR_MODULE)); bytes memory userOpCalldata = abi.encodeCall( Nexus.execute, @@ -56,4 +56,48 @@ contract TestAccountExecution_TryExecuteSingle is TestAccountExecution_Base { // Assert that the value was set ie that execution was successful assertTrue(valueTarget.balance == value); } + + /// @notice Tests successful execution of a single operation. + function test_TryExecuteDelegateCall_Success() public { + + (bool res, ) = payable(address(BOB_ACCOUNT)).call{ value: 2 ether}(""); // Fund BOB_ACCOUNT + assertEq(res, true, "Funding BOB_ACCOUNT should succeed"); + + // Initial state assertion + assertEq(counter.getNumber(), 0, "Counter should start at 0"); + // Create calldata for the account to execute + address valueTarget = makeAddr("valueTarget"); + uint256 value = 1 ether; + + bytes memory sendValue = + abi.encodeWithSelector(MockDelegateTarget.sendValue.selector, valueTarget, value); + + // placeholder + Execution[] memory execution = new Execution[](1); + execution[0] = Execution(address(counter), 0, abi.encodeWithSelector(Counter.incrementNumber.selector)); + + + // Build UserOperation for single execution + PackedUserOperation[] memory userOps = buildPackedUserOperation(BOB, BOB_ACCOUNT, EXECTYPE_TRY, execution, address(VALIDATOR_MODULE)); + + bytes memory userOpCalldata = abi.encodeCall( + Nexus.execute, + ( + ModeLib.encode( + CALLTYPE_DELEGATECALL, EXECTYPE_TRY, MODE_DEFAULT, ModePayload.wrap(0x00) + ), + abi.encodePacked(address(delegateTarget), sendValue) + ) + ); + + userOps[0].callData = userOpCalldata; + + // Sign the operation + bytes32 userOpHash = ENTRYPOINT.getUserOpHash(userOps[0]); + userOps[0].signature = signMessage(BOB, userOpHash); + + ENTRYPOINT.handleOps(userOps, payable(address(BOB.addr))); + // Assert that the value was set ie that execution was successful + assertTrue(valueTarget.balance == (value)); + } } From de62519344ed3960c87a40218549d61d4a2c382f Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:24:42 +0530 Subject: [PATCH 10/11] lint fixes --- .solhint.json | 2 +- contracts/Nexus.sol | 1 - contracts/interfaces/INexusEventsAndErrors.sol | 2 +- foundry.toml | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.solhint.json b/.solhint.json index 0e9fc477..80ef4aa3 100644 --- a/.solhint.json +++ b/.solhint.json @@ -20,7 +20,7 @@ "visibility-modifier-order": "error", "code-complexity": ["error", 9], "function-max-lines": ["error", 80], - "max-line-length": ["error", 150], + "max-line-length": ["error", 160], "no-empty-blocks": "off", "no-unused-vars": "error", "payable-fallback": "off", diff --git a/contracts/Nexus.sol b/contracts/Nexus.sol index b22eb1a1..23bb02ff 100644 --- a/contracts/Nexus.sol +++ b/contracts/Nexus.sol @@ -15,7 +15,6 @@ pragma solidity ^0.8.26; import { UUPSUpgradeable } from "solady/src/utils/UUPSUpgradeable.sol"; import { PackedUserOperation } from "account-abstraction/contracts/interfaces/PackedUserOperation.sol"; import { ExecLib } from "./lib/ExecLib.sol"; -import { Execution } from "./types/DataTypes.sol"; import { INexus } from "./interfaces/INexus.sol"; import { IModule } from "./interfaces/modules/IModule.sol"; import { BaseAccount } from "./base/BaseAccount.sol"; diff --git a/contracts/interfaces/INexusEventsAndErrors.sol b/contracts/interfaces/INexusEventsAndErrors.sol index dd8875fa..8c848e44 100644 --- a/contracts/interfaces/INexusEventsAndErrors.sol +++ b/contracts/interfaces/INexusEventsAndErrors.sol @@ -12,7 +12,7 @@ pragma solidity ^0.8.26; // Nexus: A suite of contracts for Modular Smart Accounts compliant with ERC-7579 and ERC-4337, developed by Biconomy. // Learn more at https://biconomy.io. To report security issues, please contact us at: security@biconomy.io -import { CallType, ExecType } from "../lib/ModeLib.sol"; +import { CallType } from "../lib/ModeLib.sol"; import { PackedUserOperation } from "account-abstraction/contracts/interfaces/PackedUserOperation.sol"; /// @title Nexus - INexus Events and Errors diff --git a/foundry.toml b/foundry.toml index 7cd41b6b..f1bc4412 100644 --- a/foundry.toml +++ b/foundry.toml @@ -35,7 +35,7 @@ [fmt] bracket_spacing = true int_types = "long" - line_length = 150 + line_length = 160 multiline_func_header = "all" number_underscore = "thousands" quote_style = "double" From 48176cb5097ac0db4335a9a09028a20cf767b369 Mon Sep 17 00:00:00 2001 From: livingrockrises <90545960+livingrockrises@users.noreply.github.com> Date: Sun, 7 Jul 2024 22:47:19 +0530 Subject: [PATCH 11/11] refactor and update test --- contracts/base/ExecutionHelper.sol | 5 +++- .../base/IExecutionHelperEventsAndErrors.sol | 3 +++ ...AccountExecution_ExecuteFromExecutor.t.sol | 27 ++++++++++--------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/contracts/base/ExecutionHelper.sol b/contracts/base/ExecutionHelper.sol index 3beaa70a..5512e6dc 100644 --- a/contracts/base/ExecutionHelper.sol +++ b/contracts/base/ExecutionHelper.sol @@ -197,11 +197,14 @@ contract ExecutionHelper is IExecutionHelperEventsAndErrors { /// @param execType The execution type, which can be DEFAULT (revert on failure) or TRY (return on failure). function _handleDelegateCallExecutionAndReturnData(bytes calldata executionCalldata, ExecType execType) internal returns(bytes[] memory returnData) { (address delegate, bytes calldata callData) = executionCalldata.decodeDelegateCall(); + returnData = new bytes[](1); + bool success; if (execType == EXECTYPE_DEFAULT) { returnData[0] = _executeDelegatecall(delegate, callData); } else if (execType == EXECTYPE_TRY) { - (, returnData[0]) = _tryExecuteDelegatecall(delegate, callData); + (success, returnData[0]) = _tryExecuteDelegatecall(delegate, callData); + if (!success) emit TryDelegateCallUnsuccessful(0, returnData[0]); } else revert UnsupportedExecType(execType); } diff --git a/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol b/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol index ab334e15..71b988aa 100644 --- a/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol +++ b/contracts/interfaces/base/IExecutionHelperEventsAndErrors.sol @@ -28,6 +28,9 @@ interface IExecutionHelperEventsAndErrors { /// @notice Event emitted when a transaction fails to execute successfully. event TryExecuteUnsuccessful(uint256 batchExecutionindex, bytes result); + /// @notice Event emitted when a transaction fails to execute successfully. + event TryDelegateCallUnsuccessful(uint256 batchExecutionindex, bytes result); + /// @notice Error thrown when an execution with an unsupported ExecType was made. /// @param execType The unsupported execution type. error UnsupportedExecType(ExecType execType); diff --git a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol index 883ba134..83d6e16a 100644 --- a/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol +++ b/test/foundry/unit/concrete/accountexecution/TestAccountExecution_ExecuteFromExecutor.t.sol @@ -52,19 +52,20 @@ contract TestAccountExecution_ExecuteFromExecutor is TestAccountExecution_Base { } /// @notice Tests delegate call execution via MockExecutor - // function test_ExecuteDelegateCallFromExecutor_Success() public { - - // (bool res, ) = payable(address(BOB_ACCOUNT)).call{ value: 2 ether}(""); // Fund BOB_ACCOUNT - // assertEq(res, true, "Funding BOB_ACCOUNT should succeed"); - - // address valueTarget = makeAddr("valueTarget"); - // uint256 value = 1 ether; - // bytes memory sendValueCallData = - // abi.encodeWithSelector(MockDelegateTarget.sendValue.selector, valueTarget, value); - // mockExecutor.execDelegatecall(BOB_ACCOUNT, sendValueCallData); - // // Assert that the value was set ie that execution was successful - // assertTrue(valueTarget.balance == value); - // } + // Review + function test_ExecuteDelegateCallFromExecutor_Success() public { + + (bool res, ) = payable(address(BOB_ACCOUNT)).call{ value: 2 ether}(""); // Fund BOB_ACCOUNT + assertEq(res, true, "Funding BOB_ACCOUNT should succeed"); + + address valueTarget = makeAddr("valueTarget"); + uint256 value = 1 ether; + bytes memory sendValueCallData = + abi.encodeWithSelector(MockDelegateTarget.sendValue.selector, valueTarget, value); + mockExecutor.execDelegatecall(BOB_ACCOUNT, sendValueCallData); + // Assert that the value was set ie that execution was successful + // assertTrue(valueTarget.balance == value); + } /// @notice Tests batch execution via MockExecutor function test_ExecBatchFromExecutor_Success() public {