token

Documentation for eth_defi.token Python module.

ERC-20 token deployment and manipulation.

Deploy ERC-20 tokens to be used within your test suite.

Read also unit test suite for tokens to see how ERC-20 can be manipulated in pytest.

Module Attributes

WRAPPED_NATIVE_TOKEN

Addresses of wrapped native token (WETH9) of different chains

USDC_NATIVE_TOKEN

Addresses of USDC of different chains

BRIDGED_USDC_TOKEN

Bridged USDC of different chains

USDC_WHALE

Used in fork testing

USDT_WHALE

Used in fork testing

USDT_NATIVE_TOKEN

Addresses USDT Tether of different chains

SUSDS_NATIVE_TOKEN

Sky (MakerDAO) new tokens

HONEY_NATIVE_TOKEN

Berachain 0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce https://docs.berachain.com/learn/pol/tokens/honey

STABLECOIN_LIKE

Token symbols that are stablecoin like.

YIELD_BEARING_STABLES

Stablecoins which can be used as collateral, but which also have built-in yield bearing function with rebasing.

WRAPPED_STABLECOIN_LIKE

Stablecoins plus their interest wrapped counterparts on Compound and Aave.

ALL_STABLECOIN_LIKE

All stablecoin likes - both interested bearing and non interest bearing.

LARGE_USDC_HOLDERS

Some test accounts with funded USDC for Anvil mainnet forking

Functions

create_token(web3, deployer, name, symbol, ...)

Deploys a new ERC-20 token on local dev, testnet or mainnet.

fetch_erc20_details(web3, token_address[, ...])

Read token details from on-chain data.

get_chain_known_quote_tokens(chain_id)

Get all good quote tokens on chain.

get_chain_stablecoins(chain_id)

Get all good known stablecoins on a chain.

get_erc20_contract(web3, address[, ...])

Wrap address as ERC-20 standard interface.

get_weth_contract(web3[, name])

Get WETH9 contract for the chain

get_wrapped_native_token_address(chain_id)

is_stablecoin_like(token_symbol[, symbol_list])

Check if specific token symbol is likely a stablecoin.

normalise_token_symbol(token_symbol)

Normalise token symbol for stablecoin detection.

reset_default_token_cache()

Purge the cached token data.

Classes

DummyPickledContract

Contract placeholder making contract references pickable

TokenCacheWarmupResult

TokenDetails

ERC-20 token Python presentation.

TokenDiskCache

Token cache that stores tokens in disk.

Exceptions

TokenDetailError

Cannot extract token details for an ERC-20 token for some reason.

DEFAULT_TOKEN_CACHE = LRUCache({}, maxsize=1024, currsize=0)

By default we cache 1024 token details using LRU in the process memory.

TokenAddress

ERC-20 address, 0x prefixed string

