-
Notifications
You must be signed in to change notification settings - Fork 377
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
Conversation
…lls, does not change execution)
openrpc.json diff:
|
…H balance to a random account
Hey @epheph cool that you took on this project. First I want to add a clarification, please correct me if I'm wrong: State overrides of each block are applied to the post state of the previous block. I.e. if you want to have a test contract you don't need to override it in every block separately. On another point: I think it's nice to have call results share the same block-structure. It would be easier to map a response to the request. |
Yes exactly, state overrides are conceptually similar to a transaction type that raw updates storage slots. I went back and forth on the flattening of the calls. One argument in support of your suggestion about retaining block array structure is that it would give you a logical place to put block-level information (randDao, fee recipient) which might be useful. @MicahZoltu any opposition to adding structure to the response? |
As I see it you do retain the block-structure for the calls. The results are flat however. This is the inconsistency I was pointing out. |
Right, definitely need it for calls, I can easily see the argument for maintaining that structure in the response. I'll propose a change to see how it looks. |
Thinking more on this, I think I would like to see the response be shaped as close to an Generally speaking though, I agree with @s1na that the result should not be flat. Each block in the result should have the call results nested somewhere inside of it. |
Would this allow us to know which contracts were created in the multicall? And what about what storage addresses are changed? Or would getting of this information be too heavy for nodes? |
Good question, i imagine that is helpful, especially contract deployments. i wonder where it would go? Inside the call object, next to logs? @KillariDev |
stateOverrides: | ||
title: State overrides | ||
$ref: '#/components/schemas/StateOverrides' | ||
calls: |
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
and calls
should be mandatory variables here and stateOverrides
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):
- They all inherit the "base block" fields. In this case if user provides 2 batches without fields, it will be equivalent to appending the second batch of calls to the first.
- We magically update the fields of future batches
I'm in favor of 1. I think we should keep this method dumb rather than do something unexpected for users.
So I've implemented this spec in geth: https://github.com/s1na/go-ethereum/tree/multicall. Now planning on writing more tests.
Sorry for voicing my opinion on this with delay, but I think this method is different enough from existing ones that tooling will have to write new logic for it. Repeating request fields in the response seems wasteful. |
I agree that this will require some tooling to be written. However, it would be very nice to just have something like |
If we want to use existing structures, IMO the result should be |
In many cases, I think the client will be filling in much of the block data and it feels like it is a good idea to let the user know what was filled in. For example, the base fee will be calculated and set by the client and we should be returning that to the user in the response somewhere. |
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
docs/multicall-notes.md
Outdated
| nonce | Defaults to correct nonce | | ||
| to | null | | ||
| from | 0x0 | | ||
| gas limit | Remaining gas in the curent block | |
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.
We should make it clear that this is calculated just before the transaction is executed, after gas has been consumed by previous transactions in the block.
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Co-authored-by: Micah Zoltu <micah@zoltu.net>
Superceded by #484. |
Closing in favor of #484 |
This spec is based heavily on #312 from @s1na (
eth_batchCall
), but encompasses some of the ideas presented by @MicahZoltu and others. The original spec supported a single block override, and did not allow transactions to appear on different blocks with different states. The original spec allowed you to submit:This spec changes the spec to encapslate most of the parameters into an array:
an Array of zero or more of the following:
In addition, I added log return and gasUsed in the array response of call result/error (although I have not confirmed if this is difficult to implement).
The idea here is that simulation needs are getting more advanced:
Lacking an API like this will just lead to more non-standard proprietary solutions (Alchemy forking mainnet, Flashbots eth_callBundle, etc) that will not be accessible on self-hosted nodes.
For the "storage overrides" (which includes the ability to replace code in accounts), we should allow replacing code at precompiles, allowing for
ecrecover
to serve false results. More dapps are utilizing off-chain EIP-712 signatures (notably, Uniswap), and overriding tx.from is no longer enough to perform simulations without a private key. Alternatively, we could add an ecrecoverOverrides (at the same level as storage overrides above).