vault.valuation
Documentation for eth_defi.vault.valuation Python module.
Net asset valuation calculations for token portfolios and vaults.
Calculate the value of vault portfolio using only onchain data, available from JSON-RPC
Find best routes to buy tokens, which result to the best price, using brute force
See
NetAssetValueCalculatorfor usage
Classes
Calculate valuation of all vault spot assets, assuming we would sell them on Uniswap market sell or similar. |
|
Valuation calulated for a portfolio. |
|
One potential swap path. |
|
Brute-forced route swap result for a portfolio of buying multiple tokens. |
|
Handle Uniswap v2 quoters using Router02 contract. |
|
Handle Uniswap v3 quoters using QuoterV2 contract. |
|
Wrap the undertlying Multicall with diagnostics data. |
|
Handle asset valuation on a specific DEX/quoter. |
Exceptions
We could not route some of the spot tokens to get any valuations for them. |
- exception NoRouteFound
Bases:
ExceptionWe could not route some of the spot tokens to get any valuations for them.
- __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.
- class PortfolioValuation
Bases:
objectValuation calulated for a portfolio.
See
eth_defi.vault.base.VaultPortfoliofor the portfolio itself.- denomination_token: eth_defi.token.TokenDetails
The reserve currency of this vault
- spot_valuations: dict[eth_typing.evm.HexAddress, decimal.Decimal]
Individual spot valuations
- get_total_equity()
How much we value this portfolio in the
denomination_token- Return type
- __init__(denomination_token, spot_valuations)
- Parameters
denomination_token (eth_defi.token.TokenDetails) –
spot_valuations (dict[eth_typing.evm.HexAddress, decimal.Decimal]) –
- Return type
None
- class SwapMatrix
Bases:
objectBrute-forced route swap result for a portfolio of buying multiple tokens.
See
NetAssetValueCalculator.find_swap_routes()- results: dict[eth_defi.vault.valuation.Route, decimal.Decimal | None]
Outcome of different attempted routes.
Result is none if the path did not exist or the smart contract call failed.
- __init__(results, best_results_by_token)
- Parameters
results (dict[eth_defi.vault.valuation.Route, decimal.Decimal | None]) –
best_results_by_token (dict[eth_defi.token.TokenDetails, list[tuple[eth_defi.vault.valuation.Route, decimal.Decimal | None]]]) –
- Return type
None
- class Route
Bases:
objectOne potential swap path.
Support paths with 2 or 3 pairs
Present one potential swap path between source and target
Routes can contain any number of intermediate tokens in the path
Used to ABI encode for multicall calls
- quoter: eth_defi.vault.valuation.ValuationQuoter
What router we use
- path: tuple[eth_defi.token.TokenDetails, eth_defi.token.TokenDetails] | tuple[eth_defi.token.TokenDetails, eth_defi.token.TokenDetails, eth_defi.token.TokenDetails]
What route path we take
- __init__(quoter, path, fees=None)
- Parameters
- Return type
None
- class ValuationMulticallWrapper
Bases:
eth_defi.event_reader.multicall_batcher.MulticallWrapperWrap the undertlying Multicall with diagnostics data.
Because the underlying Multicall lib is not powerful enough.
And we do not have time to fix it
- create_multicall()
Create underlying call about.
- Return type
multicall.call.Call
- handle(success, raw_return_value)
Parse the call result.
- Parameters
succeed – Did we revert or not
raw_return_value (bytes) – Undecoded bytes from the Solidity function call
- Returns
The value placed in the return dict
- Return type
decimal.Decimal | None
- __init__(call, debug, quoter, route, amount_in)
- Parameters
call (web3.contract.contract.ContractFunction) –
debug (bool) –
quoter (eth_defi.vault.valuation.ValuationQuoter) –
route (eth_defi.vault.valuation.Route) –
amount_in (int) –
- Return type
None
- multicall_callback(succeed, raw_return_value)
Convert the raw Solidity function call result to a denominated token amount.
Multicall library callback
- class ValuationQuoter
Bases:
abc.ABCHandle asset valuation on a specific DEX/quoter.
Takes in source and target tokens as input and generate all routing path combinations
Creates routes to a specific DEX
Each DEX has its own quoter contract we need to integrate
Resolves the onchain Solidity function return value to a token amount we get
- abstract format_path(route)
Get human-readable route path line.
- Parameters
route (eth_defi.vault.valuation.Route) –
- Return type
- class UniswapV2Router02Quoter
Bases:
eth_defi.vault.valuation.ValuationQuoterHandle Uniswap v2 quoters using Router02 contract.
https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-02#getamountsout
https://basescan.org/address/0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24#readContract
- signature_string = 'getAmountsOut(uint256,address[])(uint256[])'
Quoter signature string for Multicall lib.
Not the standard string signature format, because Multicall lib wants it special output format suffix here
- __init__(swap_router_v2, debug=False)
- Parameters
swap_router_v2 (web3.contract.contract.Contract) –
debug (bool) –
- generate_routes(source_token, target_token, intermediate_tokens, amount, debug)
Create routes we need to test on Uniswap v2
- Parameters
source_token (eth_defi.token.TokenDetails) –
target_token (eth_defi.token.TokenDetails) –
intermediate_tokens (set[eth_defi.token.TokenDetails]) –
amount (decimal.Decimal) –
debug (bool) –
- Return type
- handle_onchain_return_value(wrapper, raw_return_value)
Convert getAmountsOut() return value to tokens we receive
- Parameters
wrapper (eth_defi.vault.valuation.ValuationMulticallWrapper) –
raw_return_value (bytes) –
- Return type
decimal.Decimal | None
- get_path_combinations(source_token, target_token, intermediate_tokens)
Generate Uniswap v2 swap paths with all supported intermediate tokens
- Parameters
source_token (eth_defi.token.TokenDetails) –
target_token (eth_defi.token.TokenDetails) –
intermediate_tokens (set[eth_defi.token.TokenDetails]) –
- Return type
- class UniswapV3Quoter
Bases:
eth_defi.vault.valuation.ValuationQuoterHandle Uniswap v3 quoters using QuoterV2 contract.
- signature_string = 'quoteExactInput(bytes,uint256)(uint256,uint160[],uint32[],uint256)'
Quoter signature string for Multicall lib.
https://basescan.org/address/0x3d4e44Eb1374240CE5F1B871ab261CD16335B76a#code
- __init__(quoter, debug=False, fee_hook=<function _fee_hook>)
- Parameters
quoter (web3.contract.contract.Contract) –
debug (bool) –
- generate_routes(source_token, target_token, intermediate_tokens, amount, debug)
Create routes we need to test on Uniswap v2
- Parameters
source_token (eth_defi.token.TokenDetails) –
target_token (eth_defi.token.TokenDetails) –
intermediate_tokens (set[eth_defi.token.TokenDetails]) –
amount (decimal.Decimal) –
debug (bool) –
- Return type
- handle_onchain_return_value(wrapper, raw_return_value)
Convert swapExactTokensForTokens() return value to tokens we receive
- Parameters
wrapper (eth_defi.vault.valuation.ValuationMulticallWrapper) –
raw_return_value (any) –
- Return type
decimal.Decimal | None
- get_path_combinations(source_token, target_token, intermediate_tokens)
Generate Uniswap v3 swap paths and fee with all supported intermediate tokens
- Parameters
source_token (eth_defi.token.TokenDetails) –
target_token (eth_defi.token.TokenDetails) –
intermediate_tokens (set[eth_defi.token.TokenDetails]) –
- Return type
- class NetAssetValueCalculator
Bases:
objectCalculate valuation of all vault spot assets, assuming we would sell them on Uniswap market sell or similar.
Query valuations using only onchain data / direct quoter smart contracts, no external indexers or services needed
Price impact and fees included
Brute forces all possible route combinations
Pack more RPC punch by using Multicall library
Note
Early prototype code.
Example:
vault = lagoon_vault universe = TradingUniverse( spot_token_addresses={ base_weth.address, base_usdc.address, base_dino.address, } ) latest_block = get_almost_latest_block_number(web3) portfolio = vault.fetch_portfolio(universe, latest_block) assert portfolio.get_position_count() == 3 uniswap_v2_quoter_v2 = UniswapV2Router02Quoter(uniswap_v2.router) nav_calculator = NetAssetValueCalculator( web3, denomination_token=base_usdc, intermediary_tokens={base_weth.address}, # Allow DINO->WETH->USDC quoters={uniswap_v2_quoter_v2}, debug=True, ) routes = nav_calculator.create_route_diagnostics(portfolio) print(routes)Outputs:
# Routes and their sell values: Asset Address Balance Router Works Value Path USDC USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 0.35 yes 0.35 WETH -> USDC WETH 0x4200000000000000000000000000000000000006 0.000000 UniswapV2Router02Quoter yes 0.00 DINO -> USDC DINO 0x85E90a5430AF45776548ADB82eE4cD9E33B08077 547942.000069 UniswapV2Router02Quoter no - DINO -> WETH -> USDC DINO 0x85E90a5430AF45776548ADB82eE4cD9E33B08077 547942.000069 UniswapV2Router02Quoter yes 36.69Create a new NAV calculator.
- Parameters
denomination_token –
Value the portfolio in this token.
E.g. USDC
intermediary_tokens –
When looking for sell routes, these are allowed tokens we can do three leg trades.
E.g. WETH, USDT.
quoters – Supported DEX quoters we can sell on.
block_identifier – Block number for the valuation time.
multicall –
Use multicall to optimise RPC access.
None = autodetect.
True = force.
False = disabled.
multicall_gas_limit – Let’s not explode our RPC node
batch_size – Batch size to one Multicall RPC in the number of calls.
debug –
Unit test flag.
Print out failed calldata to logging INFO, so you can inspect failed multicalls in Tenderly debugger.
- __init__(web3, denomination_token, intermediary_tokens, quoters, multicall=None, block_identifier=None, multicall_gas_limit=10000000, debug=False, batch_size=15, legacy_multicall=False)
Create a new NAV calculator.
- Parameters
denomination_token (Union[eth_typing.evm.HexAddress, eth_defi.token.TokenDetails]) –
Value the portfolio in this token.
E.g. USDC
intermediary_tokens (set[Union[eth_typing.evm.HexAddress, eth_defi.token.TokenDetails]]) –
When looking for sell routes, these are allowed tokens we can do three leg trades.
E.g. WETH, USDT.
quoters (set[eth_defi.vault.valuation.ValuationQuoter]) – Supported DEX quoters we can sell on.
block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) – Block number for the valuation time.
multicall (bool | None) –
Use multicall to optimise RPC access.
None = autodetect.
True = force.
False = disabled.
multicall_gas_limit – Let’s not explode our RPC node
batch_size – Batch size to one Multicall RPC in the number of calls.
debug –
Unit test flag.
Print out failed calldata to logging INFO, so you can inspect failed multicalls in Tenderly debugger.
web3 (web3.main.Web3) –
- generate_routes_for_router(router, portfolio, buy=False)
Create all potential routes we need to test to get quotes for a single asset.
- Parameters
buy –
Generate routes for buying: portfolio tokens present buy target.
Otherwise generate routes for selling: portfolio tokens present tokens we want to get rid off.
router (eth_defi.vault.valuation.ValuationQuoter) –
portfolio (eth_defi.vault.base.VaultPortfolio) –
- Return type
Calculate net asset value for each position.
Portfolio net asset value is the sum of positions
What is our NAV if we do market sell on DEXes for the whole portfolio now
Price impact included
- Parameters
allow_failed_routing – Raise an error if we cannot get a single route for some token
portfolio (eth_defi.vault.base.VaultPortfolio) –
- Returns
Map of token address -> valuation in denomiation token
- Return type
- resolve_best_valuations(input_tokens, routes)
Any source token may have multiple paths. Pick one that gives the best amount out.
- Parameters
input_tokens (set[eth_typing.evm.HexAddress]) –
routes (dict[eth_defi.vault.valuation.Route, decimal.Decimal]) –
- do_multicall(calls)
Multicall mess untangling.
- Parameters
calls (list[eth_defi.event_reader.multicall_batcher.MulticallWrapper]) –
- fetch_onchain_valuations(routes, portfolio, legacy=False)
Use multicall to make calls to all of our quoters.
Does not handle reserve currency, as this never has any route to itself
- Returns
Map routes -> amount out token amounts with this route
- Parameters
routes (list[eth_defi.vault.valuation.Route]) –
portfolio (eth_defi.vault.base.VaultPortfolio) –
- Return type
- try_swap_paths(routes, portfolio)
Use multicall to try all possible swap paths for tokens.
Find the best buy options
Assume
VaultPortfolio.spot_erc20contains token amounts we want to buy
- Returns
Map routes -> amount out token amounts with this route
- Parameters
routes (list[eth_defi.vault.valuation.Route]) –
portfolio (eth_defi.vault.base.VaultPortfolio) –
- Return type
- create_route_diagnostics(portfolio)
Create a route diagnotics table.
Show all routes generated for the portfolio
Flag routes that work
Show values of each portfolio position if sold with the route
Outputs:
Asset Address Balance Router Works Value Path USDC USDC 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 0.35 yes 0.35 WETH -> USDC WETH 0x4200000000000000000000000000000000000006 0.000000 UniswapV2Router02Quoter yes 0.00 DINO -> USDC DINO 0x85E90a5430AF45776548ADB82eE4cD9E33B08077 547942.000069 UniswapV2Router02Quoter no - DINO -> WETH -> USDC DINO 0x85E90a5430AF45776548ADB82eE4cD9E33B08077 547942.000069 UniswapV2Router02Quoter yes 36.69
- Returns
Human-readable DataFrame.
Indexed by asset.
- Parameters
portfolio (eth_defi.vault.base.VaultPortfolio) –
- Return type
pandas.core.frame.DataFrame
- find_swap_routes(portfolio, buy=True)
Find the best routes to buy tokens.
- Parameters
portfolio (eth_defi.vault.base.VaultPortfolio) –
- Return type