vault.base

Documentation for eth_defi.vault.base Python module.

Generic Vault adapter base classes.

  • Create unified interface across different vault protocols and their investment flows

  • Helps to create automated trading agents against any vault easily

  • Handle both trading (asset management role) and investor management (deposits/redemptions)

  • See VaultBase to get started

Module Attributes

DEPOSIT_CLOSED_EPOCH_WINDOW

Deposit closed reason constants

REDEMPTION_CLOSED_EPOCH_WINDOW

Redemption closed reason constants

Classes

TradingUniverse

Describe assets vault can manage.

VaultBase

Base class for vault protocol adapters.

VaultFlowManager

Manage deposit/redemption events.

VaultHistoricalRead

Vault share price and fee structure at the point of time.

VaultHistoricalReader

Support reading historical vault share prices.

VaultInfo

Vault-protocol specific intormation about the vault.

VaultPortfolio

Track assets and balances in a vault.

VaultReadCondition

VaultReadCondition()

VaultSpec

Unique id for a vault.

DEPOSIT_CLOSED_EPOCH_WINDOW = 'Epoch deposit window closed'

Deposit closed reason constants

REDEMPTION_CLOSED_EPOCH_WINDOW = 'Epoch redemption window closed'

Redemption closed reason constants

class VaultSpec

Bases: object

Unique id for a vault.

  • Each vault can be identified by smart contract address by one of the contracts, related to its deployment. Usually this contract is vault contract itself.

  • We need both chain and address to specify vault we mean.

chain_id: int

Ethereum chain id

vault_address: Union[eth_typing.evm.HexAddress, str]

Vault smart contract address or whatever is the primary address for unravelling a vault deployment for a vault protocol.

Always forced to lowercase.

static parse_string(spec, separator='auto')

Parse vault spec from a string.

Parameters
  • spec (str) – String in the format of “chain_id,address” or “chain_id-address”

  • separator – Either “auto” or “-” or “,”

Returns

VaultSpec instance

Return type

eth_defi.vault.base.VaultSpec

__init__(chain_id, vault_address)
Parameters
Return type

None

class VaultInfo

Bases: TypedDict

Vault-protocol specific intormation about the vault.

  • A dictionary of data we gathered about the vault deployment, like various smart contracts associated with the vault

  • Not standardised yet

__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 TradingUniverse

Bases: object

Describe assets vault can manage.

  • Because of brainrotten and awful ERC-20 token standard, the vault does not know what tokens it owns and this needs to be specific offchain

__init__(spot_token_addresses)
Parameters

spot_token_addresses (set[str]) –

Return type

None

class VaultPortfolio

Bases: object

Track assets and balances in a vault.

  • Offchain method to track what assets a vault contains

  • Takes TradingUniverse as an input and resolves all relevant balances the vault holds for this trading universe

  • Because of brainrotten and awful ERC-20 token standard, the vault does not know what tokens it owns and this needs to be specific offchain

  • See VaultBase.fetch_portfolio()

spot_erc20: eth_defi.vault.lower_case_dict.LowercaseDict

List of tokens and their amounts

Addresses not checksummed

dex_hints: dict[eth_typing.evm.HexAddress, list[str]]

For route finding, which DEX tokens should use.

Token address -> DEX id string mapping

property tokens: set[eth_typing.evm.HexAddress]

Get list of tokens held in this portfolio

is_spot_only()

Do we have only ERC-20 hold positions in this portfolio

Return type

bool

get_raw_spot_balances(web3)

Convert spot balances to raw token balances

Parameters

web3 (web3.main.Web3) –

Return type

eth_defi.vault.lower_case_dict.LowercaseDict

__init__(spot_erc20, dex_hints=<factory>)
Parameters
Return type

None

class VaultHistoricalRead

Bases: object

Vault share price and fee structure at the point of time.

vault: eth_defi.vault.base.VaultBase

Vault for this result is

block_number: int

block number of the reade

timestamp: datetime.datetime

Naive datetime in UTC

share_price: decimal.Decimal | None

What was the share price in vault denomination token

None if the read failed (call execution reverted)

total_assets: decimal.Decimal | None

NAV / Assets under management in denomination token

None if the read failed (call execution reverted)

total_supply: decimal.Decimal | None

