diff --git a/testgen/generators.go b/testgen/generators.go index d91cf48..0825d47 100644 --- a/testgen/generators.go +++ b/testgen/generators.go @@ -19,6 +19,7 @@ import ( "github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient/gethclient" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" ) @@ -447,14 +448,14 @@ var EthCall = MethodTests{ // EthMulticall stores a list of all tests against the method. var EthMulticall = MethodTests{ - "eth_multicallV1", + "eth_simulateV1", []Test{ { "multicall-simple", "simulates a multicall transfer", func(ctx context.Context, t *T) error { params := multicallOpts{ - Blocks: []CallBatch{ + BlockStateCalls: []CallBatch{ { StateOverrides: &StateOverride{ common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(1000)}, @@ -471,213 +472,3120 @@ var EthMulticall = MethodTests{ }, }, } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-simple-with-validation-no-funds", + "simulates a multicall transfer with validation and not enough funds", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(1000)}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }, { + From: &common.Address{0xc1}, + To: &common.Address{0xc2}, + Value: *newRPCBalance(1000), + }}, + }, + }, + Validation: false, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-simple-no-funds", + "simulates a simple multicall transfer when account has no funds", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }, { + From: &common.Address{0xc1}, + To: &common.Address{0xc2}, + Value: *newRPCBalance(1000), + }}, + }, + }, + Validation: false, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-overwrite-existing-contract", + "overwrites existing contract with new contract", + func(ctx context.Context, t *T) error { + contractAddr := common.HexToAddress("0000000000000000000000000000000000031ec7") + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &contractAddr, + Input: hex2Bytes("a9059cbb0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a"), // transfer(address,uint256) + }}, + }, + { + StateOverrides: &StateOverride{ + contractAddr: OverrideAccount{Code: getBlockProperties()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &contractAddr, + Input: hex2Bytes("a9059cbb0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000a"), // transfer(address,uint256) + }}, + }, + }, + Validation: false, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + + { + "multicall-overflow-nonce", + "test to overflow nonce", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Nonce: getUint64Ptr(0xFFFFFFFFFFFFFFFF)}, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + }, + }, + }, + }, + Validation: false, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-overflow-nonce-validation", + "test to overflow nonce-validation", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Nonce: getUint64Ptr(0xFFFFFFFFFFFFFFFF)}, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + }, + }, + }, + }, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-simple-no-funds-with-balance-querying", + "simulates a simple multicall transfer when account has no funds with querying balances before and after", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc2}: OverrideAccount{ + Code: getBalanceGetter(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c000000000000000000000000000000000000000"), // gets balance of c0 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c100000000000000000000000000000000000000"), // gets balance of c1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c000000000000000000000000000000000000000"), // gets balance of c0 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c100000000000000000000000000000000000000"), // gets balance of c1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c000000000000000000000000000000000000000"), // gets balance of c0 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c100000000000000000000000000000000000000"), // gets balance of c1 + }, + }, + }}, + Validation: false, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-check-that-balance-is-there-after-new-block", + "checks that balances are kept to next block", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(10000), + }, + common.Address{0xc2}: OverrideAccount{ + Code: getBalanceGetter(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c000000000000000000000000000000000000000"), // gets balance of c0 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c100000000000000000000000000000000000000"), // gets balance of c1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }, + }, + }, { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c000000000000000000000000000000000000000"), // gets balance of c0 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c100000000000000000000000000000000000000"), // gets balance of c1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }, + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-simple-no-funds-with-validation", + "simulates a simple multicall transfer when account has no funds with validation", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + Nonce: getUint64Ptr(0), + }, { + From: &common.Address{0xc1}, + To: &common.Address{0xc2}, + Value: *newRPCBalance(1000), + Nonce: getUint64Ptr(1), + }}, + }, + }, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-simple-no-funds-with-validation-without-nonces", + "simulates a simple multicall transfer when account has no funds with validation. This should fail as the nonce is not set for the second transaction.", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + Nonce: getUint64Ptr(0), + }, { + From: &common.Address{0xc1}, + To: &common.Address{0xc2}, + Value: *newRPCBalance(1000), + }}, + }, + }, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-simple-send-from-contract", + "Sending eth from contract", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(1000), Code: getEthForwarder()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + Validation: false, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-simple-send-from-contract-no-balance", + "Sending eth from contract without balance", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Code: getEthForwarder()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + Validation: false, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-simple-send-from-contract-with-validation", + "Sending eth from contract with validation enabled", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(1000), Code: getEthForwarder()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-transfer-over-BlockStateCalls", + "simulates a transfering value over multiple BlockStateCalls", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(5000)}, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(2000), + }, { + From: &common.Address{0xc0}, + To: &common.Address{0xc3}, + Value: *newRPCBalance(2000), + }, + }, + }, { + StateOverrides: &StateOverride{ + {0xc3}: OverrideAccount{Balance: newRPCBalance(5000)}, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc2}, + Value: *newRPCBalance(1000), + }, { + From: &common.Address{0xc3}, + To: &common.Address{0xc2}, + Value: *newRPCBalance(1000), + }, + }, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-override-block-num", + "simulates calls overriding the block num", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(11)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + Input: &hexutil.Bytes{ + 0x43, // NUMBER + 0x60, 0x00, 0x52, // MSTORE offset 0 + 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN + }, + }, + }, + }, { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(12)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc1}, + Input: &hexutil.Bytes{ + 0x43, // NUMBER + 0x60, 0x00, 0x52, // MSTORE offset 0 + 0x60, 0x20, 0x60, 0x00, 0xf3, + }, + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-block-num-order-38020", + "simulates calls with invalid block num order (-38020)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(12)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc1}, + Input: &hexutil.Bytes{ + 0x43, // NUMBER + 0x60, 0x00, 0x52, // MSTORE offset 0 + 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN + }, + }}, + }, { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(11)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + Input: &hexutil.Bytes{ + 0x43, // NUMBER + 0x60, 0x00, 0x52, // MSTORE offset 0 + 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN + }, + }}, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-block-timestamp-order-38021", + "Error: simulates calls with invalid timestamp order (-38021)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(12), + }, + }, { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(11), + }, + }, + }, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-block-timestamp-non-increment", + "Error: simulates calls with timestamp staying the same", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(12), + }, + }, { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(12), + }, + }, + }, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-block-timestamps-incrementing", + "checks that you can set timestamp and increment it in next block", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(11), + }, + }, { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(12), + }, + }, + }, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-block-timestamp-auto-increment", + "Error: simulates calls with timestamp incrementing over another", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(11), + }, + }, + { + BlockOverrides: &BlockOverrides{}, + }, + { + BlockOverrides: &BlockOverrides{ + Time: getUint64Ptr(12), + }, + }, + { + BlockOverrides: &BlockOverrides{}, + }, + }, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-set-read-storage", + "simulates calls setting and reading from storage contract", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc2}: OverrideAccount{ + Code: hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220404e37f487a89a932dca5e77faaf6ca2de3b991f93d230604b1b8daaef64766264736f6c63430008070033"), + }, + }, + Calls: []TransactionArgs{{ + // Set value to 5 + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("6057361d0000000000000000000000000000000000000000000000000000000000000005"), + }, { + // Read value + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("2e64cec1"), + }, + }, + }}, + } res := make([]interface{}, 0) - if err := t.rpc.Call(&res, "eth_multicallV1", params, "latest"); err != nil { + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-logs", + "simulates calls with logs", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc2}: OverrideAccount{ + // Yul Code: + // object "Test" { + // code { + // let hash:u256 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + // log1(0, 0, hash) + // return (0, 0) + // } + // } + Code: hex2Bytes("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80600080a1600080f3"), + }, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("6057361d0000000000000000000000000000000000000000000000000000000000000005"), + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-blockhash-simple", + "gets blockhash of previous block (included in original chain)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc2}: OverrideAccount{ + Code: blockHashCallerByteCode(), + }, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("ee82ac5e0000000000000000000000000000000000000000000000000000000000000001"), + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if err := checkBlockNumber(res[0].Number, 10); err != nil { + return err + } + if len(res[0].Calls) != 1 { + return fmt.Errorf("unexpected number of call results (have: %d, want: %d)", len(res[0].Calls), 1) + } + if err := checkBlockHash(common.BytesToHash(res[0].Calls[0].ReturnData), t.chain.GetHeaderByNumber(1).Hash()); err != nil { + return err + } + return nil + }, + }, + { + "multicall-blockhash-complex", + "gets blockhash of simulated block", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc2}: OverrideAccount{ + Code: blockHashCallerByteCode(), + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("ee82ac5e0000000000000000000000000000000000000000000000000000000000000001"), + }}, + }, { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(20)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("ee82ac5e000000000000000000000000000000000000000000000000000000000000000f"), + }}, + }, { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(30)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("ee82ac5e000000000000000000000000000000000000000000000000000000000000001d"), + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + + for i := 0; i < len(res); i++ { + if len(res[i].Calls) != 1 { + return fmt.Errorf("unexpected number of call results (have: %d, want: %d)", len(res[i].Calls), 1) + } + if res[i].Calls[0].Status != 0x1 { + return fmt.Errorf("unexpected status value(have: %d, want: %d)", res[i].Calls[0].Status, 0x1) + } + } + if err := checkBlockNumber(res[0].Number, 15); err != nil { + return err + } + if err := checkBlockNumber(res[1].Number, 20); err != nil { + return err + } + if err := checkBlockNumber(res[2].Number, 30); err != nil { + return err + } + + // should equal to block number ones hash + if err := checkBlockHash(common.BytesToHash(res[0].Calls[0].ReturnData), t.chain.GetHeaderByNumber(1).Hash()); err != nil { + return err + } + // should equal first generated BlockStateCalls hash + if err := checkBlockHash(common.BytesToHash(res[1].Calls[0].ReturnData), res[0].Hash); err != nil { + return err + } + // should equal keccack256(rlp([blockhash_20, 29])) + rlp, rlpError := rlp.EncodeToBytes([][]byte{res[1].Hash.Bytes(), big.NewInt(int64(29)).Bytes()}) + if rlpError != nil { + return rlpError + } + if err := checkBlockHash(common.BytesToHash(res[2].Calls[0].ReturnData), crypto.Keccak256Hash(rlp)); err != nil { + return err + } + return nil + }, + }, + { + "multicall-blockhash-start-before-head", + "gets blockhash of simulated block", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc2}: OverrideAccount{ + Code: blockHashCallerByteCode(), + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("ee82ac5e0000000000000000000000000000000000000000000000000000000000000001"), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("ee82ac5e0000000000000000000000000000000000000000000000000000000000000002"), + }, + }, + }, { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(20)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("ee82ac5e0000000000000000000000000000000000000000000000000000000000000013"), + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, (*hexutil.Big)(big.NewInt(1))); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + + if err := checkBlockNumber(res[0].Number, 15); err != nil { + return err + } + if err := checkBlockNumber(res[1].Number, 20); err != nil { + return err + } + + rlp_1, rlpError := rlp.EncodeToBytes([][]byte{res[0].Calls[0].ReturnData, big.NewInt(int64(2)).Bytes()}) + if rlpError != nil { + return rlpError + } + //keccack256(rlp([blockhash_1, 2]) + if err := checkBlockHash(common.BytesToHash(res[0].Calls[1].ReturnData), crypto.Keccak256Hash(rlp_1)); err != nil { + return err + } + + rlp_10, rlpError := rlp.EncodeToBytes([][]byte{res[0].Hash.Bytes(), big.NewInt(int64(19)).Bytes()}) + if rlpError != nil { + return rlpError + } + //keccack256(rlp([blockhash_10, 19]) + if err := checkBlockHash(common.BytesToHash(res[1].Calls[0].ReturnData), crypto.Keccak256Hash(rlp_10)); err != nil { + return err + } + + return nil + }, + }, + { + "multicall-self-destructing-state-override", + "when selfdestructing a state override, the state override should go away", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc2}: OverrideAccount{ + Code: selfDestructor(), + }, + common.Address{0xc3}: OverrideAccount{ + Code: getCode(), + }, + }, + }, { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc3}, + Input: hex2Bytes("dce4a447000000000000000000000000c200000000000000000000000000000000000000"), //at(0xc2) + }}, + }, { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("83197ef0"), //destroy() + }}, + }, { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc3}, + Input: hex2Bytes("dce4a447000000000000000000000000c200000000000000000000000000000000000000"), //at(0xc2) + }}, + }, { + StateOverrides: &StateOverride{ + common.Address{0xc2}: OverrideAccount{ + Code: selfDestructor(), + }, + }, + }, { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc3}, + Input: hex2Bytes("dce4a447000000000000000000000000c200000000000000000000000000000000000000"), //at(0xc2) + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, (*hexutil.Big)(big.NewInt(1))); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + noCode := "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000" + if res[1].Calls[0].ReturnData.String() == noCode { + return fmt.Errorf("res 1 overrided contract does not have contract code: %s", res[1].Calls[0].ReturnData.String()) + } + if res[3].Calls[0].ReturnData.String() != noCode { + return fmt.Errorf("res 3 self destructed code does have contract code: %s", res[3].Calls[0].ReturnData.String()) + } + if res[5].Calls[0].ReturnData.String() == noCode { + return fmt.Errorf("res 5 overrided contract does not have contract code: %s", res[5].Calls[0].ReturnData.String()) + } + return nil + }, + }, + { + "multicall-run-out-of-gas-in-block-38015", + "we should get out of gas error if a block consumes too much gas (-38015)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc2}: OverrideAccount{ + Code: gasSpender(), + }, + }, + BlockOverrides: &BlockOverrides{ + GasLimit: getUint64Ptr(1500000), + }, + }, { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("815b8ab400000000000000000000000000000000000000000000000000000000000f4240"), //spendGas(1000000) + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("815b8ab400000000000000000000000000000000000000000000000000000000000f4240"), //spendGas(1000000) + }, + }}, + }, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-run-gas-spending", + "spend a lot gas in separate blocks", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc2}: OverrideAccount{ + Code: gasSpender(), + }, + }, + BlockOverrides: &BlockOverrides{ + GasLimit: getUint64Ptr(1500000), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("815b8ab40000000000000000000000000000000000000000000000000000000000000000"), //spendGas(0) + }, + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("815b8ab40000000000000000000000000000000000000000000000000000000000000000"), //spendGas(0) + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("815b8ab400000000000000000000000000000000000000000000000000000000000f4240"), //spendGas(1000000) + }, + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("815b8ab40000000000000000000000000000000000000000000000000000000000000000"), //spendGas(0) + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("815b8ab400000000000000000000000000000000000000000000000000000000000f4240"), //spendGas(1000000) + }, + }, + }, + }, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-eth-send-should-produce-logs", + "when sending eth we should get ETH logs when traceTransfers is set", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls[0].Logs) != 1 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 1) + } + if res[0].Calls[0].Logs[0].Address.String() != "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" { + return fmt.Errorf("unexpected log address (have: %s, want: %s)", res[0].Calls[0].Logs[0].Address.String(), "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") + } + return nil + }, + }, + { + "multicall-override-address-twice", + "override address twice", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + common.Address{0xc0}: OverrideAccount{Code: getRevertingContract()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-empty-multicall", + "multicall without parameters", + func(ctx context.Context, t *T) error { + params := multicallOpts{} + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-empty-calls-and-overrides-multicall", + "multicall with state overrides and calls but they are empty", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{}, + Calls: []TransactionArgs{{}}, + }, + { + StateOverrides: &StateOverride{}, + Calls: []TransactionArgs{{}}, + }, + }, + TraceTransfers: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-override-address-twice-in-separate-BlockStateCalls", + "override address twice in separate BlockStateCalls", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }, + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }, + }, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-eth-send-should-not-produce-logs-on-revert", + "we should not be producing eth logs if the transaction reverts and ETH is not sent", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + common.Address{0xc1}: OverrideAccount{Code: getRevertingContract()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls[0].Logs) != 0 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 0) + } + return nil + }, + }, + { + "multicall-eth-send-should-produce-more-logs-on-forward", + "we should be getting more logs if eth is forwarded", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + common.Address{0xc1}: OverrideAccount{Code: getEthForwarder()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + Input: hex2Bytes("4b64e4920000000000000000000000000000000000000000000000000000000000000100"), + }}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls[0].Logs) != 2 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 2) + } + return nil + }, + }, + { + "multicall-eth-send-should-produce-no-logs-on-forward-revert", + "we should be getting no logs if eth is forwarded but then the tx reverts", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + common.Address{0xc1}: OverrideAccount{Code: getEthForwarder()}, + common.Address{0xc2}: OverrideAccount{Code: getRevertingContract()}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + Input: hex2Bytes("4b64e492c200000000000000000000000000000000000000000000000000000000000000"), //foward(0xc2) + }}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls[0].Logs) != 0 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 0) + } + return nil + }, + }, + { + "multicall-eth-send-should-not-produce-logs-by-default", + "when sending eth we should not get ETH logs by default", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls[0].Logs) != 0 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 0) + } + return nil + }, + }, + { + "multicall-transaction-too-low-nonce-38010", + "Error: Nonce too low (-38010)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Nonce: getUint64Ptr(10)}, + }, + Calls: []TransactionArgs{{ + Nonce: getUint64Ptr(0), + From: &common.Address{0xc1}, + To: &common.Address{0xc1}, + }}, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-transaction-too-high-nonce", + "Error: Nonce too high", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{{ + Nonce: getUint64Ptr(100), + From: &common.Address{0xc1}, + To: &common.Address{0xc1}, + }}, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-basefee-too-low-with-validation-38012", + "Error: BaseFee too low with validation (-38012)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + }, + BlockOverrides: &BlockOverrides{ + BaseFee: (*hexutil.Big)(big.NewInt(10)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc1}, + To: &common.Address{0xc1}, + MaxFeePerGas: (*hexutil.Big)(big.NewInt(0)), + MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(0)), + }}, + }}, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-basefee-too-low-without-validation-38012", + "Error: BaseFee too low with no validation (-38012)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, + }, + BlockOverrides: &BlockOverrides{ + BaseFee: (*hexutil.Big)(big.NewInt(10)), + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc1}, + To: &common.Address{0xc1}, + MaxFeePerGas: (*hexutil.Big)(big.NewInt(0)), + MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(0)), + }}, + }}, + Validation: false, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-basefee-too-low-without-validation-38012-without-basefee-override", + "tries to send transaction with zero basefee", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{{ + From: &common.Address{0xc1}, + To: &common.Address{0xc1}, + MaxFeePerGas: (*hexutil.Big)(big.NewInt(0)), + MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(0)), + }}, + }}, + Validation: false, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-instrict-gas-38013", + "Error: Not enough gas provided to pay for intrinsic gas (-38013)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{{ + From: &common.Address{0xc1}, + To: &common.Address{0xc1}, + Gas: getUint64Ptr(0), + }}, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-gas-fees-and-value-error-38014", + "Error: Insufficient funds to pay for gas fees and value (-38014)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-gas-fees-and-value-error-38014-with-validation", + "Error: Insufficient funds to pay for gas fees and value (-38014) with validation", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1000), + }}, + }}, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-move-to-address-itself-reference-38022", + "Error: MovePrecompileToAddress referenced itself in replacement (-38022)", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(200000)}, + common.Address{0xc1}: OverrideAccount{MovePrecompileToAddress: &common.Address{0xc1}}, + }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Value: *newRPCBalance(1), + }}, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-move-two-non-precompiles-accounts-to-same", + "Move two non-precompiles to same adddress", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0x1}: OverrideAccount{ + MovePrecompileToAddress: &common.Address{0xc2}, + }, + common.Address{0x2}: OverrideAccount{ + MovePrecompileToAddress: &common.Address{0xc2}, + }, + }, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-move-two-accounts-to-same-38023", + "Move two accounts to the same destination (-38023)", + func(ctx context.Context, t *T) error { + ecRecoverAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000001")) + keccakAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000002")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + ecRecoverAddress: OverrideAccount{ + MovePrecompileToAddress: &common.Address{0xc2}, + }, + keccakAddress: OverrideAccount{ + MovePrecompileToAddress: &common.Address{0xc2}, + }, + }, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-try-to-move-non-precompile", + "try to move non-precompile", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Nonce: getUint64Ptr(5)}, + }, + }, { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{MovePrecompileToAddress: &common.Address{0xc1}}, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + Nonce: getUint64Ptr(0), + }, + { + From: &common.Address{0xc1}, + To: &common.Address{0xc1}, + Nonce: getUint64Ptr(5), + }, + }, + }, + }, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-make-call-with-future-block", + "start multicall with future block", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + }}, + }, + }, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "0x111") + return nil + }, + }, + { + "multicall-check-that-nonce-increases", + "check that nonce increases", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(20000)}, + }, + }, { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + Nonce: getUint64Ptr(0), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + Nonce: getUint64Ptr(1), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + Nonce: getUint64Ptr(2), + }, + }, + }, + }, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-check-invalid-nonce", + "check that nonce cannot decrease", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(20000)}, + }, + }, { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + Nonce: getUint64Ptr(0), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + Nonce: getUint64Ptr(1), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + Nonce: getUint64Ptr(0), + }, + }, + }, + }, + Validation: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-override-all-in-BlockStateCalls", + "override all values in block and see that they are set in return value", + func(ctx context.Context, t *T) error { + feeRecipient := common.Address{0xc2} + randDao := common.Hash{0xc3} + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(1001)), + Time: getUint64Ptr(1003), + GasLimit: getUint64Ptr(1004), + FeeRecipient: &feeRecipient, + PrevRandao: &randDao, + BaseFee: (*hexutil.Big)(big.NewInt(1007)), + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if res[0].Number != 1001 { + return fmt.Errorf("unexpected Number (have: %d, want: %d)", res[0].Number, 1001) + } + if res[0].Time != 1003 { + return fmt.Errorf("unexpectedTime (have: %d, want: %d)", res[0].Time, 1003) + } + if res[0].GasLimit != 1004 { + return fmt.Errorf("unexpected GasLimit (have: %d, want: %d)", res[0].GasLimit, 1004) + } + if res[0].FeeRecipient != feeRecipient { + return fmt.Errorf("unexpected FeeRecipient (have: %d, want: %d)", res[0].FeeRecipient, feeRecipient) + } + if *res[0].PrevRandao != randDao { + return fmt.Errorf("unexpected PrevRandao (have: %d, want: %d)", res[0].PrevRandao, randDao) + } + if res[0].BaseFeePerGas.ToInt().Cmp(big.NewInt(1007)) != 0 { + return fmt.Errorf("unexpected BaseFeePerGas (have: %d, want: %d)", res[0].BaseFeePerGas.ToInt(), big.NewInt(1007)) + } + return nil + }, + }, + { + "multicall-move-ecrecover-and-call", + "move ecrecover and try calling it", + func(ctx context.Context, t *T) error { + ecRecoverAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000001")) + ecRecoverMovedToAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000123456")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{ // just call ecrecover normally + { // call with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + }, + }, { + StateOverrides: &StateOverride{ // move ecRecover and call it in new address + ecRecoverAddress: OverrideAccount{ + MovePrecompileToAddress: &ecRecoverMovedToAddress, + }, + }, + Calls: []TransactionArgs{ + { // call with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + { // call with valid params, the old address, should fail as it was moved + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls) != len(params.BlockStateCalls[0].Calls) { + return fmt.Errorf("unexpected number of call results (have: %d, want: %d)", len(res[0].Calls), len(params.BlockStateCalls[0].Calls)) + } + return nil + }, + }, + { + "multicall-move-ecrecover-twice-and-call", + "move ecrecover and try calling it, then move it again and call it", + func(ctx context.Context, t *T) error { + ecRecoverAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000001")) + ecRecoverMovedToAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000123456")) + ecRecoverMovedToAddress2 := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000123457")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{ // just call ecrecover normally + { // call with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + }, + }, { + StateOverrides: &StateOverride{ // move ecRecover and call it in new address + ecRecoverAddress: OverrideAccount{ + MovePrecompileToAddress: &ecRecoverMovedToAddress, + }, + }, + Calls: []TransactionArgs{ + { // call with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + { // call with valid params, the old address, should fail as it was moved + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + }, + }, { + StateOverrides: &StateOverride{ // move ecRecover and call it in new address + ecRecoverAddress: OverrideAccount{ + MovePrecompileToAddress: &ecRecoverMovedToAddress2, + }, + }, + Calls: []TransactionArgs{ + { // call with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + { // call with valid params, the old address, should fail as it was moved + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress2, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + }, + }}, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-override-ecrecover", + "override ecrecover", + func(ctx context.Context, t *T) error { + ecRecoverAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000001")) + ecRecoverMovedToAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000123456")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + ecRecoverAddress: OverrideAccount{ + Code: getEcRecoverOverride(), + MovePrecompileToAddress: &ecRecoverMovedToAddress, + }, + common.Address{0xc1}: OverrideAccount{Balance: newRPCBalance(200000)}, + }, + Calls: []TransactionArgs{ + { // call with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + { // add override + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("c00692604554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"), + }, + { // now it should resolve to 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + { // call with new invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554490000000000000000000000000000000000000000000000000000000000"), + }, + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls) != len(params.BlockStateCalls[0].Calls) { + return fmt.Errorf("unexpected number of call results (have: %d, want: %d)", len(res[0].Calls), len(params.BlockStateCalls[0].Calls)) + } + zeroAddr := common.Address{0x0} + if common.BytesToAddress(res[0].Calls[0].ReturnData) != zeroAddr { + return fmt.Errorf("unexpected ReturnData (have: %d, want: %d)", common.BytesToAddress(res[0].Calls[0].ReturnData), zeroAddr) + } + successReturn := common.BytesToAddress(*hex2Bytes("b11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a")) + if common.BytesToAddress(res[0].Calls[1].ReturnData) != successReturn { + return fmt.Errorf("unexpected calls 1 ReturnData (have: %d, want: %d)", common.BytesToAddress(res[0].Calls[1].ReturnData), successReturn) + } + vitalikReturn := common.BytesToAddress(*hex2Bytes("d8dA6BF26964aF9D7eEd9e03E53415D37aA96045")) + if common.BytesToAddress(res[0].Calls[3].ReturnData) != vitalikReturn { + return fmt.Errorf("unexpected calls 3 ReturnData (have: %d, want: %d)", common.BytesToAddress(res[0].Calls[3].ReturnData), vitalikReturn) + } + if common.BytesToAddress(res[0].Calls[4].ReturnData) != successReturn { + return fmt.Errorf("unexpected calls 4 ReturnData (have: %d, want: %d)", common.BytesToAddress(res[0].Calls[4].ReturnData), successReturn) + } + if common.BytesToAddress(res[0].Calls[5].ReturnData) != zeroAddr { + return fmt.Errorf("unexpected calls 5 ReturnData (have: %d, want: %d)", common.BytesToAddress(res[0].Calls[5].ReturnData), zeroAddr) + } + return nil + }, + }, + { + "multicall-override-sha256", + "override sha256 precompile", + func(ctx context.Context, t *T) error { + sha256Address := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000002")) + sha256MovedToAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000123456")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + sha256Address: OverrideAccount{ + Code: hex2Bytes(""), + MovePrecompileToAddress: &sha256MovedToAddress, + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &sha256MovedToAddress, + Input: hex2Bytes("1234"), + }, + { + From: &common.Address{0xc0}, + To: &sha256Address, + Input: hex2Bytes("1234"), + }, + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-override-identity", + "override identity precompile", + func(ctx context.Context, t *T) error { + identityAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000004")) + identityMovedToAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000123456")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + identityAddress: OverrideAccount{ + Code: hex2Bytes(""), + MovePrecompileToAddress: &identityMovedToAddress, + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &identityMovedToAddress, + Input: hex2Bytes("1234"), + }, + { + From: &common.Address{0xc0}, + To: &identityAddress, + Input: hex2Bytes("1234"), + }, + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-precompile-is-sending-transaction", + "send transaction from a precompile", + func(ctx context.Context, t *T) error { + identityAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000004")) + sha256Address := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000002")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{ + { + From: &identityAddress, + To: &sha256Address, + Input: hex2Bytes("1234"), + }, + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-simple-state-diff", + "override one state variable with statediff", + func(ctx context.Context, t *T) error { + stateChanges := make(map[common.Hash]common.Hash) + stateChanges[common.BytesToHash(*hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"))] = common.Hash{0x12} //slot 0 -> 0x12 + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: getStorageTester(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("7b8d56e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), // set storage slot 0 -> 1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("7b8d56e300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), // set storage slot 1 -> 2 + }, + }, + }, + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + StateDiff: &stateChanges, // state diff override + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000000"), // gets storage slot 0, should be 0x12 as overrided + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000001"), // gets storage slot 1, should be 2 + }, + }, + }, + }, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-simple-state-diff", + "override one state variable with state", + func(ctx context.Context, t *T) error { + stateChanges := make(map[common.Hash]common.Hash) + stateChanges[common.BytesToHash(*hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"))] = common.Hash{0x12} //slot 0 -> 0x12 + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: getStorageTester(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("7b8d56e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), // set storage slot 0 -> 1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("7b8d56e300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), // set storage slot 1 -> 2 + }, + }, + }, + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + State: &stateChanges, // state diff override + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000000"), // gets storage slot 0, should be 0x12 as overrided + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000001"), // gets storage slot 1, should be 0 + }, + }, + }, + }, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-override-storage-slots", + "override storage slots", + func(ctx context.Context, t *T) error { + stateChanges := make(map[common.Hash]common.Hash) + stateChanges[common.BytesToHash(*hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000"))] = common.Hash{0x12} //slot 0 -> 0x12 + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: getStorageTester(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("7b8d56e300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"), // set storage slot 0 -> 1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("7b8d56e300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), // set storage slot 1 -> 2 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000000"), // gets storage slot 0, should be 1 + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000001"), // gets storage slot 1, should be 2 + }, + }, + }, + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + StateDiff: &stateChanges, // state diff override + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000000"), // gets storage slot 0, should be 0x12 as overrided + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000001"), // gets storage slot 1, should be 2 + }, + }, + }, + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + State: &stateChanges, // whole state override + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000000"), // gets storage slot 0, should be 0x12 as overrided + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("0ff4c9160000000000000000000000000000000000000000000000000000000000000001"), // gets storage slot 1, should be 0 as the whole storage was replaced + }, + }, + }, + }, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if res[0].Calls[2].ReturnData.String() != "0x0000000000000000000000000000000000000000000000000000000000000001" { + return fmt.Errorf("unexpected call result (res[0].Calls[2]) (have: %s, want: %s)", res[0].Calls[2].ReturnData.String(), "0x0000000000000000000000000000000000000000000000000000000000000001") + } + if res[0].Calls[3].ReturnData.String() != "0x0000000000000000000000000000000000000000000000000000000000000002" { + return fmt.Errorf("unexpected call result (res[0].Calls[3]) (have: %s, want: %s)", res[0].Calls[3].ReturnData.String(), "0x0000000000000000000000000000000000000000000000000000000000000002") + } + + if res[1].Calls[0].ReturnData.String() != "0x1200000000000000000000000000000000000000000000000000000000000000" { + return fmt.Errorf("unexpected call result (res[1].Calls[0]) (have: %s, want: %s)", res[1].Calls[0].ReturnData.String(), "0x1200000000000000000000000000000000000000000000000000000000000000") + } + if res[1].Calls[1].ReturnData.String() != "0x0000000000000000000000000000000000000000000000000000000000000002" { + return fmt.Errorf("unexpected call result (res[1].Calls[1]) (have: %s, want: %s)", res[1].Calls[1].ReturnData.String(), "0x0000000000000000000000000000000000000000000000000000000000000002") + } + + if res[2].Calls[0].ReturnData.String() != "0x1200000000000000000000000000000000000000000000000000000000000000" { + return fmt.Errorf("unexpected call result (res[2].Calls[0]) (have: %s, want: %s)", res[2].Calls[0].ReturnData.String(), "0x1200000000000000000000000000000000000000000000000000000000000000") + } + if res[2].Calls[1].ReturnData.String() != "0x0000000000000000000000000000000000000000000000000000000000000000" { + return fmt.Errorf("unexpected call result (res[2].Calls[1]) (have: %s, want: %s)", res[2].Calls[1].ReturnData.String(), "0x0000000000000000000000000000000000000000000000000000000000000000") + } + return nil + }, + }, + { + "multicall-block-override-reflected-in-contract-simple", + "Checks that block overrides are true in contract for block number and time", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + Time: getUint64Ptr(100), + }, + }, + { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(20)), + Time: getUint64Ptr(101), + }, + }, + { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(21)), + Time: getUint64Ptr(200), + }, + }, + }, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-get-block-properties", + "gets various block properties from chain", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + Code: getBlockProperties(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + }, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-block-override-reflected-in-contract", + "Checks that block overrides are true in contract", + func(ctx context.Context, t *T) error { + prevRandDao1 := common.BytesToHash(*hex2Bytes("123")) + prevRandDao2 := common.BytesToHash(*hex2Bytes("1234")) + prevRandDao3 := common.BytesToHash(*hex2Bytes("12345")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + Code: getBlockProperties(), + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + Time: getUint64Ptr(100), + GasLimit: getUint64Ptr(190000), + FeeRecipient: &common.Address{0xc0}, + PrevRandao: &prevRandDao1, + BaseFee: (*hexutil.Big)(big.NewInt(10)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(20)), + Time: getUint64Ptr(2000), + GasLimit: getUint64Ptr(300000), + FeeRecipient: &common.Address{0xc1}, + PrevRandao: &prevRandDao2, + BaseFee: (*hexutil.Big)(big.NewInt(20)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(21)), + Time: getUint64Ptr(30000), + GasLimit: getUint64Ptr(190002), + FeeRecipient: &common.Address{0xc2}, + PrevRandao: &prevRandDao3, + BaseFee: (*hexutil.Big)(big.NewInt(30)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + }, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-add-more-non-defined-BlockStateCalls-than-fit", + "Add more BlockStateCalls between two BlockStateCalls than it actually fits there", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + Code: getBlockProperties(), + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(16)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + }, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-add-more-non-defined-BlockStateCalls-than-fit-but-now-with-fit", + "Not all block numbers are defined", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + Code: getBlockProperties(), + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + { + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(20)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + }, + }, + }, + }, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-fee-recipient-receiving-funds", + "Check that fee recipient gets funds", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(200000000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: getBalanceGetter(), + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + FeeRecipient: &common.Address{0xc2}, + BaseFee: (*hexutil.Big)(big.NewInt(10)), + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + MaxFeePerGas: (*hexutil.Big)(big.NewInt(10)), + MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(10)), + Input: hex2Bytes(""), + Nonce: getUint64Ptr(0), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c000000000000000000000000000000000000000"), // gets balance of c0 + Nonce: getUint64Ptr(1), + }, + { + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("f8b2cb4f000000000000000000000000c200000000000000000000000000000000000000"), // gets balance of c2 + Nonce: getUint64Ptr(2), + }, + }, + }, + }, + Validation: true, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + return nil + }, + }, + { + "multicall-contract-calls-itself", + "contract calls itself", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Code: getBlockProperties(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc0}, + To: &common.Address{0xc0}, + }, + }, + }, + }, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { return err } - if len(res) != len(params.Blocks) { - return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.Blocks)) + return nil + }, + }, + { + "multicall-long-block-distances", + "check that parameters adjust the same way when there's big distances between block numbers", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{ + { + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Code: getBlockProperties(), + }, + }, + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc0}, + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(15)), + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc0}, + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(100)), + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc0}, + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(101)), + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc0}, + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(1000)), + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc0}, + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(10000)), + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc0}, + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(10001)), + }, + }, + { + Calls: []TransactionArgs{ + { + From: &common.Address{0xc1}, + To: &common.Address{0xc0}, + }, + }, + BlockOverrides: &BlockOverrides{ + Number: (*hexutil.Big)(big.NewInt(100000)), + }, + }, + }, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err } return nil }, }, { - "multicall-transfer-over-blocks", - "simulates a transfering value over multiple blocks", + "multicall-send-eth-and-delegate-call", + "sending eth and delegate calling should only produce one log", func(ctx context.Context, t *T) error { params := multicallOpts{ - Blocks: []CallBatch{{ + BlockStateCalls: []CallBatch{{ StateOverrides: &StateOverride{ - common.Address{0xc0}: OverrideAccount{Balance: newRPCBalance(2000)}, - }, - Calls: []TransactionArgs{ - { - From: &common.Address{0xc0}, - To: &common.Address{0xc1}, - Value: (*hexutil.Big)(big.NewInt(1000)), - }, { - From: &common.Address{0xc0}, - To: &common.Address{0xc3}, - Value: (*hexutil.Big)(big.NewInt(1000)), + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: delegateCaller(), + }, + common.Address{0xc2}: OverrideAccount{ + Code: getBlockProperties(), }, }, - }, { + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("5c19a95c000000000000000000000000c200000000000000000000000000000000000000"), + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + Validation: false, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if res[0].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if len(res[0].Calls[0].Logs) != 1 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 1) + } + return nil + }, + }, + { + "multicall-send-eth-and-delegate-call-to-payble-contract", + "sending eth and delegate calling a payable contract should only produce one log", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ StateOverrides: &StateOverride{ - {0xc3}: OverrideAccount{Balance: newRPCBalance(0)}, - }, - Calls: []TransactionArgs{ - { - From: &common.Address{0xc1}, - To: &common.Address{0xc2}, - Value: *newRPCBalance(1000), - }, { - From: &common.Address{0xc3}, - To: &common.Address{0xc2}, - Value: *newRPCBalance(1000), + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: delegateCaller2(), + }, + common.Address{0xc2}: OverrideAccount{ + Code: payableFallBack(), }, }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + Value: *newRPCBalance(1000), + }}, }}, + TraceTransfers: true, + Validation: false, } - res := make([]interface{}, 0) - if err := t.rpc.Call(&res, "eth_multicallV1", params, "latest"); err != nil { + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { return err } - if len(res) != len(params.Blocks) { - return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.Blocks)) + if res[0].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if len(res[0].Calls[0].Logs) != 1 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 1) } return nil }, }, { - "multicall-override-block-num", - "simulates calls overriding the block num", + "multicall-send-eth-and-delegate-call-to-eoa", + "sending eth and delegate calling a eoa should only produce one log", func(ctx context.Context, t *T) error { params := multicallOpts{ - Blocks: []CallBatch{{ - BlockOverrides: &BlockOverrides{ - Number: (*hexutil.Big)(big.NewInt(11)), + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: delegateCaller2(), + }, }, - Calls: []TransactionArgs{ - { - From: &common.Address{0xc0}, - Input: &hexutil.Bytes{ - 0x43, // NUMBER - 0x60, 0x00, 0x52, // MSTORE offset 0 - 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN - }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes(""), + Value: *newRPCBalance(1000), + }}, + }}, + TraceTransfers: true, + Validation: false, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + if res[0].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if len(res[0].Calls[0].Logs) != 1 { + return fmt.Errorf("unexpected number of logs (have: %d, want: %d)", len(res[0].Calls[0].Logs), 1) + } + return nil + }, + }, + { + "multicall-extcodehash-override", + "test extcodehash getting of overriden contract", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc0}: OverrideAccount{ + Balance: newRPCBalance(2000000), + }, + common.Address{0xc1}: OverrideAccount{ + Code: extCodeHashContract(), }, }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("b9724d63000000000000000000000000c200000000000000000000000000000000000000"), // getExtCodeHash(0xc2) + }}, }, { - BlockOverrides: &BlockOverrides{ - Number: (*hexutil.Big)(big.NewInt(12)), + StateOverrides: &StateOverride{ + common.Address{0xc2}: OverrideAccount{ + Code: getBlockProperties(), + }, }, Calls: []TransactionArgs{{ - From: &common.Address{0xc1}, - Input: &hexutil.Bytes{ - 0x43, // NUMBER - 0x60, 0x00, 0x52, // MSTORE offset 0 - 0x60, 0x20, 0x60, 0x00, 0xf3, - }, + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("b9724d63000000000000000000000000c200000000000000000000000000000000000000"), // getExtCodeHash(0xc2) }}, }}, + TraceTransfers: true, + Validation: false, } - res := make([]interface{}, 0) - if err := t.rpc.Call(&res, "eth_multicallV1", params, "latest"); err != nil { + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { return err } - if len(res) != len(params.Blocks) { - return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.Blocks)) + if res[0].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if res[1].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if res[0].Calls[0].ReturnData.String() == res[1].Calls[0].ReturnData.String() { + return fmt.Errorf("returndata did not change (have: %s, want: %s)", res[0].Calls[0].ReturnData.String(), res[1].Calls[0].ReturnData.String()) } return nil }, }, { - "multicall-block-num-order", - "simulates calls with invalid block num order", + "multicall-extcodehash-existing-contract", + "test extcodehash getting of existing contract and then overriding it", func(ctx context.Context, t *T) error { + contractAddr := common.HexToAddress("0000000000000000000000000000000000031ec7") params := multicallOpts{ - Blocks: []CallBatch{{ - BlockOverrides: &BlockOverrides{ - Number: (*hexutil.Big)(big.NewInt(12)), + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ + common.Address{0xc1}: OverrideAccount{ + Code: extCodeHashContract(), + }, }, Calls: []TransactionArgs{{ - From: &common.Address{0xc1}, - Input: &hexutil.Bytes{ - 0x43, // NUMBER - 0x60, 0x00, 0x52, // MSTORE offset 0 - 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN - }, + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("b9724d630000000000000000000000000000000000000000000000000000000000031ec7"), // getExtCodeHash(0000000000000000000000000000000000031ec7) }}, }, { - BlockOverrides: &BlockOverrides{ - Number: (*hexutil.Big)(big.NewInt(11)), + StateOverrides: &StateOverride{ + contractAddr: OverrideAccount{ + Code: getBlockProperties(), + }, }, Calls: []TransactionArgs{{ - From: &common.Address{0xc0}, - Input: &hexutil.Bytes{ - 0x43, // NUMBER - 0x60, 0x00, 0x52, // MSTORE offset 0 - 0x60, 0x20, 0x60, 0x00, 0xf3, // RETURN - }, + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("b9724d630000000000000000000000000000000000000000000000000000000000031ec7"), // getExtCodeHash(0000000000000000000000000000000000031ec7) }}, }}, + TraceTransfers: true, + Validation: false, } - res := make([]interface{}, 0) - if err := t.rpc.Call(&res, "eth_multicallV1", params, "latest"); err != nil { + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { return err } - if len(res) != len(params.Blocks) { - return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.Blocks)) + if res[0].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if res[1].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if res[0].Calls[0].ReturnData.String() == res[1].Calls[0].ReturnData.String() { + return fmt.Errorf("returndata did not change (have: %s, want: %s)", res[0].Calls[0].ReturnData.String(), res[1].Calls[0].ReturnData.String()) } return nil }, }, { - "multicall-set-read-storage", - "simulates calls setting and reading from storage contract", + "multicall-extcodehash-precompile", + "test extcodehash getting of precompile and then again after override", func(ctx context.Context, t *T) error { + ecRecoverAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000001")) params := multicallOpts{ - Blocks: []CallBatch{{ + BlockStateCalls: []CallBatch{{ StateOverrides: &StateOverride{ - common.Address{0xc2}: OverrideAccount{ - Code: hex2Bytes("608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220404e37f487a89a932dca5e77faaf6ca2de3b991f93d230604b1b8daaef64766264736f6c63430008070033"), + common.Address{0xc1}: OverrideAccount{ + Code: extCodeHashContract(), }, }, Calls: []TransactionArgs{{ - // Set value to 5 - From: &common.Address{0xc0}, - To: &common.Address{0xc2}, - Input: hex2Bytes("6057361d0000000000000000000000000000000000000000000000000000000000000005"), - }, { - // Read value From: &common.Address{0xc0}, - To: &common.Address{0xc2}, - Input: hex2Bytes("2e64cec1"), - }, + To: &common.Address{0xc1}, + Input: hex2Bytes("b9724d630000000000000000000000000000000000000000000000000000000000000001"), // getExtCodeHash(0x1) + }}, + }, { + StateOverrides: &StateOverride{ + ecRecoverAddress: OverrideAccount{ + Code: getBlockProperties(), + }, }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + Input: hex2Bytes("b9724d630000000000000000000000000000000000000000000000000000000000000001"), // getExtCodeHash(0x1) + }}, }}, + TraceTransfers: true, + Validation: false, } - res := make([]interface{}, 0) - if err := t.rpc.Call(&res, "eth_multicallV1", params, "latest"); err != nil { + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { return err } - if len(res) != len(params.Blocks) { - return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.Blocks)) + if res[0].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if res[1].Calls[0].Status != 1 { + return fmt.Errorf("unexpected call status (have: %d, want: %d)", res[0].Calls[0].Status, 1) + } + if res[0].Calls[0].ReturnData.String() == res[1].Calls[0].ReturnData.String() { + return fmt.Errorf("returndata did not change (have: %s, want: %s)", res[0].Calls[0].ReturnData.String(), res[1].Calls[0].ReturnData.String()) } return nil }, }, { - "multicall-logs", - "simulates calls with logs", + "multicall-self-destructive-contract-produces-logs", + "self destructive contract produces logs", func(ctx context.Context, t *T) error { params := multicallOpts{ - Blocks: []CallBatch{{ + BlockStateCalls: []CallBatch{{ StateOverrides: &StateOverride{ common.Address{0xc2}: OverrideAccount{ - // Yul code: - // object "Test" { - // code { - // let hash:u256 := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - // log1(0, 0, hash) - // return (0, 0) - // } - // } - Code: hex2Bytes("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80600080a1600080f3"), + Code: selfDestructor(), + Balance: newRPCBalance(2000000), }, }, + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc2}, + Input: hex2Bytes("83197ef0"), //destroy() + }}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-no-fields-call", + "make a call with no fields", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{{}}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-only-from-transaction", + "make a call with only from field", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ Calls: []TransactionArgs{{ From: &common.Address{0xc0}, - To: &common.Address{0xc2}, }}, }}, + TraceTransfers: true, } - res := make([]interface{}, 0) - if err := t.rpc.Call(&res, "eth_multicallV1", params, "latest"); err != nil { + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-only-from-to-transaction", + "make a call with only from and to fields", + func(ctx context.Context, t *T) error { + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + Calls: []TransactionArgs{{ + From: &common.Address{0xc0}, + To: &common.Address{0xc1}, + }}, + }}, + TraceTransfers: true, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { + return err + } + return nil + }, + }, + { + "multicall-big-block-state-calls-array", + "Have a block state calls with 300 blocks", + func(ctx context.Context, t *T) error { + calls := make([]CallBatch, 300) + params := multicallOpts{BlockStateCalls: calls} + res := make([]blockResult, 0) + t.rpc.Call(&res, "eth_simulateV1", params, "latest") + return nil + }, + }, + { + "multicall-move-ecrecover-and-call-old-and-new", + "move ecrecover and try calling the moved and non-moved version", + func(ctx context.Context, t *T) error { + ecRecoverAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000000001")) + ecRecoverMovedToAddress := common.BytesToAddress(*hex2Bytes("0000000000000000000000000000000000123456")) + params := multicallOpts{ + BlockStateCalls: []CallBatch{{ + StateOverrides: &StateOverride{ // move ecRecover and call it in new address + ecRecoverAddress: OverrideAccount{ + MovePrecompileToAddress: &ecRecoverMovedToAddress, + }, + }, + Calls: []TransactionArgs{ + { // call new address with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call new address with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverMovedToAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + { // call old address with invalid params, should fail (resolve to 0x0) + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b45544800000000000000000000000000000000000000000000000000000000004554480000000000000000000000000000000000000000000000000000000000"), + }, + { // call old address with valid params, should resolve to 0xb11CaD98Ad3F8114E0b3A1F6E7228bc8424dF48a + From: &common.Address{0xc1}, + To: &ecRecoverAddress, + Input: hex2Bytes("1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8000000000000000000000000000000000000000000000000000000000000001cb7cf302145348387b9e69fde82d8e634a0f8761e78da3bfa059efced97cbed0d2a66b69167cafe0ccfc726aec6ee393fea3cf0e4f3f9c394705e0f56d9bfe1c9"), + }, + }, + }}, + } + res := make([]blockResult, 0) + if err := t.rpc.Call(&res, "eth_simulateV1", params, "latest"); err != nil { return err } - if len(res) != len(params.Blocks) { - return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.Blocks)) + if len(res) != len(params.BlockStateCalls) { + return fmt.Errorf("unexpected number of results (have: %d, want: %d)", len(res), len(params.BlockStateCalls)) + } + if len(res[0].Calls) != len(params.BlockStateCalls[0].Calls) { + return fmt.Errorf("unexpected number of call results (have: %d, want: %d)", len(res[0].Calls), len(params.BlockStateCalls[0].Calls)) } return nil }, @@ -1696,13 +4604,12 @@ type TransactionArgs struct { // BlockOverrides is a set of header fields to override. type BlockOverrides struct { - Number *hexutil.Big `json:"number,omitempty"` - Difficulty *hexutil.Big `json:"difficulty,omitempty"` - Time *hexutil.Uint64 `json:"time,omitempty"` - GasLimit *hexutil.Uint64 `json:"gasLimit,omitempty"` - Coinbase *common.Address `json:"coinbase,omitempty"` - Random *common.Hash `json:"random,omitempty"` - BaseFee *hexutil.Big `json:"baseFee,omitempty"` + Number *hexutil.Big `json:"number,omitempty"` + Time *hexutil.Uint64 `json:"time,omitempty"` + GasLimit *hexutil.Uint64 `json:"gasLimit,omitempty"` + FeeRecipient *common.Address `json:"feeRecipient,omitempty"` + PrevRandao *common.Hash `json:"prevRandao,omitempty"` + BaseFee *hexutil.Big `json:"baseFeePerGas,omitempty"` } // OverrideAccount indicates the overriding fields of account during the execution @@ -1712,11 +4619,12 @@ type BlockOverrides struct { // if statDiff is set, all diff will be applied first and then execute the call // message. type OverrideAccount struct { - Nonce *hexutil.Uint64 `json:"nonce,omitempty"` - Code *hexutil.Bytes `json:"code,omitempty"` - Balance **hexutil.Big `json:"balance,omitempty"` - State *map[common.Hash]common.Hash `json:"state,omitempty"` - StateDiff *map[common.Hash]common.Hash `json:"stateDiff,omitempty"` + Nonce *hexutil.Uint64 `json:"nonce,omitempty"` + Code *hexutil.Bytes `json:"code,omitempty"` + Balance **hexutil.Big `json:"balance,omitempty"` + State *map[common.Hash]common.Hash `json:"state,omitempty"` + StateDiff *map[common.Hash]common.Hash `json:"stateDiff,omitempty"` + MovePrecompileToAddress *common.Address `json:"MovePrecompileToAddress,omitempty"` } // StateOverride is the collection of overridden accounts. @@ -1724,36 +4632,42 @@ type StateOverride map[common.Address]OverrideAccount // multicallOpts is the wrapper for multicall parameters. type multicallOpts struct { - Blocks []CallBatch `json:"blockStateCalls,omitempty"` - TraceTransfers bool `json:"traceTransfers,omitempty"` + BlockStateCalls []CallBatch `json:"blockStateCalls,omitempty"` + TraceTransfers bool `json:"traceTransfers,omitempty"` + Validation bool `json:"validation,omitempty"` } // CallBatch is a batch of calls to be simulated sequentially. type CallBatch struct { - BlockOverrides *BlockOverrides `json:"blockOverrides,omitempty"` - StateOverrides *StateOverride `json:"stateOverrides,omitempty"` - ECRecoverOverride *hexutil.Bytes `json:"ecrecoverOverride,omitempty"` - Calls []TransactionArgs `json:"calls,omitempty"` + BlockOverrides *BlockOverrides `json:"blockOverrides,omitempty"` + StateOverrides *StateOverride `json:"stateOverrides,omitempty"` + Calls []TransactionArgs `json:"calls,omitempty"` } type blockResult struct { - Number hexutil.Uint64 `json:"number"` - Hash common.Hash `json:"hash"` - Time hexutil.Uint64 `json:"timestamp"` - GasLimit hexutil.Uint64 `json:"gasLimit"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - FeeRecipient common.Address `json:"feeRecipient"` - BaseFee *hexutil.Big `json:"baseFeePerGas"` - Calls []callResult `json:"calls"` + Number hexutil.Uint64 `json:"number"` + Hash common.Hash `json:"hash"` + Time hexutil.Uint64 `json:"timestamp"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + FeeRecipient common.Address `json:"feeRecipient"` + BaseFeePerGas *hexutil.Big `json:"baseFeePerGas"` + PrevRandao *common.Hash `json:"prevRandao,omitempty"` + Calls []callResult `json:"calls"` } type callResult struct { - ReturnValue hexutil.Bytes `json:"return"` - Logs []*types.Log `json:"logs"` - Transfers []transfer `json:"transfers,omitempty"` - GasUsed hexutil.Uint64 `json:"gasUsed"` - Status hexutil.Uint64 `json:"status"` - Error *string `json:"error,omitempty"` + ReturnData hexutil.Bytes `json:"ReturnData"` + Logs []*types.Log `json:"logs"` + Transfers []transfer `json:"transfers,omitempty"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Status hexutil.Uint64 `json:"status"` + Error errorResult `json:"error,omitempty"` +} + +type errorResult struct { + Code *big.Int `json:"code"` + Message *string `json:"message"` } type transfer struct { diff --git a/testgen/utils.go b/testgen/utils.go index 0fa87fc..cb7c083 100644 --- a/testgen/utils.go +++ b/testgen/utils.go @@ -2,11 +2,14 @@ package testgen import ( "bytes" + "errors" "fmt" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" ) func checkHeaderRLP(t *T, n uint64, got []byte) error { @@ -39,6 +42,304 @@ func checkBlockRLP(t *T, n uint64, got []byte) error { return nil } +// I guess Go doesn't have uint256 type? +func checkBlockNumber(value hexutil.Uint64, expected hexutil.Uint64) error { + if value != expected { + return fmt.Errorf("unexpected block number value(have: %d, want: %d)", value, expected) + } + return nil +} + +func checkBlockHash(value common.Hash, expected common.Hash) error { + if value != expected { + return fmt.Errorf("unexpected block hash value(have: %s, want: %s)", value.Hex(), expected.Hex()) + } + return nil +} + +func checkStatus(value hexutil.Uint64, expected hexutil.Uint64) error { + if value != expected { + return fmt.Errorf("expected status (have: %d, want: %d)", value, expected) + } + return nil +} + +func checkError(err error, expectedErrorCode int) error { + var ec rpc.Error + if !errors.As(err, &ec) { + return fmt.Errorf("expected error (have: some other output, want: %d)", expectedErrorCode) + } + if errors.As(err, &ec) && ec.ErrorCode() != expectedErrorCode { + return fmt.Errorf("expected error (have: %d, want: %d)", ec.ErrorCode(), expectedErrorCode) + } + return nil +} + +func blockHashCallerByteCode() *hexutil.Bytes { + //Solidity code: + //contract blockHashCaller { + // function getBlockHash(uint256 blockNumber) public view returns (bytes32 blockHash) { + // blockHash = blockhash(blockNumber); + // } + //} + return hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c8063ee82ac5e14602d575b600080fd5b60436004803603810190603f91906098565b6057565b604051604e919060d7565b60405180910390f35b600081409050919050565b600080fd5b6000819050919050565b6078816067565b8114608257600080fd5b50565b6000813590506092816071565b92915050565b60006020828403121560ab5760aa6062565b5b600060b7848285016085565b91505092915050565b6000819050919050565b60d18160c0565b82525050565b600060208201905060ea600083018460ca565b9291505056fea2646970667358221220a4d7face162688805e99e86526524ac3dadfb01cc29366d0d68b70dadcf01afe64736f6c63430008120033") +} + +func selfDestructor() *hexutil.Bytes { + //Solidity code: + //contract SelfDestructor { + // function destroy() public { + // selfdestruct(payable(0x0)); + // } + //} + return hex2Bytes("6080604052348015600f57600080fd5b506004361060285760003560e01c806383197ef014602d575b600080fd5b60336035565b005b600073ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212208e566fde20a17fff9658b9b1db37e27876fd8934ccf9b2aa308cabd37698681f64736f6c63430008120033") +} + +func getCode() *hexutil.Bytes { + //library GetCode { + // function at(address addr) public view returns (bytes memory code) { + // assembly { + // // retrieve the size of the code, this needs assembly + // let size := extcodesize(addr) + // // allocate output byte array - this could also be done without assembly + // // by using code = new bytes(size) + // code := mload(0x40) + // // new "memory end" including padding + // mstore(0x40, add(code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) + // // store length in memory + // mstore(code, size) + // // actually retrieve the code, this needs assembly + // extcodecopy(addr, add(code, 0x20), 0, size) + // } + // } + //} + return hex2Bytes("73000000000000000000000000000000000000000030146080604052600436106100355760003560e01c8063dce4a4471461003a575b600080fd5b610054600480360381019061004f91906100f8565b61006a565b60405161006191906101b5565b60405180910390f35b6060813b6040519150601f19601f602083010116820160405280825280600060208401853c50919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100c58261009a565b9050919050565b6100d5816100ba565b81146100e057600080fd5b50565b6000813590506100f2816100cc565b92915050565b60006020828403121561010e5761010d610095565b5b600061011c848285016100e3565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561015f578082015181840152602081019050610144565b60008484015250505050565b6000601f19601f8301169050919050565b600061018782610125565b6101918185610130565b93506101a1818560208601610141565b6101aa8161016b565b840191505092915050565b600060208201905081810360008301526101cf818461017c565b90509291505056fea26469706673582212206a5f0cd9f230619fa520fc4b9d4b518643258cad412f2fa33945ce528b4b895164736f6c63430008120033") +} +func gasSpender() *hexutil.Bytes { + //contract GasSpender { + // function spendGas(gasToSpend: uint) view external { + // uint public gasLeftInitially = gasleft(); + // while(true) { + // if (gasLeftInitially - gasleft() >= gasToSpend) return; + // } + // } + //} + return hex2Bytes("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063815b8ab414610030575b600080fd5b61004a600480360381019061004591906100b6565b61004c565b005b60005a90505b60011561007657815a826100669190610112565b106100715750610078565b610052565b505b50565b600080fd5b6000819050919050565b61009381610080565b811461009e57600080fd5b50565b6000813590506100b08161008a565b92915050565b6000602082840312156100cc576100cb61007b565b5b60006100da848285016100a1565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061011d82610080565b915061012883610080565b92508282039050818111156101405761013f6100e3565b5b9291505056fea2646970667358221220a659ba4db729a6ee4db02fcc5c1118db53246b0e5e686534fc9add6f2e93faec64736f6c63430008120033") +} + +func getRevertingContract() *hexutil.Bytes { + //contract RevertingContract { + // fallback() payable external { + // require(false, "Always reverting contract"); + // } + //} + return hex2Bytes("608060405260006042576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401603990609d565b60405180910390fd5b005b600082825260208201905092915050565b7f416c7761797320726576657274696e6720636f6e747261637400000000000000600082015250565b600060896019836044565b91506092826055565b602082019050919050565b6000602082019050818103600083015260b481607e565b905091905056fea264697066735822122005cbbbc709291f66fadc17416c1b0ed4d72941840db11468a21b8e1a0362024c64736f6c63430008120033") +} + +func getEthForwarder() *hexutil.Bytes { + //contract EthForwarder { + // function execute(address payable forwardTo) payable external { + // bool sent = forwardTo.send(msg.value); + // require(sent, "Failed to send Ether"); + // } + //} + return hex2Bytes("60806040526004361061001e5760003560e01c80634b64e49214610023575b600080fd5b61003d6004803603810190610038919061011f565b61003f565b005b60008173ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f193505050509050806100b8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100af906101a9565b60405180910390fd5b5050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100ec826100c1565b9050919050565b6100fc816100e1565b811461010757600080fd5b50565b600081359050610119816100f3565b92915050565b600060208284031215610135576101346100bc565b5b60006101438482850161010a565b91505092915050565b600082825260208201905092915050565b7f4661696c656420746f2073656e64204574686572000000000000000000000000600082015250565b600061019360148361014c565b915061019e8261015d565b602082019050919050565b600060208201905081810360008301526101c281610186565b905091905056fea2646970667358221220563acd6f5b8ad06a3faf5c27fddd0ecbc198408b99290ce50d15c2cf7043694964736f6c63430008120033") +} + +func getEcRecoverOverride() *hexutil.Bytes { + /* + pragma solidity ^0.8.18; + + contract ecRecoverOverride { + struct EcRecoverOverrideParams { + bytes32 hash; + uint8 v; + bytes32 r; + bytes32 s; + address returnAddress; + } + mapping(bytes32 => address) overrideToAddress; + + function setOverrides(EcRecoverOverrideParams[] memory overrides) public { + for (uint i = 0; i < overrides.length; i++) { + setOverride(overrides[i].hash, overrides[i].v, overrides[i].r, overrides[i].s, overrides[i].returnAddress); + } + } + fallback (bytes calldata input) external returns (bytes memory) { + (bytes32 hash, uint8 v, bytes32 r, bytes32 s) = abi.decode(input, (bytes32, uint8, bytes32, bytes32)); + address overridedAddress = overrideToAddress[keccak256(abi.encodePacked(hash, v, r, s))]; + if (overridedAddress == address(0x0)) { + (bool success, bytes memory data) = address(0x0000000000000000000000000000000000123456).call{gas: 10000}(input); + require(success, 'failed to call moved ecrecover at address 0x0000000000000000000000000000000000123456'); + return data; + } else { + return abi.encodePacked(overridedAddress); + } + } + } + */ + return hex2Bytes("608060405234801561001057600080fd5b506004361061003a5760003560e01c806305fdbc81146101ee578063c00692601461020a5761003b565b5b600036606060008060008086868101906100559190610462565b93509350935093506000806000868686866040516020016100799493929190610520565b60405160208183030381529060405280519060200120815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036101bb576000806212345673ffffffffffffffffffffffffffffffffffffffff166127108b8b6040516101249291906105ad565b60006040518083038160008787f1925050503d8060008114610162576040519150601f19603f3d011682016040523d82523d6000602084013e610167565b606091505b5091509150816101ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101a39061066f565b60405180910390fd5b809750505050505050506101e3565b806040516020016101cc9190610709565b604051602081830303815290604052955050505050505b915050805190602001f35b6102086004803603810190610203919061093a565b610226565b005b610224600480360381019061021f9190610983565b6102ec565b005b60005b81518110156102e8576102d5828281518110610248576102476109fe565b5b602002602001015160000151838381518110610267576102666109fe565b5b602002602001015160200151848481518110610286576102856109fe565b5b6020026020010151604001518585815181106102a5576102a46109fe565b5b6020026020010151606001518686815181106102c4576102c36109fe565b5b6020026020010151608001516102ec565b80806102e090610a66565b915050610229565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361035b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035290610afa565b60405180910390fd5b80600080878787876040516020016103769493929190610520565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050505050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b610406816103f3565b811461041157600080fd5b50565b600081359050610423816103fd565b92915050565b600060ff82169050919050565b61043f81610429565b811461044a57600080fd5b50565b60008135905061045c81610436565b92915050565b6000806000806080858703121561047c5761047b6103e9565b5b600061048a87828801610414565b945050602061049b8782880161044d565b93505060406104ac87828801610414565b92505060606104bd87828801610414565b91505092959194509250565b6000819050919050565b6104e46104df826103f3565b6104c9565b82525050565b60008160f81b9050919050565b6000610502826104ea565b9050919050565b61051a61051582610429565b6104f7565b82525050565b600061052c82876104d3565b60208201915061053c8286610509565b60018201915061054c82856104d3565b60208201915061055c82846104d3565b60208201915081905095945050505050565b600081905092915050565b82818337600083830152505050565b6000610594838561056e565b93506105a1838584610579565b82840190509392505050565b60006105ba828486610588565b91508190509392505050565b600082825260208201905092915050565b7f6661696c656420746f2063616c6c206d6f7665642065637265636f766572206160008201527f742061646472657373203078303030303030303030303030303030303030303060208201527f3030303030303030303030303030313233343536000000000000000000000000604082015250565b60006106596054836105c6565b9150610664826105d7565b606082019050919050565b600060208201905081810360008301526106888161064c565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006106ba8261068f565b9050919050565b60008160601b9050919050565b60006106d9826106c1565b9050919050565b60006106eb826106ce565b9050919050565b6107036106fe826106af565b6106e0565b82525050565b600061071582846106f2565b60148201915081905092915050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61077282610729565b810181811067ffffffffffffffff821117156107915761079061073a565b5b80604052505050565b60006107a46103df565b90506107b08282610769565b919050565b600067ffffffffffffffff8211156107d0576107cf61073a565b5b602082029050602081019050919050565b600080fd5b600080fd5b6107f4816106af565b81146107ff57600080fd5b50565b600081359050610811816107eb565b92915050565b600060a0828403121561082d5761082c6107e6565b5b61083760a061079a565b9050600061084784828501610414565b600083015250602061085b8482850161044d565b602083015250604061086f84828501610414565b604083015250606061088384828501610414565b606083015250608061089784828501610802565b60808301525092915050565b60006108b66108b1846107b5565b61079a565b90508083825260208201905060a084028301858111156108d9576108d86107e1565b5b835b8181101561090257806108ee8882610817565b84526020840193505060a0810190506108db565b5050509392505050565b600082601f83011261092157610920610724565b5b81356109318482602086016108a3565b91505092915050565b6000602082840312156109505761094f6103e9565b5b600082013567ffffffffffffffff81111561096e5761096d6103ee565b5b61097a8482850161090c565b91505092915050565b600080600080600060a0868803121561099f5761099e6103e9565b5b60006109ad88828901610414565b95505060206109be8882890161044d565b94505060406109cf88828901610414565b93505060606109e088828901610414565b92505060806109f188828901610802565b9150509295509295909350565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000819050919050565b6000610a7182610a5c565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610aa357610aa2610a2d565b5b600182019050919050565b7f72657475726e20616464726573732063616e6e6f742062652030783000000000600082015250565b6000610ae4601c836105c6565b9150610aef82610aae565b602082019050919050565b60006020820190508181036000830152610b1381610ad7565b905091905056fea2646970667358221220154f5b68ccfa5be744e7245765a3530dac4035052284a68b5dded1945b45075e64736f6c63430008120033") +} + +func getBalanceGetter() *hexutil.Bytes { + /* + pragma solidity ^0.8.18; + + contract BalanceGetter { + function getBalance(address addr) view external returns (uint256) { + return address(addr).balance; + } + } + */ + return hex2Bytes("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f8b2cb4f14610030575b600080fd5b61004a600480360381019061004591906100e4565b610060565b604051610057919061012a565b60405180910390f35b60008173ffffffffffffffffffffffffffffffffffffffff16319050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100b182610086565b9050919050565b6100c1816100a6565b81146100cc57600080fd5b50565b6000813590506100de816100b8565b92915050565b6000602082840312156100fa576100f9610081565b5b6000610108848285016100cf565b91505092915050565b6000819050919050565b61012481610111565b82525050565b600060208201905061013f600083018461011b565b9291505056fea2646970667358221220172c443a163d8a43e018c339d1b749c312c94b6de22835953d960985daf228c764736f6c63430008120033") +} + +func getEcRecoverCaller() *hexutil.Bytes { + /* + contract EcRecoverCaller { + function callEcRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) pure external returns (address) { + return ecrecover(hash, v, r, s); + } + } + */ + return hex2Bytes("608060405234801561001057600080fd5b506004361061002b5760003560e01c8063265dc68c14610030575b600080fd5b61004a6004803603810190610045919061012f565b610060565b60405161005791906101d7565b60405180910390f35b6000600185858585604051600081526020016040526040516100859493929190610210565b6020604051602081039080840390855afa1580156100a7573d6000803e3d6000fd5b505050602060405103519050949350505050565b600080fd5b6000819050919050565b6100d3816100c0565b81146100de57600080fd5b50565b6000813590506100f0816100ca565b92915050565b600060ff82169050919050565b61010c816100f6565b811461011757600080fd5b50565b60008135905061012981610103565b92915050565b60008060008060808587031215610149576101486100bb565b5b6000610157878288016100e1565b94505060206101688782880161011a565b9350506040610179878288016100e1565b925050606061018a878288016100e1565b91505092959194509250565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101c182610196565b9050919050565b6101d1816101b6565b82525050565b60006020820190506101ec60008301846101c8565b92915050565b6101fb816100c0565b82525050565b61020a816100f6565b82525050565b600060808201905061022560008301876101f2565b6102326020830186610201565b61023f60408301856101f2565b61024c60608301846101f2565b9594505050505056fea26469706673582212206c520c25fdd1108030611d622b1aa1038ff1b4820f27608d6867d249c1e3ed7664736f6c63430008120033") +} + +func getStorageTester() *hexutil.Bytes { + /* + pragma solidity ^0.8.18; + + contract StorageTester { + uint256 public value0; + uint256 public value1; + + function setValue(uint256 slot, uint256 value) external { + require(slot < 2, "too big slot"); + if (slot == 0) { value0 = value; return; } + if (slot == 1) { value1 = value; return; } + } + function getValue(uint256 slot) view external returns (uint256 ReturnData) { + require(slot < 2, "too big slot"); + if (slot == 0) { ReturnData = value0; } + if (slot == 1) { ReturnData = value1; } + } + } + */ + return hex2Bytes("608060405234801561001057600080fd5b506004361061004c5760003560e01c80630ff4c916146100515780633033413b1461008157806344e12f871461009f5780637b8d56e3146100bd575b600080fd5b61006b600480360381019061006691906101f6565b6100d9565b6040516100789190610232565b60405180910390f35b61008961013f565b6040516100969190610232565b60405180910390f35b6100a7610145565b6040516100b49190610232565b60405180910390f35b6100d760048036038101906100d2919061024d565b61014b565b005b60006002821061011e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610115906102ea565b60405180910390fd5b6000820361012c5760005490505b6001820361013a5760015490505b919050565b60015481565b60005481565b6002821061018e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610185906102ea565b60405180910390fd5b600082036101a257806000819055506101b7565b600182036101b657806001819055506101b7565b5b5050565b600080fd5b6000819050919050565b6101d3816101c0565b81146101de57600080fd5b50565b6000813590506101f0816101ca565b92915050565b60006020828403121561020c5761020b6101bb565b5b600061021a848285016101e1565b91505092915050565b61022c816101c0565b82525050565b60006020820190506102476000830184610223565b92915050565b60008060408385031215610264576102636101bb565b5b6000610272858286016101e1565b9250506020610283858286016101e1565b9150509250929050565b600082825260208201905092915050565b7f746f6f2062696720736c6f740000000000000000000000000000000000000000600082015250565b60006102d4600c8361028d565b91506102df8261029e565b602082019050919050565b60006020820190508181036000830152610303816102c7565b905091905056fea2646970667358221220ceea194bb66b5b9f52c83e5bf5a1989255de8cb7157838eff98f970c3a04cb3064736f6c63430008120033") +} + +func getBlockProperties() *hexutil.Bytes { + /* + pragma solidity ^0.8.18; + contract blockProperties { + fallback (bytes calldata input) external returns (bytes memory) { + return abi.encode( + block.basefee, + block.chainid, + block.coinbase, + block.difficulty, + block.gaslimit, + block.number, + block.timestamp, + gasleft(), + tx.gasprice, + blockhash(block.number - 1) + ); + } + } + */ + return hex2Bytes("608060405234801561001057600080fd5b506000366060484641444543425a3a60014361002c919061009b565b406040516020016100469a99989796959493929190610138565b6040516020818303038152906040529050915050805190602001f35b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006100a682610062565b91506100b183610062565b92508282039050818111156100c9576100c861006c565b5b92915050565b6100d881610062565b82525050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610109826100de565b9050919050565b610119816100fe565b82525050565b6000819050919050565b6101328161011f565b82525050565b60006101408201905061014e600083018d6100cf565b61015b602083018c6100cf565b610168604083018b610110565b610175606083018a6100cf565b61018260808301896100cf565b61018f60a08301886100cf565b61019c60c08301876100cf565b6101a960e08301866100cf565b6101b76101008301856100cf565b6101c5610120830184610129565b9b9a505050505050505050505056fea26469706673582212205139ae3ba8d46d11c29815d001b725f9840c90e330884ed070958d5af4813d8764736f6c63430008120033") +} +func payableFallBack() *hexutil.Bytes { + /* + pragma solidity ^0.8.18; + contract payableFallBack { + fallback (bytes calldata input) external payable returns (bytes memory) {} + } + */ + return hex2Bytes("60806040525f366060915050805190602001f3fea2646970667358221220a6fbca1301e2861ff1227c7478f4351a0978cca210732aaf5d036123a0d2f68664736f6c63430008160033") +} + +func delegateCaller() *hexutil.Bytes { + /* + pragma solidity ^0.8.20; + + contract delegateCaller { + function delegate(address contract) public payable returns (bool success, bytes memory data) { + return contract.delegatecall(""); + } + } + */ + return hex2Bytes("60806040526004361061001d575f3560e01c80635c19a95c14610021575b5f80fd5b61003b6004803603810190610036919061011d565b610052565b6040516100499291906101ec565b60405180910390f35b5f60608273ffffffffffffffffffffffffffffffffffffffff1660405161007890610247565b5f60405180830381855af49150503d805f81146100b0576040519150601f19603f3d011682016040523d82523d5f602084013e6100b5565b606091505b5091509150915091565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6100ec826100c3565b9050919050565b6100fc816100e2565b8114610106575f80fd5b50565b5f81359050610117816100f3565b92915050565b5f60208284031215610132576101316100bf565b5b5f61013f84828501610109565b91505092915050565b5f8115159050919050565b61015c81610148565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561019957808201518184015260208101905061017e565b5f8484015250505050565b5f601f19601f8301169050919050565b5f6101be82610162565b6101c8818561016c565b93506101d881856020860161017c565b6101e1816101a4565b840191505092915050565b5f6040820190506101ff5f830185610153565b818103602083015261021181846101b4565b90509392505050565b5f81905092915050565b50565b5f6102325f8361021a565b915061023d82610224565b5f82019050919050565b5f61025182610227565b915081905091905056fea2646970667358221220c79623409cf56de66f689f36463cb9aa88ed853501e557889625b16b1a602b2764736f6c63430008160033") +} + +func delegateCaller2() *hexutil.Bytes { + /* + + // SPDX-License-Identifier: MIT + pragma solidity ^0.8.20; + + contract delegateCaller2 { + fallback() external payable{ + // solium-disable-next-line security/no-inline-assembly + assembly { + let masterCopy := and(0x000000000000000000000000c200000000000000000000000000000000000000, 0xffffffffffffffffffffffffffffffffffffffff) + // 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s + if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) { + mstore(0, masterCopy) + return(0, 0x20) + } + calldatacopy(0, 0, calldatasize()) + let success := delegatecall(gas(), masterCopy, 0, calldatasize(), 0, 0) + returndatacopy(0, 0, returndatasize()) + if eq(success, 0) { revert(0, returndatasize()) } + return(0, returndatasize()) + } + } + } + */ + return hex2Bytes("608060405273ffffffffffffffffffffffffffffffffffffffff73c200000000000000000000000000000000000000167fa619486e000000000000000000000000000000000000000000000000000000005f3503605e57805f5260205ff35b365f80375f80365f845af43d5f803e5f81036077573d5ffd5b3d5ff3fea26469706673582212206b787fe3e60b14a5c449d37005afac7b1803ee7c87e12d2740b96a158f34802a64736f6c63430008160033") +} + +func extCodeHashContract() *hexutil.Bytes { + /* + pragma solidity ^0.8.18; + contract exCodeHashContract { + function isContract(address account) public view returns (bool) { + bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; + bytes32 codeHash; + assembly { codeHash := extcodehash(account) } + return (codeHash != accountHash && codeHash != 0x0); + } + function getExtCodeHash(address account) public view returns (bytes32) { + bytes32 codeHash; + assembly { codeHash := extcodehash(account) } + return codeHash; + } + function at(address _addr) public view returns (bytes memory o_code) { + assembly { + // retrieve the size of the code, this needs assembly + let size := extcodesize(_addr) + // allocate output byte array - this could also be done without assembly + // by using o_code = new bytes(size) + o_code := mload(0x40) + // new "memory end" including padding + mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), xor(0x1f, not(0))))) + // store length in memory + mstore(o_code, size) + // actually retrieve the code, this needs assembly + extcodecopy(_addr, add(o_code, 0x20), 0, size) + } + } + */ + return hex2Bytes("608060405234801561000f575f80fd5b506004361061003f575f3560e01c80631627905514610043578063b9724d6314610073578063dce4a447146100a3575b5f80fd5b61005d600480360381019061005891906101b3565b6100d3565b60405161006a91906101f8565b60405180910390f35b61008d600480360381019061008891906101b3565b61011a565b60405161009a9190610229565b60405180910390f35b6100bd60048036038101906100b891906101b3565b610129565b6040516100ca91906102cc565b60405180910390f35b5f807fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4705f1b90505f833f905081811415801561011157505f801b8114155b92505050919050565b5f80823f905080915050919050565b6060813b60405191505f19601f18601f6020830101168201604052808252805f60208401853c50919050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61018282610159565b9050919050565b61019281610178565b811461019c575f80fd5b50565b5f813590506101ad81610189565b92915050565b5f602082840312156101c8576101c7610155565b5b5f6101d58482850161019f565b91505092915050565b5f8115159050919050565b6101f2816101de565b82525050565b5f60208201905061020b5f8301846101e9565b92915050565b5f819050919050565b61022381610211565b82525050565b5f60208201905061023c5f83018461021a565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f5b8381101561027957808201518184015260208101905061025e565b5f8484015250505050565b5f601f19601f8301169050919050565b5f61029e82610242565b6102a8818561024c565b93506102b881856020860161025c565b6102c181610284565b840191505092915050565b5f6020820190508181035f8301526102e48184610294565b90509291505056fea26469706673582212206a23e83d04cb5f5ab00353d719afd8181b39baa39277b8cfa111197637a4f26064736f6c63430008160033") +} + +func getUint64Ptr(value hexutil.Uint64) *hexutil.Uint64 { + newUint64 := new(hexutil.Uint64) + newUint64 = &value + return newUint64 +} func checkBlockReceipts(t *T, n uint64, got []*types.Receipt) error { b := t.chain.GetBlockByNumber(n) if b == nil {