erc_4626.vault_protocol.lagoon.vault

Documentation for eth_defi.erc_4626.vault_protocol.lagoon.vault Python module.

Vault adapter for Lagoon Finance protocol.

Notes on active Lagoon development:

Lagoon v0.5.0 changes to the original release

  • Affect the vault interactions greatlty

  • Vault initialisation parameters changed: fee registry and wrapped native token moved from parameters payload to constructor arguments

  • Beacon proxy replaced with BeaconProxyFactory.createVault() patterns

  • pendingSilo() accessor removed, now needs a direct storage slot read

  • safe() accessor added

How to detect version:

  • Call pendingSilo(): if reverts is a new version

How to get pendingSilo(): see eth_defi.lagoon.vault.LagoonVault.silo_address().

Lagoon error code translation.

Module Attributes

DEFAULT_LAGOON_POST_VALUATION_GAS

How much gas we use for valuation post

DEFAULT_LAGOON_SETTLE_GAS

How much gas we use for valuation post

Classes

LagoonFlowManager

Manage deposit/redemption queue for Lagoon.

LagoonVault

Python interface for interacting with Lagoon Finance vaults.

LagoonVaultInfo

Capture information about Lagoon vault deployment.

LagoonVersion

Figure out Lagoon version.

DEFAULT_LAGOON_POST_VALUATION_GAS = 500000

How much gas we use for valuation post

DEFAULT_LAGOON_SETTLE_GAS = 500000

How much gas we use for valuation post

class LagoonVaultInfo

Bases: eth_defi.vault.base.VaultInfo

Capture information about Lagoon vault deployment.

asset: eth_typing.evm.HexAddress

The ERC-20 token that nominates the vault assets

safe: eth_typing.evm.HexAddress

Lagoon vault deployment info

whitelistManager: eth_typing.evm.HexAddress

Lagoon vault deployment info

feeReceiver: eth_typing.evm.HexAddress

Lagoon vault deployment info

feeRegistry: eth_typing.evm.HexAddress

Lagoon vault deployment info

valuationManager: eth_typing.evm.HexAddress

Lagoon vault deployment info

address: eth_typing.evm.ChecksumAddress

Safe multisig core info

fallback_handler: eth_typing.evm.ChecksumAddress

Safe multisig core info

guard: eth_typing.evm.ChecksumAddress

Safe multisig core info

master_copy: eth_typing.evm.ChecksumAddress

Safe multisig core info

modules: list[eth_typing.evm.ChecksumAddress]

Safe multisig core info

nonce: int

Safe multisig core info

owners: list[eth_typing.evm.ChecksumAddress]

Safe multisig core info

threshold: int

Safe multisig core info

version: str

Safe multisig core info

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

Bases: enum.Enum

Figure out Lagoon version.

class LagoonVault

Bases: eth_defi.erc_7540.vault.ERC7540Vault

Python interface for interacting with Lagoon Finance vaults.

For information see VaultBase base class documentation.

Example vault: https://basescan.org/address/0x6a5ea384e394083149ce39db29d5787a658aa98a#readContract

Notes

  • Vault contract knows about Safe, Safe does not know about the Vault

  • Ok so for settlement you dont have to worry about this metric, the only thing you have to value is the assets inside the safe (what you currently have under management) and update the NAV of the vault by calling updateNewTotalAssets (ex: if you have 1M inside the vault and 500K pending deposit you only need to call updateTotalAssets with the 1M that are currently inside the safe). Then, to settle you just call settleDeposit and the vault calculate everything for you.

  • To monitor the pending deposits it’s a bit more complicated. You have to check the balanceOf the pendingSilo contract (0xAD1241Ba37ab07fFc5d38e006747F8b92BB217D5) in term of underlying (here USDC) for pending deposit and in term of shares (so the vault itself) for pending withdraw requests

Lagoon tokens can be in

  • Safe: Tradeable assets

  • Silo: pending deposits (USDC)

  • Vault: pending redemptions (USDC)

  • User wallets: after deposit() have been called share tokens are moved to the user wallet

Parameters
  • spec – Address must be Lagoon vault address (not Safe address)

  • trading_strategy_module_address

    TradingStrategyModuleV0 enabled on Safe for automated trading.

    If not given, not known.

  • vault_abi

    ABI filename we use.

    Lagoon has different versions.

    None = autodetect.

  • default_block_identifier

    Override block identifier for on-chain metadata reads.

    See ERC4626Vault for details.

