Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 51 additions & 51 deletions snapshots/BenchmarkTest.json
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
{
"testERC20Transfer_AlchemyModularAccount": "179494",
"testERC20Transfer_AlchemyModularAccount_AppSponsor": "176436",
"testERC20Transfer_AlchemyModularAccount_ERC20SelfPay": "207779",
"testERC20Transfer_Batch100_AlchemyModularAccount_AppSponsor": "8897167",
"testERC20Transfer_Batch100_CoinbaseSmartWallet": "9952203",
"testERC20Transfer_Batch100_CoinbaseSmartWallet_AppSponsor": "8787327",
"testERC20Transfer_Batch100_CoinbaseSmartWallet_ERC20SelfPay": "11335605",
"testERC20Transfer_Batch100_Safe4337": "11685484",
"testERC20Transfer_Batch100_Safe4337_AppSponsor": "10198375",
"testERC20Transfer_Batch100_Safe4337_ERC20SelfPay": "12757523",
"testERC20Transfer_Batch100_ZerodevKernel_AppSponsor": "11427583",
"testERC20Transfer_CoinbaseSmartWallet": "177855",
"testERC20Transfer_CoinbaseSmartWallet_AppSponsor": "175259",
"testERC20Transfer_CoinbaseSmartWallet_ERC20SelfPay": "204919",
"testERC20Transfer_ERC4337MinimalAccount": "171509",
"testERC20Transfer_ERC4337MinimalAccount_AppSponsor": "168500",
"testERC20Transfer_ERC4337MinimalAccount_ERC20SelfPay": "199831",
"testERC20Transfer_IthacaAccount": "128195",
"testERC20Transfer_IthacaAccountWithSpendLimits": "193658",
"testERC20Transfer_IthacaAccount_AppSponsor": "138709",
"testERC20Transfer_IthacaAccount_AppSponsor_ERC20": "144010",
"testERC20Transfer_IthacaAccount_ERC20SelfPay": "128684",
"testERC20Transfer_Safe4337": "197561",
"testERC20Transfer_Safe4337_AppSponsor": "191725",
"testERC20Transfer_Safe4337_ERC20SelfPay": "221464",
"testERC20Transfer_ZerodevKernel": "207117",
"testERC20Transfer_ZerodevKernel_AppSponsor": "204120",
"testERC20Transfer_ZerodevKernel_ERC20SelfPay": "235489",
"testERC20Transfer_batch100_AlchemyModularAccount": "10109066",
"testERC20Transfer_batch100_AlchemyModularAccount_ERC20SelfPay": "11609298",
"testERC20Transfer_batch100_IthacaAccount": "7545392",
"testERC20Transfer_batch100_IthacaAccount_AppSponsor": "8151496",
"testERC20Transfer_batch100_IthacaAccount_AppSponsor_ERC20": "7977508",
"testERC20Transfer_batch100_IthacaAccount_ERC20SelfPay": "7366652",
"testERC20Transfer_batch100_ZerodevKernel": "12631318",
"testERC20Transfer_batch100_ZerodevKernel_ERC20SelfPay": "14149937",
"testNativeTransfer_AlchemyModularAccount": "180829",
"testNativeTransfer_CoinbaseSmartWallet": "178916",
"testNativeTransfer_IthacaAccount": "129551",
"testNativeTransfer_IthacaAccount_AppSponsor": "140096",
"testNativeTransfer_IthacaAccount_ERC20SelfPay": "137340",
"testNativeTransfer_Safe4337": "198595",
"testNativeTransfer_ZerodevKernel": "208635",
"testUniswapV2Swap_AlchemyModularAccount": "238647",
"testUniswapV2Swap_CoinbaseSmartWallet": "237451",
"testUniswapV2Swap_ERC4337MinimalAccount": "230691",
"testUniswapV2Swap_IthacaAccount": "187339",
"testUniswapV2Swap_IthacaAccount_AppSponsor": "197817",
"testUniswapV2Swap_IthacaAccount_ERC20SelfPay": "192628",
"testUniswapV2Swap_Safe4337": "257333",
"testUniswapV2Swap_ZerodevKernel": "266367"
"testERC20Transfer_AlchemyModularAccount": "159052",
"testERC20Transfer_AlchemyModularAccount_AppSponsor": "134278",
"testERC20Transfer_AlchemyModularAccount_ERC20SelfPay": "160169",
"testERC20Transfer_Batch100_AlchemyModularAccount_AppSponsor": "7053863",
"testERC20Transfer_Batch100_CoinbaseSmartWallet": "9801043",
"testERC20Transfer_Batch100_CoinbaseSmartWallet_AppSponsor": "6483523",
"testERC20Transfer_Batch100_CoinbaseSmartWallet_ERC20SelfPay": "8487729",
"testERC20Transfer_Batch100_Safe4337": "11165400",
"testERC20Transfer_Batch100_Safe4337_AppSponsor": "7525827",
"testERC20Transfer_Batch100_Safe4337_ERC20SelfPay": "9540939",
"testERC20Transfer_Batch100_ZerodevKernel_AppSponsor": "8956539",
"testERC20Transfer_CoinbaseSmartWallet": "150133",
"testERC20Transfer_CoinbaseSmartWallet_AppSponsor": "126009",
"testERC20Transfer_CoinbaseSmartWallet_ERC20SelfPay": "150241",
"testERC20Transfer_ERC4337MinimalAccount": "148602",
"testERC20Transfer_ERC4337MinimalAccount_AppSponsor": "123885",
"testERC20Transfer_ERC4337MinimalAccount_ERC20SelfPay": "149776",
"testERC20Transfer_IthacaAccount": "91780",
"testERC20Transfer_IthacaAccountWithSpendLimits": "113281",
"testERC20Transfer_IthacaAccount_AppSponsor": "99287",
"testERC20Transfer_IthacaAccount_AppSponsor_ERC20": "104388",
"testERC20Transfer_IthacaAccount_ERC20SelfPay": "92081",
"testERC20Transfer_Safe4337": "163675",
"testERC20Transfer_Safe4337_AppSponsor": "136323",
"testERC20Transfer_Safe4337_ERC20SelfPay": "160610",
"testERC20Transfer_ZerodevKernel": "175447",
"testERC20Transfer_ZerodevKernel_AppSponsor": "150734",
"testERC20Transfer_ZerodevKernel_ERC20SelfPay": "176663",
"testERC20Transfer_batch100_AlchemyModularAccount": "10438358",
"testERC20Transfer_batch100_AlchemyModularAccount_ERC20SelfPay": "9221814",
"testERC20Transfer_batch100_IthacaAccount": "6213328",
"testERC20Transfer_batch100_IthacaAccount_AppSponsor": "6765128",
"testERC20Transfer_batch100_IthacaAccount_AppSponsor_ERC20": "6572328",
"testERC20Transfer_batch100_IthacaAccount_ERC20SelfPay": "6015728",
"testERC20Transfer_batch100_ZerodevKernel": "12332690",
"testERC20Transfer_batch100_ZerodevKernel_ERC20SelfPay": "11134785",
"testNativeTransfer_AlchemyModularAccount": "168453",
"testNativeTransfer_CoinbaseSmartWallet": "159248",
"testNativeTransfer_IthacaAccount": "101178",
"testNativeTransfer_IthacaAccount_AppSponsor": "108704",
"testNativeTransfer_IthacaAccount_ERC20SelfPay": "101479",
"testNativeTransfer_Safe4337": "172763",
"testNativeTransfer_ZerodevKernel": "184855",
"testUniswapV2Swap_AlchemyModularAccount": "210111",
"testUniswapV2Swap_CoinbaseSmartWallet": "201623",
"testUniswapV2Swap_ERC4337MinimalAccount": "199690",
"testUniswapV2Swap_IthacaAccount": "142842",
"testUniswapV2Swap_IthacaAccount_AppSponsor": "150313",
"testUniswapV2Swap_IthacaAccount_ERC20SelfPay": "143143",
"testUniswapV2Swap_Safe4337": "215353",
"testUniswapV2Swap_ZerodevKernel": "226591"
}
2 changes: 1 addition & 1 deletion src/GuardedExecutor.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {ERC7821} from "solady/accounts/ERC7821.sol";
import {LibSort} from "solady/utils/LibSort.sol";
import {LibBytes} from "solady/utils/LibBytes.sol";
import {LibZip} from "solady/utils/LibZip.sol";
Expand All @@ -13,6 +12,7 @@ import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {FixedPointMathLib as Math} from "solady/utils/FixedPointMathLib.sol";
import {DateTimeLib} from "solady/utils/DateTimeLib.sol";
import {ICallChecker} from "./interfaces/ICallChecker.sol";
import {ERC7821Ithaca as ERC7821} from "./libraries/ERC7821Ithaca.sol";

