Skip to content

Commit

Permalink
Deal with Ethereum fucking checksum addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
miohtama committed Dec 24, 2024
1 parent e683b11 commit 6fa60aa
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 17 deletions.
4 changes: 4 additions & 0 deletions eth_defi/event_reader/multicall_batcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ def __repr__(self):
"""Log output about this call"""
raise NotImplementedError(f"Please implement in a subclass")

@property
def contract_address(self) -> HexAddress:
return self.call.address

@abstractmethod
def get_key(self) -> Hashable:
"""Get key that will identify this call in the result dictionary"""
Expand Down
8 changes: 8 additions & 0 deletions eth_defi/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ def chain_id(self) -> int:
def address(self) -> HexAddress:
"""The address of this token.
Always lowercase.
"""
return self.contract.address

@cached_property
def address_lower(self) -> HexAddress:
"""The address of this token.
Always lowercase.
"""
return self.contract.address.lower()
Expand Down
6 changes: 6 additions & 0 deletions eth_defi/vault/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ class VaultPortfolio:
"""

#: List of tokens and their amounts
#:
#: Addresses not checksummed
#:
spot_erc20: dict[HexAddress, Decimal]

#: For route finding, which DEX tokens should use.
Expand All @@ -92,6 +95,9 @@ def __post_init__(self):
assert type(token) == str
assert isinstance(value, Decimal)

# Always lowercased
self.spot_erc20 = {k.lower(): v for k, v in self.spot_erc20.items()}

@property
def tokens(self) -> set[HexAddress]:
"""Get list of tokens held in this portfolio"""
Expand Down
4 changes: 2 additions & 2 deletions eth_defi/vault/valuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ def generate_routes_for_router(
"""
for token_address, amount in portfolio.spot_erc20.items():

if token_address == self.denomination_token.address:
if token_address == self.denomination_token.address_lower:
# Reserve currency does not need to be valued in the reserve currency
continue

Expand Down Expand Up @@ -715,7 +715,7 @@ def calculate_market_sell_nav(

# Reserve currency does not need to be traded
if self.denomination_token.address in portfolio.spot_erc20:
best_result_by_token[self.denomination_token.address] = portfolio.spot_erc20[self.denomination_token.address]
best_result_by_token[self.denomination_token.address_lower] = portfolio.spot_erc20[self.denomination_token.address_lower]

# Discard bad paths with None value
valulation = PortfolioValuation(
Expand Down
30 changes: 15 additions & 15 deletions tests/lagoon/test_lagoon_valuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from web3 import Web3
from web3.contract.contract import ContractFunction

from eth_defi.event_reader.multicall_batcher import get_multicall_contract, call_multicall_batched_single_thread, MulticallWrapper
from eth_defi.lagoon.vault import LagoonVault
from eth_defi.provider.broken_provider import get_almost_latest_block_number
from eth_defi.safe.trace import assert_execute_module_success
Expand Down Expand Up @@ -242,10 +243,8 @@ def test_uniswap_v2_weth_usdc_sell_route(
)

route = Route(
source_token=base_weth,
target_token=base_usdc,
path=[base_weth, base_usdc],
quoter=uniswap_v2_quoter_v2,
path=(base_weth.address, base_usdc.address),
)

# Sell 1000 WETH
Expand All @@ -254,17 +253,22 @@ def test_uniswap_v2_weth_usdc_sell_route(

assert wrapped_call.contract_address == "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24"

test_call_result = uniswap_v2_quoter_v2.swap_router_v2.functions.getAmountsOut(amount, route.path).call()
test_call_result = uniswap_v2_quoter_v2.swap_router_v2.functions.getAmountsOut(amount, route.address_path).call()
assert test_call_result is not None

# Another method to double check call data encoding
tx_data_2 = uniswap_v2_quoter_v2.swap_router_v2.functions.getAmountsOut(amount, route.path).build_transaction(
bound_call = uniswap_v2_quoter_v2.swap_router_v2.functions.getAmountsOut(amount, route.address_path)
tx_data_2 = bound_call.build_transaction(
{"from": ZERO_ADDRESS}
)
correct_bytes = tx_data_2["data"][2:]

tx_data = wrapped_call.create_tx_data()
assert tx_data["data"].hex() == correct_bytes
address, data = wrapped_call.get_address_and_data()
tx_data ={
"data": data,
"address": address,
}
assert tx_data["data"].hex()[2:] == correct_bytes

# 0xd06ca61f00000000000000000000000000000002f050fe938943acc45f65568000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913
try:
Expand All @@ -275,15 +279,11 @@ def test_uniswap_v2_weth_usdc_sell_route(

assert raw_result is not None

# Now using Multicall
multicall = Multicall(
calls=[wrapped_call.create_multicall()],
block_id=web3.eth.block_number,
_w3=web3,
require_success=False,
gas_limit=10_000_000,
multicall_contract = get_multicall_contract(web3)
batched_result = call_multicall_batched_single_thread(
multicall_contract,
calls=[MulticallWrapper(call=bound_call, debug=False)]
)
batched_result = multicall()
result = batched_result[route]
assert result is not None, f"Reading quoter using Multicall failed"

Expand Down

0 comments on commit 6fa60aa

Please sign in to comment.