__init__(web3, spec, trading_strategy_module_address=None, token_cache=None, vault_abi=None, features=None, default_block_identifier=None)
Parameters
  • spec (eth_defi.vault.base.VaultSpec) – Address must be Lagoon vault address (not Safe address)

  • trading_strategy_module_address (Optional[eth_typing.evm.HexAddress]) –

    TradingStrategyModuleV0 enabled on Safe for automated trading.

    If not given, not known.

  • vault_abi (str | None) –

    ABI filename we use.

    Lagoon has different versions.

    None = autodetect.

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

    Override block identifier for on-chain metadata reads.

    See ERC4626Vault for details.

  • web3 (web3.main.Web3) –

  • token_cache (dict | None) –

  • features (set[eth_defi.erc_4626.core.ERC4626Feature]) –

fetch_version()

Figure out Lagoon version.

  • Poke the smart contract with probe functions to get version

  • Specifically call pendingSilo() that has been removed because the contract is too big

  • Our ABI definitions and callign conventions change between Lagoon versions

Return type

eth_defi.erc_4626.vault_protocol.lagoon.vault.LagoonVersion

fetch_trading_strategy_module_version()

“Perform deployed smart contract probing.

Returns

v0.1.0 or v0.1.1.

None if not TS module associated.

Return type

str | None

check_version_compatibility()

Throw if there is mismatch between ABI and contract exposed EVM calls

property version: eth_defi.erc_4626.vault_protocol.lagoon.vault.LagoonVersion

Get Lagoon version.

  • Cached property to avoid multiple calls

property trading_strategy_module_version: str

Get TradingStrategyModuleV0 contract ABI version.

  • Subject to change, development in progress

property vault_contract: web3.contract.contract.Contract

Get vault deployment.

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

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

get_flow_manager()

Get flow manager to read indiviaul settle events.

Return type

eth_defi.erc_4626.vault_protocol.lagoon.vault.LagoonFlowManager

fetch_safe(address)

Use safe() property for cached access

Return type

safe_eth.safe.safe.Safe

fetch_vault_info()

Get all information we can extract from the vault smart contracts.

Return type

dict

fetch_info()

Use info() property for cached access.

Returns

See LagoonVaultInfo

Return type

eth_defi.erc_4626.vault_protocol.lagoon.vault.LagoonVaultInfo

property safe_address: eth_typing.evm.HexAddress

Get Safe multisig contract address

property safe: safe_eth.safe.safe.Safe

Get the underlying Safe object used as an API from safe-eth-py library.

  • Warps Safe Contract using Gnosis’s in-house library

property safe_contract: web3.contract.contract.Contract

Safe multisig as a contract.

  • Interact with Safe multisig ABI

property valuation_manager: eth_typing.evm.HexAddress

Valuation manager role on the vault.

property silo_address: eth_typing.evm.HexAddress

Pending Silo contract address.

Returns

Checksummed Silo contract addrewss “pendingSilo”.

property silo_contract: web3.contract.contract.Contract

Pending Silo contract.

  • This contract does not have any functionality, but stores deposits (pending USDC) and redemptions (pending share token)

transact_via_exec_module(func_call, value=0, operation=0)

Create a multisig transaction using a module.

  • Calls execTransactionFromModule on Gnosis Safe contract

  • Executes a transaction as a multisig

  • Mostly used for testing w/whitelist ignore

Warning

A special gas fix is needed, because eth_estimateGas seems to fail for these Gnosis Safe transactions.

Example:

# Then settle the valuation as the vault owner (Safe multisig) in this case
settle_call = vault.settle()
moduled_tx = vault.transact_through_module(settle_call)
tx_data = moduled_tx.build_transaction(
    {
        "from": asset_manager,
    }
)
# Normal estimate_gas does not give enough gas for
# Safe execTransactionFromModule() transaction for some reason
gnosis_gas_fix = 1_000_000
tx_data["gas"] = web3.eth.estimate_gas(tx_data) + gnosis_gas_fix
tx_hash = web3.eth.send_transaction(tx_data)
assert_execute_module_success(web3, tx_hash)
Parameters
  • func_call (web3.contract.contract.ContractFunction) – Bound smart contract function call

  • value (int) – ETH attached to the transaction

  • operation

    Gnosis enum.

    Call = 0, DelegateCall = 1.

Return type

web3.contract.contract.ContractFunction

transact_via_trading_strategy_module(func_call, value=0, abi_version=None)

Create a Safe multisig transaction using TradingStrategyModuleV0.

