From 3b288e01ee9398385acbf14b58d9a2ae9d1cd9d2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 24 Apr 2026 22:59:00 +0200 Subject: [PATCH 01/66] Add first version deposit contract --- run_contests.sh | 1 + test/Cases.hs | 3 +- test/examples/dispatch/deposit.json | 138 +++++++++++++++ test/examples/dispatch/deposit.solc | 249 ++++++++++++++++++++++++++++ 4 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 test/examples/dispatch/deposit.json create mode 100644 test/examples/dispatch/deposit.solc diff --git a/run_contests.sh b/run_contests.sh index d72ee1c02..9a2b90370 100755 --- a/run_contests.sh +++ b/run_contests.sh @@ -17,3 +17,4 @@ bash ./contest.sh test/examples/dispatch/slices.json bash ./contest.sh test/examples/dispatch/fallback.json bash ./contest.sh test/examples/dispatch/ecrecover.json bash ./contest.sh test/examples/dispatch/memory.json +bash ./contest.sh test/examples/dispatch/deposit.json diff --git a/test/Cases.hs b/test/Cases.hs index 5da75eba0..c36710f14 100644 --- a/test/Cases.hs +++ b/test/Cases.hs @@ -117,7 +117,8 @@ dispatches = runDispatchTest "Revert.solc", runDispatchTest "hashes.solc", runDispatchTest "empty.solc", - runDispatchTest "empty_no_constructor.solc" + runDispatchTest "empty_no_constructor.solc", + runDispatchTest "deposit.solc" ] where runDispatchTest file = runTestForFileWith (emptyOption mempty) file "./test/examples/dispatch" diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json new file mode 100644 index 000000000..4f6d2e622 --- /dev/null +++ b/test/examples/dispatch/deposit.json @@ -0,0 +1,138 @@ +{ + "depositcontract": { + "bytecode": "_CODE", + "contract": "DepositContract", + "tests": [ + { + "input": { + "comment": "constructor()", + "calldata": "", + "value": "0" + }, + "kind": "constructor" + }, + + { + "input": { + "comment": "get_zero_hash(0)", + "calldata": "43ca34020000000000000000000000000000000000000000000000000000000000000000", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "0000000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } + }, + + { + "input": { + "comment": "get_zero_hash(1)", + "calldata": "43ca34020000000000000000000000000000000000000000000000000000000000000001", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b", + "status": "success" + } + }, + + + { + "input": { + "comment": "get_zero_hash(2)", + "calldata": "43ca34020000000000000000000000000000000000000000000000000000000000000002", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "db56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71", + "status": "success" + } + }, + + + { + "input": { + "comment": "get_zero_hash(3)", + "calldata": "43ca34020000000000000000000000000000000000000000000000000000000000000003", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "c78009fdf07fc56a11f122370658a353aaa542ed63e44c4bc15ff4cd105ab33c", + "status": "success" + } + }, + + + { + "input": { + "comment": "get_zero_hash(4)", + "calldata": "43ca34020000000000000000000000000000000000000000000000000000000000000004", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c", + "status": "success" + } + }, + + { + "input": { + "comment": "get_zero_hash(15)", + "calldata": "43ca3402000000000000000000000000000000000000000000000000000000000000000f", + "value": "0" + }, + "kind": "call", + "output": { + "comment": "The correct one 0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", + "returndata": "0000000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } + }, + + { + "input": { + "comment": "get_zero_hash(31)", + "calldata": "43ca3402000000000000000000000000000000000000000000000000000000000000001f", + "value": "0" + }, + "kind": "call", + "output": { + "comment": "The correct one 0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7", + "returndata": "0000000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } + }, + + { + "input": { + "comment": "get_deposit_root()", + "calldata": "c5f2892f", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "0000000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } + }, + + { + "input": { + "comment": "get_deposit_count()", + "calldata": "621fd130", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } + } + ] + } +} diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc new file mode 100644 index 000000000..e1cda48ac --- /dev/null +++ b/test/examples/dispatch/deposit.solc @@ -0,0 +1,249 @@ +import std; +import dispatch; + +function caller() -> address { + let res: word; + assembly { + res := caller() + } + return address(res); +} + +function callvalue() -> uint256 { + let res : word; + assembly { + res := callvalue() + } + return uint256(res); +} + +function assert(cond: bool) { + if (!cond) { + assembly { + invalid() + } + } +} + +function require(cond: bool) { + if (!cond) { + assembly { + revert(0, 0) + } + } +} + +forall T . class T:MemoryHelpers { + function ptr(a: T) -> word; + function len(a: T) -> word; + function concat(a: T, b: T) -> memory(bytes); +} + +instance memory(bytes):MemoryHelpers { + function ptr(a: memory(bytes)) -> word { + let rep = Typedef.rep(a); + return rep + 32; + } + + function len(a: memory(bytes)) -> word { + let rep = Typedef.rep(a); + let res: word; + assembly { + res := mload(rep) + } + return res; + } + + function concat(a: memory(bytes), b: memory(bytes)) -> memory(bytes) { + let a_ptr = MemoryHelpers.ptr(a); + let a_len = MemoryHelpers.len(a); + let b_ptr = MemoryHelpers.ptr(b); + let b_len = MemoryHelpers.len(b); + + let len = 32 + a_len + b_len; + let res: word = allocate_memory(len); + assembly { + mstore(res, len) + mcopy(add(res, 32), a_ptr, a_len) + mcopy(add(add(res, 32), a_len), b_ptr, b_len) + } + return memory(res); + } +} + +instance uint256:MemoryHelpers { + function ptr(a: uint256) -> word { + return 0; + } + function len(a: uint256) -> word { + return 0; + } + function concat(a: uint256, b: uint256) -> memory(bytes) { + let res: word = allocate_memory(96); + let a_val = Typedef.rep(a); + let b_val = Typedef.rep(b); + assembly { + mstore(res, 64) + mstore(add(res, 32), a_val) + mstore(add(res, 64), b_val) + } + return memory(res); + } +} + +// This calls the SHA256 precompile. +// TODO: Should return bytes32. +function sha256(input: memory(bytes)) -> uint256 { + // TODO: `ptr()` / `len()` helpers would be useful + let ptr : word = Typedef.rep(input); + let res : word; + // We assume the [0, 32] scratch space is reserved. + assembly { + let ret := staticcall(gas(), 2, add(ptr, 32), mload(ptr), 0, 32) + if iszero(ret) { + revert(0, 0) + } + res := mload(0) + } + return uint256(res); +} + +// Should use uint64 and bytes. +// Assumes 64-bit input. +function to_little_endian_64(v: uint256) -> memory(string) { + let res: word = allocate_memory(32 + 8); + let value: word = Typedef.rep(v); + assembly { + mstore(res, 8) + mstore8(add(res, 32), and(value, 0xff)) + mstore8(add(res, 33), and(shr(value, 8), 0xff)) + mstore8(add(res, 34), and(shr(value, 16), 0xff)) + mstore8(add(res, 35), and(shr(value, 24), 0xff)) + mstore8(add(res, 36), and(shr(value, 32), 0xff)) + mstore8(add(res, 37), and(shr(value, 40), 0xff)) + mstore8(add(res, 38), and(shr(value, 48), 0xff)) + mstore8(add(res, 39), and(shr(value, 56), 0xff)) + } + return memory(res); +} + +// No constants are supported yet, using this as a workaround. +// Defining variables outside of contract/function is not supported. +function DEPOSIT_CONTRACT_TREE_DEPTH() -> uint256 { + return uint256(32); +} +function MAX_DEPOSIT_COUNT() -> uint256 { + // Could use Bounded(uint256).maxVal() + return uint256(0xFFFFFFFF); +} + +contract DepositContract { + deposit_count : uint256; + // TODO: need storage array support + // branch: array(uint256, DEPOSIT_CONTRACT_TREE_DEPTH()); + // zero_hashes : array(uint256, DEPOSIT_CONTRACT_TREE_DEPTH()); + // misusing mappings here, the index is the key + zero_hashes : mapping(uint256, uint256); + + constructor() { + // Compute hashes in empty sparse Merkle tree + // TODO: no for loops yet, so unrolled + let height: uint256 = uint256(0); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); +/* + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + height = height + uint256(1); + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); +*/ + + //for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++) + // zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height])); + } + + // TODO: this is for testing only + function get_zero_hash(index: uint256) -> uint256 { + return zero_hashes[index]; + } + + // TODO: return bytes32 + function get_deposit_root() -> uint256 { + // FIXME: implement + return uint256(0); + } + + // TODO: return memory(bytes) + function get_deposit_count() -> memory(string) { + return to_little_endian_64(deposit_count); + } + + // TOOD: use memory(bytes) and bytes32 + function deposit(pubkey: memory(string), withdrawal_credentials: memory(string), signature: memory(string), deposit_data_root: uint256) -> () { + require(callvalue() != uint256(0)); + // FIXME: implement + assert(false); + } + + // TODO: use bytes4 + function supportsInterface(interfaceId: uint256) -> bool { + return false; + } +} \ No newline at end of file From 79581926edff462c6b8d902f1da9a77b331a7060 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 30 Apr 2026 00:38:29 +0200 Subject: [PATCH 02/66] Fix to_little_endian_64 --- test/examples/dispatch/deposit.solc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index e1cda48ac..28f97a487 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -116,13 +116,13 @@ function to_little_endian_64(v: uint256) -> memory(string) { assembly { mstore(res, 8) mstore8(add(res, 32), and(value, 0xff)) - mstore8(add(res, 33), and(shr(value, 8), 0xff)) - mstore8(add(res, 34), and(shr(value, 16), 0xff)) - mstore8(add(res, 35), and(shr(value, 24), 0xff)) - mstore8(add(res, 36), and(shr(value, 32), 0xff)) - mstore8(add(res, 37), and(shr(value, 40), 0xff)) - mstore8(add(res, 38), and(shr(value, 48), 0xff)) - mstore8(add(res, 39), and(shr(value, 56), 0xff)) + mstore8(add(res, 33), and(shr(8, value), 0xff)) + mstore8(add(res, 34), and(shr(16, value), 0xff)) + mstore8(add(res, 35), and(shr(24, value), 0xff)) + mstore8(add(res, 36), and(shr(32, value), 0xff)) + mstore8(add(res, 37), and(shr(40, value), 0xff)) + mstore8(add(res, 38), and(shr(48, value), 0xff)) + mstore8(add(res, 39), and(shr(56, value), 0xff)) } return memory(res); } From 498a0716234937d6fcfd9930341885fef6cedf35 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 30 Apr 2026 18:45:07 +0200 Subject: [PATCH 03/66] more implementaton of deposit --- test/examples/dispatch/deposit.solc | 52 +++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 28f97a487..052b35268 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -27,9 +27,9 @@ function assert(cond: bool) { function require(cond: bool) { if (!cond) { - assembly { +/* assembly { revert(0, 0) - } + }*/ } } @@ -71,6 +71,12 @@ instance memory(bytes):MemoryHelpers { } } +function empty_string(len: word) -> memory(string) { + let res: word = allocate_memory(32 + len); + mstore(res, len); + return memory(res); +} + instance uint256:MemoryHelpers { function ptr(a: uint256) -> word { return 0; @@ -237,7 +243,47 @@ contract DepositContract { // TOOD: use memory(bytes) and bytes32 function deposit(pubkey: memory(string), withdrawal_credentials: memory(string), signature: memory(string), deposit_data_root: uint256) -> () { - require(callvalue() != uint256(0)); + //require(callvalue() != uint256(0)); + + let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei + + let amount: memory(string) = to_little_endian_64(deposit_amount); + // TODO: emit DepositEvent + + // Compute deposit data root (`DepositData` hash tree root) + let pubkey_root = sha256(MemoryHelpers.concat(pubkey, empty_string(16))); + let signature_root = sha256(MemoryHelpers.concat( + sha256(signature), // slice + sha256(MemoryHelpers.concat(signature, empty_string(32))), // slice + )); + let node = sha256(MemoryHelpers.concat( + sha256(MemoryHelpers.concat(pubkey_root, withdrawal_credentials)), + sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_string(24)), signature_root)) + )); + +/* + // Emit `DepositEvent` log + bytes memory amount = to_little_endian_64(uint64(deposit_amount)); + emit DepositEvent( + pubkey, + withdrawal_credentials, + amount, + signature, + to_little_endian_64(uint64(deposit_count)) + ); + + // Compute deposit data root (`DepositData` hash tree root) + bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0))); + bytes32 signature_root = sha256(abi.encodePacked( + sha256(abi.encodePacked(signature[:64])), + sha256(abi.encodePacked(signature[64:], bytes32(0))) + )); + bytes32 node = sha256(abi.encodePacked( + sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)), + sha256(abi.encodePacked(amount, bytes24(0), signature_root)) + )); +*/ + // FIXME: implement assert(false); } From 61117832ad7bbdbd1f19621fde75751a692d4ef3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 30 Apr 2026 21:34:43 +0200 Subject: [PATCH 04/66] Use memorybytes --- test/examples/dispatch/deposit.json | 13 ++++++ test/examples/dispatch/deposit.solc | 68 +++++++++++++++-------------- 2 files changed, 48 insertions(+), 33 deletions(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index 4f6d2e622..fc0fc8ec5 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -132,6 +132,19 @@ "returndata": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000", "status": "success" } + }, + + { + "input": { + "comment": "deposit(bytes,bytes,bytes,uint256)", + "calldata": "edf26288", + "value": "1000000000" + }, + "kind": "call", + "output": { + "returndata": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } } ] } diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 052b35268..305bd9438 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -71,7 +71,7 @@ instance memory(bytes):MemoryHelpers { } } -function empty_string(len: word) -> memory(string) { +function empty_bytes(len: word) -> memory(bytes) { let res: word = allocate_memory(32 + len); mstore(res, len); return memory(res); @@ -114,9 +114,19 @@ function sha256(input: memory(bytes)) -> uint256 { return uint256(res); } +function to_bytes(v: uint256) -> memory(bytes) { + let res: word = allocate_memory(64); + let value: word = Typedef.rep(v); + assembly { + mstore(res, 32) + mstore(add(res, 32), value) + } + return memory(res); +} + // Should use uint64 and bytes. // Assumes 64-bit input. -function to_little_endian_64(v: uint256) -> memory(string) { +function to_little_endian_64(v: uint256) -> memory(bytes) { let res: word = allocate_memory(32 + 8); let value: word = Typedef.rep(v); assembly { @@ -237,54 +247,46 @@ contract DepositContract { } // TODO: return memory(bytes) - function get_deposit_count() -> memory(string) { + function get_deposit_count() -> memory(bytes) { return to_little_endian_64(deposit_count); } // TOOD: use memory(bytes) and bytes32 - function deposit(pubkey: memory(string), withdrawal_credentials: memory(string), signature: memory(string), deposit_data_root: uint256) -> () { - //require(callvalue() != uint256(0)); + function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: uint256) -> () { + require(callvalue() != uint256(0)); - let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei +// let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei + let deposit_amount = uint256(Typedef.rep(callvalue()) / 1000000000); - let amount: memory(string) = to_little_endian_64(deposit_amount); + let amount: memory(bytes) = to_little_endian_64(deposit_amount); // TODO: emit DepositEvent + /* + emit DepositEvent( + pubkey, + withdrawal_credentials, + amount, + signature, + to_little_endian_64(uint64(deposit_count)) + ); + */ // Compute deposit data root (`DepositData` hash tree root) - let pubkey_root = sha256(MemoryHelpers.concat(pubkey, empty_string(16))); + let pubkey_root = sha256(MemoryHelpers.concat(pubkey, empty_bytes(16))); let signature_root = sha256(MemoryHelpers.concat( sha256(signature), // slice - sha256(MemoryHelpers.concat(signature, empty_string(32))), // slice + sha256(MemoryHelpers.concat(signature, empty_bytes(32))), // slice )); let node = sha256(MemoryHelpers.concat( - sha256(MemoryHelpers.concat(pubkey_root, withdrawal_credentials)), - sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_string(24)), signature_root)) + sha256(MemoryHelpers.concat(to_bytes(pubkey_root), withdrawal_credentials)), + sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) )); -/* - // Emit `DepositEvent` log - bytes memory amount = to_little_endian_64(uint64(deposit_amount)); - emit DepositEvent( - pubkey, - withdrawal_credentials, - amount, - signature, - to_little_endian_64(uint64(deposit_count)) - ); + require(node == deposit_data_root); - // Compute deposit data root (`DepositData` hash tree root) - bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0))); - bytes32 signature_root = sha256(abi.encodePacked( - sha256(abi.encodePacked(signature[:64])), - sha256(abi.encodePacked(signature[64:], bytes32(0))) - )); - bytes32 node = sha256(abi.encodePacked( - sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)), - sha256(abi.encodePacked(amount, bytes24(0), signature_root)) - )); -*/ + require(deposit_count < MAX_DEPOSIT_COUNT()); + + // FIXME: implement checks and updating branch[] - // FIXME: implement assert(false); } From bc79bbb7cde4bd910f5f3dc0e5b2ed3346964559 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 30 Apr 2026 21:41:24 +0200 Subject: [PATCH 05/66] Add more checks --- test/examples/dispatch/deposit.solc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 305bd9438..3c37a2861 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -253,10 +253,18 @@ contract DepositContract { // TOOD: use memory(bytes) and bytes32 function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: uint256) -> () { - require(callvalue() != uint256(0)); + // Extended ABI length checks since dynamic types are used. + require(MemoryHelpers.len(pubkey) == 48); + require(MemoryHelpers.len(withdrawal_credentials) == 32); + require(MemoryHelpers.len(signature) == 96); + + // Check deposit amount + require(Typedef.rep(callvalue()) >= 1000000000000000000); // >= 1 ether + require(Typedef.rep(callvalue()) % 1000000000 == 0); // % 1 gwei == 0 // let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei let deposit_amount = uint256(Typedef.rep(callvalue()) / 1000000000); + require(Typedef.rep(deposit_amount) <= 0xffffffffffffffff); // <= type(uint64).max let amount: memory(bytes) = to_little_endian_64(deposit_amount); // TODO: emit DepositEvent From 2d4c2ecf33c9dd0b1927071e9cdc25bca872bc80 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 8 May 2026 20:55:42 +0200 Subject: [PATCH 06/66] Use bytes32 in deposit --- test/examples/dispatch/deposit.solc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 3c37a2861..a940725ea 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -252,7 +252,7 @@ contract DepositContract { } // TOOD: use memory(bytes) and bytes32 - function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: uint256) -> () { + function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. require(MemoryHelpers.len(pubkey) == 48); require(MemoryHelpers.len(withdrawal_credentials) == 32); @@ -289,7 +289,7 @@ contract DepositContract { sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) )); - require(node == deposit_data_root); + require(node == uint256(Typedef.rep(deposit_data_root))); require(deposit_count < MAX_DEPOSIT_COUNT()); From 4617b1bd272f8f8fff99d0f16c00a011a7c12efc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 8 May 2026 22:05:53 +0200 Subject: [PATCH 07/66] Update to new import style --- test/examples/dispatch/deposit.solc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index a940725ea..f488b1e98 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -1,5 +1,5 @@ -import std; -import dispatch; +import std.{*}; +import std.dispatch.{*}; function caller() -> address { let res: word; From 7151ce2dd6e1212a7695862a044cbbfb5f57aabd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 8 May 2026 22:06:14 +0200 Subject: [PATCH 08/66] Use uint256 division --- test/examples/dispatch/deposit.solc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index f488b1e98..abbbcc2fc 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -262,8 +262,7 @@ contract DepositContract { require(Typedef.rep(callvalue()) >= 1000000000000000000); // >= 1 ether require(Typedef.rep(callvalue()) % 1000000000 == 0); // % 1 gwei == 0 -// let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei - let deposit_amount = uint256(Typedef.rep(callvalue()) / 1000000000); + let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei require(Typedef.rep(deposit_amount) <= 0xffffffffffffffff); // <= type(uint64).max let amount: memory(bytes) = to_little_endian_64(deposit_amount); From 8b847f16c18f0122a5a61d38dacfe16ca4e7d6c8 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 9 May 2026 00:08:25 +0200 Subject: [PATCH 09/66] Testrunner debugging --- test/testrunner/testrunner.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/testrunner/testrunner.cpp b/test/testrunner/testrunner.cpp index 8e26fd869..2c46815b0 100644 --- a/test/testrunner/testrunner.cpp +++ b/test/testrunner/testrunner.cpp @@ -138,6 +138,8 @@ int main(int argc, char** argv) } bool status = result.status_code == EVMC_SUCCESS; + + std::cout << "Status: " << result.status_code << " Output: " << toHex(output) << std::endl; if (kind == "constructor") { From ebbd8a79e5124986831eeaadf05b78c7aa2a0e87 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 9 May 2026 00:08:51 +0200 Subject: [PATCH 10/66] Update deposit test with real data --- test/examples/dispatch/deposit.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index fc0fc8ec5..f79163db7 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -136,13 +136,13 @@ { "input": { - "comment": "deposit(bytes,bytes,bytes,uint256)", - "calldata": "edf26288", - "value": "1000000000" + "comment": "deposit(bytes,bytes,bytes,bytes32)", + "calldata": "22895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120d2b73e995b4c94bf83bcbf58cb9dede2c5e0211ec61eeb09c43aeaad97ce02620000000000000000000000000000000000000000000000000000000000000030abe405f4ca553d02da780b187e15c767fc89d4f9a707bd54c98a8d8e401124dedc6bc14dcdf0ed690e9ec424deae49a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200100000000000000000000007e2a2fa2a064f693f0a55c5639476d913ff12d0500000000000000000000000000000000000000000000000000000000000000608927fa063b70c19a28c3c78aa19bbb2e7b971fc1b8de2033930952a11ef17709348cc2cc14ae810ab249a1fe39e959470b850608afc299811c5cce190f92dd79529ba11846c138073ae200782476c8e5e4c6ea3b5938a16398835e4964c6bac0", + "value": "32000000000000000000" }, "kind": "call", "output": { - "returndata": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000", + "returndata": "", "status": "success" } } From d20dde688cbf5341427e4d48c742d65c9416b9b3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 9 May 2026 00:09:08 +0200 Subject: [PATCH 11/66] Add missing loops to deposit --- test/examples/dispatch/deposit.solc | 32 ++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index abbbcc2fc..e8aaa4822 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -159,6 +159,7 @@ contract DepositContract { // branch: array(uint256, DEPOSIT_CONTRACT_TREE_DEPTH()); // zero_hashes : array(uint256, DEPOSIT_CONTRACT_TREE_DEPTH()); // misusing mappings here, the index is the key + branch : mapping(uint256, uint256); zero_hashes : mapping(uint256, uint256); constructor() { @@ -244,6 +245,21 @@ contract DepositContract { function get_deposit_root() -> uint256 { // FIXME: implement return uint256(0); + + bytes32 node; + let size = deposit_count; + for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height++) { + if ((size & 1) == 1) + node = sha256(MemoryHelpers.concat(branch[height], node)); + else + node = sha256(MemoryHelpers.concat(node, zero_hashes[height])); + size /= 2; + } + return sha256(MemoryHelpers.concat( + node, + to_little_endian_64(uint64(deposit_count)), + bytes24(0) + )); } // TODO: return memory(bytes) @@ -253,7 +269,7 @@ contract DepositContract { // TOOD: use memory(bytes) and bytes32 function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { - // Extended ABI length checks since dynamic types are used. + // Extended ABI length checks since dynamic types are used. require(MemoryHelpers.len(pubkey) == 48); require(MemoryHelpers.len(withdrawal_credentials) == 32); require(MemoryHelpers.len(signature) == 96); @@ -294,6 +310,20 @@ contract DepositContract { // FIXME: implement checks and updating branch[] + // Add deposit data root to Merkle tree (update a single `branch` node) + deposit_count += 1; + let size = deposit_count; + for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height++) { + if ((size & 1) == 1) { + branch[height] = node; + return; + } + node = sha256(MemoryHelpers.concat(branch[height], node)); + size /= 2; + } + + // As the loop should always end prematurely with the `return` statement, + // this code should be unreachable. We assert `false` just to be safe. assert(false); } From 15ea86c117722b49de9426b808f979cef6e2ab49 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 9 May 2026 23:15:33 +0200 Subject: [PATCH 12/66] is_odd and syntax --- test/examples/dispatch/deposit.solc | 49 +++++++++++++++++++---------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index e8aaa4822..d6a860a24 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -143,6 +143,15 @@ function to_little_endian_64(v: uint256) -> memory(bytes) { return memory(res); } +function is_odd(v:uint256) -> bool { + let res: word; + let v_ = Typedef.rep(v); + assembly { + res := eq(mod(v_, 2), 1) + } + return tobool(res); +} + // No constants are supported yet, using this as a workaround. // Defining variables outside of contract/function is not supported. function DEPOSIT_CONTRACT_TREE_DEPTH() -> uint256 { @@ -244,21 +253,26 @@ contract DepositContract { // TODO: return bytes32 function get_deposit_root() -> uint256 { // FIXME: implement - return uint256(0); +// return uint256(0); - bytes32 node; + let node: uint256; let size = deposit_count; - for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height++) { - if ((size & 1) == 1) + for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { + //if ((size & 1) == 1) { + //if ((size % uint256(2)) == uint256(1)) { + if (is_odd(size)) { node = sha256(MemoryHelpers.concat(branch[height], node)); - else + } else { node = sha256(MemoryHelpers.concat(node, zero_hashes[height])); - size /= 2; + } + size = size / uint256(2); } return sha256(MemoryHelpers.concat( - node, - to_little_endian_64(uint64(deposit_count)), - bytes24(0) + MemoryHelpers.concat( + to_bytes(node), + to_little_endian_64(deposit_count) + ), + empty_bytes(24) )); } @@ -311,15 +325,18 @@ contract DepositContract { // FIXME: implement checks and updating branch[] // Add deposit data root to Merkle tree (update a single `branch` node) - deposit_count += 1; + deposit_count += uint256(1); let size = deposit_count; - for (let height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH(); height++) { - if ((size & 1) == 1) { - branch[height] = node; - return; + let node_ = uint256(Typedef.rep(node)); + for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { + //if ((size & 1) == 1) { + //if ((size % 2) == 1) { + if (is_odd(size)) { + branch[height] = node_; + return (); } - node = sha256(MemoryHelpers.concat(branch[height], node)); - size /= 2; + node_ = sha256(MemoryHelpers.concat(branch[height], node_)); + size = size / uint256(2); } // As the loop should always end prematurely with the `return` statement, From 07013617b7d3f306b4d3a27dc21a290c65ed2230 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 10:58:59 +0200 Subject: [PATCH 13/66] f --- test/examples/dispatch/deposit.solc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index d6a860a24..047c5dfeb 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -252,14 +252,11 @@ contract DepositContract { // TODO: return bytes32 function get_deposit_root() -> uint256 { - // FIXME: implement -// return uint256(0); - let node: uint256; let size = deposit_count; for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { //if ((size & 1) == 1) { - //if ((size % uint256(2)) == uint256(1)) { + //if ((size % 2)) == 1) { if (is_odd(size)) { node = sha256(MemoryHelpers.concat(branch[height], node)); } else { @@ -322,8 +319,6 @@ contract DepositContract { require(deposit_count < MAX_DEPOSIT_COUNT()); - // FIXME: implement checks and updating branch[] - // Add deposit data root to Merkle tree (update a single `branch` node) deposit_count += uint256(1); let size = deposit_count; From 1ab02da8a1bc9fe8528cd0151c89a6ee25ceadb5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 11:01:13 +0200 Subject: [PATCH 14/66] Remove unrolled loops --- test/examples/dispatch/deposit.solc | 73 ++--------------------------- 1 file changed, 3 insertions(+), 70 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 047c5dfeb..44c106d6b 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -173,76 +173,9 @@ contract DepositContract { constructor() { // Compute hashes in empty sparse Merkle tree - // TODO: no for loops yet, so unrolled - let height: uint256 = uint256(0); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); -/* - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); - height = height + uint256(1); - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); -*/ - - //for (uint height = 0; height < DEPOSIT_CONTRACT_TREE_DEPTH - 1; height++) - // zero_hashes[height + 1] = sha256(abi.encodePacked(zero_hashes[height], zero_hashes[height])); + for (let height = uint256(0); height < (DEPOSIT_CONTRACT_TREE_DEPTH() - uint256(1)); height = height + uint256(1)) { + zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + } } // TODO: this is for testing only From 7f485edeefd9d2127002028e1bb711b29a98f654 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 11:02:37 +0200 Subject: [PATCH 15/66] Reenable require --- test/examples/dispatch/deposit.solc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 44c106d6b..8479375e0 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -27,9 +27,9 @@ function assert(cond: bool) { function require(cond: bool) { if (!cond) { -/* assembly { + assembly { revert(0, 0) - }*/ + } } } From f059f1b92ed27a58b75ad3a1ed23a88e545fcd51 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 11:04:14 +0200 Subject: [PATCH 16/66] update tests --- test/examples/dispatch/deposit.json | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index f79163db7..d209b16a5 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -88,8 +88,7 @@ }, "kind": "call", "output": { - "comment": "The correct one 0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", - "returndata": "0000000000000000000000000000000000000000000000000000000000000000", + "returndata": "d49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb", "status": "success" } }, @@ -102,8 +101,7 @@ }, "kind": "call", "output": { - "comment": "The correct one 0x985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7", - "returndata": "0000000000000000000000000000000000000000000000000000000000000000", + "returndata": "985e929f70af28d0bdd1a90a808f977f597c7c778c489e98d3bd8910d31ac0f7", "status": "success" } }, @@ -116,7 +114,7 @@ }, "kind": "call", "output": { - "returndata": "0000000000000000000000000000000000000000000000000000000000000000", + "returndata": "831636bbfb9a1ba8d6bc51f507affaabd0fa8d9eafed96461663ab0cfdd5c826", "status": "success" } }, @@ -145,6 +143,19 @@ "returndata": "", "status": "success" } + }, + + { + "input": { + "comment": "deposit(bytes,bytes,bytes,bytes32)", + "calldata": "22895118000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120d2b73e995b4c94bf83bcbf58cb9dede2c5e0211ec61eeb09c43aeaad97ce02620000000000000000000000000000000000000000000000000000000000000030abe405f4ca553d02da780b187e15c767fc89d4f9a707bd54c98a8d8e401124dedc6bc14dcdf0ed690e9ec424deae49a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200100000000000000000000007e2a2fa2a064f693f0a55c5639476d913ff12d0500000000000000000000000000000000000000000000000000000000000000608927fa063b70c19a28c3c78aa19bbb2e7b971fc1b8de2033930952a11ef17709348cc2cc14ae810ab249a1fe39e959470b850608afc299811c5cce190f92dd79529ba11846c138073ae200782476c8e5e4c6ea3b5938a16398835e4964c6bac0", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "", + "status": "success" + } } ] } From ebead5a2af52ac12bb580266f7113b00c4e71f21 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 11:42:19 +0200 Subject: [PATCH 17/66] Add bytes_slice and bytes_truncate --- test/examples/dispatch/deposit.solc | 82 +++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 8479375e0..6bb34bb33 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -74,6 +74,14 @@ instance memory(bytes):MemoryHelpers { function empty_bytes(len: word) -> memory(bytes) { let res: word = allocate_memory(32 + len); mstore(res, len); + // TODO: zero out space + // TODO: make this efficient + assembly { + let ptr := add(res, 32) + for { let i := 0 } lt(i, len) { i := add(i, 1) } { + mstore8(add(ptr, i), 0) + } + } return memory(res); } @@ -124,6 +132,41 @@ function to_bytes(v: uint256) -> memory(bytes) { return memory(res); } +// TODO: use slice() +function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { + let len = MemoryHelpers.len(input); + if (len < start) { + assembly { + revert(0, 0) + } + } + len = len - start; + let res = empty_bytes(len); + let src_ptr = MemoryHelpers.ptr(input); + let dst_ptr = MemoryHelpers.ptr(res); + assembly { + mcopy(dst_ptr, add(src_ptr, start), len) + } + return res; +} + +// TODO: use slice() +function bytes_truncate(input: memory(bytes), end: word) -> memory(bytes) { + let len = MemoryHelpers.len(input); + if (len < end) { + assembly { + revert(0, 0) + } + } + let res = empty_bytes(end); + let src_ptr = MemoryHelpers.ptr(input); + let dst_ptr = MemoryHelpers.ptr(res); + assembly { + mcopy(dst_ptr, src_ptr, end) + } + return res; +} + // Should use uint64 and bytes. // Assumes 64-bit input. function to_little_endian_64(v: uint256) -> memory(bytes) { @@ -237,18 +280,51 @@ contract DepositContract { ); */ +/* +let x = bytes_truncate(signature, 64); +let x_ = MemoryHelpers.ptr(x); +require(MemoryHelpers.len(x) == 64); +let y = bytes_slice(signature, 64); +let y_ = MemoryHelpers.ptr(y); +require(MemoryHelpers.len(y) == 32); + assembly { + mstore(0, mload(y_)) + return(0, 32) + } +*/ + +/* + // Compute deposit data root (`DepositData` hash tree root) + bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0))); + bytes32 signature_root = sha256(abi.encodePacked( + sha256(abi.encodePacked(signature[:64])), + sha256(abi.encodePacked(signature[64:], bytes32(0))) + )); + bytes32 node = sha256(abi.encodePacked( + sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)), + sha256(abi.encodePacked(amount, bytes24(0), signature_root)) + )); +*/ + // Compute deposit data root (`DepositData` hash tree root) let pubkey_root = sha256(MemoryHelpers.concat(pubkey, empty_bytes(16))); let signature_root = sha256(MemoryHelpers.concat( - sha256(signature), // slice - sha256(MemoryHelpers.concat(signature, empty_bytes(32))), // slice + sha256(bytes_truncate(signature, 64)), // slice + sha256(MemoryHelpers.concat(bytes_slice(signature, 64), empty_bytes(32))), // slice )); let node = sha256(MemoryHelpers.concat( sha256(MemoryHelpers.concat(to_bytes(pubkey_root), withdrawal_credentials)), sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) )); - require(node == uint256(Typedef.rep(deposit_data_root))); +// Expected: d2b73e995b4c94bf83bcbf58cb9dede2c5e0211ec61eeb09c43aeaad97ce0262 +let node_ = Typedef.rep(node); + assembly { + mstore(0, node_) + return(0, 32) + } + +// require(node == uint256(Typedef.rep(deposit_data_root))); require(deposit_count < MAX_DEPOSIT_COUNT()); From 40862614229811872d9dcaa11459dd22a1c8fe70 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 12:40:37 +0200 Subject: [PATCH 18/66] Fix memory concat --- test/examples/dispatch/deposit.solc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 6bb34bb33..117b5b6c6 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -60,8 +60,8 @@ instance memory(bytes):MemoryHelpers { let b_ptr = MemoryHelpers.ptr(b); let b_len = MemoryHelpers.len(b); - let len = 32 + a_len + b_len; - let res: word = allocate_memory(len); + let len = a_len + b_len; + let res: word = allocate_memory(32 + len); assembly { mstore(res, len) mcopy(add(res, 32), a_ptr, a_len) From 59688d2e0151e277f319f06c68bc34ba682f44a0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 12:42:03 +0200 Subject: [PATCH 19/66] Remove debugging code --- test/examples/dispatch/deposit.solc | 35 +---------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 117b5b6c6..842a5a250 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -280,32 +280,6 @@ contract DepositContract { ); */ -/* -let x = bytes_truncate(signature, 64); -let x_ = MemoryHelpers.ptr(x); -require(MemoryHelpers.len(x) == 64); -let y = bytes_slice(signature, 64); -let y_ = MemoryHelpers.ptr(y); -require(MemoryHelpers.len(y) == 32); - assembly { - mstore(0, mload(y_)) - return(0, 32) - } -*/ - -/* - // Compute deposit data root (`DepositData` hash tree root) - bytes32 pubkey_root = sha256(abi.encodePacked(pubkey, bytes16(0))); - bytes32 signature_root = sha256(abi.encodePacked( - sha256(abi.encodePacked(signature[:64])), - sha256(abi.encodePacked(signature[64:], bytes32(0))) - )); - bytes32 node = sha256(abi.encodePacked( - sha256(abi.encodePacked(pubkey_root, withdrawal_credentials)), - sha256(abi.encodePacked(amount, bytes24(0), signature_root)) - )); -*/ - // Compute deposit data root (`DepositData` hash tree root) let pubkey_root = sha256(MemoryHelpers.concat(pubkey, empty_bytes(16))); let signature_root = sha256(MemoryHelpers.concat( @@ -317,14 +291,7 @@ require(MemoryHelpers.len(y) == 32); sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) )); -// Expected: d2b73e995b4c94bf83bcbf58cb9dede2c5e0211ec61eeb09c43aeaad97ce0262 -let node_ = Typedef.rep(node); - assembly { - mstore(0, node_) - return(0, 32) - } - -// require(node == uint256(Typedef.rep(deposit_data_root))); + require(node == uint256(Typedef.rep(deposit_data_root))); require(deposit_count < MAX_DEPOSIT_COUNT()); From e047d5254550ecae8f718cb499840f674d19d68b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 12:44:34 +0200 Subject: [PATCH 20/66] more tests --- test/examples/dispatch/deposit.json | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index d209b16a5..77ef54c71 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -145,6 +145,19 @@ } }, + { + "input": { + "comment": "get_deposit_root()", + "calldata": "c5f2892f", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "831636bbfb9a1ba8d6bc51f507affaabd0fa8d9eafed96461663ab0cfdd5c826", + "status": "success" + } + }, + { "input": { "comment": "deposit(bytes,bytes,bytes,bytes32)", @@ -154,7 +167,7 @@ "kind": "call", "output": { "returndata": "", - "status": "success" + "status": "failure" } } ] From db752382547edf514f08079fc65cbd45a39d6e79 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 12:45:09 +0200 Subject: [PATCH 21/66] max_count --- test/examples/dispatch/deposit.solc | 1 + 1 file changed, 1 insertion(+) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 842a5a250..49fa705c2 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -201,6 +201,7 @@ function DEPOSIT_CONTRACT_TREE_DEPTH() -> uint256 { return uint256(32); } function MAX_DEPOSIT_COUNT() -> uint256 { + // uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1; // Could use Bounded(uint256).maxVal() return uint256(0xFFFFFFFF); } From 4062d684e230d54789778b6791b43fc18b7b26eb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 12:48:39 +0200 Subject: [PATCH 22/66] andd expected root to test --- test/examples/dispatch/deposit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index 77ef54c71..54cfb7e72 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -114,7 +114,7 @@ }, "kind": "call", "output": { - "returndata": "831636bbfb9a1ba8d6bc51f507affaabd0fa8d9eafed96461663ab0cfdd5c826", + "returndata": "d70a234731285c6804c2a4f56711ddb8c82c99740f207854891028af34e27e5e", "status": "success" } }, From 4a5681e88d1f2c900f0e5c2029301d6f14f33371 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 12:49:00 +0200 Subject: [PATCH 23/66] fix calcualted root --- test/examples/dispatch/deposit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index 54cfb7e72..0836a4e49 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -153,7 +153,7 @@ }, "kind": "call", "output": { - "returndata": "831636bbfb9a1ba8d6bc51f507affaabd0fa8d9eafed96461663ab0cfdd5c826", + "returndata": "d3014e96ac4ef15ccbac8047549cac09ad687f6169cba901b92c8875a62ca917", "status": "success" } }, From 0d93199d6d41c3112c6fd925ca856bd75e5ebf0c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 12:52:49 +0200 Subject: [PATCH 24/66] Use bytes32 everywhere --- test/examples/dispatch/deposit.solc | 47 +++++++++++++++++++---------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 49fa705c2..e5e5d3241 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -105,9 +105,28 @@ instance uint256:MemoryHelpers { } } +instance bytes32:MemoryHelpers { + function ptr(a: bytes32) -> word { + return 0; + } + function len(a: bytes32) -> word { + return 0; + } + function concat(a: bytes32, b: bytes32) -> memory(bytes) { + let res: word = allocate_memory(96); + let a_val = Typedef.rep(a); + let b_val = Typedef.rep(b); + assembly { + mstore(res, 64) + mstore(add(res, 32), a_val) + mstore(add(res, 64), b_val) + } + return memory(res); + } +} + // This calls the SHA256 precompile. -// TODO: Should return bytes32. -function sha256(input: memory(bytes)) -> uint256 { +function sha256(input: memory(bytes)) -> bytes32 { // TODO: `ptr()` / `len()` helpers would be useful let ptr : word = Typedef.rep(input); let res : word; @@ -119,10 +138,10 @@ function sha256(input: memory(bytes)) -> uint256 { } res := mload(0) } - return uint256(res); + return bytes32(res); } -function to_bytes(v: uint256) -> memory(bytes) { +function to_bytes(v: bytes32) -> memory(bytes) { let res: word = allocate_memory(64); let value: word = Typedef.rep(v); assembly { @@ -212,8 +231,8 @@ contract DepositContract { // branch: array(uint256, DEPOSIT_CONTRACT_TREE_DEPTH()); // zero_hashes : array(uint256, DEPOSIT_CONTRACT_TREE_DEPTH()); // misusing mappings here, the index is the key - branch : mapping(uint256, uint256); - zero_hashes : mapping(uint256, uint256); + branch : mapping(uint256, bytes32); + zero_hashes : mapping(uint256, bytes32); constructor() { // Compute hashes in empty sparse Merkle tree @@ -223,13 +242,12 @@ contract DepositContract { } // TODO: this is for testing only - function get_zero_hash(index: uint256) -> uint256 { + function get_zero_hash(index: uint256) -> bytes32 { return zero_hashes[index]; } - // TODO: return bytes32 - function get_deposit_root() -> uint256 { - let node: uint256; + function get_deposit_root() -> bytes32 { + let node: bytes32; let size = deposit_count; for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { //if ((size & 1) == 1) { @@ -250,12 +268,10 @@ contract DepositContract { )); } - // TODO: return memory(bytes) function get_deposit_count() -> memory(bytes) { return to_little_endian_64(deposit_count); } - // TOOD: use memory(bytes) and bytes32 function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. require(MemoryHelpers.len(pubkey) == 48); @@ -292,22 +308,21 @@ contract DepositContract { sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) )); - require(node == uint256(Typedef.rep(deposit_data_root))); + require(node == deposit_data_root); require(deposit_count < MAX_DEPOSIT_COUNT()); // Add deposit data root to Merkle tree (update a single `branch` node) deposit_count += uint256(1); let size = deposit_count; - let node_ = uint256(Typedef.rep(node)); for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { //if ((size & 1) == 1) { //if ((size % 2) == 1) { if (is_odd(size)) { - branch[height] = node_; + branch[height] = node; return (); } - node_ = sha256(MemoryHelpers.concat(branch[height], node_)); + node = sha256(MemoryHelpers.concat(branch[height], node)); size = size / uint256(2); } From d6791cf346d1d4c0b442e15c8315b2f747b5cab6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 13:02:46 +0200 Subject: [PATCH 25/66] Add log1 --- test/examples/dispatch/deposit.solc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index e5e5d3241..2270d6ad0 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -288,6 +288,14 @@ contract DepositContract { let amount: memory(bytes) = to_little_endian_64(deposit_amount); // TODO: emit DepositEvent /* + event DepositEvent( + bytes pubkey, + bytes withdrawal_credentials, + bytes amount, + bytes signature, + bytes index + ); + emit DepositEvent( pubkey, withdrawal_credentials, @@ -296,6 +304,10 @@ contract DepositContract { to_little_endian_64(uint64(deposit_count)) ); */ + assembly { + // TODO: need to ABI-encode all the arguments + log1(0, 0, 0x649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c5) + } // Compute deposit data root (`DepositData` hash tree root) let pubkey_root = sha256(MemoryHelpers.concat(pubkey, empty_bytes(16))); From b3231b2cc4d87cd0dbb0c71d03787798c5acb3cc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 13:38:05 +0200 Subject: [PATCH 26/66] Use mstore helpers --- test/examples/dispatch/deposit.solc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 2270d6ad0..f513c7d8c 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -143,11 +143,8 @@ function sha256(input: memory(bytes)) -> bytes32 { function to_bytes(v: bytes32) -> memory(bytes) { let res: word = allocate_memory(64); - let value: word = Typedef.rep(v); - assembly { - mstore(res, 32) - mstore(add(res, 32), value) - } + mstore(res, 32); + mstore(res + 32, Typedef.rep(v)); return memory(res); } From 7b8f486b72d56ab8d930327fffeac63c7401b32f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 13:41:19 +0200 Subject: [PATCH 27/66] Remove is_odd --- test/examples/dispatch/deposit.solc | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index f513c7d8c..ed0dffb5d 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -202,15 +202,6 @@ function to_little_endian_64(v: uint256) -> memory(bytes) { return memory(res); } -function is_odd(v:uint256) -> bool { - let res: word; - let v_ = Typedef.rep(v); - assembly { - res := eq(mod(v_, 2), 1) - } - return tobool(res); -} - // No constants are supported yet, using this as a workaround. // Defining variables outside of contract/function is not supported. function DEPOSIT_CONTRACT_TREE_DEPTH() -> uint256 { @@ -248,8 +239,7 @@ contract DepositContract { let size = deposit_count; for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { //if ((size & 1) == 1) { - //if ((size % 2)) == 1) { - if (is_odd(size)) { + if ((size % uint256(2)) == uint256(1)) { node = sha256(MemoryHelpers.concat(branch[height], node)); } else { node = sha256(MemoryHelpers.concat(node, zero_hashes[height])); @@ -326,8 +316,7 @@ contract DepositContract { let size = deposit_count; for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { //if ((size & 1) == 1) { - //if ((size % 2) == 1) { - if (is_odd(size)) { + if ((size % uint256(2)) == uint256(1)) { branch[height] = node; return (); } From 3e61b92f963a72bc2127cb1def23028c716ce461 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 13:58:32 +0200 Subject: [PATCH 28/66] use uint256 in comparisons and not word --- test/examples/dispatch/deposit.solc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index ed0dffb5d..17461eb56 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -207,6 +207,7 @@ function to_little_endian_64(v: uint256) -> memory(bytes) { function DEPOSIT_CONTRACT_TREE_DEPTH() -> uint256 { return uint256(32); } + function MAX_DEPOSIT_COUNT() -> uint256 { // uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1; // Could use Bounded(uint256).maxVal() @@ -266,11 +267,11 @@ contract DepositContract { require(MemoryHelpers.len(signature) == 96); // Check deposit amount - require(Typedef.rep(callvalue()) >= 1000000000000000000); // >= 1 ether - require(Typedef.rep(callvalue()) % 1000000000 == 0); // % 1 gwei == 0 + require(callvalue() >= uint256(1000000000000000000)); // >= 1 ether + require(callvalue() % uint256(1000000000) == uint256(0)); // % 1 gwei == 0 let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei - require(Typedef.rep(deposit_amount) <= 0xffffffffffffffff); // <= type(uint64).max + require(deposit_amount <= uint256(0xffffffffffffffff)); // <= type(uint64).max let amount: memory(bytes) = to_little_endian_64(deposit_amount); // TODO: emit DepositEvent From 9119036e7e87f7ef46aa8af8ee50574c4ca280d1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 13:59:27 +0200 Subject: [PATCH 29/66] Check count after deposit in tests --- test/examples/dispatch/deposit.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index 0836a4e49..0d2436efa 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -158,6 +158,19 @@ } }, + { + "input": { + "comment": "get_deposit_count()", + "calldata": "621fd130", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } + }, + { "input": { "comment": "deposit(bytes,bytes,bytes,bytes32)", From ea15250b1af4041cf6263cccdaa0d707203b8cbc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 14:00:10 +0200 Subject: [PATCH 30/66] Check that root/count is unchagned after failed deposit --- test/examples/dispatch/deposit.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index 0d2436efa..017692750 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -182,6 +182,32 @@ "returndata": "", "status": "failure" } + }, + + { + "input": { + "comment": "get_deposit_root()", + "calldata": "c5f2892f", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "d3014e96ac4ef15ccbac8047549cac09ad687f6169cba901b92c8875a62ca917", + "status": "success" + } + }, + + { + "input": { + "comment": "get_deposit_count()", + "calldata": "621fd130", + "value": "0" + }, + "kind": "call", + "output": { + "returndata": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000080100000000000000000000000000000000000000000000000000000000000000", + "status": "success" + } } ] } From 7b408d710ed8cbf88c78f2e8c83a864ef29259a6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 10 May 2026 14:24:09 +0200 Subject: [PATCH 31/66] Add zeroize_memory helper --- test/examples/dispatch/deposit.solc | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 17461eb56..30f8c9f34 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -71,17 +71,28 @@ instance memory(bytes):MemoryHelpers { } } -function empty_bytes(len: word) -> memory(bytes) { - let res: word = allocate_memory(32 + len); - mstore(res, len); - // TODO: zero out space - // TODO: make this efficient +function zeroize_memory(ptr: word, len: word) { assembly { - let ptr := add(res, 32) - for { let i := 0 } lt(i, len) { i := add(i, 1) } { - mstore8(add(ptr, i), 0) + let end_ptr := add(ptr, len) + + // Zero out 32-byte words + let words := div(len, 32) + for { let i := 0 } lt(i, words) { i := add(i, 1) } { + mstore(ptr, 0) + ptr := add(ptr, 32) + } + + // Zero out trailing bytes + for { } lt(ptr, end_ptr) { ptr := add(ptr, 1) } { + mstore8(ptr, 0) } } +} + +function empty_bytes(len: word) -> memory(bytes) { + let res: word = allocate_memory(32 + len); + mstore(res, len); + zeroize_memory(res + 32, len); return memory(res); } @@ -308,8 +319,10 @@ contract DepositContract { sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) )); + // Verify computed and expected deposit data roots match require(node == deposit_data_root); + // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) require(deposit_count < MAX_DEPOSIT_COUNT()); // Add deposit data root to Merkle tree (update a single `branch` node) From ceb1684094c23f12f94af97d289e97e1f7b26259 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 11 May 2026 18:48:40 +0200 Subject: [PATCH 32/66] use std.mload --- test/examples/dispatch/deposit.solc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 30f8c9f34..62b8a0f45 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -47,11 +47,7 @@ instance memory(bytes):MemoryHelpers { function len(a: memory(bytes)) -> word { let rep = Typedef.rep(a); - let res: word; - assembly { - res := mload(rep) - } - return res; + return mload(rep); } function concat(a: memory(bytes), b: memory(bytes)) -> memory(bytes) { From f11f52288daa82593b1d93af3adb770a39094ce3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 15 May 2026 19:00:56 +0200 Subject: [PATCH 33/66] Add explicit return types --- test/examples/dispatch/deposit.solc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 62b8a0f45..754b4fbdf 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -17,7 +17,7 @@ function callvalue() -> uint256 { return uint256(res); } -function assert(cond: bool) { +function assert(cond: bool) -> () { if (!cond) { assembly { invalid() @@ -25,7 +25,7 @@ function assert(cond: bool) { } } -function require(cond: bool) { +function require(cond: bool) -> () { if (!cond) { assembly { revert(0, 0) @@ -67,7 +67,7 @@ instance memory(bytes):MemoryHelpers { } } -function zeroize_memory(ptr: word, len: word) { +function zeroize_memory(ptr: word, len: word) -> () { assembly { let end_ptr := add(ptr, len) From 11b925e996c6a9670d4fdadd37e4ebeaf769b074 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 15 May 2026 23:54:16 +0200 Subject: [PATCH 34/66] Add class ToBytesLike --- test/examples/dispatch/deposit.solc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 754b4fbdf..7c7c69327 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -148,6 +148,19 @@ function sha256(input: memory(bytes)) -> bytes32 { return bytes32(res); } +forall t . class t:ToBytesLike { + function to_bytes(value: t) -> memory(bytes); +} + +instance bytes32:ToBytesLike { + function to_bytes(value: bytes32) -> memory(bytes) { + let res: word = allocate_memory(64); + mstore(res, 32); + mstore(res + 32, Typedef.rep(value)); + return memory(res); + } +} + function to_bytes(v: bytes32) -> memory(bytes) { let res: word = allocate_memory(64); mstore(res, 32); From d703c2a6e86a1fe4ccbbff1da93f5dc3d9afc1cc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 16 May 2026 00:35:22 +0200 Subject: [PATCH 35/66] Add a new version of concat --- test/examples/dispatch/deposit.solc | 62 ++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 7c7c69327..17d7af6f2 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -148,6 +148,62 @@ function sha256(input: memory(bytes)) -> bytes32 { return bytes32(res); } +forall t . class t:MemoryAttributes { + // The size needed for the value. + function len(v: t) -> word; + // If this is a non-memory type, memory is allocated first. + function ptr(v: t) -> word; + // Serialize the entire contents at a provided memory area. + function encodeInto(v: t, target: word) -> (); +} + +forall a b . a:MemoryAttributes, b:MemoryAttributes => function concat(x: a, y: b) -> memory(bytes) { + let x_len = MemoryAttributes.len(x); + let y_len = MemoryAttributes.len(y); + let res: word = allocate_memory(32 + x_len + y_len); + mstore(res, x_len + y_len); + MemoryAttributes.encodeInto(x, res + 32); + MemoryAttributes.encodeInto(y, res + 32 + x_len); + return memory(res); +} + +instance bytes32:MemoryAttributes { + function len(v: bytes32) -> word { + return 32; + } + function ptr(v: bytes32) -> word { + // TODO: use `allocate_bytes_array` + `encodeInto` + return Typedef.rep(to_bytes(v)); + } + function encodeInto(v: bytes32, target: word) -> () { + mstore(target, Typedef.rep(v)); + } +} + +instance memory(bytes):MemoryAttributes { + function len(v: memory(bytes)) -> word { + return mload(Typedef.rep(v)); + } + function ptr(v: memory(bytes)) -> word { + return Typedef.rep(v); + } + function encodeInto(v: memory(bytes), target: word) -> () { + let v_ = Typedef.rep(v); + assembly { + mcopy(target, add(v_, 32), mload(v_)) + } + } +} + +/* +data Concatenator = Concatenator(t)); + +forall t . class t:Concaternator { + function concat() +} +*/ + + forall t . class t:ToBytesLike { function to_bytes(value: t) -> memory(bytes); } @@ -246,7 +302,8 @@ contract DepositContract { constructor() { // Compute hashes in empty sparse Merkle tree for (let height = uint256(0); height < (DEPOSIT_CONTRACT_TREE_DEPTH() - uint256(1)); height = height + uint256(1)) { - zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); +// zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); + zero_hashes[height + uint256(1)] = sha256(concat(zero_hashes[height], zero_hashes[height])); } } @@ -324,7 +381,8 @@ contract DepositContract { sha256(MemoryHelpers.concat(bytes_slice(signature, 64), empty_bytes(32))), // slice )); let node = sha256(MemoryHelpers.concat( - sha256(MemoryHelpers.concat(to_bytes(pubkey_root), withdrawal_credentials)), +// sha256(MemoryHelpers.concat(to_bytes(pubkey_root), withdrawal_credentials)), + sha256(concat(pubkey_root, withdrawal_credentials)), sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) )); From 5ca11ac7ea07d1f234ed6c3ecaa404773ffd148c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 16 May 2026 01:01:46 +0200 Subject: [PATCH 36/66] New version of to_bytes --- test/examples/dispatch/deposit.solc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 17d7af6f2..f59d4417e 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -195,6 +195,14 @@ instance memory(bytes):MemoryAttributes { } } +forall a . a:MemoryAttributes => function to_bytes(x: a) -> memory(bytes) { + let len = MemoryAttributes.len(x); + let res = allocate_memory(32 + len); + mstore(res, len); + MemoryAttributes.encodeInto(x, res + 32); + return memory(res); +} + /* data Concatenator = Concatenator(t)); @@ -217,7 +225,7 @@ instance bytes32:ToBytesLike { } } -function to_bytes(v: bytes32) -> memory(bytes) { +function to_bytes2(v: bytes32) -> memory(bytes) { let res: word = allocate_memory(64); mstore(res, 32); mstore(res + 32, Typedef.rep(v)); From ca604128cddf9ce610e6d46caea3da4844077a02 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 16 May 2026 01:03:47 +0200 Subject: [PATCH 37/66] use new to_bytes/concat --- test/examples/dispatch/deposit.solc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index f59d4417e..a9fe874bd 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -326,15 +326,15 @@ contract DepositContract { for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { //if ((size & 1) == 1) { if ((size % uint256(2)) == uint256(1)) { - node = sha256(MemoryHelpers.concat(branch[height], node)); + node = sha256(concat(branch[height], node)); } else { - node = sha256(MemoryHelpers.concat(node, zero_hashes[height])); + node = sha256(concat(node, zero_hashes[height])); } size = size / uint256(2); } - return sha256(MemoryHelpers.concat( - MemoryHelpers.concat( - to_bytes(node), + return sha256(concat( + concat( + node, to_little_endian_64(deposit_count) ), empty_bytes(24) @@ -383,15 +383,15 @@ contract DepositContract { } // Compute deposit data root (`DepositData` hash tree root) - let pubkey_root = sha256(MemoryHelpers.concat(pubkey, empty_bytes(16))); + let pubkey_root = sha256(concat(pubkey, empty_bytes(16))); let signature_root = sha256(MemoryHelpers.concat( sha256(bytes_truncate(signature, 64)), // slice - sha256(MemoryHelpers.concat(bytes_slice(signature, 64), empty_bytes(32))), // slice + sha256(concat(bytes_slice(signature, 64), empty_bytes(32))), // slice )); let node = sha256(MemoryHelpers.concat( -// sha256(MemoryHelpers.concat(to_bytes(pubkey_root), withdrawal_credentials)), +// sha256(MemoryHelpers.concat(pubkey_root, withdrawal_credentials)), sha256(concat(pubkey_root, withdrawal_credentials)), - sha256(MemoryHelpers.concat(MemoryHelpers.concat(amount, empty_bytes(24)), to_bytes(signature_root))) + sha256(concat(MemoryHelpers.concat(amount, empty_bytes(24)), signature_root)) )); // Verify computed and expected deposit data roots match From bd771a54fdd05e0f56a2ea791c7290173cdd0c2b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 16 May 2026 01:11:41 +0200 Subject: [PATCH 38/66] Add empty(word) --- test/examples/dispatch/deposit.solc | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index a9fe874bd..f02624e7e 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -203,6 +203,29 @@ forall a . a:MemoryAttributes => function to_bytes(x: a) -> memory(bytes) { return memory(res); } +// Placeholder for an empty memory area. +// The value is the size of the area in bytes. The area will be zeroed upon serialization. +// NOTE: not implementing Typedef by design. +data empty = empty(word); + +instance empty:MemoryAttributes { + function len(v: empty) -> word { + match v { + | empty(size) => return size; + } + } + function ptr(v: empty) -> word { + return 0; // TODO + } + function encodeInto(v: empty, target: word) -> () { + let size; + match v { + | empty(size_) => size = size_; + } + zeroize_memory(target, size); + } +} + /* data Concatenator = Concatenator(t)); @@ -386,7 +409,8 @@ contract DepositContract { let pubkey_root = sha256(concat(pubkey, empty_bytes(16))); let signature_root = sha256(MemoryHelpers.concat( sha256(bytes_truncate(signature, 64)), // slice - sha256(concat(bytes_slice(signature, 64), empty_bytes(32))), // slice +// sha256(concat(bytes_slice(signature, 64), empty_bytes(32))), // slice + sha256(concat(bytes_slice(signature, 64), empty(32))) )); let node = sha256(MemoryHelpers.concat( // sha256(MemoryHelpers.concat(pubkey_root, withdrawal_credentials)), From 45d6fe2ec3b296cc5981cc0783108a1755afdc47 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 17 May 2026 22:39:27 +0200 Subject: [PATCH 39/66] Variadic --- test/examples/dispatch/deposit.solc | 41 ++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index f02624e7e..cea4b6feb 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -148,6 +148,33 @@ function sha256(input: memory(bytes)) -> bytes32 { return bytes32(res); } +data variadic(a, b, c, d) = v0 | v1(a) | v2(a, b) | v3(a, b, c) | v4(a, b, c, d); + +forall a b c d . a:MemoryAttributes, b:MemoryAttributes, c:MemoryAttributes, d:MemoryAttributes => +function concat2(x: variadic(a, b, c, d)) -> memory(bytes) { + let res; + match x { + | .v2(a, b) => + let a_len = MemoryAttributes.len(a); + let b_len = MemoryAttributes.len(b); + let res: word = allocate_memory(32 + a_len + b_len); + mstore(res, a_len + b_len); + MemoryAttributes.encodeInto(a, res + 32); + MemoryAttributes.encodeInto(b, res + 32 + a_len); + | .v3(a, b, c) => + let a_len = MemoryAttributes.len(a); + let b_len = MemoryAttributes.len(b); + let c_len = MemoryAttributes.len(c); + let res: word = allocate_memory(32 + a_len + b_len + c_len); + mstore(res, a_len + b_len + c_len); + MemoryAttributes.encodeInto(a, res + 32); + MemoryAttributes.encodeInto(b, res + 32 + a_len); + MemoryAttributes.encodeInto(c, res + 32 + a_len + b_len); + | _ => revertEmpty(); + } + return memory(res); +} + forall t . class t:MemoryAttributes { // The size needed for the value. function len(v: t) -> word; @@ -355,6 +382,13 @@ contract DepositContract { } size = size / uint256(2); } + let count = to_little_endian_64(deposit_count); +// return sha256(concat2(variadic.v3(node, to_little_endian_64(deposit_count), empty(24)))); +let x = variadic.v3(node, count, empty(24)); +return bytes32(0); + //return sha256(concat2(variadic.v3(node, count, empty(24)))); + +/* return sha256(concat( concat( node, @@ -362,6 +396,7 @@ contract DepositContract { ), empty_bytes(24) )); +*/ } function get_deposit_count() -> memory(bytes) { @@ -406,16 +441,16 @@ contract DepositContract { } // Compute deposit data root (`DepositData` hash tree root) - let pubkey_root = sha256(concat(pubkey, empty_bytes(16))); + let pubkey_root = sha256(concat(pubkey, empty(16))); let signature_root = sha256(MemoryHelpers.concat( sha256(bytes_truncate(signature, 64)), // slice // sha256(concat(bytes_slice(signature, 64), empty_bytes(32))), // slice sha256(concat(bytes_slice(signature, 64), empty(32))) )); - let node = sha256(MemoryHelpers.concat( + let node = sha256(concat( // sha256(MemoryHelpers.concat(pubkey_root, withdrawal_credentials)), sha256(concat(pubkey_root, withdrawal_credentials)), - sha256(concat(MemoryHelpers.concat(amount, empty_bytes(24)), signature_root)) + sha256(concat(concat(amount, empty(24)), signature_root)) )); // Verify computed and expected deposit data roots match From 0d413db3fef866d9646323fb34a62e221c679262 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 1 Jun 2026 22:57:25 +0200 Subject: [PATCH 40/66] remove assert/require --- test/examples/dispatch/deposit.solc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index cea4b6feb..a01ed88c0 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -17,7 +17,7 @@ function callvalue() -> uint256 { return uint256(res); } -function assert(cond: bool) -> () { +function assert_(cond: bool) -> () { if (!cond) { assembly { invalid() @@ -25,7 +25,7 @@ function assert(cond: bool) -> () { } } -function require(cond: bool) -> () { +function require_(cond: bool) -> () { if (!cond) { assembly { revert(0, 0) From 14a2a8f4114029340003b6700281230e50836136 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 1 Jun 2026 22:57:29 +0200 Subject: [PATCH 41/66] Revert "Variadic" This reverts commit b202d67a7565a48eabefbe2c0cb67a89771187cb. --- test/examples/dispatch/deposit.solc | 41 +++-------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index a01ed88c0..570bf7ab3 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -148,33 +148,6 @@ function sha256(input: memory(bytes)) -> bytes32 { return bytes32(res); } -data variadic(a, b, c, d) = v0 | v1(a) | v2(a, b) | v3(a, b, c) | v4(a, b, c, d); - -forall a b c d . a:MemoryAttributes, b:MemoryAttributes, c:MemoryAttributes, d:MemoryAttributes => -function concat2(x: variadic(a, b, c, d)) -> memory(bytes) { - let res; - match x { - | .v2(a, b) => - let a_len = MemoryAttributes.len(a); - let b_len = MemoryAttributes.len(b); - let res: word = allocate_memory(32 + a_len + b_len); - mstore(res, a_len + b_len); - MemoryAttributes.encodeInto(a, res + 32); - MemoryAttributes.encodeInto(b, res + 32 + a_len); - | .v3(a, b, c) => - let a_len = MemoryAttributes.len(a); - let b_len = MemoryAttributes.len(b); - let c_len = MemoryAttributes.len(c); - let res: word = allocate_memory(32 + a_len + b_len + c_len); - mstore(res, a_len + b_len + c_len); - MemoryAttributes.encodeInto(a, res + 32); - MemoryAttributes.encodeInto(b, res + 32 + a_len); - MemoryAttributes.encodeInto(c, res + 32 + a_len + b_len); - | _ => revertEmpty(); - } - return memory(res); -} - forall t . class t:MemoryAttributes { // The size needed for the value. function len(v: t) -> word; @@ -382,13 +355,6 @@ contract DepositContract { } size = size / uint256(2); } - let count = to_little_endian_64(deposit_count); -// return sha256(concat2(variadic.v3(node, to_little_endian_64(deposit_count), empty(24)))); -let x = variadic.v3(node, count, empty(24)); -return bytes32(0); - //return sha256(concat2(variadic.v3(node, count, empty(24)))); - -/* return sha256(concat( concat( node, @@ -396,7 +362,6 @@ return bytes32(0); ), empty_bytes(24) )); -*/ } function get_deposit_count() -> memory(bytes) { @@ -441,16 +406,16 @@ return bytes32(0); } // Compute deposit data root (`DepositData` hash tree root) - let pubkey_root = sha256(concat(pubkey, empty(16))); + let pubkey_root = sha256(concat(pubkey, empty_bytes(16))); let signature_root = sha256(MemoryHelpers.concat( sha256(bytes_truncate(signature, 64)), // slice // sha256(concat(bytes_slice(signature, 64), empty_bytes(32))), // slice sha256(concat(bytes_slice(signature, 64), empty(32))) )); - let node = sha256(concat( + let node = sha256(MemoryHelpers.concat( // sha256(MemoryHelpers.concat(pubkey_root, withdrawal_credentials)), sha256(concat(pubkey_root, withdrawal_credentials)), - sha256(concat(concat(amount, empty(24)), signature_root)) + sha256(concat(MemoryHelpers.concat(amount, empty_bytes(24)), signature_root)) )); // Verify computed and expected deposit data roots match From 7e6362806eea131fc55e72ac90c5db827ed19fd3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 1 Jun 2026 23:06:51 +0200 Subject: [PATCH 42/66] rename to myrequire --- test/examples/dispatch/deposit.solc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 570bf7ab3..de4fe3973 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -25,7 +25,7 @@ function assert_(cond: bool) -> () { } } -function require_(cond: bool) -> () { +function myrequire(cond: bool) -> () { if (!cond) { assembly { revert(0, 0) @@ -370,16 +370,16 @@ contract DepositContract { function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. - require(MemoryHelpers.len(pubkey) == 48); - require(MemoryHelpers.len(withdrawal_credentials) == 32); - require(MemoryHelpers.len(signature) == 96); + myrequire(MemoryHelpers.len(pubkey) == 48); + myrequire(MemoryHelpers.len(withdrawal_credentials) == 32); + myrequire(MemoryHelpers.len(signature) == 96); // Check deposit amount - require(callvalue() >= uint256(1000000000000000000)); // >= 1 ether - require(callvalue() % uint256(1000000000) == uint256(0)); // % 1 gwei == 0 + myrequire(callvalue() >= uint256(1000000000000000000)); // >= 1 ether + myrequire(callvalue() % uint256(1000000000) == uint256(0)); // % 1 gwei == 0 let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei - require(deposit_amount <= uint256(0xffffffffffffffff)); // <= type(uint64).max + myrequire(deposit_amount <= uint256(0xffffffffffffffff)); // <= type(uint64).max let amount: memory(bytes) = to_little_endian_64(deposit_amount); // TODO: emit DepositEvent @@ -419,10 +419,10 @@ contract DepositContract { )); // Verify computed and expected deposit data roots match - require(node == deposit_data_root); + myrequire(node == deposit_data_root); // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) - require(deposit_count < MAX_DEPOSIT_COUNT()); + myrequire(deposit_count < MAX_DEPOSIT_COUNT()); // Add deposit data root to Merkle tree (update a single `branch` node) deposit_count += uint256(1); From 8d8930e9ba75076e85813c8d2f21ab025207fa6e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 2 Jun 2026 22:26:26 +0200 Subject: [PATCH 43/66] mark it payable --- test/examples/dispatch/deposit.solc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index de4fe3973..a7222f52d 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -368,7 +368,7 @@ contract DepositContract { return to_little_endian_64(deposit_count); } - function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { + payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. myrequire(MemoryHelpers.len(pubkey) == 48); myrequire(MemoryHelpers.len(withdrawal_credentials) == 32); From f6f1be4a32487111620ffa3228053e8dac5a111b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:26:00 +0200 Subject: [PATCH 44/66] Remove empty() (in std now) --- test/examples/dispatch/deposit.solc | 32 ----------------------------- 1 file changed, 32 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index a7222f52d..9830db801 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -203,38 +203,6 @@ forall a . a:MemoryAttributes => function to_bytes(x: a) -> memory(bytes) { return memory(res); } -// Placeholder for an empty memory area. -// The value is the size of the area in bytes. The area will be zeroed upon serialization. -// NOTE: not implementing Typedef by design. -data empty = empty(word); - -instance empty:MemoryAttributes { - function len(v: empty) -> word { - match v { - | empty(size) => return size; - } - } - function ptr(v: empty) -> word { - return 0; // TODO - } - function encodeInto(v: empty, target: word) -> () { - let size; - match v { - | empty(size_) => size = size_; - } - zeroize_memory(target, size); - } -} - -/* -data Concatenator = Concatenator(t)); - -forall t . class t:Concaternator { - function concat() -} -*/ - - forall t . class t:ToBytesLike { function to_bytes(value: t) -> memory(bytes); } From 5a612f0958e8154d823ffaee87844a6745c328ef Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:26:25 +0200 Subject: [PATCH 45/66] Remove MemoryAttributes (in std now) --- test/examples/dispatch/deposit.solc | 55 ----------------------------- 1 file changed, 55 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 9830db801..75ac5ac16 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -148,61 +148,6 @@ function sha256(input: memory(bytes)) -> bytes32 { return bytes32(res); } -forall t . class t:MemoryAttributes { - // The size needed for the value. - function len(v: t) -> word; - // If this is a non-memory type, memory is allocated first. - function ptr(v: t) -> word; - // Serialize the entire contents at a provided memory area. - function encodeInto(v: t, target: word) -> (); -} - -forall a b . a:MemoryAttributes, b:MemoryAttributes => function concat(x: a, y: b) -> memory(bytes) { - let x_len = MemoryAttributes.len(x); - let y_len = MemoryAttributes.len(y); - let res: word = allocate_memory(32 + x_len + y_len); - mstore(res, x_len + y_len); - MemoryAttributes.encodeInto(x, res + 32); - MemoryAttributes.encodeInto(y, res + 32 + x_len); - return memory(res); -} - -instance bytes32:MemoryAttributes { - function len(v: bytes32) -> word { - return 32; - } - function ptr(v: bytes32) -> word { - // TODO: use `allocate_bytes_array` + `encodeInto` - return Typedef.rep(to_bytes(v)); - } - function encodeInto(v: bytes32, target: word) -> () { - mstore(target, Typedef.rep(v)); - } -} - -instance memory(bytes):MemoryAttributes { - function len(v: memory(bytes)) -> word { - return mload(Typedef.rep(v)); - } - function ptr(v: memory(bytes)) -> word { - return Typedef.rep(v); - } - function encodeInto(v: memory(bytes), target: word) -> () { - let v_ = Typedef.rep(v); - assembly { - mcopy(target, add(v_, 32), mload(v_)) - } - } -} - -forall a . a:MemoryAttributes => function to_bytes(x: a) -> memory(bytes) { - let len = MemoryAttributes.len(x); - let res = allocate_memory(32 + len); - mstore(res, len); - MemoryAttributes.encodeInto(x, res + 32); - return memory(res); -} - forall t . class t:ToBytesLike { function to_bytes(value: t) -> memory(bytes); } From fa9c642eccb2f419bab59c209a8063d3c2e15350 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:26:44 +0200 Subject: [PATCH 46/66] Remove sha256 (in std now) --- test/examples/dispatch/deposit.solc | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 75ac5ac16..290f0229e 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -132,22 +132,6 @@ instance bytes32:MemoryHelpers { } } -// This calls the SHA256 precompile. -function sha256(input: memory(bytes)) -> bytes32 { - // TODO: `ptr()` / `len()` helpers would be useful - let ptr : word = Typedef.rep(input); - let res : word; - // We assume the [0, 32] scratch space is reserved. - assembly { - let ret := staticcall(gas(), 2, add(ptr, 32), mload(ptr), 0, 32) - if iszero(ret) { - revert(0, 0) - } - res := mload(0) - } - return bytes32(res); -} - forall t . class t:ToBytesLike { function to_bytes(value: t) -> memory(bytes); } From e583718eaff6750ca0ace9437437a7a38cc8e547 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:29:31 +0200 Subject: [PATCH 47/66] Use MemoryAttributes over MemoryHelpers --- test/examples/dispatch/deposit.solc | 92 +++-------------------------- 1 file changed, 9 insertions(+), 83 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 290f0229e..9e3dec46c 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -33,40 +33,6 @@ function myrequire(cond: bool) -> () { } } -forall T . class T:MemoryHelpers { - function ptr(a: T) -> word; - function len(a: T) -> word; - function concat(a: T, b: T) -> memory(bytes); -} - -instance memory(bytes):MemoryHelpers { - function ptr(a: memory(bytes)) -> word { - let rep = Typedef.rep(a); - return rep + 32; - } - - function len(a: memory(bytes)) -> word { - let rep = Typedef.rep(a); - return mload(rep); - } - - function concat(a: memory(bytes), b: memory(bytes)) -> memory(bytes) { - let a_ptr = MemoryHelpers.ptr(a); - let a_len = MemoryHelpers.len(a); - let b_ptr = MemoryHelpers.ptr(b); - let b_len = MemoryHelpers.len(b); - - let len = a_len + b_len; - let res: word = allocate_memory(32 + len); - assembly { - mstore(res, len) - mcopy(add(res, 32), a_ptr, a_len) - mcopy(add(add(res, 32), a_len), b_ptr, b_len) - } - return memory(res); - } -} - function zeroize_memory(ptr: word, len: word) -> () { assembly { let end_ptr := add(ptr, len) @@ -92,46 +58,6 @@ function empty_bytes(len: word) -> memory(bytes) { return memory(res); } -instance uint256:MemoryHelpers { - function ptr(a: uint256) -> word { - return 0; - } - function len(a: uint256) -> word { - return 0; - } - function concat(a: uint256, b: uint256) -> memory(bytes) { - let res: word = allocate_memory(96); - let a_val = Typedef.rep(a); - let b_val = Typedef.rep(b); - assembly { - mstore(res, 64) - mstore(add(res, 32), a_val) - mstore(add(res, 64), b_val) - } - return memory(res); - } -} - -instance bytes32:MemoryHelpers { - function ptr(a: bytes32) -> word { - return 0; - } - function len(a: bytes32) -> word { - return 0; - } - function concat(a: bytes32, b: bytes32) -> memory(bytes) { - let res: word = allocate_memory(96); - let a_val = Typedef.rep(a); - let b_val = Typedef.rep(b); - assembly { - mstore(res, 64) - mstore(add(res, 32), a_val) - mstore(add(res, 64), b_val) - } - return memory(res); - } -} - forall t . class t:ToBytesLike { function to_bytes(value: t) -> memory(bytes); } @@ -154,7 +80,7 @@ function to_bytes2(v: bytes32) -> memory(bytes) { // TODO: use slice() function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { - let len = MemoryHelpers.len(input); + let len = MemoryAttributes.len(input); if (len < start) { assembly { revert(0, 0) @@ -162,8 +88,8 @@ function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { } len = len - start; let res = empty_bytes(len); - let src_ptr = MemoryHelpers.ptr(input); - let dst_ptr = MemoryHelpers.ptr(res); + let src_ptr = MemoryAttributes.ptr(input); + let dst_ptr = MemoryAttributes.ptr(res); assembly { mcopy(dst_ptr, add(src_ptr, start), len) } @@ -172,15 +98,15 @@ function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { // TODO: use slice() function bytes_truncate(input: memory(bytes), end: word) -> memory(bytes) { - let len = MemoryHelpers.len(input); + let len = MemoryAttributes.len(input); if (len < end) { assembly { revert(0, 0) } } let res = empty_bytes(end); - let src_ptr = MemoryHelpers.ptr(input); - let dst_ptr = MemoryHelpers.ptr(res); + let src_ptr = MemoryAttributes.ptr(input); + let dst_ptr = MemoryAttributes.ptr(res); assembly { mcopy(dst_ptr, src_ptr, end) } @@ -267,9 +193,9 @@ contract DepositContract { payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. - myrequire(MemoryHelpers.len(pubkey) == 48); - myrequire(MemoryHelpers.len(withdrawal_credentials) == 32); - myrequire(MemoryHelpers.len(signature) == 96); + myrequire(MemoryAttributes.len(pubkey) == 48); + myrequire(MemoryAttributes.len(withdrawal_credentials) == 32); + myrequire(MemoryAttributes.len(signature) == 96); // Check deposit amount myrequire(callvalue() >= uint256(1000000000000000000)); // >= 1 ether From 9632a53ab11a9b98ebe71533e2102883ba39a867 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:34:06 +0200 Subject: [PATCH 48/66] Use concat/bytes from std --- test/examples/dispatch/deposit.solc | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 9e3dec46c..7445d54cb 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -156,7 +156,6 @@ contract DepositContract { constructor() { // Compute hashes in empty sparse Merkle tree for (let height = uint256(0); height < (DEPOSIT_CONTRACT_TREE_DEPTH() - uint256(1)); height = height + uint256(1)) { -// zero_hashes[height + uint256(1)] = sha256(MemoryHelpers.concat(zero_hashes[height], zero_hashes[height])); zero_hashes[height + uint256(1)] = sha256(concat(zero_hashes[height], zero_hashes[height])); } } @@ -183,7 +182,7 @@ contract DepositContract { node, to_little_endian_64(deposit_count) ), - empty_bytes(24) + empty(24) )); } @@ -229,16 +228,14 @@ contract DepositContract { } // Compute deposit data root (`DepositData` hash tree root) - let pubkey_root = sha256(concat(pubkey, empty_bytes(16))); - let signature_root = sha256(MemoryHelpers.concat( + let pubkey_root = sha256(concat(pubkey, empty(16))); + let signature_root = sha256(concat( sha256(bytes_truncate(signature, 64)), // slice -// sha256(concat(bytes_slice(signature, 64), empty_bytes(32))), // slice sha256(concat(bytes_slice(signature, 64), empty(32))) )); - let node = sha256(MemoryHelpers.concat( -// sha256(MemoryHelpers.concat(pubkey_root, withdrawal_credentials)), + let node = sha256(concat( sha256(concat(pubkey_root, withdrawal_credentials)), - sha256(concat(MemoryHelpers.concat(amount, empty_bytes(24)), signature_root)) + sha256(concat(concat(amount, empty(24)), signature_root)) )); // Verify computed and expected deposit data roots match @@ -256,7 +253,7 @@ contract DepositContract { branch[height] = node; return (); } - node = sha256(MemoryHelpers.concat(branch[height], node)); + node = sha256(concat(branch[height], node)); size = size / uint256(2); } From 7bfeab5174b58da24a620afff729c5c96faa68e1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:39:45 +0200 Subject: [PATCH 49/66] Use zeroize_memory from std --- test/examples/dispatch/deposit.solc | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 7445d54cb..1eaf01b14 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -33,24 +33,6 @@ function myrequire(cond: bool) -> () { } } -function zeroize_memory(ptr: word, len: word) -> () { - assembly { - let end_ptr := add(ptr, len) - - // Zero out 32-byte words - let words := div(len, 32) - for { let i := 0 } lt(i, words) { i := add(i, 1) } { - mstore(ptr, 0) - ptr := add(ptr, 32) - } - - // Zero out trailing bytes - for { } lt(ptr, end_ptr) { ptr := add(ptr, 1) } { - mstore8(ptr, 0) - } - } -} - function empty_bytes(len: word) -> memory(bytes) { let res: word = allocate_memory(32 + len); mstore(res, len); From 7e9d75241c28ba79b40d3d7ef5d80f308a303259 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:40:08 +0200 Subject: [PATCH 50/66] Drop obsolete to_bytes --- test/examples/dispatch/deposit.solc | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 1eaf01b14..61321ec1f 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -40,26 +40,6 @@ function empty_bytes(len: word) -> memory(bytes) { return memory(res); } -forall t . class t:ToBytesLike { - function to_bytes(value: t) -> memory(bytes); -} - -instance bytes32:ToBytesLike { - function to_bytes(value: bytes32) -> memory(bytes) { - let res: word = allocate_memory(64); - mstore(res, 32); - mstore(res + 32, Typedef.rep(value)); - return memory(res); - } -} - -function to_bytes2(v: bytes32) -> memory(bytes) { - let res: word = allocate_memory(64); - mstore(res, 32); - mstore(res + 32, Typedef.rep(v)); - return memory(res); -} - // TODO: use slice() function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { let len = MemoryAttributes.len(input); From 338e12d14abe2f8efbea70cb6e6f72ea79df8ef1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:43:45 +0200 Subject: [PATCH 51/66] Use callvalue from opcodes --- test/examples/dispatch/deposit.solc | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 61321ec1f..a5c6ff24d 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -1,21 +1,6 @@ import std.{*}; import std.dispatch.{*}; - -function caller() -> address { - let res: word; - assembly { - res := caller() - } - return address(res); -} - -function callvalue() -> uint256 { - let res : word; - assembly { - res := callvalue() - } - return uint256(res); -} +import std.opcodes.{callvalue}; function assert_(cond: bool) -> () { if (!cond) { @@ -159,13 +144,13 @@ contract DepositContract { myrequire(MemoryAttributes.len(signature) == 96); // Check deposit amount - myrequire(callvalue() >= uint256(1000000000000000000)); // >= 1 ether - myrequire(callvalue() % uint256(1000000000) == uint256(0)); // % 1 gwei == 0 + myrequire(callvalue() >= 1000000000000000000); // >= 1 ether + myrequire((callvalue() % 1000000000) == 0); // % 1 gwei == 0 - let deposit_amount = callvalue() / uint256(1000000000); // 1 gwei - myrequire(deposit_amount <= uint256(0xffffffffffffffff)); // <= type(uint64).max + let deposit_amount = callvalue() / 1000000000; // 1 gwei + myrequire(deposit_amount <= 0xffffffffffffffff); // <= type(uint64).max - let amount: memory(bytes) = to_little_endian_64(deposit_amount); + let amount: memory(bytes) = to_little_endian_64(uint256(deposit_amount)); // TODO: emit DepositEvent /* event DepositEvent( From 8267b3ba05d6174a39b0b0f271a1fcac585cc4f0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:44:10 +0200 Subject: [PATCH 52/66] Drop unused assert --- test/examples/dispatch/deposit.solc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index a5c6ff24d..b76c21789 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -2,14 +2,6 @@ import std.{*}; import std.dispatch.{*}; import std.opcodes.{callvalue}; -function assert_(cond: bool) -> () { - if (!cond) { - assembly { - invalid() - } - } -} - function myrequire(cond: bool) -> () { if (!cond) { assembly { From f39233891ca2d293bb7952c6bac29a5a0b782f2f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:47:48 +0200 Subject: [PATCH 53/66] Use usptream require --- test/examples/dispatch/deposit.solc | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index b76c21789..f15ac755e 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -2,14 +2,6 @@ import std.{*}; import std.dispatch.{*}; import std.opcodes.{callvalue}; -function myrequire(cond: bool) -> () { - if (!cond) { - assembly { - revert(0, 0) - } - } -} - function empty_bytes(len: word) -> memory(bytes) { let res: word = allocate_memory(32 + len); mstore(res, len); @@ -131,16 +123,16 @@ contract DepositContract { payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. - myrequire(MemoryAttributes.len(pubkey) == 48); - myrequire(MemoryAttributes.len(withdrawal_credentials) == 32); - myrequire(MemoryAttributes.len(signature) == 96); + require(MemoryAttributes.len(pubkey) == 48, Error(0x12345678)); + require(MemoryAttributes.len(withdrawal_credentials) == 32, Error(0x12345678)); + require(MemoryAttributes.len(signature) == 96, Error(0x12345678)); // Check deposit amount - myrequire(callvalue() >= 1000000000000000000); // >= 1 ether - myrequire((callvalue() % 1000000000) == 0); // % 1 gwei == 0 + require(callvalue() >= 1000000000000000000, Error(0x12345678)); // >= 1 ether + require((callvalue() % 1000000000) == 0, Error(0x12345678)); // % 1 gwei == 0 let deposit_amount = callvalue() / 1000000000; // 1 gwei - myrequire(deposit_amount <= 0xffffffffffffffff); // <= type(uint64).max + require(deposit_amount <= 0xffffffffffffffff, Error(0x12345678)); // <= type(uint64).max let amount: memory(bytes) = to_little_endian_64(uint256(deposit_amount)); // TODO: emit DepositEvent @@ -178,10 +170,10 @@ contract DepositContract { )); // Verify computed and expected deposit data roots match - myrequire(node == deposit_data_root); + require(node == deposit_data_root, Error(0x12345678)); // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) - myrequire(deposit_count < MAX_DEPOSIT_COUNT()); + require(deposit_count < MAX_DEPOSIT_COUNT(), Error(0x12345678)); // Add deposit data root to Merkle tree (update a single `branch` node) deposit_count += uint256(1); From 53b283f059fbd2af55243f4bb236678b28fee923 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:55:49 +0200 Subject: [PATCH 54/66] Use new split --- test/examples/dispatch/deposit.solc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index f15ac755e..5e263f12e 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -11,7 +11,7 @@ function empty_bytes(len: word) -> memory(bytes) { // TODO: use slice() function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { - let len = MemoryAttributes.len(input); + let len = MemorySize.len(input); if (len < start) { assembly { revert(0, 0) @@ -19,8 +19,8 @@ function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { } len = len - start; let res = empty_bytes(len); - let src_ptr = MemoryAttributes.ptr(input); - let dst_ptr = MemoryAttributes.ptr(res); + let src_ptr = MemoryPointer.ptr(input); + let dst_ptr = MemoryPointer.ptr(res); assembly { mcopy(dst_ptr, add(src_ptr, start), len) } @@ -29,15 +29,15 @@ function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { // TODO: use slice() function bytes_truncate(input: memory(bytes), end: word) -> memory(bytes) { - let len = MemoryAttributes.len(input); + let len = MemorySize.len(input); if (len < end) { assembly { revert(0, 0) } } let res = empty_bytes(end); - let src_ptr = MemoryAttributes.ptr(input); - let dst_ptr = MemoryAttributes.ptr(res); + let src_ptr = MemoryPointer.ptr(input); + let dst_ptr = MemoryPointer.ptr(res); assembly { mcopy(dst_ptr, src_ptr, end) } @@ -123,9 +123,9 @@ contract DepositContract { payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. - require(MemoryAttributes.len(pubkey) == 48, Error(0x12345678)); - require(MemoryAttributes.len(withdrawal_credentials) == 32, Error(0x12345678)); - require(MemoryAttributes.len(signature) == 96, Error(0x12345678)); + require(MemorySize.len(pubkey) == 48, Error(0x12345678)); + require(MemorySize.len(withdrawal_credentials) == 32, Error(0x12345678)); + require(MemorySize.len(signature) == 96, Error(0x12345678)); // Check deposit amount require(callvalue() >= 1000000000000000000, Error(0x12345678)); // >= 1 ether From ebfca97a7a9c492b5d93393ab701d5fcf588079d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 22:59:02 +0200 Subject: [PATCH 55/66] Fix test --- test/examples/dispatch/deposit.json | 2 +- test/examples/dispatch/deposit.solc | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index 017692750..50da9b435 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -179,7 +179,7 @@ }, "kind": "call", "output": { - "returndata": "", + "returndata": "42345678", "status": "failure" } }, diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 5e263f12e..f6fb792ff 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -124,15 +124,15 @@ contract DepositContract { payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. require(MemorySize.len(pubkey) == 48, Error(0x12345678)); - require(MemorySize.len(withdrawal_credentials) == 32, Error(0x12345678)); - require(MemorySize.len(signature) == 96, Error(0x12345678)); + require(MemorySize.len(withdrawal_credentials) == 32, Error(0x22345678)); + require(MemorySize.len(signature) == 96, Error(0x32345678)); // Check deposit amount - require(callvalue() >= 1000000000000000000, Error(0x12345678)); // >= 1 ether - require((callvalue() % 1000000000) == 0, Error(0x12345678)); // % 1 gwei == 0 + require(callvalue() >= 1000000000000000000, Error(0x42345678)); // >= 1 ether + require((callvalue() % 1000000000) == 0, Error(0x52345678)); // % 1 gwei == 0 let deposit_amount = callvalue() / 1000000000; // 1 gwei - require(deposit_amount <= 0xffffffffffffffff, Error(0x12345678)); // <= type(uint64).max + require(deposit_amount <= 0xffffffffffffffff, Error(0x62345678)); // <= type(uint64).max let amount: memory(bytes) = to_little_endian_64(uint256(deposit_amount)); // TODO: emit DepositEvent From 63aa0c8a4d8fe521c3fae6fea7b8cef421fa3097 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 23:25:29 +0200 Subject: [PATCH 56/66] Add bytes_slice abstraction --- test/examples/dispatch/deposit.solc | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index f6fb792ff..eed3c8754 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -1,6 +1,33 @@ import std.{*}; import std.dispatch.{*}; -import std.opcodes.{callvalue}; +import std.opcodes.{callvalue, mcopy}; + +data bytes_slice = bytes_slice(memory(bytes), word, word); + +instance bytes_slice:MemorySize { + function len(v: bytes_slice) -> word { + match v { + | bytes_slice(ptr, start, len) => return len; + } + } +} + +instance bytes_slice:MemoryPointer { + function ptr(v: bytes_slice) -> word { + match v { + | bytes_slice(ptr_, start, len) => return MemoryPointer.ptr(ptr_) + start; + } + } +} + +instance bytes_slice:MemoryEncode { + function encodeInto(v: bytes_slice, target: word) -> () { + match v { + | bytes_slice(ptr_, start, len) => + mcopy(target, MemoryPointer.ptr(ptr_) + start, len); + } + } +} function empty_bytes(len: word) -> memory(bytes) { let res: word = allocate_memory(32 + len); From db9c81f1503fc632296c5804c64ec6ec76aa962d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 9 Jun 2026 23:30:49 +0200 Subject: [PATCH 57/66] Rwerite bytes_slice/bytes_truncate with new abstraction --- test/examples/dispatch/deposit.solc | 34 +++++------------------------ 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index eed3c8754..781b635c3 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -29,46 +29,24 @@ instance bytes_slice:MemoryEncode { } } -function empty_bytes(len: word) -> memory(bytes) { - let res: word = allocate_memory(32 + len); - mstore(res, len); - zeroize_memory(res + 32, len); - return memory(res); -} - -// TODO: use slice() -function bytes_slice(input: memory(bytes), start: word) -> memory(bytes) { +function bytes_slice_(input: memory(bytes), start: word) -> bytes_slice { let len = MemorySize.len(input); if (len < start) { assembly { revert(0, 0) } } - len = len - start; - let res = empty_bytes(len); - let src_ptr = MemoryPointer.ptr(input); - let dst_ptr = MemoryPointer.ptr(res); - assembly { - mcopy(dst_ptr, add(src_ptr, start), len) - } - return res; + return bytes_slice(input, start, len - start); } -// TODO: use slice() -function bytes_truncate(input: memory(bytes), end: word) -> memory(bytes) { +function bytes_truncate(input: memory(bytes), end: word) -> bytes_slice { let len = MemorySize.len(input); if (len < end) { assembly { revert(0, 0) } } - let res = empty_bytes(end); - let src_ptr = MemoryPointer.ptr(input); - let dst_ptr = MemoryPointer.ptr(res); - assembly { - mcopy(dst_ptr, src_ptr, end) - } - return res; + return bytes_slice(input, 0, end); } // Should use uint64 and bytes. @@ -188,8 +166,8 @@ contract DepositContract { // Compute deposit data root (`DepositData` hash tree root) let pubkey_root = sha256(concat(pubkey, empty(16))); let signature_root = sha256(concat( - sha256(bytes_truncate(signature, 64)), // slice - sha256(concat(bytes_slice(signature, 64), empty(32))) + sha256(to_bytes(bytes_truncate(signature, 64))), // slice + sha256(concat(bytes_slice_(signature, 64), empty(32))) )); let node = sha256(concat( sha256(concat(pubkey_root, withdrawal_credentials)), From 233912835fb4487342653c0d5bd3ffa8efb7bfa4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 10 Jun 2026 00:13:25 +0200 Subject: [PATCH 58/66] Sliceable abstraction --- test/examples/dispatch/deposit.solc | 61 ++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 781b635c3..c530799d8 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -29,24 +29,57 @@ instance bytes_slice:MemoryEncode { } } -function bytes_slice_(input: memory(bytes), start: word) -> bytes_slice { - let len = MemorySize.len(input); - if (len < start) { - assembly { - revert(0, 0) +forall t . class t:Sliceable { + function slice(v: t, start: word) -> bytes_slice; + function truncate(v: t, end: word) -> bytes_slice; +} + +instance memory(bytes):Sliceable { + function slice(input: memory(bytes), start: word) -> bytes_slice { + let len = MemorySize.len(input); + if (len < start) { + assembly { + revert(0, 0) + } + } + return bytes_slice(input, start, len - start); + } + + function truncate(input: memory(bytes), end: word) -> bytes_slice { + let len = MemorySize.len(input); + if (len < end) { + assembly { + revert(0, 0) + } } + return bytes_slice(input, 0, end); } - return bytes_slice(input, start, len - start); } -function bytes_truncate(input: memory(bytes), end: word) -> bytes_slice { - let len = MemorySize.len(input); - if (len < end) { - assembly { - revert(0, 0) +instance bytes_slice:Sliceable { + function slice(input: bytes_slice, start: word) -> bytes_slice { + match input { + | bytes_slice(ptr, start_, len) => + if (len < start) { + assembly { + revert(0, 0) + } + } + return bytes_slice(ptr, start_ + start, len - start); + } + } + + function truncate(input: bytes_slice, end: word) -> bytes_slice { + match input { + | bytes_slice(ptr, start, len) => + if (len < end) { + assembly { + revert(0, 0) + } + } + return bytes_slice(ptr, start, end); } } - return bytes_slice(input, 0, end); } // Should use uint64 and bytes. @@ -166,8 +199,8 @@ contract DepositContract { // Compute deposit data root (`DepositData` hash tree root) let pubkey_root = sha256(concat(pubkey, empty(16))); let signature_root = sha256(concat( - sha256(to_bytes(bytes_truncate(signature, 64))), // slice - sha256(concat(bytes_slice_(signature, 64), empty(32))) + sha256(to_bytes(Sliceable.truncate(signature, 64))), // slice + sha256(concat(Sliceable.slice(signature, 64), empty(32))) )); let node = sha256(concat( sha256(concat(pubkey_root, withdrawal_credentials)), From f68845fc9ffff52fd743d3cb58a78f138e1f4cc6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 10 Jun 2026 00:25:47 +0200 Subject: [PATCH 59/66] Make sha256 generic --- test/examples/dispatch/deposit.solc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index c530799d8..13cb580dc 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -199,7 +199,7 @@ contract DepositContract { // Compute deposit data root (`DepositData` hash tree root) let pubkey_root = sha256(concat(pubkey, empty(16))); let signature_root = sha256(concat( - sha256(to_bytes(Sliceable.truncate(signature, 64))), // slice + sha256(Sliceable.truncate(signature, 64)), // slice sha256(concat(Sliceable.slice(signature, 64), empty(32))) )); let node = sha256(concat( From b979cd02e60d21c8c9bc2666f7a3cdd6235ec0b1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 10 Jun 2026 00:55:02 +0200 Subject: [PATCH 60/66] Greatly simplify bytes_slice --- test/examples/dispatch/deposit.solc | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 13cb580dc..a0cf02c9a 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -2,12 +2,12 @@ import std.{*}; import std.dispatch.{*}; import std.opcodes.{callvalue, mcopy}; -data bytes_slice = bytes_slice(memory(bytes), word, word); +data bytes_slice = bytes_slice(word, word); instance bytes_slice:MemorySize { function len(v: bytes_slice) -> word { match v { - | bytes_slice(ptr, start, len) => return len; + | bytes_slice(ptr, len) => return len; } } } @@ -15,7 +15,7 @@ instance bytes_slice:MemorySize { instance bytes_slice:MemoryPointer { function ptr(v: bytes_slice) -> word { match v { - | bytes_slice(ptr_, start, len) => return MemoryPointer.ptr(ptr_) + start; + | bytes_slice(ptr, len) => return ptr; } } } @@ -23,8 +23,8 @@ instance bytes_slice:MemoryPointer { instance bytes_slice:MemoryEncode { function encodeInto(v: bytes_slice, target: word) -> () { match v { - | bytes_slice(ptr_, start, len) => - mcopy(target, MemoryPointer.ptr(ptr_) + start, len); + | bytes_slice(ptr, len) => + mcopy(target, ptr, len); } } } @@ -42,7 +42,8 @@ instance memory(bytes):Sliceable { revert(0, 0) } } - return bytes_slice(input, start, len - start); + let ptr = MemoryPointer.ptr(input); + return bytes_slice(ptr + start, len - start); } function truncate(input: memory(bytes), end: word) -> bytes_slice { @@ -52,32 +53,33 @@ instance memory(bytes):Sliceable { revert(0, 0) } } - return bytes_slice(input, 0, end); + let ptr = MemoryPointer.ptr(input); + return bytes_slice(ptr, end); } } instance bytes_slice:Sliceable { function slice(input: bytes_slice, start: word) -> bytes_slice { match input { - | bytes_slice(ptr, start_, len) => + | bytes_slice(ptr, len) => if (len < start) { assembly { revert(0, 0) } } - return bytes_slice(ptr, start_ + start, len - start); + return bytes_slice(ptr + start, len - start); } } function truncate(input: bytes_slice, end: word) -> bytes_slice { match input { - | bytes_slice(ptr, start, len) => + | bytes_slice(ptr, len) => if (len < end) { assembly { revert(0, 0) } } - return bytes_slice(ptr, start, end); + return bytes_slice(ptr, end); } } } From 84aae04362897e00ffc9705675bcee9088bbdbd2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 10 Jun 2026 01:07:54 +0200 Subject: [PATCH 61/66] Generalize even further --- test/examples/dispatch/deposit.solc | 66 ++++++++--------------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index a0cf02c9a..554782644 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -29,59 +29,27 @@ instance bytes_slice:MemoryEncode { } } -forall t . class t:Sliceable { - function slice(v: t, start: word) -> bytes_slice; - function truncate(v: t, end: word) -> bytes_slice; -} - -instance memory(bytes):Sliceable { - function slice(input: memory(bytes), start: word) -> bytes_slice { - let len = MemorySize.len(input); - if (len < start) { - assembly { - revert(0, 0) - } - } - let ptr = MemoryPointer.ptr(input); - return bytes_slice(ptr + start, len - start); - } - - function truncate(input: memory(bytes), end: word) -> bytes_slice { - let len = MemorySize.len(input); - if (len < end) { - assembly { - revert(0, 0) - } +forall a . a:MemorySize, a:MemoryPointer => +function slice_(input: a, start: word) -> bytes_slice { + let len = MemorySize.len(input); + if (len < start) { + assembly { + revert(0, 0) } - let ptr = MemoryPointer.ptr(input); - return bytes_slice(ptr, end); } + let ptr_ = MemoryPointer.ptr(input); + return bytes_slice(ptr_ + start, len - start); } -instance bytes_slice:Sliceable { - function slice(input: bytes_slice, start: word) -> bytes_slice { - match input { - | bytes_slice(ptr, len) => - if (len < start) { - assembly { - revert(0, 0) - } - } - return bytes_slice(ptr + start, len - start); - } - } - - function truncate(input: bytes_slice, end: word) -> bytes_slice { - match input { - | bytes_slice(ptr, len) => - if (len < end) { - assembly { - revert(0, 0) - } - } - return bytes_slice(ptr, end); +forall a . a:MemorySize, a:MemoryPointer => +function truncate(input: a, end: word) -> bytes_slice { + let len = MemorySize.len(input); + if (len < end) { + assembly { + revert(0, 0) } } + return bytes_slice(MemoryPointer.ptr(input), end); } // Should use uint64 and bytes. @@ -201,8 +169,8 @@ contract DepositContract { // Compute deposit data root (`DepositData` hash tree root) let pubkey_root = sha256(concat(pubkey, empty(16))); let signature_root = sha256(concat( - sha256(Sliceable.truncate(signature, 64)), // slice - sha256(concat(Sliceable.slice(signature, 64), empty(32))) + sha256(truncate(signature, 64)), // slice + sha256(concat(slice_(signature, 64), empty(32))) )); let node = sha256(concat( sha256(concat(pubkey_root, withdrawal_credentials)), From 87ed26357bd663d678213f394b31ca0fe277587e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 12 Jun 2026 14:17:26 +0200 Subject: [PATCH 62/66] Use upstream slice_/truncate --- test/examples/dispatch/deposit.solc | 52 +---------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 554782644..12adf23d1 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -1,56 +1,6 @@ import std.{*}; import std.dispatch.{*}; -import std.opcodes.{callvalue, mcopy}; - -data bytes_slice = bytes_slice(word, word); - -instance bytes_slice:MemorySize { - function len(v: bytes_slice) -> word { - match v { - | bytes_slice(ptr, len) => return len; - } - } -} - -instance bytes_slice:MemoryPointer { - function ptr(v: bytes_slice) -> word { - match v { - | bytes_slice(ptr, len) => return ptr; - } - } -} - -instance bytes_slice:MemoryEncode { - function encodeInto(v: bytes_slice, target: word) -> () { - match v { - | bytes_slice(ptr, len) => - mcopy(target, ptr, len); - } - } -} - -forall a . a:MemorySize, a:MemoryPointer => -function slice_(input: a, start: word) -> bytes_slice { - let len = MemorySize.len(input); - if (len < start) { - assembly { - revert(0, 0) - } - } - let ptr_ = MemoryPointer.ptr(input); - return bytes_slice(ptr_ + start, len - start); -} - -forall a . a:MemorySize, a:MemoryPointer => -function truncate(input: a, end: word) -> bytes_slice { - let len = MemorySize.len(input); - if (len < end) { - assembly { - revert(0, 0) - } - } - return bytes_slice(MemoryPointer.ptr(input), end); -} +import std.opcodes.{callvalue}; // Should use uint64 and bytes. // Assumes 64-bit input. From b239676846f3d3dd12d8bccf8c68e4a5fcc21277 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 12 Jun 2026 14:19:08 +0200 Subject: [PATCH 63/66] Comments --- test/examples/dispatch/deposit.solc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 12adf23d1..baef2deca 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -2,7 +2,7 @@ import std.{*}; import std.dispatch.{*}; import std.opcodes.{callvalue}; -// Should use uint64 and bytes. +// TODO: Should use uint64. // Assumes 64-bit input. function to_little_endian_64(v: uint256) -> memory(bytes) { let res: word = allocate_memory(32 + 8); @@ -29,7 +29,7 @@ function DEPOSIT_CONTRACT_TREE_DEPTH() -> uint256 { function MAX_DEPOSIT_COUNT() -> uint256 { // uint constant MAX_DEPOSIT_COUNT = 2**DEPOSIT_CONTRACT_TREE_DEPTH - 1; - // Could use Bounded(uint256).maxVal() + // Could use Bounded(uint64).maxVal() return uint256(0xFFFFFFFF); } From 5ff1a1085f427cc7536bc539899d3bac33cda6c0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 12 Jun 2026 14:27:43 +0200 Subject: [PATCH 64/66] Use proper error codes --- test/examples/dispatch/deposit.json | 2 +- test/examples/dispatch/deposit.solc | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/test/examples/dispatch/deposit.json b/test/examples/dispatch/deposit.json index 50da9b435..137c5e2aa 100644 --- a/test/examples/dispatch/deposit.json +++ b/test/examples/dispatch/deposit.json @@ -179,7 +179,7 @@ }, "kind": "call", "output": { - "returndata": "42345678", + "returndata": "bdfc7472", "status": "failure" } }, diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index baef2deca..7535d90a1 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -79,18 +79,23 @@ contract DepositContract { return to_little_endian_64(deposit_count); } + // TODO: once string literals are properly supported, change errors to messages + // matching the deposit contract, full 100% identical behaviour. payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. - require(MemorySize.len(pubkey) == 48, Error(0x12345678)); - require(MemorySize.len(withdrawal_credentials) == 32, Error(0x22345678)); - require(MemorySize.len(signature) == 96, Error(0x32345678)); + require(MemorySize.len(pubkey) == 48, Error(0x9ca717ed)); // InvalidPubkeyLength() + require(MemorySize.len(withdrawal_credentials) == 32, Error(0x3debbf1e)); // InvalidWithdrawalCredentialsLength() + require(MemorySize.len(signature) == 96, Error(0x4be6321b)); // InvalidSignatureLength() // Check deposit amount - require(callvalue() >= 1000000000000000000, Error(0x42345678)); // >= 1 ether - require((callvalue() % 1000000000) == 0, Error(0x52345678)); // % 1 gwei == 0 + // >= 1ether + require(callvalue() >= 1000000000000000000, Error(0xbdfc7472)); // DepositValueTooLow() + // % 1 gwei == 0 + require((callvalue() % 1000000000) == 0, Error(0x9c7417e9)); // DepositValueNotMultipleOfOneGwei() let deposit_amount = callvalue() / 1000000000; // 1 gwei - require(deposit_amount <= 0xffffffffffffffff, Error(0x62345678)); // <= type(uint64).max + // <= type(uint64).max + require(deposit_amount <= 0xffffffffffffffff, Error(0x2aa66734)); // DepositValueTooHigh() let amount: memory(bytes) = to_little_endian_64(uint256(deposit_amount)); // TODO: emit DepositEvent @@ -128,10 +133,10 @@ contract DepositContract { )); // Verify computed and expected deposit data roots match - require(node == deposit_data_root, Error(0x12345678)); + require(node == deposit_data_root, Error(0x2ec2f183)); // ReconstructedDepositDataMismatch() // Avoid overflowing the Merkle tree (and prevent edge case in computing `branch`) - require(deposit_count < MAX_DEPOSIT_COUNT(), Error(0x12345678)); + require(deposit_count < MAX_DEPOSIT_COUNT(), Error(0xef5ccf66)); // MerkleTreeFull() // Add deposit data root to Merkle tree (update a single `branch` node) deposit_count += uint256(1); From e5362a542570018e31b7bc00256a353e40a7da46 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 12 Jun 2026 14:32:21 +0200 Subject: [PATCH 65/66] Use += --- test/examples/dispatch/deposit.solc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 7535d90a1..867934378 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -44,7 +44,7 @@ contract DepositContract { constructor() { // Compute hashes in empty sparse Merkle tree - for (let height = uint256(0); height < (DEPOSIT_CONTRACT_TREE_DEPTH() - uint256(1)); height = height + uint256(1)) { + for (let height = uint256(0); height < (DEPOSIT_CONTRACT_TREE_DEPTH() - uint256(1)); height += uint256(1)) { zero_hashes[height + uint256(1)] = sha256(concat(zero_hashes[height], zero_hashes[height])); } } @@ -57,7 +57,7 @@ contract DepositContract { function get_deposit_root() -> bytes32 { let node: bytes32; let size = deposit_count; - for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { + for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height += uint256(1)) { //if ((size & 1) == 1) { if ((size % uint256(2)) == uint256(1)) { node = sha256(concat(branch[height], node)); @@ -141,7 +141,7 @@ contract DepositContract { // Add deposit data root to Merkle tree (update a single `branch` node) deposit_count += uint256(1); let size = deposit_count; - for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height = height + uint256(1)) { + for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height += uint256(1)) { //if ((size & 1) == 1) { if ((size % uint256(2)) == uint256(1)) { branch[height] = node; From cab284145f431370f189641ef439995856ccd9b2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 12 Jun 2026 16:22:44 +0200 Subject: [PATCH 66/66] Mark functions public --- test/examples/dispatch/deposit.solc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/examples/dispatch/deposit.solc b/test/examples/dispatch/deposit.solc index 867934378..c05455817 100644 --- a/test/examples/dispatch/deposit.solc +++ b/test/examples/dispatch/deposit.solc @@ -50,11 +50,11 @@ contract DepositContract { } // TODO: this is for testing only - function get_zero_hash(index: uint256) -> bytes32 { + public function get_zero_hash(index: uint256) -> bytes32 { return zero_hashes[index]; } - function get_deposit_root() -> bytes32 { + public function get_deposit_root() -> bytes32 { let node: bytes32; let size = deposit_count; for (let height = uint256(0); height < DEPOSIT_CONTRACT_TREE_DEPTH(); height += uint256(1)) { @@ -75,13 +75,13 @@ contract DepositContract { )); } - function get_deposit_count() -> memory(bytes) { + public function get_deposit_count() -> memory(bytes) { return to_little_endian_64(deposit_count); } // TODO: once string literals are properly supported, change errors to messages // matching the deposit contract, full 100% identical behaviour. - payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { + public payable function deposit(pubkey: memory(bytes), withdrawal_credentials: memory(bytes), signature: memory(bytes), deposit_data_root: bytes32) -> () { // Extended ABI length checks since dynamic types are used. require(MemorySize.len(pubkey) == 48, Error(0x9ca717ed)); // InvalidPubkeyLength() require(MemorySize.len(withdrawal_credentials) == 32, Error(0x3debbf1e)); // InvalidWithdrawalCredentialsLength() @@ -157,7 +157,7 @@ contract DepositContract { } // TODO: use bytes4 - function supportsInterface(interfaceId: uint256) -> bool { + public function supportsInterface(interfaceId: uint256) -> bool { return false; } } \ No newline at end of file