Number of share tokens

None if the read failed (call execution reverted)

performance_fee: float | None

What was the vault performance fee around the time

management_fee: float | None

What was the vault management fee around the time

errors: list[str] | None

Add RPC error messages and such related to this read

Exported as empty string in Parquet if no errors, otherwise concat strings

vault_poll_frequency: str | None

What dynamic read frequency was used at the time of taking this sample

Useful for diagnostics of scanning process

max_deposit: decimal.Decimal | None

Maximum deposit amount allowed at this point in time (ERC-4626 maxDeposit).

In denomination token units.

max_redeem: decimal.Decimal | None

Maximum redeem amount allowed at this point in time (ERC-4626 maxRedeem).

In share token units.

deposits_open: bool | None

Whether deposits were open at this point in time (protocol-specific logic)

redemption_open: bool | None

Whether redemptions were open at this point in time (protocol-specific logic)

trading: bool | None

Whether the vault was actively trading at this point in time.

Currently only supported for D2 Finance vaults.

is_almost_equal(other, epsilon=0.001)

Check if the read statistics match.

  • Throttle with epsilon relative difference to get rid of small increment rows

Parameters
Return type

bool

export()

Convert historical read for a Parquet/DataFrame export.

Return type

dict

classmethod to_pyarrow_schema()

Get parquet schema for writing this data.

  • Write multiple chains, multiple vaults, to a single Parquet file

Return type

pyarrow.Schema

__init__(vault, block_number, timestamp, share_price, total_assets, total_supply, performance_fee, management_fee, errors, vault_poll_frequency=None, max_deposit=None, max_redeem=None, deposits_open=None, redemption_open=None, trading=None)
Parameters
Return type

None

class VaultReadCondition

Bases: object

VaultReadCondition()

__init__()
class VaultHistoricalReader

Bases: abc.ABC

Support reading historical vault share prices.

  • Allows to construct historical returns

__init__(vault)
Parameters

vault (eth_defi.vault.base.VaultBase) –

abstract construct_multicalls()

Create smart contract calls needed to read the historical state of this vault.

  • Multicall machinery will call these calls at a specific block and report back to process_result()

Return type

Iterable[eth_defi.event_reader.multicall_batcher.EncodedCall]

abstract process_result(block_number, timestamp, call_results)

Process the result of mult

Parameters
Return type

eth_defi.vault.base.VaultHistoricalRead

class VaultFlowManager

Bases: abc.ABC

Manage deposit/redemption events.

  • For some vault structures, we need to know how much redemptions there are in the queue, so we can rebalance to have enough cash

  • Create a replay of flow events that happened for a vault within a specific block range

  • Not implemented yet

abstract fetch_pending_redemption(block_identifier)

Get how much users want to redeem from the vault.

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) – Block number

Returns

Number of share tokens the users want to redeem from the vault.

Shares must be valued separately.

Return type

decimal.Decimal

abstract fetch_pending_deposit(block_identifier)

Get how much users want to redeem from the vault.

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) – Block number

Returns

Number of underlying tokens the users want to redeem from the vault.

Return type

decimal.Decimal

abstract fetch_pending_deposit_events(range)

Read incoming pending deposits.

Parameters

range (Tuple[eth_typing.evm.BlockNumber, eth_typing.evm.BlockNumber]) –

Return type

None

abstract fetch_pending_redemption_event(range)

Read outgoing pending withdraws.

Parameters

range (Tuple[eth_typing.evm.BlockNumber, eth_typing.evm.BlockNumber]) –

Return type

None

abstract fetch_processed_deposit_event(range)

Read incoming pending deposits.

Parameters

range (Tuple[eth_typing.evm.BlockNumber, eth_typing.evm.BlockNumber]) –

Return type

None

abstract fetch_processed_redemption_event(vault, range)

Read outgoing pending withdraws.

Parameters
Return type

None

class VaultBase

Bases: abc.ABC

Base class for vault protocol adapters.

  • Allows automated interaction with different vault protocols.

  • Contains various abstract methods that the implementation class must override

Supported protocols include

Code exists, but does not confirm the interface yet:

Vault covered functionality

  • Fetching the current balances, deposits or redemptions

  • Get vault information with fetch_info()
    • No standardised data structures or functions yet

  • Build a swap through a vault
    • No standardised data structure yet

  • Update vault position valuations
    • No standardised data structure yet