Parameters
  • module – Deployed TradingStrategyModuleV0 contract that is enabled on Safe.

  • func_call (web3.contract.contract.ContractFunction) – Bound smart contract function call

  • abi_version (str) – Use specific TradingStrategyModuleV0 ABI version.

  • value (int) –

Returns

Bound Solidity functionc all you need to turn to a transaction

Return type

web3.contract.contract.ContractFunction

post_new_valuation(total_valuation)

Update the valuations of this vault.

  • Lagoon vault does not currently track individual positions, but takes a “total value” number

  • Updating this number also allows deposits and redemptions to proceed

Notes:

How can I post a valuation commitee update 1. as the valuationManager, call the function updateNewTotalAssets(_newTotalAssets) _newTotalAssets being expressed in underlying in its smallest unit for usdc, it would with its 6 decimals. Do not take into account requestDeposit and requestRedeem in your valuation

  1. as the safe, call the function settleDeposit()

Parameters

total_valuation (decimal.Decimal) – The vault value nominated in denomination_token().

Returns

Bound contract function that can be turned to a transaction

Return type

web3.contract.contract.ContractFunction

settle_via_trading_strategy_module(valuation=None, abi_version=None)

Settle the new valuation and deposits.

  • settleDeposit will also settle the redeems request if possible. If there are enough assets in the safe it will settleRedeem It there are not enough assets, it will only settleDeposit.

  • if there is nothing to settle: no deposit and redeem requests you can still call settleDeposit/settleRedeem to validate the new nav

  • If there is not enough USDC to redeem, the transaction will revert

Parameters
  • abi_version (None) – Use specific ABI version.

  • raw_amount – Needed in Lagoon v0.5+

  • valuation (decimal.Decimal) –

Return type

web3.contract.contract.ContractFunction

post_valuation_and_settle(valuation, asset_manager, gas=1000000)

Do both new valuation and settle.

  • Quickhand method for asset_manager code

  • Only after this we can read back

  • Broadcasts two transactions and waits for the confirmation

  • If there is not enough USDC to redeem, the second transaction will fail with revert

Returns

The transaction hash of the settlement transaction

Parameters
Return type

hexbytes.main.HexBytes

request_deposit(depositor, raw_amount, check_allowance=True, check_balance=True)

Build a deposit transction.

  • Phase 1 of deposit before settlement

  • Used for testing

  • Must be approved() first

  • Uses the vault underlying token (USDC)

Note

Legacy. Use get_deposit_manager() instead.

Parameters
Return type

web3.contract.contract.ContractFunction

finalise_deposit(depositor, raw_amount=None)

Move shares we received to the user wallet.

  • Phase 2 of deposit after settlement

Parameters
Return type

web3.contract.contract.ContractFunction

request_redeem(depositor, raw_amount)

Build a redeem transction.

  • Phase 1 of redemption, before settlement

  • Used for testing

  • Sets up a redemption request for X shares

Parameters
Return type

web3.contract.contract.ContractFunction

finalise_redeem(depositor, raw_amount=None)

Move redeemed assets to the user wallet.

  • Phase 2 of the redemption

Parameters
Return type

web3.contract.contract.ContractFunction

get_management_fee(block_identifier)

Get Lagoon vault rates

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 Lagoon vault rates

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

is_trading_strategy_module_enabled()

Check if TradingStrategyModuleV0 is enabled on the Safe multisig.

Return type

bool

get_deposit_manager()

Get deposit manager to deposit/redeem from the vault.

Return type

eth_defi.lagoon.deposit_redeem.ERC7540DepositManager

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

property address: eth_typing.evm.HexAddress

Get the vault smart contract address.

property chain_id: int

Chain this vault is on

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

property deposit_manager: eth_defi.vault.deposit_redeem.VaultDepositManager

Deposit manager assocaited with this vault

property erc_7540: bool

Is this ERC-7540 vault with asynchronous deposits.

  • For example previewDeposit() function and other functions will revert

fetch_denomination_token()

Read denomination token from onchain.

Use denomination_token() for cached access.

Return type

eth_defi.token.TokenDetails | None

fetch_denomination_token_address()

Get the address for the denomination token.

Triggers RCP call

Return type

Optional[eth_typing.evm.HexAddress]

fetch_nav(block_identifier=None)

Fetch the most recent onchain NAV value.

  • In the case of Lagoon, this is the last value written in the contract with updateNewTotalAssets() and ` settleDeposit()`

  • TODO: updateNewTotalAssets() there is no way to read pending asset update on chain

Returns

Vault NAV, denominated in denomination_token()

Return type

decimal.Decimal

