-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add eth_multicall , support array of eth_call for simulation across multiple blocks #383
Changes from 55 commits
5556499
95822af
602d2bf
6ce85e2
2fcbf1d
046502a
e4ec926
0fbdf40
ccfc70b
7590b80
bbf34e8
c147f92
23ef5b8
73897a7
152cf09
c8abc82
14e9b35
94786e2
4b63f49
dc38150
9463ba6
51fad35
1802812
6bd8e50
2cf1b8e
ff39b4a
81e6730
4a42aa2
7d159f8
7787ce8
d8ca0a4
90016c0
d596116
0f8426a
6928d24
9528d89
c6a3397
b587f7f
818ef4a
3c7c14e
1814075
c3999c2
1a92538
41213eb
e785349
024958c
385791c
557038a
e2ae78f
d1cce31
dfdab76
2dde369
dd0fe15
6cde1ab
62ab430
bde1830
8d8725a
dc4017b
a3f89ad
28d995f
50c09b8
be26261
0630df2
e861a4a
cba6d4e
2196a29
40daadb
d831c5f
995acb2
88f1f7b
235c40c
e817b74
78b3a92
cb5d419
8d3b4d4
5f09367
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,347 @@ | ||||||
MultiCallPayload: | ||||||
title: Arguments for multi call | ||||||
required: | ||||||
- blockStateCalls | ||||||
properties: | ||||||
blockStateCalls: | ||||||
title: Block State Calls | ||||||
description: Definition of blocks that can contain calls and overrides | ||||||
$ref: '#/components/schemas/BlockStateCalls' | ||||||
traceTransfers: | ||||||
title: Trace ETH Transfers | ||||||
description: |- | ||||||
Adds ETH transfers as ERC20 transfer events to the logs. These transfers have emitter contract parameter set as address(0x0). | ||||||
Default: false. | ||||||
type: boolean | ||||||
validation: | ||||||
title: Validation | ||||||
description: |- | ||||||
When true, the multicall does all validations that a normal EVM would do, except contract sender and signature checks. When false, multicall behaves like eth_call. | ||||||
Default: false. | ||||||
type: boolean | ||||||
BlockStateCalls: | ||||||
title: Array of block state calls to be executed at specific, optional block/state. | ||||||
description: "The size of this array may be limited depending on the client as a DOS protection. 256 is a common/recommended limit as it is the same limit used by BLOCKHASH opcode." | ||||||
type: array | ||||||
properties: | ||||||
blockOverrides: | ||||||
title: Block overrides | ||||||
description: |- | ||||||
Block overrides can be used to replace fields in a block. | ||||||
default: no block override. | ||||||
$ref: '#/components/schemas/BlockOverrides' | ||||||
stateOverrides: | ||||||
title: State overrides | ||||||
description: |- | ||||||
State overrides can be used to replace existing blockchain state with new state. | ||||||
Default: no state overrides | ||||||
$ref: '#/components/schemas/StateOverrides' | ||||||
calls: | ||||||
type: array | ||||||
title: calls | ||||||
description: |- | ||||||
List of transactions to execute at this block/state. | ||||||
Default: [] | ||||||
items: | ||||||
$ref: '#/components/schemas/GenericCallTransaction' | ||||||
StateOverrides: | ||||||
title: Dictionary of addresses in the state to be overridden | ||||||
type: object | ||||||
patternProperties: | ||||||
'^0x[a-fA-F0-9]{40}$': | ||||||
$ref: '#/components/schemas/AccountOverride' | ||||||
additionalProperties: false | ||||||
AccountOverride: | ||||||
title: Details of an account to be overridden | ||||||
type: object | ||||||
oneOf: | ||||||
- $ref: '#/components/schemas/AccountOverrideState' | ||||||
- $ref: '#/components/schemas/AccountOverrideStateDiff' | ||||||
AccountOverrideState: | ||||||
title: Account override with whole storage replacement | ||||||
description: It is possible to override any kind of address (EOA's, contracts and precompiles) | ||||||
required: | ||||||
- state | ||||||
properties: | ||||||
nonce: | ||||||
title: Nonce | ||||||
$ref: '#/components/schemas/uint64' | ||||||
balance: | ||||||
title: Balance | ||||||
$ref: '#/components/schemas/uint256' | ||||||
code: | ||||||
title: Code | ||||||
$ref: '#/components/schemas/bytes' | ||||||
moveToAddress: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
title: MoveToAddress | ||||||
description: An Address where the current address contents are moved into (includes nonce, state, balance and code). This move is done before the other overrides take an action. | ||||||
$ref: '#/components/schemas/address' | ||||||
state: | ||||||
title: Storage | ||||||
description: Key-value mapping to override all slots in the account storage before executing the call. This functions similar to eth_call's state parameter. | ||||||
$ref: '#/components/schemas/AccountStorage' | ||||||
AccountOverrideStateDiff: | ||||||
title: Account override with partial storage modification | ||||||
required: | ||||||
- stateDiff | ||||||
properties: | ||||||
nonce: | ||||||
title: Nonce | ||||||
$ref: '#/components/schemas/uint64' | ||||||
balance: | ||||||
title: Balance | ||||||
$ref: '#/components/schemas/uint256' | ||||||
code: | ||||||
title: Code | ||||||
$ref: '#/components/schemas/bytes' | ||||||
moveToAddress: | ||||||
title: MoveToAddress | ||||||
$ref: '#/components/schemas/address' | ||||||
description: An Address where the current address contents are moved into (includes nonce, state, balance and code). This move is done before the other overrides take an action. | ||||||
stateDiff: | ||||||
title: Storage difference | ||||||
description: Key-value mapping to override individual slots in the account storage before executing the call. This functions similar to eth_call's state parameter. | ||||||
$ref: '#/components/schemas/AccountStorage' | ||||||
AccountStorage: | ||||||
title: Storage slots for an account | ||||||
type: object | ||||||
patternProperties: | ||||||
'^0x[a-fA-F0-9]{64}$': | ||||||
$ref: '#/components/schemas/hash32' | ||||||
additionalProperties: false | ||||||
BlockOverrides: | ||||||
title: Context fields related to the block being executed | ||||||
type: object | ||||||
properties: | ||||||
number: | ||||||
title: Number | ||||||
$ref: '#/components/schemas/uint64' | ||||||
description: When overriding block numbers across multiple blocks, block number need to be increasing. Skipping over blocks numbers is possible. If block number is not specified, it's incremented by one for each block. | ||||||
prevRandao: | ||||||
title: The Previous value of randomness beacon | ||||||
$ref: '#/components/schemas/uint256' | ||||||
time: | ||||||
title: Time | ||||||
$ref: '#/components/schemas/uint64' | ||||||
description: When overriding Time across multiple blocks, Time need to be increasing. If time is not specified, it's incremented by one for each block. | ||||||
gasLimit: | ||||||
title: Gas limit | ||||||
$ref: '#/components/schemas/uint64' | ||||||
feeRecipient: | ||||||
title: Fee Recipient (also known as coinbase) | ||||||
$ref: '#/components/schemas/address' | ||||||
baseFeePerGas: | ||||||
title: Base fee per unit of gas | ||||||
$ref: '#/components/schemas/uint256' | ||||||
MultiCallResult: | ||||||
title: Full results of multi call | ||||||
type: array | ||||||
items: | ||||||
$ref: '#/components/schemas/MultiCallBlockResult' | ||||||
MultiCallBlockResult: | ||||||
title: Result of multicall block-level, with array of calls | ||||||
type: object | ||||||
properties: | ||||||
number: | ||||||
title: Number | ||||||
$ref: '#/components/schemas/uint64' | ||||||
hash: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If possible I'd like us to remove this field. I feel it'll be a random hash that doesn't have a use-case anyway and to produce it we have to merkleize the state as well as receipts and withdrawals. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe blocks still need a hash, because the |
||||||
title: Block Hash | ||||||
$ref: '#/components/schemas/hash32' | ||||||
timestamp: | ||||||
title: Timestamp | ||||||
$ref: '#/components/schemas/uint64' | ||||||
gasLimit: | ||||||
title: Gas limit | ||||||
$ref: '#/components/schemas/uint64' | ||||||
gasUsed: | ||||||
title: Gas used | ||||||
$ref: '#/components/schemas/uint64' | ||||||
feeRecipient: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: I realize the engine API and CL folks call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with this, it seemed like feeRecipient was going to be the one that everyone ran with right after 1559, but it slowly made its way back to coinbase. Any disagreement @MicahZoltu ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I pretty strongly favor accurate names rather than names that match internal implementation details that exist for legacy reasons. In this case, That being said, if |
||||||
title: Fee Recipient (also known as coinbase) | ||||||
$ref: '#/components/schemas/address' | ||||||
baseFeePerGas: | ||||||
title: Base fee per unit of gas | ||||||
$ref: '#/components/schemas/uint256' | ||||||
calls: | ||||||
$ref: '#/components/schemas/CallResults' | ||||||
KillariDev marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
prevRandao: | ||||||
title: The Previous value of randomness beacon | ||||||
$ref: '#/components/schemas/uint256' | ||||||
CallResults: | ||||||
title: Results of multi call within block | ||||||
type: array | ||||||
items: | ||||||
oneOf: | ||||||
- $ref: '#/components/schemas/CallResultFailure' | ||||||
- $ref: '#/components/schemas/CallResultSuccess' | ||||||
- $ref: '#/components/schemas/CallResultInvalid' | ||||||
CallResultFailure: | ||||||
title: Result of call failure | ||||||
description: The error messages are suggestions, and clients might implement different error messages. However, the error codes are enforced by the spec. | ||||||
type: object | ||||||
required: | ||||||
- status | ||||||
- returnData | ||||||
- gasUsed | ||||||
- error | ||||||
properties: | ||||||
status: | ||||||
title: Call Status Failure | ||||||
type: string | ||||||
pattern: ^0x0$ | ||||||
returnData: | ||||||
title: Return data | ||||||
$ref: '#/components/schemas/bytes' | ||||||
gasUsed: | ||||||
title: Return gasUsed | ||||||
$ref: '#/components/schemas/uint64' | ||||||
error: | ||||||
oneOf: | ||||||
- code: -32000 | ||||||
message: 'Execution reverted' | ||||||
- code: -32015 | ||||||
message: 'VM execution error' | ||||||
CallResultSuccess: | ||||||
title: Result of call success | ||||||
type: object | ||||||
required: | ||||||
- status | ||||||
- returnData | ||||||
- gasUsed | ||||||
epheph marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
- logs | ||||||
properties: | ||||||
status: | ||||||
title: Call Status Success | ||||||
type: string | ||||||
pattern: ^0x1$ | ||||||
returnData: | ||||||
title: Return data | ||||||
$ref: '#/components/schemas/bytes' | ||||||
gasUsed: | ||||||
title: Return gasUsed | ||||||
$ref: '#/components/schemas/uint64' | ||||||
logs: | ||||||
title: Return logs | ||||||
type: array | ||||||
items: | ||||||
$ref: '#/components/schemas/CallResultLog' | ||||||
CallResultInvalid: | ||||||
title: Result of call not being valid (nonce, baseFeePerGas, etc) | ||||||
description: The error messages are suggestions, and clients might implement different error messages. However, the error codes are enforced by the spec. | ||||||
type: object | ||||||
required: | ||||||
- status | ||||||
- error | ||||||
properties: | ||||||
status: | ||||||
title: Call Status Invalid | ||||||
type: string | ||||||
pattern: ^0x2$ | ||||||
error: | ||||||
oneOf: | ||||||
- code: -32000 | ||||||
message: Missing or invalid parameters | ||||||
- code: -32005 | ||||||
message: Limit exceeded | ||||||
- code: -32015 | ||||||
messagE: Execution error | ||||||
- code: -32016 | ||||||
message: Timeout | ||||||
- code: -32602 | ||||||
message: Invalid params | ||||||
- code: -32603 | ||||||
message: The Ethereum node encountered an internal error | ||||||
- code: -38010 | ||||||
message: Transactions nonce is too low | ||||||
- code: -38011 | ||||||
message: Transactions nonce is too high | ||||||
- code: -38012 | ||||||
message: Transactions baseFeePerGas is too low | ||||||
- code: -38013 | ||||||
message: Not enough gas provided to pay for intrinsic gas for a transaction | ||||||
- code: -38014 | ||||||
message: Insufficient funds to pay for gas fees and value fo a transaction | ||||||
- code: -38015 | ||||||
message: Block gas limit exceeded by the block's transactions | ||||||
- code: -38020 | ||||||
message: Block number in sequence did not increase | ||||||
- code: -38021 | ||||||
message: Block timestamp in sequence did not increase | ||||||
- code: -38022 | ||||||
message: MoveToAddress referenced itself in replacement | ||||||
- code: -38023 | ||||||
message: Multiple MoveToAddress referencing the same address to replace | ||||||
CallResultLog: | ||||||
title: log | ||||||
type: object | ||||||
required: | ||||||
- logIndex | ||||||
- blockhash | ||||||
- blockNumber | ||||||
- address | ||||||
- data | ||||||
- topics | ||||||
properties: | ||||||
logIndex: | ||||||
title: log index | ||||||
$ref: '#/components/schemas/uint256' | ||||||
blockHash: | ||||||
title: block hash | ||||||
$ref: '#/components/schemas/hash32' | ||||||
blockNumber: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. inconsistent with BlockOverrides.number where it is uint256 and here uint64, I would change BlockOverrides.number to be uint64. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed (changed to uint64). |
||||||
title: block number | ||||||
$ref: '#/components/schemas/uint64' | ||||||
address: | ||||||
title: address | ||||||
description: When trace transfers is enabled, this field is address(0x0) for ETH transfers. | ||||||
$ref: '#/components/schemas/address' | ||||||
data: | ||||||
title: data | ||||||
$ref: '#/components/schemas/bytes' | ||||||
topics: | ||||||
title: topics | ||||||
type: array | ||||||
items: | ||||||
$ref: '#/components/schemas/bytes32' | ||||||
GenericCallTransaction: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not to use #/components/schemas/GenericTransaction - it has same fields... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could use the same, but its missing default values |
||||||
type: object | ||||||
title: Transaction object type for call | ||||||
properties: | ||||||
type: | ||||||
title: type | ||||||
$ref: '#/components/schemas/byte' | ||||||
nonce: | ||||||
title: nonce | ||||||
$ref: '#/components/schemas/uint64' | ||||||
to: | ||||||
title: to address | ||||||
$ref: '#/components/schemas/address' | ||||||
from: | ||||||
title: from address | ||||||
$ref: '#/components/schemas/address' | ||||||
gas: | ||||||
title: gas limit | ||||||
$ref: '#/components/schemas/uint64' | ||||||
value: | ||||||
title: value | ||||||
$ref: '#/components/schemas/uint256' | ||||||
input: | ||||||
title: input data | ||||||
$ref: '#/components/schemas/bytes' | ||||||
gasPrice: | ||||||
title: gas price | ||||||
description: The gas price willing to be paid by the sender in wei | ||||||
$ref: '#/components/schemas/uint256' | ||||||
maxPriorityFeePerGas: | ||||||
title: max priority fee per gas | ||||||
description: Maximum fee per gas the sender is willing to pay to miners in wei | ||||||
$ref: '#/components/schemas/uint256' | ||||||
maxFeePerGas: | ||||||
title: max fee per gas | ||||||
description: The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei | ||||||
$ref: '#/components/schemas/uint256' | ||||||
accessList: | ||||||
title: accessList | ||||||
description: EIP-2930 access list | ||||||
$ref: '#/components/schemas/AccessList' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
>> {"jsonrpc":"2.0","id":1,"method":"eth_multicallV1","params":[[],"latest"]} | ||
<< {"jsonrpc":"2.0","id":1,"result":[]} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
>> {"jsonrpc":"2.0","id":1,"method":"eth_multicallV1","params":[[{"stateOverrides":[{"nonce":"0x1", "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "balance": "0xde0b6b3a7640000", "state": {"0x2292f0db49e1af24fbcac7f32b7537f334244455ad0ed0b46a78202982e7b70d": "0xde0b6b3a7640000", "0x877bd4632ef8c8ddc43b67e5a4511d939eadb612553c8a060670e05ebb1bb83c": "0xde0b6b3a7640000"}}],"calls":[{"from":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","to":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","data":"0x18160ddd"}]}],"latest"]} | ||
<< {"jsonrpc":"2.0","id":1,"result":[{"status":"0x1","returnData":"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000","gasUsed":"0x5338","logs":[]}]} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
>> {"jsonrpc": "2.0", "id": 1, "method": "eth_multicallV1", "params": [ [ { "calls": [ { "to": "0x4B62D7C9C4e5c7150Eda45F7552a25C7Cd726bF6", "data": "0x42cbb15c" } ] }, { "blockOverrides": { "number": "0x4999999" }, "calls": [ { "to": "0x4B62D7C9C4e5c7150Eda45F7552a25C7Cd726bF6", "data": "0x42cbb15c" } ] } ], "latest" ] } | ||
<< {"jsonrpc":"2.0","id":1,"result":[{"status":"0x1","returnData":"0x0000000000000000000000000000000000000000000000000000000001031b64","gasUsed":"0x5338","logs":[]},{"status":"0x1","returnData":"0x0000000000000000000000000000000000000000000000000000000004999999","gasUsed":"0x5338","logs":[]}]} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
>> {"jsonrpc":"2.0","id":1,"method":"eth_multicallV1","params":[[],"latest", false]} | ||
<< {"jsonrpc":"2.0","id":1,"result":[]} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
>> {"jsonrpc":"2.0","id":1,"method":"eth_multicallV1","params":[[{"calls":[{"from":"0xde67356daf70aba7582d9b313d29de02681dfbd8","to":"0x5cd267914528e83422cafa6718153eafc67a9e6c","data":"0xef41d20800000000000000000000000000000000000000000000000000000000000000017691e8f7c821ff647ecb48aab3c593b316d7c705fa9ccc61778ceab57acd1bb200000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000"}]}],"latest"]} | ||
<< {"jsonrpc":"2.0","id":1,"result":[{"status":"0x0","returnData":"0x0000000000000000000000000000000000000000000000000000000000000012","gasUsed":"0x5338","error":{"code":-32015,"message":"VM execution error.","data":"Reverted 0x5472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472616e7366657246726f6d206661696c6564"}}]} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
>> {"jsonrpc":"2.0","id":1,"method":"eth_multicallV1","params":[[{"calls":[{"to":"0x6b175474e89094c44da98b954eedeac495271d0f","data":"0x313ce567"}]}],"latest"]} | ||
<< {"jsonrpc":"2.0","id":1,"result":[ { "number": "0x1", "hash": "0x2222222222222222222222222222222222222222222222222222222222222222", "timestamp": "0x929292", "gasLimit": "0x2000", "gasUsed": "0x1000", "feeRecipient": "0x1111111111111111111111111111111111111111", "baseFeePerGas": "0x827129", "calls": [ { "status": "0x1", "returnData": "0x0000000000000000000000000000000000000000000000000000000000000012", "gasUsed": "0x5338", "logs": [] } ] } ]} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,3 +57,4 @@ randao | |
src | ||
https | ||
forkchoiceupdatedresponsev | ||
feeRecipient |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess
blockOverride
andcalls
should be mandatory variables here andstateOverrides
optional?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No I'd leave
blockOverride
also optional. For a multi-call single-block simulation users shouldn't need to override block fields.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally prefer empty arrays over missing/null arrays. I find it leads to improved developer ergonomics (no need for conditional branching on presence, you can just map/loop over the array). Functionally, I think both are fine though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if blockoverride is empty, which block is used? latest I guess?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The third parameter is a "block tag" which serves to determine the fallback block fields (as well as base state). Something we have to consider is when there are multiple batches of calls (without block override fields):
I'm in favor of 1. I think we should keep this method dumb rather than do something unexpected for users.