erc_4626.vault_protocol.d2.vault

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

D2 Finance vault support.

Module Attributes

D2_GLOBAL_MACRO_CHAIN_ID

GlobalMacro on Robinhood Chain, reviewed on 2026-11-05.

D2_GLOBAL_MACRO_UPDATE_URL

Official post; text checked through the mirror below because X refused access.

D2_GLOBAL_MACRO_DESCRIPTION

Maintained Markdown for this deployment only; historical claims are dated.

D2_VAULT_SHORT_DESCRIPTIONS

Reviewed against the official catalogue on 2026-11-05, including hidden entries.

D2_UNDISCLOSED_SHORT_DESCRIPTION

Do not guess strategies for test, retired or undocumented deployments.

D2_VAULT_ARTICLE_URLS

Optional D2-authored strategy articles from the D2 vault API.

D2_VAULT_ARTICLE_OVERRIDES

The same address hosts HYPE++ Ethena on Ethereum and Alpha Strategies on Base.

D2_VAULT_ADDRESSES

D2 vault addresses checked from the D2 Finance API and the Trading Strategy public vault export on 2026-07-09.

D2_VAULT_LINK_MATRIX

D2 Finance strategy page for every known D2 vault.

D2_WITHDRAWAL_NOTE

All D2 strategies use epoch-gated withdrawals; documentation reviewed 2026-11-02 following the user's request to explain closed withdrawal windows.

Functions

format_d2_strategy_url(address)

Format a D2 Finance strategy page URL.

format_d2_texas_hedge_note()

Format the D2 texasHedge vault note.

format_d2_vault_note(address[, chain_id])

Format a generic D2 Finance vault note.

Classes

D2DepositManager

D2 Finance epoch-gated ERC-4626 lifecycle with zero-price admission failure.

D2HistoricalReader

Read D2 Finance vault core data + epoch-based deposit/redemption/trading state.

D2Vault

D2 Finance vaults.

Epoch

Epoch(funding_start: datetime.datetime, epoch_start: datetime.datetime, epoch_end: datetime.datetime)

class D2DepositManager

Bases: eth_defi.erc_4626.deposit_redeem.ERC4626DepositManager

D2 Finance epoch-gated ERC-4626 lifecycle with zero-price admission failure.

D2 vaults are managed derivative-strategy vaults that cycle through offchain-scheduled funding, trading (epoch) and withdrawal phases. Onchain deposits and redemptions use the standard synchronous ERC-4626 entry points, but they only succeed inside the correct phase. This manager keeps the plain ERC-4626 transaction construction of its parent while adding preflight phase gating and an explicit failure when D2 pricing is unavailable, so a caller sees an actionable VaultFlowUnavailable instead of paying gas for a guaranteed revert or trusting a zero estimate.

Deposit process

Synchronous. The owner approve()``s the denomination token and :meth:`create_deposit_request` builds a single ERC-4626 ``deposit call (inherited construction), but only after _assert_flow_open() confirms the vault is in its funding phase (isFunding()); otherwise it raises VaultFlowUnavailable carrying the next funding open time. estimate_deposit() overrides the parent: it first rejects a closed funding phase, then calls the standard previewDeposit-based estimator, and finally raises ValueError when the estimate is zero, because a zero share price means D2 pricing is undefined rather than that the deposit is free.

Redemption process

Synchronous. create_redemption_request() builds a single ERC-4626 redeem call (inherited construction) after _assert_flow_open() confirms redemptions are open — D2 permits withdrawal only when funds are not custodied and no epoch is running (notCustodiedAndNotDuringEpoch()). A closed window raises VaultFlowUnavailable with the next redemption open time.

Queues and settlement

No per-owner request queue: each deposit / redeem settles in its own transaction. The only gating is the vault-wide epoch phase, evaluated live from isFunding() and notCustodiedAndNotDuringEpoch(). Custodied epochs, operator NAV changes and delayed withdrawals are outside this adapter and are not modelled as tickets.

Lockups and cooldowns

No per-owner cooldown, but capital is effectively locked for the trading epoch: D2Vault.get_estimated_lock_up() reports the current epoch duration (epoch_end - epoch_start), which D2 documents as roughly 30-60 days. Deposits made during funding are custodied through the following trading epoch and can only be redeemed once the vault returns to a not-custodied, not-in-epoch state.

Deposit eligibility

The historical onlyWhitelisted modifier is not an identity/KYC gate: it also admits any account holding more than the public minimum balance of whitelistAsset. The lifecycle experiment funds that eligibility asset where necessary and reports a failed balance condition as flow availability, never as whitelisting-needed.

Anvil settlement (force_settle)

The standard deposit and redeem calls complete in their originating transaction, so the inherited force_settle() accepts None and performs the Anvil-validated shared no-op. This adapter improves preflight estimation and phase gating only; a successful D2 transaction path has not been fork-proven.

estimate_deposit(owner, amount, block_identifier='latest')

Return an estimate or an actionable zero-price failure.

Parameters
  • owner (Optional[eth_typing.evm.HexAddress]) – Deposit owner passed to the standard ERC-4626 estimator.

  • amount (decimal.Decimal) – Decimal denomination amount.

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

Returns

Estimated decimal shares.

Raises

ValueError – If D2 pricing is undefined or the funding phase is closed.

Return type

decimal.Decimal

create_deposit_request(owner, to=None, amount=None, raw_amount=None, check_max_deposit=True, check_enough_token=True)

Build a deposit only during D2’s funding phase.

Arguments match the inherited ERC-4626 request interface so existing positional callers remain compatible.

Returns

Standard synchronous deposit request.

Raises

VaultFlowUnavailable – If the current D2 epoch is not accepting deposits.

Parameters
Return type

eth_defi.erc_4626.deposit_redeem.ERC4626DepositRequest

create_deposit_request_for_guard_validation(owner, raw_amount)

Build D2 deposit calldata while its funding epoch is closed.

This narrow diagnostic exception bypasses D2’s temporary funding window and denomination-token amount/balance checks so a caller can validate the exact manager-generated deposit call through GuardV0. D2’s separate mapping-or-eligibility-asset admission check still applies. It intentionally does not construct or validate an ERC-20 approval, nor prove an approval-before-deposit sequence: those checks add no evidence to a standalone validateCall() policy check and remain normal live simulation responsibilities.

Parameters
  • owner (eth_typing.evm.HexAddress) – SimpleVaultV0/Safe address that would submit the D2 deposit.

  • raw_amount (int) – Raw D2 denomination-token amount from the rejected preflight.

Returns

One standard ERC-4626 deposit call for isolated GuardV0 validation.

Raises
Return type

eth_defi.erc_4626.deposit_redeem.ERC4626DepositRequest

create_redemption_request(owner, to=None, shares=None, raw_shares=None, check_max_deposit=True, check_enough_token=True)

Build a redemption only during D2’s withdrawal window.

Arguments match the inherited ERC-4626 request interface so existing positional callers remain compatible.

Returns

Standard synchronous redemption request.

Raises

VaultFlowUnavailable – If the current D2 epoch is not accepting redemptions.

Parameters
Return type

eth_defi.erc_4626.deposit_redeem.ERC4626RedemptionRequest

__init__(vault)
Parameters

vault (ERC4626Vault) –

analyse_deposit(claim_tx_hash, deposit_ticket)

Analyse a mined ERC-4626 deposit or guarded SimpleVault wrapper.

A ticket permits a settlement call through a non-vault wrapper, such as a SimpleVault Safe or its module. The event analyser still filters events by the underlying vault address.

Parameters
Returns

Decoded executed deposit quantities or a revert description.

Return type

Union[eth_defi.vault.deposit_redeem.DepositRedeemEventAnalysis, eth_defi.vault.deposit_redeem.DepositRedeemEventFailure]

analyse_redemption(claim_tx_hash, redemption_ticket)

Analyse a mined ERC-4626 redemption or guarded SimpleVault wrapper.

A ticket permits a non-vault transaction target for a guarded settlement; the decoded Withdraw event must still originate from this vault.

Parameters
Returns

Decoded executed redemption quantities or a revert description.

Return type

Union[eth_defi.vault.deposit_redeem.DepositRedeemEventAnalysis, eth_defi.vault.deposit_redeem.DepositRedeemEventFailure]

can_create_deposit_request(owner)

Can we start depositing now.

Vault can be full?

Parameters

owner (eth_typing.evm.HexAddress) –

Return type

bool

can_create_redemption_request(owner)

Gains allows request redepetion only two first days of three days epoch.

Returns

True if can create a redemption request now

Parameters

owner (eth_typing.evm.HexAddress) –

Return type

bool

can_finish_deposit(deposit_ticket)

Synchronous deposits can be finished immediately.

Parameters

deposit_ticket (eth_defi.erc_4626.deposit_redeem.ERC4626DepositTicket) –

can_finish_redeem(redemption_ticket)

Synchronous redemptions can be finished immediately.

Parameters

redemption_ticket (eth_defi.erc_4626.deposit_redeem.ERC4626RedemptionTicket) –

check_deposit_whitelist(owner)

Reject a deposit when the vault’s whitelist excludes the owner.

Shared deposit-preflight helper implementing the whitelisting contract every manager must honour: when a vault applies a deposit whitelist policy that is applicable and queryable, and owner is not a member of it, raise WhitelistingRequired before any transaction is broadcast so the caller can surface a “whitelisting required” state instead of paying gas for a guaranteed revert.

The check is intentionally conservative — it only raises when the whitelist information can be obtained and is applicable:

  • if is_whitelisted_deposit() raises NotImplementedError, the vault-wide policy cannot be determined for this adapter/version, so no exception is raised;

  • if the vault is permissionless, no exception is raised;

  • if is_account_whitelisted() raises NotImplementedError, per-account membership cannot be queried, so no exception is raised;

  • only when the policy is applicable and the owner is provably not admitted is WhitelistingRequired raised.

Adapters that need a stricter fail-closed policy for an unknown admission state should override their own preflight and raise VaultFlowUnavailable in addition to calling this helper (see the Lagoon manager for an example).

Parameters

owner (eth_typing.evm.HexAddress) – Deposit owner and controller whose whitelist membership is checked.

Raises

WhitelistingRequired – When the vault applies an applicable, queryable whitelist policy and owner is not permitted to deposit.

Return type

None

create_redemption_request_for_analysis(owner, raw_shares)

Rebuild request metadata for a transaction that already executed.

The default preserves existing manager behaviour without checking an escrowed wallet balance. Protocols whose admission changes on request must override this hook so receipt parsing never reruns live preflights.

Parameters
Returns

Request parser and original call shape.

Return type

eth_defi.vault.deposit_redeem.RedemptionRequest

estimate_redeem(owner, shares, block_identifier='latest')

How many denomination tokens we get for a redeem.

Parameters
Return type

decimal.Decimal

estimate_redemption_delay()

Get the redemption delay for this vault.

  • What is overall redemption delay: not related to the current moment

  • How long it takes before a redemption request is allowed

  • This is not specific for any address, but the general vault rule

  • E.g. you get 0xa592703b is an IPOR Fusion error code AccountIsLocked, if you try to instantly redeem from IPOR vaults

Returns

Redemption delay as a datetime.timedelta

Raises

NotImplementedError – If not implemented for this vault protocoll.

Return type

datetime.timedelta

fetch_completed_redemption_tx_hash(ticket)

Find an operator-owned terminal redemption transaction when available.

Claim-based protocols finish through finish_redemption() and do not need this lookup. Operator-finalised protocols override the hook to find and validate the transaction that paid the requested receiver.

Parameters

ticket (eth_defi.vault.deposit_redeem.RedemptionTicket) – Persisted redemption request to locate.

Returns

Terminal transaction hash, or None if the protocol has not observed one yet.

Return type

Optional[hexbytes.main.HexBytes]

fetch_depositable_raw_assets(owner)

Read the vault’s current raw deposit limit for an owner.

Overridable deposit-limit hook. The base implementation reads the standard ERC-4626 maxDeposit(). Multi-asset or non-standard vaults that do not implement maxDeposit (for example Upshift’s multi-asset vault) override this to answer from their own limit reader, so the deposit preflight does not depend on the ERC-4626 method being present.

Parameters

owner (eth_typing.evm.HexAddress) – Account the deposit limit is queried for.

Returns

Raw deposit limit, or None when the vault exposes no limit. A zero maxDeposit is omitted from this owner-specific capacity hook (EIP-4626 is not universally honoured), consistent with eth_defi.erc_4626.flow.deposit_4626(). The normal deposit preflight separately recognises a meaningful global zero through ERC4626Vault.fetch_deposit_closed_reason().

Raises

VaultFlowUnavailable – When the vault does not expose a readable ERC-4626 maxDeposit and no protocol-specific override is provided, instead of leaking a raw web3 ABI/read error.

Return type

Optional[int]

fetch_pending_redemption_value(ticket, block_identifier='latest')

Quote a ticket when ordinary share valuation no longer represents it.

The default retains normal share valuation. Protocols with fixed request-time entitlements or burned pending shares override this hook. Returning a value does not recognise cash or complete settlement.

Parameters
  • ticket (eth_defi.vault.deposit_redeem.RedemptionTicket) – Persisted request belonging to one trade.

  • block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) – Block used for all valuation reads.

Returns

Net denomination value, or None to value ordinary shares.

Return type

Optional[decimal.Decimal]

fetch_vault_flow_events(hypersync_client, start_block, end_block)

Fetch asynchronous vault request events from an indexed backend.

The base implementation returns no events for vault managers that do not have a two-phase deposit or redemption flow.

Parameters
  • hypersync_client – Configured Hypersync client for this vault’s chain.

  • start_block (int) – Inclusive start block.

  • end_block (int) – Inclusive end block.

Returns

Iterator of protocol-neutral pending vault flow events.

Return type

collections.abc.Iterator[eth_defi.vault.flow_events.PendingVaultFlow]

finish_deposit(deposit_ticket)

Can we finish the deposit process in async vault.

  • We can claim our shares from the vault now

Parameters

deposit_ticket (eth_defi.vault.deposit_redeem.DepositTicket) –

Return type

web3.contract.contract.ContractFunction

finish_redemption(redemption_ticket)

Build the depositor-owned final redemption transaction when one exists.

Some asynchronous vaults, such as Ember, transfer funds directly from an operator transaction. They deliberately return None here: an asset manager must not attempt to invoke an operator-only settlement method on behalf of its depositor.

Parameters

redemption_ticket (eth_defi.vault.deposit_redeem.RedemptionTicket) – Persisted asynchronous redemption request.

Returns

Bound depositor claim call, or None when the protocol has no depositor-owned finish action.

Return type

web3.contract.contract.ContractFunction

force_redemption_liquidity(owner, raw_shares, failure)

Provision an unavailable synchronous redemption on an Anvil fork.

Concrete managers may implement this only for a source-proven liquidity failure. The default is deliberately unsupported: this hook must never bypass admission, minimums, maturity or time locks.

Parameters
Returns

Structured intervention evidence from a concrete manager.

Raises

UnsupportedVaultSimulation – Always for managers without a protocol-specific implementation.

Return type

eth_defi.vault.deposit_redeem.VaultRedemptionSimulationIntervention

force_settle(ticket, *, mock=None, ignore_liquidity=False)

Force the selected ticket forward on an Anvil simulation.

Synchronous managers do not require settlement and return a no-op result when called with None. Asynchronous managers must override this method and supply their request ticket.

Parameters
  • ticket (Optional[Union[eth_defi.vault.deposit_redeem.DepositTicket, eth_defi.vault.deposit_redeem.RedemptionTicket]]) – Pending async request ticket, or None for a synchronous flow.

  • mock (Optional[object]) – Optional deployed protocol mock used only by focused local tests. Concrete asynchronous managers may use it to execute their operator/keeper settlement path without broadening production Anvil-fork authority. Passing a mock to a manager that does not implement mock settlement remains a typed unsupported simulation.

  • ignore_liquidity (bool) – Permit a protocol-specific, Anvil-only mock or fork driver to bypass an otherwise unavailable redemption-liquidity gate. This base implementation defaults to False; a documented protocol override may choose a different Anvil-only default. Managers must reject this request unless they have a tested, explicit implementation; it must never weaken a production preflight or live settlement path.

Returns

Settlement outcome with before/after status and transaction hashes.

Raises

UnsupportedVaultSimulation – If the provider is not Anvil or an async manager lacks a driver.

Return type

eth_defi.vault.deposit_redeem.VaultForcedSettlementResult

get_deposit_approval_target()

Return the ERC-20 spender required for a deposit request.

Standard ERC-4626 and the currently supported async adapters pull denomination tokens from the vault address itself. An adapter using a different router or silo must override this method; guarded callers use it to whitelist and validate the exact approval calldata.

Returns

ERC-20 approval spender address.

Return type

eth_typing.evm.HexAddress

get_deposit_delay_over(address)

Estimate when a pending async deposit request will settle.

  • Mirror of get_redemption_delay_over() for the deposit side.

  • Used to show an estimated settlement time for unsettled deposits (e.g. in the trade-executor trade-ui table).

  • Default returns None: the protocol has no deterministic onchain settlement schedule (e.g. operator-driven ERC-7540 vaults like Lagoon). Subclasses with a predictable settlement cadence (e.g. Ostium V1.5) override this to return an estimated UTC timestamp.

Parameters

address (Union[eth_typing.evm.HexAddress, str]) – Owner of the pending deposit request.

Returns

Naive UTC timestamp when the deposit is expected to settle, or None when no onchain estimate is available.

Return type

Optional[datetime.datetime]

get_deposit_request_status(ticket)

Query the current status of an async deposit request.

Default implementation probes via can_finish_deposit(). Subclasses should override for more accurate status reporting (e.g. distinguishing reclaimable from pending).

Parameters

ticket (eth_defi.vault.deposit_redeem.DepositTicket) –

Return type

eth_defi.vault.deposit_redeem.AsyncVaultRequestStatus

get_max_deposit(owner)

How much we can deposit

Parameters

owner (eth_typing.evm.HexAddress) –

Return type

Optional[decimal.Decimal]

get_redemption_delay_over(address)

Get the redemption timer left for an address.

  • How long it takes before a redemption request is allowed

  • This is not specific for any address, but the general vault rule

  • E.g. you get 0xa592703b is an IPOR Fusion error code AccountIsLocked, if you try to instantly redeem from IPOR vaults

Returns

UTC timestamp when the account can redeem.

Naive datetime, or None when the protocol has no deterministic onchain deadline.

Raises

NotImplementedError – If not implemented for this vault protocoll.

Parameters

address (Union[eth_typing.evm.HexAddress, str]) –

Return type

datetime.datetime

get_redemption_request_status(ticket)

Query the current status of an async redemption request.

Default implementation probes via can_finish_redeem(). Subclasses should override for more accurate status reporting.

Parameters

ticket (eth_defi.vault.deposit_redeem.RedemptionTicket) –

Return type

eth_defi.vault.deposit_redeem.AsyncVaultRequestStatus

has_synchronous_deposit()

Does this vault support synchronous deposits?

  • E.g. ERC-4626 vaults

Return type

bool

has_synchronous_redemption()

Does this vault support synchronous deposits?

  • E.g. ERC-4626 vaults

Return type

bool

is_deposit_in_progress(owner)

Check if the owner has an active deposit request.

Parameters

owner (eth_typing.evm.HexAddress) – Owner of the shares

Returns

True if there is an active redemption request

Return type

bool

is_redemption_in_progress(owner)

Check if the owner has an active redemption request.

Parameters

owner (eth_typing.evm.HexAddress) – Owner of the shares

Returns

True if there is an active redemption request

Return type

bool

reclaim_deposit(ticket)

Return a function to recover funds after a failed async deposit settlement.

Returns None if the protocol does not support reclaim.

Parameters

ticket (eth_defi.vault.deposit_redeem.DepositTicket) –

Return type

Optional[web3.contract.contract.ContractFunction]

reclaim_withdrawal(ticket)

Return a function to recover shares after a failed async withdrawal settlement.

Returns None if the protocol does not support reclaim.

Parameters

ticket (eth_defi.vault.deposit_redeem.RedemptionTicket) –

Return type

Optional[web3.contract.contract.ContractFunction]

reconstruct_deposit_ticket(data)

Reconstruct a deposit ticket from a serialised dict.

Default returns a base DepositTicket. Subclasses override for protocol-specific ticket types.

Parameters

data (dict) –

Return type

eth_defi.vault.deposit_redeem.DepositTicket

reconstruct_redemption_ticket(data)

Reconstruct a redemption ticket from a serialised dict.

Async vault managers must override this to return their protocol-specific ticket subclass. The base implementation raises NotImplementedError because RedemptionTicket has abstract methods.

Parameters

data (dict) –

Return type

eth_defi.vault.deposit_redeem.RedemptionTicket

serialize_deposit_ticket(ticket)

Serialise a deposit ticket to a dict for persistence.

The trade-executor stores this in trade.other_data so that the settlement retry module can reconstruct the ticket after a process restart.

Default implementation stores base DepositTicket fields. Subclasses override to add protocol-specific fields (e.g. settlement_id for Ostium, requestId for ERC-7540).

Parameters

ticket (eth_defi.vault.deposit_redeem.DepositTicket) –

Return type

dict

serialize_redemption_ticket(ticket)

Serialise a redemption ticket to a dict for persistence.

Default implementation stores base RedemptionTicket fields. Subclasses override to add protocol-specific fields.

Parameters

ticket (eth_defi.vault.deposit_redeem.RedemptionTicket) –

Return type

dict

D2_GLOBAL_MACRO_CHAIN_ID = 4663

GlobalMacro on Robinhood Chain, reviewed on 2026-11-05. https://tradingstrategy.ai/vaults/globalmacro https://robinhoodchain.blockscout.com/address/0x3a87f340c7aa52694c3dd806ba13d1b2ebf48dd8

D2_GLOBAL_MACRO_UPDATE_URL = 'https://x.com/D2_Finance/status/2117815338616152350'

Official post; text checked through the mirror below because X refused access. https://instalker.org/D2_Finance/status/2117815338616152350

D2_GLOBAL_MACRO_DESCRIPTION = "Deposited USDG, a US dollar stablecoin, funds short-term bets on rising or falling equity indices, commodities and currencies. The [launch design](https://medium.com/@D2.Finance/globalmacro-gm-vault-on-robinhood-take-from-the-markets-that-sleep-give-to-the-one-that-never-a66b2c62f300) combines options, contracts giving the right to buy or sell at an agreed price, with [perpetual futures](https://tradingstrategy.ai/glossary/perpetual-future), contracts that track prices without an expiry date. The trading signals come from HYPE++, a separate investment strategy whose performance history does not belong to this vault.\n\n#### Lending and trading\n\nAt launch in August 2026, the principal was allocated to a USDG lending vault managed by Steakhouse on Morpho, a lending platform. A smaller allocation supported trades on Hyperliquid, a derivatives exchange. Lending income supplements trading results but introduces borrower and smart contract risks.\n\n#### Risk and withdrawals\n\nThe strategy uses **leverage**, taking positions larger than the money backing them, which magnifies gains and losses. The manager's **10% loss-limit policy per trading cycle**, called an epoch, is not guaranteed protection or a limit on losses measured from an earlier peak. Lending income may offset only part of a trading loss.\n\n**Deposits and withdrawals close during trading.** Under the [vault design](https://docs.d2.finance/docs/vault-design), the trader may take custody of funds, which must be returned before withdrawals reopen. An announced epoch-end date does not guarantee withdrawal availability.\n\n#### Announced changes\n\nThe [November 2026 update](https://x.com/D2_Finance/status/2117815338616152350) reviewed substantial funding costs, announced another trading cycle and proposed changes to the strategy. [Funding payments](https://tradingstrategy.ai/glossary/funding-rate) pass periodically between traders on opposite sides of perpetual futures and can reduce trading returns.\n\nPlans included Pendle Boros, a market for trading funding rates, and a possible split into GM 2.0 and Overnight Edge, two proposed strategy variants. These were proposals, not confirmed changes to the vault's investments.\n"

Maintained Markdown for this deployment only; historical claims are dated.

D2_VAULT_SHORT_DESCRIPTIONS: dict[tuple[int, str], str] = {(1, '0x07dff4087b43c4a759f4fc69511c26d51929daf4'): 'Combines lending income with trading contracts whose value follows other assets.', (1, '0x09dfcf4731149582d163a763e7dd553ebc18852d'): 'Lends USDC at a fixed rate to finance the HYPE++ trading strategy.', (1, '0x1f7bae0549f9f0baf15d0d637900056bb9434c36'): 'Combines gold and bitcoin exposure with options income and bets against shares in Strategy, a bitcoin-holding company.', (1, '0x2406aacbdf8463176deb285adaa81768415b6c7e'): 'Trades major cryptoasset contracts to profit from differences in prices, payments between traders and expected price fluctuations.', (10, '0x20833f2e96ab236d66d18a0064e1e8171b70e22e'): "Bets that Korean share price swings will shrink relative to bitcoin's.", (10, '0xafd240a8e564ad34663b6c42475027ed083088a2'): 'Earns income when Korean stock-market price fluctuations are smaller than the level agreed in its contracts.', (999, '0x0f00008223b4d4c2c466b4456df1d483743cbd60'): 'Borrows against HYPE tokens to invest in gold and bitcoin.', (999, '0x140e81f8c033c0705036e55be2f2fbce43e4595c'): 'Borrows against HYPE tokens to buy fixed-maturity investments linked to the HYPE++ trading strategy.', (999, '0x195eb4d088f222c982282b5dd495e76dba4bc7d1'): 'Trades bitcoin and ether contracts to profit from differences in prices and expected price fluctuations.', (999, '0x208f63a7f60c319597c05fa5ec67fde41839bad6'): 'Makes concentrated bets across asset markets using options and borrowed money.', (999, '0x3ebb11ba6a5b61c04d1a703ea10728d519945440'): 'Earns income by selling rights to future HYPE price gains, alongside lending and offsetting trades.', (999, '0x4de611ed46aaf54a6850f97542ce7a58e7c8d0fe'): 'Sells rights to future Kraken share price gains against claims on shares held before a public listing.', (999, '0x6bf9345b5d6b27b5cbf2e463dc5e0b2afcedc21c'): 'Uses ether as backing for options that increase exposure to ether price rises.', (999, '0x7410e69958a8ece2a51c231c8528513d4d668c7a'): 'Combines contracts tied to traditional financial markets with cryptoasset lending and trading income.', (999, '0x8ef30c5ce9a460bfae82f1f039f7c5e5427d7018'): 'Earns income from selling SOL options alongside other trading income.', (999, '0xade27c7dec9211973278876f3819aedc28cd50ca'): 'Combines contracts tied to traditional financial markets with cryptoasset lending and trading income.', (999, '0xf44f49e6577b3934f981c6f0629d15154d2606e6'): 'Earns income from bitcoin options and price differences while using offsetting trades to manage risk.', (1116, '0xa6c99234ebcda225ef32cefb8472363e09a51aea'): 'Earns income by selling rights to future bitcoin price gains against bitcoin-backed SolvBTC holdings.', (4663, '0x3a87f340c7aa52694c3dd806ba13d1b2ebf48dd8'): 'Trades equity indices, commodities and currencies alongside income from USDG lending.', (8453, '0x2406aacbdf8463176deb285adaa81768415b6c7e'): 'Trades bitcoin and ether contracts to profit from differences in prices and expected price fluctuations.', (8453, '0x6c05a7d2c24b48fc3c615d294fec2eb068548897'): 'Uses options to increase exposure to bitcoin price rises.', (8453, '0xafd240a8e564ad34663b6c42475027ed083088a2'): 'Trades a basket of seven US stocks with options income and protection against some price falls.', (42161, '0x0178b56fea3d7b5b9f9e0cdad486522de948730f'): 'Combines ether price exposure with trading income and liquidity rewards.', (42161, '0x0f76de33a3679a6065d14780618b54584a3907d4'): 'Uses options to increase exposure to ether price rises.', (42161, '0x1176c3760af6a1dbaa5bbd0cc6cda8a2ed6b785e'): 'Executes large trades gradually to spread purchases and sales over time.', (42161, '0x195a9e0f29f96d4ab2139ee1272380a4aa352890'): 'Borrows USDC against ether to invest partly in the ETH++ trading strategy while retaining ether price exposure.', (42161, '0x27d22eb71f00495eccc89bb02c2b68e6988c6a42'): 'Trades ether and other major cryptoassets to profit from differences in price fluctuations and payments between traders.', (42161, '0x291344fbaac4fe14632061e4c336fe3b94c52320'): 'Seeks income while retaining partial exposure to ARB price movements.', (42161, '0x34f0fdd80a51dfd8ba42343c20f89217280d760e'): 'Allocates investments to suit an institutional investor.', (42161, '0x36b1939adf539a4ac94b57dbad32faecd5bcf4d0'): 'Allocates investments to suit an institutional investor.', (42161, '0x75288264fdfea8ce68e6d852696ab1ce2f3e5004'): 'Trades bitcoin and ether contracts to profit from differences in prices and expected price fluctuations.', (42161, '0x907a9f69061736ad82811cccd6add9dc4a2352a9'): "Uses borrowed money to increase investment in Hyperliquid's trading liquidity pool.", (42161, '0x999a57ae7694298126a5db2e44f778ca486b14fc'): 'Holds a transferable form of xD2 tokens for participation in protocol rewards.', (42161, '0xab2743a3a2e06d457368e901f5f927f271fa1374'): 'Borrows against GMX tokens to fund other investments while retaining exposure to GMX prices.', (42161, '0xac75f0c46723432a2303f2a7c7769535a179ed56'): 'Holds wrapped HYPE tokens, a transferable form of the Hyperliquid exchange token.', (42161, '0xb0730aa7d6e880f901b5d71a971096db56895a0f'): 'Seeks income while retaining partial exposure to ARB price movements.', (42161, '0xc027ec28f76d92d4124fcbffcf6b25137a84968c'): 'Combines bitcoin price exposure with trading income and liquidity rewards.', (42161, '0xd1d64daeed7504ef3eb056aa2d973bd064843a84'): 'Uses USDC to earn investment rewards and profit from price fluctuations and differences in payments between traders.', (42161, '0xfddd73ecd0d0d75e902a567811a70e167a262fab'): 'Trades TRUMP tokens and related contracts to profit from price swings and payments between traders.', (80094, '0x2b8d0420996a2753ef21c25c94eae9fc0c0aed1e'): 'Uses borrowed money to supply trading liquidity on Kodiak and earn rewards, with options to manage risk.', (80094, '0x36b933554782b108bb9962ac00c498acbceb706d'): 'Seeks income from investment rewards, options trading and differences in payments between traders.', (80094, '0xace42f7e3f4672607897bf1951468031f0214359'): 'Holds BERA while betting against other blockchain tokens to offset some market risk.', (80094, '0xbe75c8a7e58c7901d2e128dc8d3b6de2481f1f79'): 'Uses options to increase exposure to BERA price rises.', (80094, '0xbf075980792f8cc89dfb74b553acf6750a7e941b'): 'Uses options to increase exposure to bitcoin, ether and BERA prices alongside liquidity rewards.', (80094, '0xc4fee8c68293a63241b64e5a2ef07fcf89005dd3'): 'Moves BERA investments between lending and trading platforms to earn fees and rewards.', (80094, '0xcd18006cc69c6d5fa4fd4eaf99910b58464fa3ae'): 'Uses borrowed money to increase exposure to BERA prices and earn rewards.', (80094, '0xf650ba4303ce164e1f6b215d4cbb5e212d307056'): 'Uses borrowed money to earn lending and trading liquidity rewards and trade differences in payments between traders.'}

Reviewed against the official catalogue on 2026-11-05, including hidden entries. Sources and evidence gaps: docs/protocol-research/d2-vault-descriptions.md. Chain/address keys distinguish different products deployed at the same address.

D2_UNDISCLOSED_SHORT_DESCRIPTION = 'Strategy details are unavailable for this vault.'

Do not guess strategies for test, retired or undocumented deployments.

D2_VAULT_ARTICLE_URLS: dict[str, str] = {'0x195eb4d088f222c982282b5dd495e76dba4bc7d1': 'https://medium.com/@D2.Finance/hype-strategy-faq-654bc8d0efc9', '0x208f63a7f60c319597c05fa5ec67fde41839bad6': 'https://medium.com/@D2.Finance/texas-hedge-epoch-2-elon-takes-us-to-space-x-fec66a354ad3', '0x2406aacbdf8463176deb285adaa81768415b6c7e': 'https://medium.com/@D2.Finance/hype-base-institutional-grade-defi-bdce160e66b6', '0x27d22eb71f00495eccc89bb02c2b68e6988c6a42': 'https://medium.com/@D2.Finance/d2-finance-eth-market-launching-on-camelot-5e420cff0181', '0x3ebb11ba6a5b61c04d1a703ea10728d519945440': 'https://medium.com/@D2.Finance/d2hype-turn-hype-volatility-into-20-apr-yield-b570d72a4ec0', '0x4de611ed46aaf54a6850f97542ce7a58e7c8d0fe': 'https://medium.com/@D2.Finance/introducing-hkraken-pioneering-tokenized-pre-ipo-options-6292f2f3d03c', '0x7410e69958a8ece2a51c231c8528513d4d668c7a': 'https://medium.com/@D2.Finance/introducing-usdt0-hrwa-the-ultimate-stablecoin-yield-engine-on-hyperevm-f7e8b58068db', '0x75288264fdfea8ce68e6d852696ab1ce2f3e5004': 'https://medium.com/@D2.Finance/hype-strategy-faq-654bc8d0efc9', '0x8ef30c5ce9a460bfae82f1f039f7c5e5427d7018': 'https://medium.com/@D2.Finance/introducing-hsol-20-apr-target-on-sol-with-hyperliquid-5b10672f6369', '0xace42f7e3f4672607897bf1951468031f0214359': 'https://medium.com/@D2.Finance/hedged-bera-hbera-betting-on-berachains-community-hedging-against-ghost-towns-72d6332e95e2', '0xc4fee8c68293a63241b64e5a2ef07fcf89005dd3': 'https://medium.com/@D2.Finance/4f10c3fbf0a3', '0xcd18006cc69c6d5fa4fd4eaf99910b58464fa3ae': 'https://medium.com/@D2.Finance/infrared-origami-d2-2xbera-df7e7129df9a', '0xf44f49e6577b3934f981c6f0629d15154d2606e6': 'https://medium.com/@D2.Finance/introducing-hyperliquid-xxi-hxxi-the-future-of-bitcoin-accumulation-on-chain-499c488e67ca', '0xf650ba4303ce164e1f6b215d4cbb5e212d307056': 'https://medium.com/@D2.Finance/hypereuler-x-pol-pol-is-here-is-time-to-supercharge-your-bera-4dfaa8805d23'}

Optional D2-authored strategy articles from the D2 vault API.

D2_VAULT_ARTICLE_OVERRIDES: dict[tuple[int, str], str] = {(1, '0x2406aacbdf8463176deb285adaa81768415b6c7e'): 'https://medium.com/@D2.Finance/hype-strategy-faq-654bc8d0efc9'}

The same address hosts HYPE++ Ethena on Ethereum and Alpha Strategies on Base. Keep the legacy address table unchanged for migration 034’s old-note recognition.

D2_VAULT_ADDRESSES: set[str] = {'0x0178b56fea3d7b5b9f9e0cdad486522de948730f', '0x07dff4087b43c4a759f4fc69511c26d51929daf4', '0x09dfcf4731149582d163a763e7dd553ebc18852d', '0x0f00008223b4d4c2c466b4456df1d483743cbd60', '0x0f76de33a3679a6065d14780618b54584a3907d4', '0x1176c3760af6a1dbaa5bbd0cc6cda8a2ed6b785e', '0x140e81f8c033c0705036e55be2f2fbce43e4595c', '0x17fd8c3d1e0379cf6b1dace21750e624eb9573c2', '0x183424d5ae5ec9fd486634bc566d0f75ad9c9109', '0x195a9e0f29f96d4ab2139ee1272380a4aa352890', '0x195eb4d088f222c982282b5dd495e76dba4bc7d1', '0x1f1fc659e69318a5f3aab5d69aaad9c9a6245c9a', '0x208f63a7f60c319597c05fa5ec67fde41839bad6', '0x21219173ff017cd333364612764fb2579fd9b0c1', '0x23f556f4df7a263ce68ee29659aa4ea632a6a5bc', '0x2406aacbdf8463176deb285adaa81768415b6c7e', '0x26eca524d0eb6ce02603850424c56bc42f53e54b', '0x27d22eb71f00495eccc89bb02c2b68e6988c6a42', '0x291344fbaac4fe14632061e4c336fe3b94c52320', '0x2b8d0420996a2753ef21c25c94eae9fc0c0aed1e', '0x34f0fdd80a51dfd8ba42343c20f89217280d760e', '0x36b1939adf539a4ac94b57dbad32faecd5bcf4d0', '0x36b933554782b108bb9962ac00c498acbceb706d', '0x3d493db0d3d616937a930b29c57b3f654f9b41d9', '0x3ebb11ba6a5b61c04d1a703ea10728d519945440', '0x4ada76cc8755f62508a2df65d7fafa4fd26e76c6', '0x4d823951a8b3a614667e9cabf6948d7d0e73911d', '0x4de611ed46aaf54a6850f97542ce7a58e7c8d0fe', '0x575224c6b1fa1d26977cd651974b6d7694f30d52', '0x57f467c9c4639b066f5a4d676cd8ed7d87c1791b', '0x5b49d7fae00de64779ddcd6b067c8eb046bd9a0b', '0x64167cd42859f64cff2aa4b63c3175ccef9659dd', '0x6a4d2462c5f9cf21f05e441911d88d38754a1137', '0x6a798ae978a51f976ec667bdd94a9dcf81d940ed', '0x6bf9345b5d6b27b5cbf2e463dc5e0b2afcedc21c', '0x6c05a7d2c24b48fc3c615d294fec2eb068548897', '0x7348925d3c63e4e61e9f5308eeec0f06eaa3bb7b', '0x7410e69958a8ece2a51c231c8528513d4d668c7a', '0x75288264fdfea8ce68e6d852696ab1ce2f3e5004', '0x7da637df83778704464208f092dbdbf9f386df96', '0x7efe92efb73e95fe6481e9bf81bbbe8c03fe2d61', '0x80c403807b1032d7cb19b6d612ce23f05a213d36', '0x864f2414a99f56e28fcc4deac520169f6e81f4ae', '0x8b8773f88816387f8aba5bef5cfc2d5e329600cb', '0x8ef30c5ce9a460bfae82f1f039f7c5e5427d7018', '0x907a9f69061736ad82811cccd6add9dc4a2352a9', '0x90a23c5cf9dd0897ced19de6a77a856c228b57c3', '0x91acd32da9bea6da3751dc12ee0fbe47169349c1', '0x999a57ae7694298126a5db2e44f778ca486b14fc', '0x9aa9bd7b032e527b0820c85cee89093dc33df8bd', '0xa0820f0934e47d6c191450f47ec6430483d1394e', '0xa6c99234ebcda225ef32cefb8472363e09a51aea', '0xab2743a3a2e06d457368e901f5f927f271fa1374', '0xac75f0c46723432a2303f2a7c7769535a179ed56', '0xace42f7e3f4672607897bf1951468031f0214359', '0xb0730aa7d6e880f901b5d71a971096db56895a0f', '0xbe75c8a7e58c7901d2e128dc8d3b6de2481f1f79', '0xbf075980792f8cc89dfb74b553acf6750a7e941b', '0xc027ec28f76d92d4124fcbffcf6b25137a84968c', '0xc4fee8c68293a63241b64e5a2ef07fcf89005dd3', '0xc51971f0676c58c1156e3e08e5e66367f2aa3d1e', '0xc5baffd6d9b3755ea680e6c630c44a120154dc58', '0xc9ebd0975e7d207c2f8ca2c82007cbbafb262c8c', '0xcd18006cc69c6d5fa4fd4eaf99910b58464fa3ae', '0xd0db54d54e227584563226206e0f74a7e4ef54af', '0xd1d64daeed7504ef3eb056aa2d973bd064843a84', '0xeb3194d69201b04729f1a618d95519cdae24d6c0', '0xee8bbccaa590a4c087d9d2e48b92f60813ed2b43', '0xeee6e115cb08ac280fc55642a61d8adfba85dfed', '0xf44f49e6577b3934f981c6f0629d15154d2606e6', '0xf650ba4303ce164e1f6b215d4cbb5e212d307056', '0xfddd73ecd0d0d75e902a567811a70e167a262fab'}

D2 vault addresses checked from the D2 Finance API and the Trading Strategy public vault export on 2026-07-09.

D2 Finance strategy page for every known D2 vault.

Keep native vault links on D2’s own website. Trading Strategy page links are generated separately by the vault metrics reporting layer.

format_d2_strategy_url(address)

Format a D2 Finance strategy page URL.

Each D2 vault has an authoritative strategy page under the D2 website. This helper keeps vault note links consistent across D2 notes.

Parameters

address (Union[eth_typing.evm.HexAddress, str]) – Vault address.

Returns

D2 Finance strategy page URL.

Return type

str

D2_WITHDRAWAL_NOTE = "#### Withdrawal restrictions\n\n**Deposits and withdrawals are closed during the trading phase.** Each [trading cycle](https://docs.d2.finance/docs/vault-design), called an epoch, has separate funding, trading and withdrawal phases. During trading, the trader may take custody of the vault's funds.\n\nWithdrawals reopen after the trader returns the funds and the epoch ends. A displayed epoch-end time is an estimate, not a guaranteed withdrawal deadline."

All D2 strategies use epoch-gated withdrawals; documentation reviewed 2026-11-02 following the user’s request to explain closed withdrawal windows. https://docs.d2.finance/docs/vault-design

format_d2_vault_note(address, chain_id=None)

Format a generic D2 Finance vault note.

D2 vaults are managed strategy vaults with proprietary offchain trading logic. The authoritative source for the per-vault setup is the D2 strategy page, so link to it for every D2 vault. Chain-specific corrections take precedence; omitting the chain retains legacy rendering for migrations.

Parameters
Returns

Markdown vault note.

Return type

str

format_d2_texas_hedge_note()

Format the D2 texasHedge vault note.

The texasHedge vault has a D2-authored strategy article with specific tactical and risk context that is more useful than the generic D2 note.

Returns

Markdown vault note.

Return type

str

class Epoch

Bases: object

Epoch(funding_start: datetime.datetime, epoch_start: datetime.datetime, epoch_end: datetime.datetime)

__init__(funding_start, epoch_start, epoch_end)
Parameters
Return type

None

class D2HistoricalReader

Bases: eth_defi.erc_4626.vault.ERC4626HistoricalReader

Read D2 Finance vault core data + epoch-based deposit/redemption/trading state.

  • Deposits are open during the funding phase (isFunding())

  • Redemptions are open when funds are not custodied and not during epoch (notCustodiedAndNotDuringEpoch())

  • Trading is active when the vault is in an epoch (isInEpoch())

construct_multicalls()

Get the onchain calls that are needed to read the share price.

Return type

collections.abc.Iterable[eth_defi.event_reader.multicall_batcher.EncodedCall]

construct_d2_epoch_calls()

Add D2-specific epoch state calls.

Return type

collections.abc.Iterable[eth_defi.event_reader.multicall_batcher.EncodedCall]

process_result(block_number, timestamp, call_results)

Process the result of mult

  • Calls are created in construct_multicalls()

  • This method combines result of this calls to a easy to manage historical record VaultHistoricalRead

Parameters
Return type

eth_defi.vault.base.VaultHistoricalRead

__init__(vault, stateful)
Parameters
construct_core_erc_4626_multicall()

Polling endpoints defined in ERC-4626 spec.

  • Does not include fee calls which do not have standard

Return type

Iterable[eth_defi.event_reader.multicall_batcher.EncodedCall]

dictify_multicall_results(block_number, call_results, allow_failure=True)

Convert batch of multicalls made for this vault to more digestible dict.

  • Assert that all multicalls succeed

Returns

Dictionary where each multicall is keyed by its EncodedCall.extra_data["function"]

Parameters
Return type

dict[str, eth_defi.event_reader.multicall_batcher.EncodedCallResult]

fetch_contextual_historical_reads(start_block, end_block, step)

Yield protocol-sourced historical rows outside the static call grid.

Contextual readers override this method and read an already-prefilled local cache. Network collection belongs before the common price scan.

Parameters
  • start_block (int) – Inclusive archive block boundary.

  • end_block (int) – Exclusive archive block boundary.

  • step (int) – Approximate block spacing used to downsample source observations.

Returns

Ordinary VaultHistoricalRead rows for the common writer.

Raises

NotImplementedError – If a reader opts into contextual history without implementing it.

Return type

Iterable[eth_defi.vault.base.VaultHistoricalRead]

get_warmup_calls()

Yield (function_name, callable, contract_call) tuples for warmup testing.

Each callable should execute a single contract call. If it raises, the function is marked as broken.

The optional contract_call is used for gas estimation to detect expensive calls before executing them. If provided, calls using excessive gas (>1M gas) will be marked as broken without execution.

Override in subclasses to add protocol-specific calls.

Returns

Iterable of (function_name, test_callable, contract_call) tuples. contract_call may be None if gas estimation is not needed.

Return type

Iterable[tuple[str, callable, Any]]

process_core_erc_4626_result(call_by_name)

Decode common ERC-4626 calls.

Parameters

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

Return type

tuple

property share_price_change_threshold: float

Return this reader’s sparse share-price comparison threshold.

Event-only readers may require every exact price change; ordinary readers retain the existing ten-basis-point suppression threshold.

Returns

Non-negative relative suppression threshold.

should_skip_call(function_name)

Check if a specific function call should be skipped.

Uses the reader state’s call_status map if available.

Parameters

function_name (str) – The function name to check

Returns

True if the call should be skipped

Return type

bool

property uses_contextual_history: bool

Return whether this reader supplies protocol-sourced observations.

Most EVM vaults expose static calldata that the common multicaller can execute on a regular block grid. Protocols such as GMX instead expose sparse value events that are prefetched into a protocol-owned cache.

Returns

False for the ordinary static-multicall path.

property uses_share_price_equivalence: bool

Return whether cash flows must be ignored by the sparse filter.

Vault-like products without a standard share-price method can still expose a supply-normalised price equivalent. Their TVL and supply changes must not create false performance observations.

Returns

True when the vault carries the share_price_equivalence feature.

class D2Vault

Bases: eth_defi.erc_4626.vault.ERC4626Vault

D2 Finance vaults.

  • Most vault logic is offchain, proprietary

  • VaultV1Whitelisted is a wrapper around a Hyperliquid trading account

  • Deposits have asset-holding eligibility conditions but no KYC requirement

  • The vault smart contract does not have visibility to the fees

  • Redemption must happen not during epoch

  • Fees are set and calculated offchain

  • The vaults have funding, trading and withdraw phases and you can only deposit/withdraw on the correct epoch

  • Lockups are up to 30-60 days or so

  • The vault owner can set epochs offhain, up to 10 years