fetch_portfolio(universe, block_identifier=None, allow_fallback=True)

Read the current token balances of a vault.

  • SHould be supported by all implementations

Parameters
Return type

eth_defi.vault.base.VaultPortfolio

fetch_share_price(block_identifier)

Get the current share price.

Returns

The share price in underlying token.

If supply is zero return zero.

Parameters

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

Return type

decimal.Decimal

fetch_share_token()

Read share token details onchain.

Use share_token() for cached access.

Return type

eth_defi.token.TokenDetails

fetch_share_token_address(block_identifier='latest')

Get share token of this vault.

  • Vault itself (ERC-4626)

  • share() accessor (ERc-7575)

Parameters

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

Return type

eth_typing.evm.HexAddress

fetch_total_assets(block_identifier)

What is the total NAV of the vault.

Example:

assert vault.denomination_token.symbol == "USDC"
assert vault.share_token.symbol == "ipUSDCfusion"
assert vault.fetch_total_assets(block_identifier=test_block_number) == Decimal("1437072.77357")
assert vault.fetch_total_supply(block_identifier=test_block_number) == Decimal("1390401.22652875")
Parameters

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

Block number to read.

Use web3.eth.block_number for the last block.

Returns

The vault value in underlyinh token

Return type

decimal.Decimal | None

fetch_total_supply(block_identifier)

What is the current outstanding shares.

Example:

Parameters

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

Block number to read.

Use web3.eth.block_number for the last block.

Returns

The vault value in underlyinh token

Return type

decimal.Decimal

property flow_manager: eth_defi.vault.base.VaultFlowManager

Flow manager associated with this vault

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_estimated_lock_up()

ERC-7540 vaults have always a lock up.

Return type

datetime.timedelta | 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_fee_mode()

Get how this vault accounts its fees.

Return type

eth_defi.vault.fee.VaultFeeMode | 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_historical_reader(stateful)

Get share price reader to fetch historical returns.

Parameters

stateful – If True, use a stateful reading strategy.

Returns

None if unsupported

Return type

eth_defi.vault.base.VaultHistoricalReader

get_notes()

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

Return type

str | None

get_protocol_name()

Return the name of the vault protocol.

Return type

str

get_risk()

Get risk profile of this vault.

Return type

eth_defi.vault.risk.VaultTechnicalRisk | 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

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

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

is_valid()

Check if this vault is valid.

  • Call a known smart contract function to verify the function exists

Return type

bool

property name: str

Vault name.

property share_token: eth_defi.token.TokenDetails

ERC-20 that presents vault shares.

  • User gets shares on deposit and burns them on redemption

property symbol: str

Vault share token symbol

property underlying_token: eth_defi.token.TokenDetails

Alias for denomination_token()

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.

class LagoonFlowManager

Bases: eth_defi.vault.base.VaultFlowManager

Manage deposit/redemption queue for Lagoon.

  • Lagoon uses ERC-7540 Asynchronous ERC-4626 Tokenized Vaults for deposits and redemptions flow

On the Lagoon flow:

Ok so for settlement you dont have to worry about this metric, the only thing you have to value is the assets inside the safe (what you currently have under management) and update the NAV of the vault by calling updateNewTotalAssets (ex: if you have 1M inside the vault and 500K pending deposit you only need to call updateTotalAssets with the 1M that are currently inside the safe). Then, to settle you just call settleDeposit and the vault calculate everything for you.

To monitor the pending deposits it’s a bit more complicated. You have to check the balanceOf the pendingSilo contract (0xAD1241Ba37ab07fFc5d38e006747F8b92BB217D5) in term of underlying (here USDC) for pending deposit and in term of shares (so the vault itself) for pending withdraw requests

__init__(vault)
Parameters

vault (eth_defi.erc_4626.vault_protocol.lagoon.vault.LagoonVault) –

Return type

None

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

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

fetch_pending_deposit_events(range)

Read incoming pending deposits.

Parameters

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

Return type

None

fetch_pending_redemption_event(range)

Read outgoing pending withdraws.

Parameters

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

Return type

None

fetch_processed_deposit_event(range)

Read incoming pending deposits.

Parameters

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

Return type

None

fetch_processed_redemption_event(vault, range)

Read outgoing pending withdraws.

Parameters
Return type

None

calculate_underlying_needed_for_redemptions(block_identifier)

How much underlying token (USDC) we are going to need on the next redemption cycle.

Returns

Raw token amount

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

decimal.Decimal

get_estimated_lock_up()

TODO: Add vault specific lock up period retrieval.

Return type

datetime.timedelta