/// @title GuardedExecutor
/// @notice Mixin for spend limits and calldata execution guards.
Expand Down
78 changes: 75 additions & 3 deletions src/IthacaAccount.sol
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
"Execute(bool multichain,Call[] calls,uint256 nonce)Call(address to,uint256 value,bytes data)"
);

/// @dev For EIP712 signature digest calculation for the `execute` function.
bytes32 public constant OPTIMIZED_EXECUTE_TYPEHASH = keccak256(
"OptimizedExecute(bool multichain,address to,bytes[] datas,uint256 nonce)"
);

/// @dev For EIP712 signature digest calculation for the `execute` function.
bytes32 public constant CALL_TYPEHASH = keccak256("Call(address to,uint256 value,bytes data)");

Expand Down Expand Up @@ -485,6 +490,32 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
return isMultichain ? _hashTypedDataSansChainId(structHash) : _hashTypedData(structHash);
}

function computeDigest(bytes[] calldata datas, address to, uint256 nonce)
public
view
virtual
returns (bytes32 result)
{
// If to is 0, it will be replaced with address(this)
assembly ("memory-safe") {
let t := shr(96, shl(96, to))
to := or(mul(address(), iszero(t)), t)
}

bytes32[] memory a = EfficientHashLib.malloc(datas.length);
for (uint256 i; i < datas.length; ++i) {
a.set(
i,
EfficientHashLib.hashCalldata(datas[i])
);
}
bool isMultichain = nonce >> 240 == MULTICHAIN_NONCE_PREFIX;
bytes32 structHash = EfficientHashLib.hash(
uint256(OPTIMIZED_EXECUTE_TYPEHASH), LibBit.toUint(isMultichain), uint256(uint160(to)), uint256(a.hash()), nonce
);
return isMultichain ? _hashTypedDataSansChainId(structHash) : _hashTypedData(structHash);
}