WRAPPED_NATIVE_TOKEN: dict[int, Union[eth_typing.evm.HexAddress, str]] = {1: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', 56: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', 957: '0x4200000000000000000000000000000000000006', 8453: '0x4200000000000000000000000000000000000006', 42161: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', 43114: '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', 421614: '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9'}

Addresses of wrapped native token (WETH9) of different chains

USDC_NATIVE_TOKEN: dict[int, Union[eth_typing.evm.HexAddress, str]] = {1: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 56: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d', 957: '0x6879287835A86F50f784313dBEd5E5cCC5bb8481', 8453: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 42161: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', 43114: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', 421614: '0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d'}

Addresses of USDC of different chains

BRIDGED_USDC_TOKEN: dict[int, Union[eth_typing.evm.HexAddress, str]] = {42161: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8'}

Bridged USDC of different chains

USDC_WHALE: dict[int, Union[eth_typing.evm.HexAddress, str]] = {8453: '0x40EbC1Ac8d4Fedd2E144b75fe9C0420BE82750c6', 42161: '0x3DD1D15b3c78d6aCFD75a254e857Cbe5b9fF0aF2'}

Used in fork testing

USDT_WHALE: dict[int, Union[eth_typing.evm.HexAddress, str]] = {56: '0x128463A60784c4D3f46c23Af3f65Ed859Ba87974', 42161: '0x9E36CB86a159d479cEd94Fa05036f235Ac40E1d5'}

Used in fork testing

USDT_NATIVE_TOKEN: dict[int, eth_typing.evm.HexAddress] = {1: '0xdAC17F958D2ee523a2206206994597C13D831ec7', 56: '0x55d398326f99059fF775485246999027B3197955', 42161: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', 43114: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7'}

Addresses USDT Tether of different chains

SUSDS_NATIVE_TOKEN: dict[int, eth_typing.evm.HexAddress] = {8453: '0x5875eEE11Cf8398102FdAd704C9E96607675467a'}

Sky (MakerDAO) new tokens

HONEY_NATIVE_TOKEN: dict[int, eth_typing.evm.HexAddress] = {80094: '0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce'}

Berachain 0xFCBD14DC51f0A4d49d5E53C2E0950e0bC26d0Dce https://docs.berachain.com/learn/pol/tokens/honey

STABLECOIN_LIKE = {'ALUSD', 'AUDT', 'AUSD', 'BAC', 'BDO', 'BEAN', 'BOB', 'BOLD', 'BUSD', 'BYUSD', 'CADC', 'CEUR', 'CJPY', 'CNHT', 'CRVUSD', 'CUSD', 'DAI', 'DJED', 'DOLADUSD', 'EOSDT', 'EURA', 'EURCV', 'EUROC', 'EUROe', 'EURS', 'EURT', 'EURe', 'EUSD', 'FDUSD', 'FEI', 'FLEXUSD', 'FUSD', 'FXD', 'FXUSD', 'GBPT', 'GHO', 'GHST', 'GUSD', 'GYD', 'GYEN', 'HAI', 'HUSD', 'IRON', 'JCHF', 'JPYC', 'KDAI', 'LISUSD', 'LUSD', 'MIM', 'MIMATIC', 'MKUSD', 'MTUSD', 'MUSD', 'ONC', 'OUSD', 'PAR', 'PAXG', 'PYUSD', 'RAI', 'RLUSD', 'RUSD', 'SAI', 'SDAI', 'SEUR', 'SFRAX', 'SILK', 'STUSD', 'SUSD', 'TCNH', 'TOR', 'TRYB', 'TUSD', 'USC', 'USD+', 'USD0', 'USD1', 'USD8', 'USDA', 'USDB', 'USDC', 'USDC.e', 'USDCV', 'USDD', 'USDE', 'USDF', 'USDH', 'USDHLUSDG', 'USDM', 'USDN', 'USDO', 'USDP', 'USDR', 'USDS', 'USDT', 'USDT.e', 'USDT0', 'USDV', 'USDX', 'USDXL', 'USDai', 'USDbC', 'USDe', 'USDf', 'USDs', 'USDt', 'USDtb', 'USD₮', 'USD₮0', 'USH', 'USK', 'USR', 'UST', 'USTC', 'USX', 'USXAU', 'UTY', 'UUSD', 'VAI', 'VEUR', 'VST', 'VUSD', 'WXDAI', 'XAUT', 'XDAI', 'XIDR', 'XSGD', 'XSTUSD', 'XUSD', 'YUSD', 'ZCHF', 'ZSD', 'ZUSD', 'avUSD', 'bvUSD', 'crvUSD', 'csUSD', 'dUSD', 'deUSD', 'feUSD', 'frxUSD', 'ftUSD', 'gmUSD', 'iUSD', 'jEUR', 'kUSD', 'lvlUSD', 'mUSD', 'meUSDT', 'msUSD', 'mtUSDC', 'mtUSDT', 'plUSD', 'reUSD', 'sUSDC', 'satUSD', 'scUSD', 'sosUSDT', 'vbUSDC', 'vbUSDT', 'wM', 'xUSD', 'ysUSDC'}

Token symbols that are stablecoin like. Note that it is not safe to to check the token symbol to know if a token is a specific stablecoin, but you always need to check the contract address. Checking against this list only works USDf and USDF

YIELD_BEARING_STABLES = {'sAUSD', 'sBOLD', 'sUSDai', 'sUSDe', 'sfrxUSD', 'ynUSDx'}

Stablecoins which can be used as collateral, but which also have built-in yield bearing function with rebasing.

WRAPPED_STABLECOIN_LIKE = {'aDAI', 'agEUR', 'alUSD', 'blUSD', 'cDAI', 'cUSDC', 'cUSDT', 'gDAI', 'gmdUSDC', 'sUSD', 'tfUSDC'}

Stablecoins plus their interest wrapped counterparts on Compound and Aave. Also contains other derivates.

ALL_STABLECOIN_LIKE = {'ALUSD', 'AUDT', 'AUSD', 'BAC', 'BDO', 'BEAN', 'BOB', 'BOLD', 'BUSD', 'BYUSD', 'CADC', 'CEUR', 'CJPY', 'CNHT', 'CRVUSD', 'CUSD', 'DAI', 'DJED', 'DOLADUSD', 'EOSDT', 'EURA', 'EURCV', 'EUROC', 'EUROe', 'EURS', 'EURT', 'EURe', 'EUSD', 'FDUSD', 'FEI', 'FLEXUSD', 'FUSD', 'FXD', 'FXUSD', 'GBPT', 'GHO', 'GHST', 'GUSD', 'GYD', 'GYEN', 'HAI', 'HUSD', 'IRON', 'JCHF', 'JPYC', 'KDAI', 'LISUSD', 'LUSD', 'MIM', 'MIMATIC', 'MKUSD', 'MTUSD', 'MUSD', 'ONC', 'OUSD', 'PAR', 'PAXG', 'PYUSD', 'RAI', 'RLUSD', 'RUSD', 'SAI', 'SDAI', 'SEUR', 'SFRAX', 'SILK', 'STUSD', 'SUSD', 'TCNH', 'TOR', 'TRYB', 'TUSD', 'USC', 'USD+', 'USD0', 'USD1', 'USD8', 'USDA', 'USDB', 'USDC', 'USDC.e', 'USDCV', 'USDD', 'USDE', 'USDF', 'USDH', 'USDHLUSDG', 'USDM', 'USDN', 'USDO', 'USDP', 'USDR', 'USDS', 'USDT', 'USDT.e', 'USDT0', 'USDV', 'USDX', 'USDXL', 'USDai', 'USDbC', 'USDe', 'USDf', 'USDs', 'USDt', 'USDtb', 'USD₮', 'USD₮0', 'USH', 'USK', 'USR', 'UST', 'USTC', 'USX', 'USXAU', 'UTY', 'UUSD', 'VAI', 'VEUR', 'VST', 'VUSD', 'WXDAI', 'XAUT', 'XDAI', 'XIDR', 'XSGD', 'XSTUSD', 'XUSD', 'YUSD', 'ZCHF', 'ZSD', 'ZUSD', 'aDAI', 'agEUR', 'alUSD', 'avUSD', 'blUSD', 'bvUSD', 'cDAI', 'cUSDC', 'cUSDT', 'crvUSD', 'csUSD', 'dUSD', 'deUSD', 'feUSD', 'frxUSD', 'ftUSD', 'gDAI', 'gmUSD', 'gmdUSDC', 'iUSD', 'jEUR', 'kUSD', 'lvlUSD', 'mUSD', 'meUSDT', 'msUSD', 'mtUSDC', 'mtUSDT', 'plUSD', 'reUSD', 'sAUSD', 'sBOLD', 'sUSD', 'sUSDC', 'sUSDai', 'sUSDe', 'satUSD', 'scUSD', 'sfrxUSD', 'sosUSDT', 'tfUSDC', 'vbUSDC', 'vbUSDT', 'wM', 'xUSD', 'ynUSDx', 'ysUSDC'}

All stablecoin likes - both interested bearing and non interest bearing.

LARGE_USDC_HOLDERS = {8453: '0xBaeD383EDE0e5d9d72430661f3285DAa77E9439F', 42161: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', 43114: '0x9f8c163cBA728e99993ABe7495F06c0A3c8Ac8b9'}

Some test accounts with funded USDC for Anvil mainnet forking

TBD: In theory we can find ERC-20 balance slots and write value there with Anvil, but it is difficult to do reliably.

class DummyPickledContract

Bases: object

Contract placeholder making contract references pickable

__init__(address)
Parameters

address (str) –

Return type

None

class TokenDetails

Bases: object

ERC-20 token Python presentation.

  • A helper class to work with ERC-20 tokens.

  • Read on-chain data, deal with token value decimal conversions.

  • Any field can be None for non-well-formed tokens.

  • Supports one-way pickling

Example how to get USDC details on Polygon:

usdc = fetch_erc20_details(web3, "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174")  # USDC on Polygon
formatted = f"Token {usdc.name} ({usdc.symbol}) at {usdc.address} on chain {usdc.chain_id}"
assert formatted == "Token USD Coin (PoS) (USDC) at 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174 on chain 137"
contract: web3.contract.contract.Contract

The underlying ERC-20 contract proxy class instance

name: Optional[str] = None

Token name e.g. USD Circle

symbol: Optional[str] = None

Token symbol e.g. USDC

total_supply: Optional[int] = None

Token supply as raw units

decimals: Optional[int] = None

Number of decimals

extra_data: dict[str, Any]

Extra metadata, e.g. related to caching this result

property chain_id: int

The EVM chain id where this token lives.

property address: eth_typing.evm.HexAddress

The address of this token.

See also address_lower().

property address_lower: eth_typing.evm.HexAddress

The address of this token.

Always lowercase.

property functions: web3.contract.contract.ContractFunctions

Alias for underlying Web3 contract method

convert_to_decimals(raw_amount)

Convert raw token units to decimals.

Example:

details = fetch_erc20_details(web3, token_address)
# Convert 1 wei units to edcimals
assert details.convert_to_decimals(1) == Decimal("0.0000000000000001")
Parameters

raw_amount (int) –

Return type

decimal.Decimal

convert_to_raw(decimal_amount)

Convert decimalised token amount to raw uint256.

Example:

details = fetch_erc20_details(web3, token_address)
# Convert 1.0 USDC to raw unit with 6 decimals
assert details.convert_to_raw(1) == 1_000_000
Parameters

decimal_amount (decimal.Decimal) –

Return type

int

fetch_balance_of(address, block_identifier='latest')

Get an address token balance.

Parameters
Returns

Converted to decimal using convert_to_decimal()

Return type

decimal.Decimal

transfer(to, amount)

Prepare a ERC20.transfer() transaction with human-readable amount.

Example:

another_new_depositor = web3.eth.accounts[6]
tx_hash = base_usdc.transfer(another_new_depositor, Decimal(500)).transact({"from": usdc_holder, "gas": 100_000})
assert_transaction_success_with_explanation(web3, tx_hash)
Returns

Bound contract function you need to turn to a tx

Parameters
Return type

web3.contract.contract.ContractFunction

approve(to, amount)

Prepare a ERC20.approve() transaction with human-readable amount.

Example:

usdc_amount = Decimal(9.00)
tx_hash = usdc.approve(vault.address, usdc_amount).transact({"from": depositor})
assert_transaction_success_with_explanation(web3, tx_hash)
Returns

Bound contract function you need to turn to a tx

Parameters
Return type

web3.contract.contract.ContractFunction

fetch_raw_balance_of(address, block_identifier='latest')

Get an address token balance.

Parameters
Returns

Raw token amount.

Return type

decimal.Decimal

static generate_cache_key(chain_id, address)

Generate a cache key for this token.

  • Cached by (chain, address) as a string

  • Validate the inputs before generating the key

  • Address is always lowercase

Returns

Human reaadable {chain_id}-{address}

Parameters
  • chain_id (int) –

  • address (str) –

Return type

str

export()

Create a serialisable entry of this class.

Removes web3 connection and such unserialisable data.

Returns

Python dict of exported data.

Return type

dict

is_stablecoin_like()

Smell test for stablecoins.

Returns

True if we think this could be a stablecoin.

Return type

bool

__init__(contract, name=None, symbol=None, total_supply=None, decimals=None, extra_data=<factory>)
Parameters
Return type

None

exception TokenDetailError

Bases: Exception

Cannot extract token details for an ERC-20 token for some reason.

__init__(*args, **kwargs)
__new__(**kwargs)
add_note()

Exception.add_note(note) – add a note to the exception

with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

create_token(web3, deployer, name, symbol, supply, decimals=18)

Deploys a new ERC-20 token on local dev, testnet or mainnet.

  • Uses ERC20Mock contract for the deployment.

  • Waits until the transaction has completed

Example:

# Deploys an ERC-20 token where 100,000 tokens are allocated ato the deployer address
token = create_token(web3, deployer, "Hentai books token", "HENTAI", 100_000 * 10**18)
print(f"Deployed token contract address is {token.address}")
print(f"Deployer account {deployer} has {token.functions.balanceOf(user_1).call() / 10**18} tokens")

Find more examples in Tutorials and unit testing source code.

Parameters
  • web3 (web3.main.Web3) – Web3 instance

  • deployer (str) –

    Deployer account as 0x address.

    Make sure this account has enough ETH or native token to cover the gas cost.

  • name (str) – Token name

  • symbol (str) – Token symbol

  • supply (int) –

    Token starting supply as raw units.

    E.g. 500 * 10**18 to have 500 tokens minted to the deployer at the start.

  • decimals (int) – How many decimals ERC-20 token values have

Returns

Instance to a deployed Web3 contract.

Return type

web3.contract.contract.Contract

get_erc20_contract(web3, address, contract_name='ERC20MockDecimals.json')

Wrap address as ERC-20 standard interface.

Parameters
Return type

web3.contract.contract.Contract

fetch_erc20_details(web3, token_address, max_str_length=256, raise_on_error=True, contract_name='ERC20MockDecimals.json', cache=LRUCache({}, maxsize=1024, currsize=0), chain_id=None, cause_diagnostics_message=None)

Read token details from on-chain data.

Connect to Web3 node and do RPC calls to extract the token info. We apply some sanitazation for incoming data, like length checks and removal of null bytes.

The function should not raise an exception as long as the underlying node connection does not fail.

Note

Always give chain_id when possible. Otherwise the caching of data is inefficient.

Example:

details = fetch_erc20_details(web3, token_address)
assert details.name == "Hentai books token"
assert details.decimals == 6
Parameters
  • web3 (web3.main.Web3) – Web3 instance

  • token_address (Union[eth_typing.evm.HexAddress, str]) – ERC-20 contract address:

  • max_str_length (int) – For input sanitisation

  • raise_on_error – If set, raise TokenDetailError on any error instead of silently ignoring in and setting details to None.

  • contract_name

    Contract ABI file to use.

    The default is ERC20MockDecimals.json. For USDC use centre/FiatToken.json.

  • cache (dict | None) –

    Use this cache for cache token detail calls.

    The main purpose is to easily reduce JSON-RPC API call count.

    By default, we use LRU cache of 1024 entries.

    Set to None to disable the cache.

    Instance of cachetools.Cache. See cachetools documentation for details.

  • chain_id (int) –

    Chain id hint for the cache.

    If not given do eth_chainId RPC call to figure out.

  • cause_diagnostics_message (str | None) – Log in Python logging subsystem why this fetch was done to debug RPC overuse.

Returns

Sanitised token info

Return type

eth_defi.token.TokenDetails

reset_default_token_cache()

Purge the cached token data.

See DEFAULT_TOKEN_CACHE

get_chain_stablecoins(chain_id)

Get all good known stablecoins on a chain.

Raises

AssertionError – Chain has zero stablecoins configured

Parameters

chain_id (int) –

Return type

set[eth_defi.token.TokenDetails]

get_chain_known_quote_tokens(chain_id)

Get all good quote tokens on chain.

Parameters

chain_id (int) –

Return type

set[eth_defi.token.TokenDetails]

is_stablecoin_like(token_symbol, symbol_list={'ALUSD', 'AUDT', 'AUSD', 'BAC', 'BDO', 'BEAN', 'BOB', 'BOLD', 'BUSD', 'BYUSD', 'CADC', 'CEUR', 'CJPY', 'CNHT', 'CRVUSD', 'CUSD', 'DAI', 'DJED', 'DOLADUSD', 'EOSDT', 'EURA', 'EURCV', 'EUROC', 'EUROe', 'EURS', 'EURT', 'EURe', 'EUSD', 'FDUSD', 'FEI', 'FLEXUSD', 'FUSD', 'FXD', 'FXUSD', 'GBPT', 'GHO', 'GHST', 'GUSD', 'GYD', 'GYEN', 'HAI', 'HUSD', 'IRON', 'JCHF', 'JPYC', 'KDAI', 'LISUSD', 'LUSD', 'MIM', 'MIMATIC', 'MKUSD', 'MTUSD', 'MUSD', 'ONC', 'OUSD', 'PAR', 'PAXG', 'PYUSD', 'RAI', 'RLUSD', 'RUSD', 'SAI', 'SDAI', 'SEUR', 'SFRAX', 'SILK', 'STUSD', 'SUSD', 'TCNH', 'TOR', 'TRYB', 'TUSD', 'USC', 'USD+', 'USD0', 'USD1', 'USD8', 'USDA', 'USDB', 'USDC', 'USDC.e', 'USDCV', 'USDD', 'USDE', 'USDF', 'USDH', 'USDHLUSDG', 'USDM', 'USDN', 'USDO', 'USDP', 'USDR', 'USDS', 'USDT', 'USDT.e', 'USDT0', 'USDV', 'USDX', 'USDXL', 'USDai', 'USDbC', 'USDe', 'USDf', 'USDs', 'USDt', 'USDtb', 'USD₮', 'USD₮0', 'USH', 'USK', 'USR', 'UST', 'USTC', 'USX', 'USXAU', 'UTY', 'UUSD', 'VAI', 'VEUR', 'VST', 'VUSD', 'WXDAI', 'XAUT', 'XDAI', 'XIDR', 'XSGD', 'XSTUSD', 'XUSD', 'YUSD', 'ZCHF', 'ZSD', 'ZUSD', 'aDAI', 'agEUR', 'alUSD', 'avUSD', 'blUSD', 'bvUSD', 'cDAI', 'cUSDC', 'cUSDT', 'crvUSD', 'csUSD', 'dUSD', 'deUSD', 'feUSD', 'frxUSD', 'ftUSD', 'gDAI', 'gmUSD', 'gmdUSDC', 'iUSD', 'jEUR', 'kUSD', 'lvlUSD', 'mUSD', 'meUSDT', 'msUSD', 'mtUSDC', 'mtUSDT', 'plUSD', 'reUSD', 'sAUSD', 'sBOLD', 'sUSD', 'sUSDC', 'sUSDai', 'sUSDe', 'satUSD', 'scUSD', 'sfrxUSD', 'sosUSDT', 'tfUSDC', 'vbUSDC', 'vbUSDT', 'wM', 'xUSD', 'ynUSDx', 'ysUSDC'})

Check if specific token symbol is likely a stablecoin.

Useful for quickly filtering stable/stable pairs in the pools. However, you should never rely on this check alone.

Note that new stablecoins might be introduced, so this check is never going to be future proof.

Example:

assert is_stablecoin_like("USDC") == True
assert is_stablecoin_like("USDT") == True
assert is_stablecoin_like("GHO") == True
assert is_stablecoin_like("crvUSD") == True
assert is_stablecoin_like("WBTC") == False
Parameters
  • token_symbol (str | None) – Token symbol as it is written on the contract. May contain lower and uppercase latter.

  • symbol_list – Which filtering list we use.

Return type

bool

normalise_token_symbol(token_symbol)

Normalise token symbol for stablecoin detection.

  • Uppercase

  • Remove bridge suffixes

  • Fix USDT variations

Parameters

token_symbol (str | None) – Token symbol as it is written on the contract.

Returns

Normalised token symbol

Return type

str | None

get_weth_contract(web3, name='1delta/IWETH9.json')

Get WETH9 contract for the chain

  • See WETH9 contract

  • WETH9 is different contract with different functions on different chain

Parameters
  • web3 (web3.main.Web3) – Web3 instance

  • name (str) – Alternative implementation.

Returns

WETH token details

Return type

web3.contract.contract.Contract

class TokenCacheWarmupResult

Bases: TypedDict

__init__(*args, **kwargs)
__new__(**kwargs)
clear() None.  Remove all items from D.
copy() a shallow copy of D
fromkeys(value=None, /)

Create a new dictionary with keys from iterable and values set to value.

get(key, default=None, /)

Return the value for key if key is in the dictionary, else default.

items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
pop(k[, d]) v, remove specified key and return the corresponding value.

If the key is not found, return the default if given; otherwise, raise a KeyError.

popitem()

Remove and return a (key, value) pair as a 2-tuple.

Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.

setdefault(key, default=None, /)

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

values() an object providing a view on D's values
class TokenDiskCache

Bases: eth_defi.sqlite_cache.PersistentKeyValueStore

Token cache that stores tokens in disk.

  • Use with fetch_erc20_details()

  • For loading hundreds of tokens once

  • Shared across chains

  • Enable fast cache warmup with load_token_details_with_multicall()

  • Persistent: Make sure subsequent batch jobs do not refetch token data over RPC as it is expensive

  • Store as a SQLite database

Example:

addresses = [
    "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",  # USDC
    "0x4200000000000000000000000000000000000006",  # WETH
    "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",  # DAI
    "0x554a1283cecca5a46bc31c2b82d6702785fc72d9",  # UNI
]

cache = TokenDiskCache(tmp_path / "disk_cache.sqlite")
web3factory = MultiProviderWeb3Factory(JSON_RPC_BASE)
web3 = web3factory()

#
# Do single token lookups against cache
#
token = fetch_erc20_details(
    web3,
    token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chain_id=web3.eth.chain_id,
    cache=cache,
)
assert token.extra_data["cached"] == False
assert len(cache) == 1
# After one look up, we should have it cached
token = fetch_erc20_details(
    web3,
    token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chain_id=web3.eth.chain_id,
    cache=cache,
)
assert token.extra_data["cached"] == True
cache.purge()

#
# Warm up multiple on dry cache
#
result = cache.load_token_details_with_multicall(
    chain_id=web3.eth.chain_id,
    web3factory=web3factory,
    addresses=addresses,
    max_workers=max_workers,
    display_progress=False,
)
assert result["tokens_read"] == 4
assert "8453-0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913".lower() in cache
assert "8453-0x4200000000000000000000000000000000000006".lower() in cache

cache_data = cache["8453-0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913".lower()]
assert cache_data["name"] == "USD Coin"
assert cache_data["symbol"] == "USDC"
assert cache_data["decimals"] == 6
assert cache_data["supply"] > 1_000_000

token = fetch_erc20_details(
    web3,
    token_address="0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    chain_id=web3.eth.chain_id,
    cache=cache,
)
assert token.extra_data["cached"] == True
Parameters
  • filename – Path to the sqlite database

  • autocommit – Whether to autocommit every time new entry is added to the database

__init__(filename=PosixPath('~/.cache/eth-defi-tokens.sqlite'), max_str_length=256)
Parameters
  • filename – Path to the sqlite database

  • autocommit – Whether to autocommit every time new entry is added to the database

  • max_str_length (int) –

encode_value(value)

Hook to convert Python objects to cache format

Parameters

value (dict) –

Return type

Any

decode_value(value)

Hook to convert SQLite values to Python objects

Parameters

value (str) –

Return type

Any

encode_multicalls(address)

Generate multicalls for each token address

Parameters

address (eth_typing.evm.HexAddress) –

Return type

eth_defi.event_reader.multicall_batcher.EncodedCall

create_cache_entry(call_results)

Map multicall results to token details data for one address

Parameters

call_results (dict[str, eth_defi.event_reader.multicall_batcher.EncodedCallResult]) –

Return type

dict

load_token_details_with_multicall(chain_id, web3factory, addresses, display_progress=False, max_workers=8, block_identifier='latest', checkpoint=32)

Warm up cache and load token details for multiple

Parameters
Return type

eth_defi.token.TokenCacheWarmupResult

__new__(**kwargs)
clear() None.  Remove all items from D.
property conn: sqlite3.Connection

One connection per thread

copy() a shallow copy of D
fromkeys(value=None, /)

Create a new dictionary with keys from iterable and values set to value.

get(key, default=None)

Return the value for key if key is in the dictionary, else default.

items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
pop(k[, d]) v, remove specified key and return the corresponding value.

If the key is not found, return the default if given; otherwise, raise a KeyError.

popitem()

Remove and return a (key, value) pair as a 2-tuple.

Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.

purge()

Delete all keys and save.

setdefault(key, default=None, /)

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

update([E, ]**F) None.  Update D from mapping/iterable E and F.

If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

values() an object providing a view on D's values