Skip to content

Commit

Permalink
splice owner address into bytecode
Browse files Browse the repository at this point in the history
  • Loading branch information
kopy-kat committed Sep 10, 2023
1 parent cb74fd3 commit 4b0bca8
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 53 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The most gas optimized ERC-4337 account - written in Huff

| | Creation | Native transfer | ERC20 transfer | Total |
| ---------------- | -------- | --------------- | -------------- | ------ |
| MinimalAccount | 243454 | 94023 | 82760 | 420237 |
| MinimalAccount | 243481 | 94023 | 82760 | 420264 |
| SimpleAccount | 410061 | 97690 | 86754 | 594505 |
| Biconomy | 296892 | 100780 | 89577 | 487249 |
| Etherspot | 305769 | 100091 | 89172 | 495032 |
Expand Down Expand Up @@ -54,7 +54,7 @@ For more information on how to use Foundry, check out the [Foundry Github Reposi

## Todo

- [ ] Dynamically splice owner account into bytecode
- [x] Dynamically splice owner account into bytecode
- [ ] Deploy as minimal clone

## License
Expand Down
72 changes: 22 additions & 50 deletions src/MinimalAccountFactory.huff
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@
#define constant ACCOUNT_INITCODE_HASH = 0xf62ad50462f93b6a5d1b025f45d9b88bb5ea39d677f69a666816cfdf628fd446

/* Internal functions */
#define macro GET_ACCOUNT_INITCODE() = takes (0) returns (0) {
0x61015880600a3d393df360003560e01c80633a871cdd14610021578063b61d27 0x00 mstore
0xf61461011e5760006000fd5b33735ff137d4b0fdcd49dca30c7cf57e578a026d 0x20 mstore
0x2789146100425760006000fd5b6101a43560a401803590602090038035906040 0x40 mstore
0x013560f81c6024356020527b19457468657265756d205369676e6564204d6573 0x60 mstore
0x736167653a0a3332600052603c6004207f7fffffffffffffffffffffffffffff 0x80 mstore
0xff5d576e7357a4501ddfe92f46681b20a084116100d457600052602052604052 0xa0 mstore
0x606052602060406080600060015afa5060006060523d606003516100db565b50 0xc0 mstore
0x50505060005b737e5f4552091a69125d5dfcb7b8c2659029395bdf1461010057 0xe0 mstore
#define macro GET_ACCOUNT_INITCODE() = takes (1) returns (0) {
// [ownerAddress]
0x61015880600a3d393df360003560e01c80633a871cdd14610021578063b61d27 0x00 mstore // [ownerAddress]
0xf61461011e5760006000fd5b33735ff137d4b0fdcd49dca30c7cf57e578a026d 0x20 mstore // [ownerAddress]
0x2789146100425760006000fd5b6101a43560a401803590602090038035906040 0x40 mstore // [ownerAddress]
0x013560f81c6024356020527b19457468657265756d205369676e6564204d6573 0x60 mstore // [ownerAddress]
0x736167653a0a3332600052603c6004207f7fffffffffffffffffffffffffffff 0x80 mstore // [ownerAddress]
0xff5d576e7357a4501ddfe92f46681b20a084116100d457600052602052604052 0xa0 mstore // [ownerAddress]
0x606052602060406080600060015afa5060006060523d606003516100db565b50 0xc0 mstore // [ownerAddress]

__RIGHTPAD(0x50505060005b73) 0xe0 mstore // [ownerAddress]
0x60 shl 0xe7 mstore
__RIGHTPAD(0x1461010057) 0xfb mstore

0x600160005260206000f35b604435806000146101185760006000600060009333 0x100 mstore
0x5af15b60206080f35b33735ff137d4b0fdcd49dca30c7cf57e578a026d278914 0x120 mstore
0x61013f5760006000fd5b60643580608460003760006020916000602435600435 0x140 mstore
Expand All @@ -31,47 +36,13 @@
/* External functions */
#define macro CREATE_ACCOUNT() = takes (0) returns (0) {

// [ACCOUNT_BYTECODE] 0x00 mstore

// 0x60908060093d393df360003560e01c80633a871cdd14610021578063b61d27f6 0x00 mstore
// 0x146100565760006000fd5b33735ff137d4b0fdcd49dca30c7cf57e578a026d27 0x20 mstore
// 0x89146100425760006000fd5b6000600060006000604435335af160206000f35b 0x40 mstore
// 0x33735ff137d4b0fdcd49dca30c7cf57e578a026d2789146100775760006000fd 0x60 mstore
// 0x5b606435806084600037600060209160006024356004355af100000000000000 0x80 mstore

// 0x61018480600a3d393df360003560e01c80633a871cdd14610021578063b61d27 0x00 mstore
// 0xf61461014a5760006000fd5b33735ff137d4b0fdcd49dca30c7cf57e578a026d 0x20 mstore
// 0x2789146100425760006000fd5b7f68eaa91fcee1235f6cbcdfed4958c9eb8bfe 0x40 mstore
// 0x0a270174ed08e8bca0bc5b3cf93e7f498e7bf7455b69dbf0b2e93f1c23f206f4 0x60 mstore
// 0xabd35d22bd73df5daea4f668ed43b0601c6024356020527b1945746865726575 0x80 mstore
// 0x6d205369676e6564204d6573736167653a0a3332600052603c6004207f7fffff 0xa0 mstore
// 0xffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161 0xc0 mstore
// 0x010057600052602052604052606052602060406080600060015afa5060006060 0xe0 mstore
// 0x523d60600351610107565b5050505060005b737e5f4552091a69125d5dfcb7b8 0x100 mstore
// 0xc2659029395bdf1461012c57600160005260206000f35b604435806000146101 0x120 mstore
// 0x4457600060006000600093335af15b60206080f35b33735ff137d4b0fdcd49dc 0x140 mstore
// 0xa30c7cf57e578a026d27891461016b5760006000fd5b60643580608460003760 0x160 mstore
// 0x0060209160006024356004355af1000000000000000000000000000000000000 0x180 mstore

// 0x61015880600a3d393df360003560e01c80633a871cdd14610021578063b61d27 0x00 mstore
// 0xf61461011e5760006000fd5b33735ff137d4b0fdcd49dca30c7cf57e578a026d 0x20 mstore
// 0x2789146100425760006000fd5b6101a43560a401803590602090038035906040 0x40 mstore
// 0x013560f81c6024356020527b19457468657265756d205369676e6564204d6573 0x60 mstore
// 0x736167653a0a3332600052603c6004207f7fffffffffffffffffffffffffffff 0x80 mstore
// 0xff5d576e7357a4501ddfe92f46681b20a084116100d457600052602052604052 0xa0 mstore
// 0x606052602060406080600060015afa5060006060523d606003516100db565b50 0xc0 mstore
// 0x50505060005b737e5f4552091a69125d5dfcb7b8c2659029395bdf1461010057 0xe0 mstore
// 0x600160005260206000f35b604435806000146101185760006000600060009333 0x100 mstore
// 0x5af15b60206080f35b33735ff137d4b0fdcd49dca30c7cf57e578a026d278914 0x120 mstore
// 0x61013f5760006000fd5b60643580608460003760006020916000602435600435 0x140 mstore
// 0x5af1000000000000000000000000000000000000000000000000000000000000 0x160 mstore

GET_ACCOUNT_INITCODE()

0x24 calldataload // [salt]
[ACCOUNT_INITCODE_LENGTH] // [bytesize, salt]
0x00 // [offset, bytesize, salt]
0x00 // [value, offset, bytesize, salt]
0x04 calldataload // [ownerAddress]
GET_ACCOUNT_INITCODE() // []

0x24 calldataload // [salt]
[ACCOUNT_INITCODE_LENGTH] // [bytesize, salt]
0x00 // [offset, bytesize, salt]
0x00 // [value, offset, bytesize, salt]
create2

0x00 mstore
Expand All @@ -80,6 +51,7 @@

#define macro GET_ADDRESS() = takes (0) returns (0) {
// Store the code hash @ 0x54:0x74
0x04 calldataload
GET_ACCOUNT_INITCODE()
[ACCOUNT_INITCODE_LENGTH] 0x00 sha3
0x54 mstore
Expand Down
41 changes: 40 additions & 1 deletion test/MinimalAccount.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ contract MinimalAccountTest is Test {

Owner owner;

address bytecodeOwnerAddress = 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf;

function setUp() public {
owner = Owner({key: uint256(1), addr: vm.addr(uint256(1))});
minimalAccount = MinimalAccount(HuffDeployer.deploy("MinimalAccount"));
Expand All @@ -32,7 +34,7 @@ contract MinimalAccountTest is Test {
}

function testCreateAccount() public {
address account = minimalAccountFactory.createAccount(address(this), 0);
address account = minimalAccountFactory.createAccount(bytecodeOwnerAddress, 0);
assertEq(address(minimalAccount).code, address(account).code);
}

Expand Down Expand Up @@ -74,6 +76,43 @@ contract MinimalAccountTest is Test {
vm.stopPrank();
}

function testValidateUserOpDifferentOwner() public {
vm.startPrank(entrypointAddress);
uint256 newKey = 2;
address newOwner = vm.addr(newKey);

address account = minimalAccountFactory.createAccount(newOwner, 0);
vm.deal(address(account), 1 ether);

UserOperation memory userOp = UserOperation({
sender: minimalAccountFactory.getAddress(newOwner, 0),
nonce: 0,
initCode: abi.encodePacked(
address(minimalAccountFactory),
abi.encodeWithSelector(minimalAccountFactory.createAccount.selector, newOwner, 0)
),
callData: abi.encodeWithSelector(minimalAccount.execute.selector, address(this), 1 wei, ""),
callGasLimit: 0,
verificationGasLimit: 0,
preVerificationGas: 0,
maxFeePerGas: 0,
maxPriorityFeePerGas: 0,
paymasterAndData: "",
signature: ""
});

bytes32 opHash = entryPoint.getUserOpHash(userOp);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(newKey, ECDSA.toEthSignedMessageHash(opHash));
bytes memory signature = abi.encodePacked(r, s, v);
userOp.signature = signature;

uint256 missingAccountFunds = 420 wei;
uint256 returnValue = MinimalAccount(account).validateUserOp(userOp, opHash, missingAccountFunds);
assertEq(returnValue, 0);
assertEq(entrypointAddress.balance, missingAccountFunds);
vm.stopPrank();
}

function testValidateUserOp__RervertWhen__NotFromEntrypoint() public {
vm.startPrank(address(0x69));
vm.deal(address(minimalAccount), 1 ether);
Expand Down

0 comments on commit 4b0bca8

Please sign in to comment.