/// @dev Returns if the signature is valid, along with its `keyHash`.
/// The `signature` is a wrapped signature, given by
/// `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`.
Expand Down Expand Up @@ -669,6 +700,49 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
// ERC7821
////////////////////////////////////////////////////////////////////////

function _execute(
bytes32,
bytes calldata,
address to,
bytes[] calldata datas,
bytes calldata opData
) internal virtual override {
// Orchestrator workflow.
if (msg.sender == ORCHESTRATOR) {
// opdata
// 0x00: keyHash
if (opData.length != 0x20) revert OpDataError();
bytes32 _keyHash = LibBytes.loadCalldata(opData, 0x00);

LibTStack.TStack(_KEYHASH_STACK_TRANSIENT_SLOT).push(_keyHash);
_execute(datas, to, _keyHash);
LibTStack.TStack(_KEYHASH_STACK_TRANSIENT_SLOT).pop();

return;
}

// Simple workflow without `opData`.
if (opData.length == uint256(0)) {
if (msg.sender != address(this)) revert Unauthorized();
return _execute(datas, to, bytes32(0));
}

// Simple workflow with `opData`.
if (opData.length < 0x20) revert OpDataError();
uint256 nonce = uint256(LibBytes.loadCalldata(opData, 0x00));
LibNonce.checkAndIncrement(_getAccountStorage().nonceSeqs, nonce);
emit NonceInvalidated(nonce);

(bool isValid, bytes32 keyHash) = unwrapAndValidateSignature(
computeDigest(datas, to, nonce), LibBytes.sliceCalldata(opData, 0x20)
);

if (!isValid) revert Unauthorized();
LibTStack.TStack(_KEYHASH_STACK_TRANSIENT_SLOT).push(keyHash);
_execute(datas, to, keyHash);
LibTStack.TStack(_KEYHASH_STACK_TRANSIENT_SLOT).pop();
}

/// @dev For ERC7821.
function _execute(bytes32, bytes calldata, Call[] calldata calls, bytes calldata opData)
internal
Expand Down Expand Up @@ -705,8 +779,6 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
computeDigest(calls, nonce), LibBytes.sliceCalldata(opData, 0x20)
);
if (!isValid) revert Unauthorized();

// TODO: Figure out where else to add these operations, after removing delegate call.
LibTStack.TStack(_KEYHASH_STACK_TRANSIENT_SLOT).push(keyHash);
_execute(calls, keyHash);
LibTStack.TStack(_KEYHASH_STACK_TRANSIENT_SLOT).pop();
Expand Down Expand Up @@ -747,6 +819,6 @@ contract IthacaAccount is IIthacaAccount, EIP712, GuardedExecutor {
returns (string memory name, string memory version)
{
name = "IthacaAccount";
version = "0.5.10";
version = "0.5.11";
}
}
Loading
Loading