Skip to content
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

feat: Multicall context manager #1125

Merged
merged 34 commits into from
Jul 18, 2021
Merged
Changes from 1 commit
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9a65d49
fix: failing test get_abi for uniswap
skellet0r Jul 13, 2021
3b01aeb
feat: add Multicall2 contract
skellet0r Jun 24, 2021
ff4b90d
feat: add modified Multicall2 abi
skellet0r Jun 24, 2021
e02a802
fix: update MANIFEST.in
skellet0r Jun 24, 2021
22774a3
feat: add multicall2 addresses to network config
skellet0r Jun 24, 2021
628cafb
fix(test): update optional keys in network config
skellet0r Jun 26, 2021
159901f
feat: add wrapt and lazy-object-proxy packages
skellet0r Jun 25, 2021
c94643f
test: multicall2 context manager
skellet0r Jun 26, 2021
33240e5
feat: add multicall2 context manager
skellet0r Jun 24, 2021
6d79ab4
feat: add multicall2 to brownie namespace
skellet0r Jun 26, 2021
d72d9a1
fix: rename ctx mgr from multicall2 to multicall
skellet0r Jun 26, 2021
742ce6c
test: additional test cases
skellet0r Jun 27, 2021
4ba02d0
feat: add deploy fn and handle specifying block_identifier
skellet0r Jun 27, 2021
b21807f
test: all calls come from the same block
skellet0r Jun 27, 2021
06ae659
fix: handle all calls are queried from the same block
skellet0r Jun 27, 2021
b087a74
fix: repr of lazy results
skellet0r Jun 27, 2021
25f2675
fix: remove aliasing Multicall class
skellet0r Jun 27, 2021
51334ba
test: multicall block_number getter + setter
skellet0r Jun 28, 2021
90479aa
feat: block number getter + setter
skellet0r Jun 28, 2021
18e4480
docs: add documentation for multicall
skellet0r Jun 30, 2021
33b0d84
test: modify test to simulate multi-instance safety
skellet0r Jun 30, 2021
781536c
fix: remove replacing original code object back
skellet0r Jun 30, 2021
afa3943
chore: update CHANGELOG
skellet0r Jun 30, 2021
0192311
docs: add docstrings to Multicall class
skellet0r Jul 1, 2021
3096c30
fix: update init fn
skellet0r Jul 13, 2021
7cda178
fix: update usage
skellet0r Jul 13, 2021
222b19f
fix: namespacing
skellet0r Jul 13, 2021
b1bc173
fix: attribute setting and calls
skellet0r Jul 13, 2021
a90415e
fix(test): update tests for correct usage
skellet0r Jul 13, 2021
9da7e5d
fix: public attributes
skellet0r Jul 13, 2021
8686817
docs: update with new usage
skellet0r Jul 13, 2021
dd30cef
chore: linting
skellet0r Jul 13, 2021
e85bf9f
chore: lint
skellet0r Jul 18, 2021
9868a76
fix: remove threading import in proxy fn
skellet0r Jul 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: attribute setting and calls
  • Loading branch information
skellet0r committed Jul 18, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit b1bc173bbdb02869287ce0834398cb90eb60148c
4 changes: 2 additions & 2 deletions brownie/__init__.py
Original file line number Diff line number Diff line change
@@ -8,13 +8,13 @@
from brownie.convert import Fixed, Wei
from brownie.network import accounts, alert, chain, history, rpc, web3
from brownie.network.contract import Contract # NOQA: F401
from brownie.network.multicall import Multicall as _Multicall
from brownie.network import multicall as _multicall

ETH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"
ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"

config = _CONFIG.settings
multicall = _Multicall()
multicall = _multicall.Multicall()

__all__ = [
"Contract",
41 changes: 23 additions & 18 deletions brownie/network/multicall.py
Original file line number Diff line number Diff line change
@@ -4,9 +4,10 @@
from typing import Any, Dict, List, Tuple, Union

from lazy_object_proxy import Proxy
from toolz.itertoolz import get
from wrapt import ObjectProxy

from brownie import accounts, chain, web3
from brownie.network import accounts, web3
from brownie._config import BROWNIE_FOLDER, CONFIG
from brownie.exceptions import ContractNotFound
from brownie.network.contract import Contract, ContractCall
@@ -49,22 +50,16 @@ def __init__(self) -> None:
self._contract = None
self._pending_calls: List[Call] = []

active_network = CONFIG.active_network

if "multicall2" in active_network:
self._address = active_network["multicall2"]
elif "cmd" in active_network:
deployment = self.deploy({"from": accounts[0]})
self._address = deployment.address

ContractCall.__original_call_code = ContractCall.__call__.__code__
ContractCall.__proxy_call_code = self._proxy_call.__code__
setattr(ContractCall, "__original_call_code", ContractCall.__call__.__code__)
setattr(ContractCall, "__proxy_call_code", self._proxy_call.__code__)
setattr(ContractCall, "__multicall", defaultdict(lambda: None))
ContractCall.__call__.__code__ = self._proxy_call.__code__
ContractCall.__multicall = defaultdict(lambda: None)

def __call__(self, address: str, block_identifer: Union[str, bytes, int] = None) -> "Multicall":
def __call__(
self, address: str = None, block_identifier: Union[str, bytes, int] = None
) -> "Multicall":
self._address = address
self._block_identifier = block_identifer
self._block_identifier = block_identifier
return self

def _flush(self, future_result: Result = None) -> Any:
@@ -107,7 +102,7 @@ def _proxy_call(*args: Tuple, **kwargs: Dict[str, Any]) -> Any:
being the multicall2 instance being used."""
from threading import get_ident

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this import statement necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ehh it is gross, what I can do is move the import over to the contract.py file then we won't have to import in the function


self = ContractCall.__multicall[get_ident()]
self = getattr(ContractCall, "__multicall", {}).get(get_ident())
if self:
return self._call_contract(*args, **kwargs)

@@ -120,6 +115,16 @@ def _proxy_call(*args: Tuple, **kwargs: Dict[str, Any]) -> Any:
def __enter__(self) -> "Multicall":
"""Enter the Context Manager and substitute `ContractCall.__call__`"""
# we set the code objects on ContractCall class so we can grab them later

active_network = CONFIG.active_network

if "multicall2" in active_network:
self._address = active_network["multicall2"]
elif "cmd" in active_network:
deployment = self.deploy({"from": accounts[0]})
self._address = deployment.address
self._block_identifier = deployment.tx.block_number

self._block_identifier = self._block_identifier or web3.eth.get_block_number()

if self._address == None:
@@ -128,16 +133,16 @@ def __enter__(self) -> "Multicall":
)
elif not web3.eth.get_code(self._address, block_identifier=self._block_identifier):
raise ContractNotFound(
f"Multicall at address {self._address} does not exit at block {self._block_identifier}"
f"Multicall at address {self._address} does not exist at block {self._block_identifier}"
)

self._contract = Contract.from_abi("Multicall", self._address, MULTICALL2_ABI)
ContractCall.__multicall[get_ident()] = self
getattr(ContractCall, "__multicall")[get_ident()] = self

def __exit__(self, exc_type: Exception, exc_val: Any, exc_tb: TracebackType) -> None:
"""Exit the Context Manager and reattach original `ContractCall.__call__` code"""
self.flush()
ContractCall.__multicall[get_ident()] = None
getattr(ContractCall, "__multicall")[get_ident()] = None

@staticmethod
def deploy(tx_params: Dict) -> Contract: