GainsVault

Documentation for eth_defi.erc_4626.vault_protocol.gains.vault.GainsVault Python class.

class GainsVault

Bases: eth_defi.erc_4626.vault.ERC4626Vault

Gains-like vault support.

This covers gToken smart contract based vaults:

GToken is an ERC-4626 compatible vault with a custom functions and logic for redemptions. We provide a logic to handle this custom logic in eth_defi.gains.deposit_redeem.GainsDepositManager.

For more examples see Tutorials.

Deposit and redeem example:

vault: GainsVault = create_vault_instance_autodetect(web3, "0xd3443ee1e91af28e5fb858fbd0d72a63ba8046e0")

amount = Decimal(100)

tx_hash = usdc.approve(
    vault.address,
    amount,
).transact({"from": test_user})
assert_transaction_success_with_explanation(web3, tx_hash)

bound_func = deposit_4626(
    vault,
    test_user,
    amount,
)
tx_hash = bound_func.transact({"from": test_user})
assert_transaction_success_with_explanation(web3, tx_hash)

share_token = vault.share_token
shares = share_token.fetch_balance_of(test_user)
assert shares == pytest.approx(Decimal("81.54203"))

# Withdrawals can be only executed on the first two days of an epoch.
# We start in a state that is outside of this window, so we need to move to the next epoch first.
assert vault.open_pnl_contract.functions.nextEpochValuesRequestCount().call() == 2
assert vault.can_create_redemption_request(test_user) is False

# 0. Clear epoch
force_next_gains_epoch(
    vault,
    test_user,
)

# 1. Create a redemption request
assert vault.open_pnl_contract.functions.nextEpochValuesRequestCount().call() == 0
assert vault.can_create_redemption_request(test_user) is True, f"We have {vault.open_pnl_contract.functions.nextEpochValuesRequestCount().call()}"
redemption_request = vault.create_redemption_request(
    owner=test_user,
    shares=shares,
)
assert isinstance(redemption_request, GainsRedemptionRequest)
assert redemption_request.owner == test_user
assert redemption_request.to == test_user
assert redemption_request.shares == shares

# 2.a) Broadcast and parse redemption request tx
assert vault.open_pnl_contract.functions.nextEpochValuesRequestCount().call() == 0
tx_hashes = []
funcs = redemption_request.funcs
tx_hash = funcs[0].transact({"from": test_user, "gas": 1_000_000})
assert_transaction_success_with_explanation(web3, tx_hash)
tx_hashes.append(tx_hash)

# 2.b) Parse result
redemption_ticket = redemption_request.parse_redeem_transaction(tx_hashes)
assert redemption_ticket.raw_shares == pytest.approx(81.54203 * 10**6)
assert redemption_ticket.owner == test_user
assert redemption_ticket.to == test_user
assert redemption_ticket.current_epoch == 197
assert redemption_ticket.unlock_epoch == 200

# Cannot redeem yet, need to wait for the next epoch
assert vault.can_redeem(redemption_ticket) is False

# 3. Move forward few epochs where our request unlocks
for i in range(0, 3):
    force_next_gains_epoch(
        vault,
        test_user,
    )

assert vault.fetch_current_epoch() >= 200

# Cannot redeem yet, need to wait for the next epoch
assert vault.can_redeem(redemption_ticket) is True

# 4. Settle our redemption
func = vault.settle_redemption(redemption_ticket)
tx_hash = func.transact({"from": test_user})
assert_transaction_success_with_explanation(web3, tx_hash)

shares = share_token.fetch_balance_of(test_user)
assert shares == 0
Parameters
  • web3 – Connection we bind this instance to

  • spec – Chain, address tuple

  • token_cache

    Cache used with fetch_erc20_details() to avoid multiple calls to the same token.

    Reduces the number of RPC calls when scanning multiple vaults.

  • features – Pass vault feature flags along, externally detected.

  • default_block_identifier

    Override block identifier for on-chain metadata reads.

    When None, use get_safe_cached_latest_block_number() (the default, safe for broken RPCs). Set to "latest" for freshly deployed vaults whose contracts do not exist at the safe-cached block.

Attributes summary

address

Get the vault smart contract address.

chain_id

Chain this vault is on

denomination_token

Get the token which denominates the vault valuation

deposit_manager

Deposit manager assocaited with this vault

erc_7540

Is this ERC-7540 vault with asynchronous deposits.

flow_manager

Flow manager associated with this vault

gains_open_trades_pnl_feed

Get Gains PnL feed contract.

info

Get info dictionary related to this vault deployment.

name

Vault name.

open_pnl_contract

Get OpenPNL contract.

share_token

ERC-20 that presents vault shares.

symbol

Vault share token symbol

underlying_token

Alias for denomination_token()

vault_address

vault_address_checksumless

vault_contract

Get vault deployment.

Methods summary

__init__(web3, spec[, token_cache, ...])

param web3

estimate_redemption_ready([now_])

How long we need to wait for withdraw if we start now.

fetch_current_epoch()

Get the current epoch number.

fetch_current_epoch_start()

When the current epoch started.

fetch_denomination_token()

Read denomination token from onchain.

fetch_denomination_token_address()

Get the address for the denomination token.

fetch_deposit_closed_reason()

Check maxDeposit to determine if deposits are closed.

fetch_deposit_next_open()

Deposit timing unpredictable - depends on vault supply vs cap.

fetch_epoch_duration()

How long are epochs for this vault.

fetch_info()

Use info() property for cached access.

fetch_nav([block_identifier])

Fetch the most recent onchain NAV value.

fetch_portfolio(universe[, ...])

Read the current token balances of a vault.

fetch_redemption_closed_reason()

Check epoch state - redemptions open when nextEpochValuesRequestCount == 0.

fetch_redemption_next_open()

Get when withdrawals will next be open.

fetch_share_price(block_identifier)

Get the current share price.

fetch_share_token()

Read share token details onchain.

fetch_share_token_address([block_identifier])

Get share token of this vault.

fetch_total_assets(block_identifier)

What is the total NAV of the vault.

fetch_total_supply(block_identifier)

What is the current outstanding shares.

fetch_vault_info()

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

fetch_withdraw_epochs_time_lock()

Fetch withdraw time lock in epochs.

get_deposit_fee(block_identifier)

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

get_deposit_manager()

Get deposit manager to deposit/redeem from the vault.

get_estimated_lock_up()

ERC-4626 vaults do not have a lock up by fault.

get_fee_data()

Get fee data structure for this vault.

get_fee_mode()

Get how this vault accounts its fees.

get_flags()

Get various vault state flags from the smart contract.

get_flow_manager()

Get flow manager to read indiviaul settle events.

get_historical_reader(stateful)

Get share price reader to fetch historical returns.

get_link([referral])

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

get_management_fee(block_identifier)

No management fee

get_max_discount_percent()

Get max discount percent.

get_notes()

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

get_performance_fee(block_identifier)

No performance fee

get_protocol_name()

Return the name of the vault protocol.

get_risk()

Get risk profile of this vault.

get_spec()

get_withdraw_fee(block_identifier)

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

has_block_range_event_support()

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

has_custom_fees()

Does this vault have custom fee structure reading methods.

has_deposit_distribution_to_all_positions()

Deposits go automatically to all open positions.

is_valid()

Check if this vault is valid.

property name: str

Vault name.

property vault_contract: web3.contract.contract.Contract

Get vault deployment.

property gains_open_trades_pnl_feed: web3.contract.contract.Contract | None

Get Gains PnL feed contract.

property open_pnl_contract: web3.contract.contract.Contract

Get OpenPNL contract.

  • Needed for epoch calls

  • See OstiumOpenPnl.sol

get_management_fee(block_identifier)

No management fee

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)

No performance fee

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

fetch_current_epoch()

Get the current epoch number.

Return type

int

fetch_epoch_duration()

How long are epochs for this vault.

Return type

datetime.timedelta

fetch_withdraw_epochs_time_lock()

Fetch withdraw time lock in epochs.

Epoch is set in function updateAccPnlPerTokenUsed() called by openTradesPnlFeed (Gains) or registry.getContractAddress(‘openPnl’) (Ostium).

Returns

Number of epochs

Return type

int

fetch_current_epoch_start()

When the current epoch started.

Return type

datetime.datetime

estimate_redemption_ready(now_=None)

How long we need to wait for withdraw if we start now.

Parameters

now_ (datetime.datetime) –

Return type

datetime.datetime | None

get_max_discount_percent()

Get max discount percent.

Gains and Ostium allows you to lock LP for a discount:

Staking DAI to the new vault and receiving gDAI can be done at any time during an epoch.

You can also optionally receive a “discount” on your gDAI when staking DAI in the vault by choosing to lock your deposit for a certain period of time. The discount has two components: a time-based incentive and a collateralization-based incentive.

Lock up your gDAI tokens when staking (from 2 weeks to 1 year).

Mint gDAI anytime the collateralization ratio is below 150%. The discount is proportional to the collateralization level, with a maximum discount of 5%. At a collateralization ratio below 100%, the discount is 5%. Between 100%-150%, the discount linearly decreases from 5% to 0%.

Returns

0.05 for 5% discount

Return type

float

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

Get deposit manager to deposit/redeem from the vault.

Return type

eth_defi.gains.deposit_redeem.GainsDepositManager

fetch_deposit_closed_reason()

Check maxDeposit to determine if deposits are closed.

Deposits closed when vault reaches max supply during profitable periods.

Return type

str | None

fetch_redemption_closed_reason()

Check epoch state - redemptions open when nextEpochValuesRequestCount == 0.

Return type

str | None

fetch_deposit_next_open()

Deposit timing unpredictable - depends on vault supply vs cap.

Return type

datetime.datetime | None

fetch_redemption_next_open()

Get when withdrawals will next be open.

  • Redemptions open at the start of the next epoch when nextEpochValuesRequestCount resets to 0

Return type

datetime.datetime | None

__init__(web3, spec, token_cache=None, features=None, default_block_identifier=None)
Parameters
  • web3 (web3.main.Web3) – Connection we bind this instance to

  • spec (eth_defi.vault.base.VaultSpec) – Chain, address tuple

  • token_cache (dict | None) –

    Cache used with fetch_erc20_details() to avoid multiple calls to the same token.

    Reduces the number of RPC calls when scanning multiple vaults.

  • features (set[eth_defi.erc_4626.core.ERC4626Feature] | None) – Pass vault feature flags along, externally detected.

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

    Override block identifier for on-chain metadata reads.

    When None, use get_safe_cached_latest_block_number() (the default, safe for broken RPCs). Set to "latest" for freshly deployed vaults whose contracts do not exist at the safe-cached block.

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

Use info() property for cached access.

Returns

See LagoonVaultInfo

Return type

eth_defi.erc_4626.vault.ERC4626VaultInfo

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

fetch_vault_info()

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

Return type

eth_defi.erc_4626.vault.ERC4626VaultInfo

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-4626 vaults do not have a lock up by fault.

Note

Because of so many protocol specific lockups, this must be explicitly set to zero.

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

Get flow manager to read indiviaul settle events.

Return type

eth_defi.vault.base.VaultFlowManager

get_link(referral=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

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_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_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

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

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