Integration check list

Integration tests needed for:

  • ☑️ read vault core info

  • ☑️ read vault investors

  • ☑️ read vault share price

  • ☑️ read vault share token

  • ☑️ read all positions

  • ☑️ read NAV

  • ☑️ read pending redemptions to know how much USDC we will need for the next settlement cycles

  • ☑️ deposit integration test

  • ☑️ redemption integration

  • ☑️ swap integration test

  • ☑️ re-valuation integration test

  • ☑️ only asset manager allowed to swap negative test

  • ☑️ only valuation commitee allowed to update vault valuations (if applicable)

  • ☑️ can redeem if enough USDC to settle

  • ☑️ cannot redeem not enough USDC to settle

For code examples see tests/lagoon and tests/velvet on the Github repository.

Parameters

token_cache

Token cache for vault tokens.

Allows to pass eth_defi.token.TokenDiskCache to speed up operations.

__init__(token_cache=None)
Parameters

token_cache (dict | None) –

Token cache for vault tokens.

Allows to pass eth_defi.token.TokenDiskCache to speed up operations.

first_seen_at_block: int | None

Block number hint when this vault was deployed.

Must be set externally, as because of shitty Ethereum RPC we cannot query this. Allows us to avoid unnecessary work when scanning historical price data.

abstract property chain_id: int

Chain this vault is on

abstract property address: eth_typing.evm.HexAddress

Vault contract address.

  • Often vault protocols need multiple contracts per vault, so what this function returns depends on the protocol

abstract property name: str

Vault name.

abstract property symbol: str

Vault share token symbol

property flow_manager: eth_defi.vault.base.VaultFlowManager

Flow manager associated with this vault

property deposit_manager: eth_defi.vault.deposit_redeem.VaultDepositManager

Deposit manager assocaited with this vault

abstract has_block_range_event_support()

Does this vault support block range-based event queries for deposits and redemptions.

  • If not we use chain balance polling-based approach

Return type

bool

abstract has_deposit_distribution_to_all_positions()

Deposits go automatically to all open positions.

  • Deposits do not land into the vault as cash

  • Instead, smart contracts automatically increase all open positions

  • The behaviour of Velvet Capital

Return type

bool

abstract fetch_portfolio(universe, block_identifier=None)

Read the current token balances of a vault.

  • SHould be supported by all implementations

Parameters
Return type

eth_defi.vault.base.VaultPortfolio

abstract fetch_info()

Read vault parameters from the chain.

Use info() property for cached access.

Return type

eth_defi.vault.base.VaultInfo

abstract get_flow_manager()

Get flow manager to read indiviaul settle events.

Return type

eth_defi.vault.base.VaultFlowManager

abstract get_deposit_manager()

Get deposit manager to deposit/redeem from the vault.

Return type

eth_defi.vault.deposit_redeem.VaultDepositManager

abstract get_historical_reader(stateful)

Get share price reader to fetch historical returns.

Parameters

stateful (bool) – If True, use a stateful reading strategy.

Returns

None if unsupported

Return type

eth_defi.vault.base.VaultHistoricalReader

fetch_denomination_token_address()

Get the address for the denomination token.

Triggers RCP call

Return type

eth_typing.evm.HexAddress

abstract fetch_denomination_token()

Read denomination token from onchain.

Use denomination_token() for cached access.

Return type

eth_defi.token.TokenDetails

abstract fetch_nav()

Fetch the most recent onchain NAV value.

Returns

Vault NAV, denominated in denomination_token()

Return type

decimal.Decimal

property denomination_token: eth_defi.token.TokenDetails | None

Get the token which denominates the vault valuation

  • Used in deposits and redemptions

  • Used in NAV calculation

  • Used in profit benchmarks

  • Usually USDC

Returns

Token wrapper instance.

Maybe None for broken vaults like https://arbiscan.io/address/0x9d0fbc852deccb7dcdd6cb224fa7561efda74411#code

abstract fetch_share_token()

Read share token details onchain.

Use share_token() for cached access.

Return type

eth_defi.token.TokenDetails

property share_token: eth_defi.token.TokenDetails