More information:

Despite the contract’s historical whitelisted naming, its balance and schedule conditions do not represent a KYC or manual identity gate. They must not change the public deposit_permission status.

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.

  • require_denomination_token – If True, accessing denomination_token will raise RuntimeError when the on-chain lookup returns None.

property vault_contract: web3.contract.contract.Contract

Get vault deployment.

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

Create the D2 phase-aware lifecycle manager.

Returns

D2 manager that avoids returning a zero share estimate.

Return type

eth_defi.erc_4626.vault_protocol.d2.vault.D2DepositManager

is_whitelisted_deposit()

Report whether D2 uses a mapping-only identity gate.

The historical onlyWhitelisted name is misleading for public status purposes: any account can satisfy the rule by holding the configured public asset minimum. It is an economic eligibility condition, not KYC or manual identity approval. A zero whitelist-asset address removes that public route and leaves only the explicit mapping, which is a genuine account allow-list.

Returns

False when public asset-balance admission is configured; otherwise True for a mapping-only deployment.

Return type

bool

is_account_eligible(address)

Evaluate D2’s legacy mapping-or-asset-balance eligibility rule.

Both deposit() and redeem() execute onlyWhitelisted. The balance branch is deliberately read at request construction time, rather than cached, because a deposit can consume the balance that granted admission.

Parameters

address (eth_typing.evm.HexAddress) – Account whose D2 economic eligibility is queried.

Returns

True when the explicit mapping or configured asset balance currently admits the account.

Return type

bool

is_account_whitelisted(address)

Read D2 mapping membership when no public balance route exists.

Public asset-balance eligibility is deliberately exposed through is_account_eligible() instead of being labelled as whitelist membership.

Parameters

address (eth_typing.evm.HexAddress) – Account whose explicit D2 mapping membership is inspected.

Returns

Whether the account is present in the explicit mapping.

Raises

NotImplementedError – If the deployment has a public asset-balance eligibility route.

Return type

bool

property short_description: str

Summarise the reviewed strategy without inferring it from the name.

The official catalogue contains historical and undocumented deployments. Use a clear evidence-gap sentence for these and unknown deployments.

Returns

One plain sentence, scoped to chain and case-insensitive address.

property description: Optional[str]

Expose the researched GlobalMacro strategy explanation.

Scope the maintained copy to the reviewed Robinhood deployment so unrelated D2 vaults retain their inherited metadata behaviour.

Returns

Normalised Markdown for GlobalMacro, or the inherited description.

Get the canonical public page for this D2 vault.

The matrix keeps every native vault link on D2 Finance’s strategy pages. Trading Strategy links are exported separately by the vault metrics reporting layer.

Parameters

referral (Optional[str]) – Unused because neither supported D2 destination accepts a referral parameter.

Returns

D2 Finance strategy page URL.

Return type

str

get_notes()

Get D2-specific vault notes.

D2 vault notes are owned by the D2 protocol adapter, so scanner output can persist the note in the vault metadata row. Manual shared notes and flags from the base implementation keep priority.

Returns

Markdown note for Trading Strategy vault exports.

Return type

Optional[str]

has_custom_fees()

Deposit/withdrawal fees.

Return type

bool

get_management_fee(block_identifier)

Return the separately reported 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)

Return the performance fee internalised in the share price.

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

Optional[float]

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

get_withdrawal_period()

Return the D2 redemption window bounds for the current epoch.

D2 has no post-request cooldown: redemptions become available in the withdrawal phase after a trading epoch. Consequently the shortest wait is zero and the longest normal wait is one configured epoch.

Returns

Current epoch-length withdrawal window bounds.

Return type

eth_defi.vault.base.WithdrawalPeriod

fetch_observation_time()

Read the EVM timestamp used by the current vault observation.

Delay calculations use the same block context as the vault state instead of the wall clock, which keeps historical and forked reads deterministic.

Returns

Naive UTC timestamp of the adapter’s configured block.

Return type

datetime.datetime

fetch_deposit_closed_reason()

Deposits open during isFunding() phase.

Return type

Optional[str]

fetch_redemption_closed_reason()

Redemptions open when notCustodiedAndNotDuringEpoch().

Return type

Optional[str]

fetch_deposit_next_open()

Get when deposits will next be open.

  • Deposits open at the start of the next funding phase (after epoch ends)

Return type

Optional[datetime.datetime]

fetch_redemption_next_open()

Get when withdrawals will next be open.

  • Redemptions open when funds are not custodied and not during epoch

Return type

Optional[datetime.datetime]

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

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

  • token_cache (Optional[dict]) –

    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 (Optional[set[eth_defi.erc_4626.core.ERC4626Feature]]) – 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.

  • require_denomination_token (bool) – If True, accessing denomination_token will raise RuntimeError when the on-chain lookup returns None.

property address: eth_typing.evm.HexAddress

Get the vault smart contract address.

can_check_deposit()

Check if maxDeposit(address(0)) can be used to check global deposit availability.

Most ERC-4626 vaults implement maxDeposit in a way that returns meaningful values when called with address(0):

  • Returns 0 when deposits are globally closed/capped

  • Returns a positive value indicating maximum deposit allowed

Override to return False in subclasses where maxDeposit(address(0)) doesn’t provide meaningful global availability information.

Returns

True if maxDeposit(address(0)) returns meaningful values for global deposit availability checking.

Return type

bool

can_check_redeem()

Check if maxRedeem(address(0)) can be used to check global redemption availability.

Most protocols return 0 for maxRedeem(address(0)) because that address has no balance/shares, not because redemptions are closed:

  • Gearbox: maxRedeem returns min(balanceOf(owner), convertToShares(availableLiquidity))

  • Most vaults: Return 0 because address(0) has no shares

Some protocols do use maxRedeem(address(0)) meaningfully:

  • Morpho, IPOR, Plutus: Return 0 when redemptions are globally blocked

Override to return True in subclasses that support address(0) redemption checks.

Returns

True if maxRedeem(address(0)) returns meaningful values for global redemption availability checking.

Return type

bool

property chain_id: int

Chain this vault is on

property denomination_token: Optional[eth_defi.token.TokenDetails]

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

Note

None results are not cached — the next access will retry the on-chain call. This avoids permanently caching a transient RPC failure.

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_available_liquidity(block_identifier='latest')

Read the adapter’s available denomination-token liquidity.

Single-market adapters report market liquidity. Multi-market adapters such as Morpho and IPOR currently report idle vault assets, which can understate redeemable liquidity in their underlying markets. A user’s withdrawal can also be limited by share ownership or protocol restrictions. Non-lending adapters should leave this method unimplemented.

maxRedeem(address(0)) cannot measure market-wide liquidity because ERC-4626 caps redemptions by the owner’s share balance. The zero address normally holds no shares, so a zero result says nothing about liquidity.

Parameters

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

Raises

NotImplementedError – For non-lending protocol vaults.

Returns

Amount in denomination token units (human-readable Decimal).

Return type

Optional[decimal.Decimal]

fetch_denomination_token()

Read denomination token from onchain.

Use denomination_token() for cached access.

Return type

Optional[eth_defi.token.TokenDetails]

fetch_denomination_token_address()

Get the asset() denomination token address of this vault.

Results are disk-cached per (chain_id, vault_address) via eth_defi.erc_4626.vault_token when the vault was constructed with a eth_defi.token.TokenDiskCache and no pinned default_block_identifier. Current mappings expire after seven days because proxy upgrades may change relationships. These entries are not evidence of a token relationship at an arbitrary historical block.

Only a definitive non-null answer is persisted. The None path taken on revert / broken contract is never cached, matching the behaviour of eth_defi.vault.base.VaultBase.denomination_token() which explicitly avoids memoising None so transient failures can be retried.

To disable the cache, pass token_cache=None (or any non- TokenDiskCache dict) when constructing the vault, or construct with a pinned default_block_identifier.

Returns

Denomination token address, or None if the vault contract is broken and did not return a valid address.

Return type

Optional[eth_typing.evm.HexAddress]

