Use multicalls when evaluating two or more CBD conditions
Multicall contracts allow you to aggregate several contract calls into a single call. This is very relevant when we have two or more conditions.
Advantages:
- Less latency by reducing calls to ETH provider (i.e.
Nrpc calls can be substituted by 1) - Ensures all conditions are evaluated on the same block
Disadvantages:
- More code complexity in case there's no good library support for multicalls
- Dependency on a deployed multicall contract (e.g. https://github.com/mds1/multicall), but this is actually not much of an issue
An additional disadvantage is that the multicall approach can't support RPC queries, although some of them can be substituted by contract calls. From the exhaustive list of RPC methods we described in https://github.com/nucypher/tdec/issues/64#issuecomment-1267330032, multicall would only support:
'function getBasefee() view returns (uint256 basefee)',
'function getBlockHash(uint256 blockNumber) view returns (bytes32 blockHash)',
'function getBlockNumber() view returns (uint256 blockNumber)',
'function getChainId() view returns (uint256 chainid)',
'function getCurrentBlockCoinbase() view returns (address coinbase)',
'function getCurrentBlockDifficulty() view returns (uint256 difficulty)',
'function getCurrentBlockGasLimit() view returns (uint256 gaslimit)',
'function getCurrentBlockTimestamp() view returns (uint256 timestamp)',
'function getEthBalance(address addr) view returns (uint256 balance)',
'function getLastBlockHash() view returns (bytes32 blockHash)',
Somo reference implementations of multicall support in Python:
- https://github.com/eth-brownie/brownie/blob/master/brownie/network/multicall.py
- https://github.com/safe-global/safe-eth-py/blob/master/gnosis/eth/multicall.py
- https://github.com/banteg/multicall.py
Multicall PoC in Brownie [WIP]: https://github.com/manumonti/multicall-brownie-poc
Also using Hardhat [WIP] (probably discontinued): https://github.com/manumonti/multicall-hardhat-poc
Some updates:
The main way that we are following to get multi-call working is using MakerDAO contracts https://github.com/mds1/multicall, specifically the V3.
But we have to think about how to integrate this into our Python-based nucypher code.
- https://github.com/eth-brownie/brownie/blob/master/brownie/network/multicall.py
This implementation of multi-call implies using Brownie in production, which doesn't look pretty appropriate.
- https://github.com/safe-global/safe-eth-py/blob/master/gnosis/eth/multicall.py
This Gnosis implementation uses multicall V2. It would be preferable if we implement V3.
- https://github.com/banteg/multicall.py
Banteg's multicall.py implementation works fine as we have seen in this PoC: https://github.com/manumonti/multicall-brownie-poc
So one path could be integrating this implementation in Nucypher.
-> Adding the module in requirements.txt is not directly possible since a dependencies conflict exists. nucypher requires web3==6.0.0b6 and multicall requires web3<6.0 and >=5.27.
-> Currently I'm addressing the option of integrating the multicall.py code in our web3 client layer clients.py.
Until an adopter specifically requests a (more efficient) way to evaluate multiple conditions concurrently, this can be bumped to the next version
After some talks, my current path is to create a nucypher-specific multicall.py package based on https://github.com/banteg/multicall.py and integrate it into the nucypher repo.
This implies some problems that I'm trying to solve:
-
As I mentioned, nucypher requires web3 v6, but multicall.py repo only works with web3 v5. This can be solved by deleting the brownie dependency (since this dependency uses web3 v5). But this implies that no unit tests for multicall can be run. A todo is to create unit tests for multicall (maybe using ape?).
-
multicall.py have a dependency that uses brownie for tests: eth-retry https://github.com/BobTheBuidler/eth_retry, so installing the multicall package in nucypher virtualenv results in errors. The solution for now is to not use eth-retry, but a todo is to use a modified version of eth-retry that doesn't use brownie or use other alternative package.
nucypher integration of multicall.py [WIP]: https://github.com/manumonti/multicall.py/tree/nucypher-integration