ERC-20 that presents vault shares.

  • User gets shares on deposit and burns them on redemption

property info: eth_defi.vault.base.VaultInfo

Get info dictionary related to this vault deployment.

  • Get cached data on the various vault parameters

Returns

Vault protocol specific information dictionary

get_protocol_name()

Return the name of the vault protocol.

Return type

str

get_management_fee(block_identifier)

Get the current management fee as a percent.

Internal: Use get_fee_data().

Returns

0.1 = 10%

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) –

Return type

float

get_performance_fee(block_identifier)

Get the current performance fee as a percent.

Internal: Use get_fee_data().

Returns

0.1 = 10%

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) –

Return type

float

has_custom_fees()

Does this vault have custom fee structure reading methods.

Causes risk in the vault comparison.

E.g.

  • Withdraw fee

  • Deposit fee

Returns

True if custom fee reading methods are implemented

Return type

bool

get_deposit_fee(block_identifier)

Deposit fee is set to zero by default as vaults usually do not have deposit fees.

Internal: Use get_fee_data().

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) –

Return type

float | None

get_withdraw_fee(block_identifier)

Withdraw fee is set to zero by default as vaults usually do not have withdraw fees.

Internal: Use get_fee_data().

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) –

Return type

float

get_risk()

Get risk profile of this vault.

Return type

eth_defi.vault.risk.VaultTechnicalRisk | None

get_fee_mode()

Get how this vault accounts its fees.

Return type

eth_defi.vault.fee.VaultFeeMode | None

get_fee_data()

Get fee data structure for this vault.

Raises

ValueError – In the case of broken or unimplemented fee reading methods in the smart contract

Return type

eth_defi.vault.fee.FeeData

get_estimated_lock_up()

What is the estimated lock-up period for this vault.

Returns

None if not know

Return type

datetime.timedelta | None

fetch_deposit_closed_reason()

Get human-readable reason why deposits are closed.

  • Override in protocol-specific subclasses

  • Default behaviour: assume deposits are always open (return None)

Returns

Human-readable string explaining why deposits are closed, or None if deposits are open.

Example reasons:

  • ”Epoch redemption window closed (opens in 14h)”

  • ”Vault paused by admin”

  • ”Max deposit cap reached”

  • ”Vault utilisation too high”

Return type

str | None

fetch_redemption_closed_reason()

Get human-readable reason why redemptions are closed.

  • Override in protocol-specific subclasses

  • Default behaviour: assume redemptions are always open (return None)

Returns

Human-readable string explaining why redemptions are closed, or None if redemptions are open.

Example reasons:

  • ”Epoch funding phase in progress (opens in 2d 5h)”

  • ”Vault paused by admin”

  • ”Vault utilisation too high - insufficient liquidity”

Return type

str | None

fetch_deposit_next_open()

Get when deposits will next be open.

  • For epoch-based vaults (Ostium, D2), return calculated window open time

  • For non-epoch vaults (Plutus, IPOR, Morpho), return None

  • Override in protocol-specific subclasses

Returns

Naive UTC datetime when deposits will next be available, or None if:

  • Deposits are currently open

  • Timing is unpredictable (manually controlled)

  • Protocol does not support timing information

Return type

datetime.datetime | None

fetch_redemption_next_open()

Get when withdrawals/redemptions will next be open.

  • For epoch-based vaults (Ostium, D2), return calculated window open time

  • For non-epoch vaults (Plutus, IPOR, Morpho), return None

  • Override in protocol-specific subclasses

Returns

Naive UTC datetime when withdrawals will next be available, or None if:

  • Withdrawals are currently open

  • Timing is unpredictable (manually controlled)

  • Protocol does not support timing information

Return type

datetime.datetime | None

get_flags()

Get various vault state flags from the smart contract.

  • Override to add status flags

  • Also add flags from our manual flag list in eth_defi.vault.flag

Returns

Flag set.

Do not modify in place.

Return type

set[eth_defi.vault.flag.VaultFlag]

get_notes()

Get a human readable message if we know somethign special is going on with this vault.

Return type

str | None

Get a link to the vault dashboard on its native site.

  • By default, give RouteScan link

Parameters

referral (str | None) – Optional referral code to append to the URL.

Returns

URL string

Return type

str