fetch_deposit_open()

Fetch the current protocol-defined deposit availability.

This is deliberately a tri-state value. True and False mean that the adapter read an authoritative vault-wide state; None means that the generic adapter cannot safely determine the state. It is separate from fetch_deposit_closed_reason(), because an absent human-readable reason does not prove that deposits are open.

Returns

True when deposits are open, False when they are closed, or None when the protocol does not expose a reliable global availability read.

Return type

Optional[bool]

fetch_idle_lending_snapshot(total_assets, block_identifier)

Read the idle balance once for adapters with idle-based utilisation.

This helper is opt-in: idle assets are not redeemable liquidity for every lending protocol. See the protocol’s liquidity documentation.

Morpho v1/v2 and IPOR metadata call this after reading total assets, avoiding duplicate idle-balance reads for liquidity and utilisation. Reuse the exact raw assets only when their source block and decimal value match; converting an externally supplied Decimal back to raw units is a fallback for callers that did not first fetch assets through this adapter.

Parameters
  • total_assets (decimal.Decimal) – Previously fetched TVL in denomination-token units.

  • 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]) – Numeric block used for both economic inputs.

Returns

(idle_liquidity, utilisation): a Decimal balance in denomination token units and the unclamped fraction of total assets allocated. Both members are None when the denomination token is unavailable; zero total assets produces zero utilisation.

Return type

tuple[decimal.Decimal | None, float | None]

fetch_info()

Use info() property for cached access.

Returns

See LagoonVaultInfo

Return type

eth_defi.erc_4626.vault.ERC4626VaultInfo

fetch_minimum_deposit(block_identifier='latest')

Fetch a source-proven minimum deposit in decimal token units.

A None result means this adapter does not expose a known minimum; it does not prove the protocol accepts arbitrarily small deposits. A zero result means the adapter positively established that the vault has no minimum deposit.

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) – Block at which to read the protocol configuration.

Returns

Decimal denomination-token minimum, or None when unknown.

Return type

Optional[decimal.Decimal]

fetch_minimum_redemption(block_identifier='latest')

Fetch a source-proven redemption minimum in decimal share units.

A None result means this adapter does not expose a known minimum; it does not prove the protocol accepts arbitrarily small redemptions. A zero result means the adapter positively established that the vault has no minimum redemption.

Parameters

block_identifier (Union[Literal['latest', 'earliest', 'pending', 'safe', 'finalized'], eth_typing.evm.BlockNumber, eth_typing.evm.Hash32, eth_typing.encoding.HexStr, int]) – Block at which to read the protocol configuration.

Returns

Decimal vault-share minimum, or None when unknown.

Return type

Optional[decimal.Decimal]

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

Fetch protocol-specific private scan row columns.

Some vault protocols expose structured metadata that is useful for the raw scanner output but does not fit the shared human-readable columns. Override this hook in protocol-specific subclasses instead of adding a separate branch to eth_defi.erc_4626.scan.create_vault_scan_record().

Returns

Mapping of private scan-row column names, usually prefixed with _. The default implementation returns no extra data.

Return type

dict[str, object]

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)

Results are disk-cached per (chain_id, vault_address) via eth_defi.erc_4626.vault_token when the vault was constructed with a eth_defi.token.TokenDiskCache. Under normal circumstances live reads reuse the current relationship until its seven-day deadline. Explicit historical reads bypass this disk cache because ERC-7575 relationships may change through proxy upgrades.

Only a definitive answer from the chain is ever persisted: a successful call, or a revert matching KNOWN_SHARE_TOKEN_ERROR_MESSAGES (which positively classifies the contract as non-ERC-7575). Transient RPC failures (ProbablyNodeHasNoBlock, HTTP 502) fall back to self.vault_address but are not written to the cache, so a flaky node cannot poison a real ERC-7575 vault’s entry.

To disable the cache, pass token_cache=None (or any non- TokenDiskCache dict) when constructing the vault, or construct with a pinned default_block_identifier to force the uncached historical-read path on every call.

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 to query. Cache is only consulted/written when the caller passes the default "latest" and the vault instance has no pinned default_block_identifier.

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

Optional[decimal.Decimal]

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_utilisation_percent(block_identifier='latest')

Read the adapter’s lending or capital-deployment utilisation.

Single-market adapters measure borrower utilisation. Multi-market adapters such as Morpho and IPOR currently measure allocation outside the vault, rather than aggregate utilisation by underlying borrowers. Non-lending adapters should leave this method unimplemented.

Parameters

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

Raises

NotImplementedError – For non-lending protocol vaults.

Returns

Utilisation as float between 0.0 and 1.0 (0% to 100%).

Return type

Optional[float]

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

Optional[float]

get_deposit_manager_capability()

Declare the exact generic ERC-4626 implementation’s two-way flow.

Subclasses need exact-type inclusion in CERTIFIED_SYNCHRONOUS_DEPOSIT_MANAGER_CLASSES. The guarded probe may separately use supports_generic_deposit_manager() as a temporary fallback without advertising it as public metadata.

Returns

Synchronous two-way capability for the exact base class, otherwise None.

Return type

Optional[eth_defi.vault.deposit_redeem.VaultDepositManagerCapability]

get_fee_data(block_identifier='latest')

Get fee data structure for this vault.

Component fee reads share the requested block. Existing no-argument callers retain latest-state behaviour.

Parameters

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

Returns

Fee mode and component fractions.

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

Optional[eth_defi.vault.fee.VaultFeeMode]

get_flags()

Get various vault state flags from the smart contract.

The vault metadata scan stores this result as _flags in vault-metadata-db.pickle. top_vaults_by_chain.json is generated from those stored values through scripts/erc-4626/vault-analysis-json.py / eth_defi.vault.top_vaults_json.

After editing manual flags in eth_defi.vault.flag, rerun the vault metadata scan and top-vault JSON post-processing. Regenerating the JSON from an old metadata database will not pick up new manual flags.

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

Return the name of the vault protocol.

Return type

str

get_risk()

Get risk profile of this vault.

Return type

Optional[eth_defi.vault.risk.VaultTechnicalRisk]

get_share_price_source()

Return the standard ERC-4626 share-price source.

ERC-4626 prices are calculated from vault accounting values read at a specific block, including protocol-specific overrides that expose the same state through another contract view.

Returns

Smart-contract state source.

Return type

eth_defi.vault.price_source.PriceSource

get_strategy_tags()

Return the maintained classifications for this reviewed deployment.

Only source-backed address entries are classified; an unreviewed deployment retains the explicit missing-information result.

Returns

A fresh set of strategy tags, or None when information is missing.

Return type

Optional[set[eth_defi.vault.strategy_tag.StrategyTag]]

get_synchronous_deposit_manager_capability()

Build static metadata for a verified synchronous manager.

A caller must already have established the reader class’ guarded fork evidence. This deliberately performs no RPC reads: the capability is static library metadata, not a live vault availability check.

Returns

Synchronous two-way capability.

Return type

eth_defi.vault.deposit_redeem.VaultDepositManagerCapability

get_whitelist_notes()

Return an export caveat for the vault-wide whitelist status.

Adapters may attach a concise, stable explanation when a classification is an explicitly requested operating assumption or excludes an integration-specific permission mechanism. The note describes the policy classification only; it must not be used to report temporary deposit availability.

Returns

Export note, or None when the classification needs no caveat.

Return type

Optional[str]

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_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 manager_name: Optional[str]

Protocol-supplied vault manager or curator display name.

  • Used when the vault name itself does not contain the curator brand

  • Returns None if the protocol does not expose separate manager metadata

  • Override in subclasses that support manager or operator metadata

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

supports_generic_deposit_manager()

Check whether the live vault exposes the standard ERC-4626 surface.

This is deliberately an interface check, not a public support claim. Callers must still execute a guarded fork probe before relying on the generic manager for a protocol-specific adapter.

Returns

True when asset succeeds with a non-zero asset address. Deposit and redemption availability is established only by a guarded fork transaction, not by ERC-4626 max* advisory values.

Return type

bool

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.