Multicall context manager
See original GitHub issueOverview
Brownie should offer first-class support for multicall via a context manager:
with multicall:
a = foo.bar()
b = foo.barfoo()
c = bar.foobar(a)
d = bar.otherbar()
Specification
While the context manager is open, a middleware intercepts all eth_call
requests and returns objects which represent the result of the call once it completes. When the context manager closes, or there is an attempt to use one of the pending call objects, a single multicall is made to get the actual values.
In the above example a
and b
would end up joined as a single multicall that executes when a
is needed for c
. Then c
and d
would happen as a multicall when the context manager exits.
We can bundle the MultiCall2
implementation as used by yearn, which uses try/catch to prevent calls from failing. Attempting to interact with object for the specific result that failed in the multicall should raise a ValueError
. (Or possibly the object simply returns None
? Maybe this behavior is determined via a kwarg in the context manager?)
It is important to consider thread safety during the implementation. The context manager must introspect to be aware of which thread it is running on, and not also batch eth_call
operations coming from unrelated threads.
To ensure consistent behaviour across network, we can use Geth’s state override feature to simulate the existence of a multicall contract for the purposes of the call. This is badass in so many ways I’m not even sure where to begin 😈 For dev networks where this isn’t possible, deploy multicall silently the first time it’s required.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:6 (4 by maintainers)
For sure.
I think a clean implementation would have at least two pieces: a private function to commit a single transaction block (like scenario A above), and then something higher level (probably public) that calls that function every time (1) the multicall needs to be executed, and (2) when the context manager
__exit__
s.Perhaps something like this:
We could start with the simplest thing, which would be the context manager as initially proposed, then we could see how it feels and go from there. How does that sound?
Implemented in #1125