erc_4626.vault_protocol.nara.deposit_redeem

Documentation for eth_defi.erc_4626.vault_protocol.nara.deposit_redeem Python module.

NaraUSD+ synchronous deposits and cooldown-based redemptions.

Classes

NaraDepositManager

NaraUSD+ manager with synchronous deposits and asynchronous cooldown redemptions.

NaraRedemptionRequest

Parse a completed NaraUSD+ cooldownShares transaction.

NaraRedemptionTicket

Persist a NaraUSD+ cooldown redemption request.

class NaraRedemptionTicket

Bases: eth_defi.vault.deposit_redeem.RedemptionTicket

Persist a NaraUSD+ cooldown redemption request.

The vault keeps one active cooldown per owner and does not assign request identifiers, so the request transaction hash provides a stable identity. The observed cooldown state binds the ticket to that specific request.

cooldown_end: datetime.datetime

Naive UTC deadline recorded by the vault after cooldownShares.

raw_assets: int

Raw NaraUSD assets escrowed for this cooldown.

get_request_id()

Return the request transaction hash as an integer identity.

Returns

Unique integer derived from the request transaction hash.

Return type

int

__init__(vault_address, owner, to, raw_shares, tx_hash, cooldown_end, raw_assets)
Parameters
Return type

None

class NaraRedemptionRequest

Bases: eth_defi.vault.deposit_redeem.RedemptionRequest

Parse a completed NaraUSD+ cooldownShares transaction.

parse_redeem_transaction(tx_hashes)

Create a persistent ticket after the request succeeds.

Parameters

tx_hashes (list[hexbytes.main.HexBytes]) – Broadcast transaction hashes; the final hash is cooldownShares.

Returns

Persistable ticket for the later unstake claim.

Return type

eth_defi.erc_4626.vault_protocol.nara.deposit_redeem.NaraRedemptionTicket

__init__(vault, owner, to, shares, raw_shares, funcs)
Parameters
Return type

None

broadcast(from_=None, gas=1000000)

Broadcast all the transactions in this request.

Parameters
Returns

List of transaction hashes

Return type

list[hexbytes.main.HexBytes]

class NaraDepositManager

Bases: eth_defi.erc_4626.deposit_redeem.ERC4626DepositManager

NaraUSD+ manager with synchronous deposits and asynchronous cooldown redemptions.

NaraUSD+ is Nara’s appreciating staking token for NaraUSD. Deposits mint shares immediately through the standard ERC-4626 path, but redemptions are asynchronous: the holder starts an owner-specific cooldown, waits for it to mature, then claims the underlying NaraUSD. This manager keeps the inherited synchronous deposit flow and replaces the redemption flow with a two-step cooldown/claim lifecycle tracked by NaraRedemptionTicket.

Deposit process

Synchronous, fully inherited. After approve(), create_deposit_request() builds a single ERC-4626 deposit call and estimate_deposit() uses the standard previewDeposit path. can_create_deposit_request() gates on maxDeposit(owner) > 0.

Redemption process

Asynchronous, two-step. create_redemption_request() does not build a redeem / withdraw call — it builds a single cooldownShares(raw_shares) call that escrows the shares and starts the owner’s cooldown, returning a NaraRedemptionRequest. After the request confirms, NaraRedemptionRequest.parse_redeem_transaction() reads the vault’s cooldowns(owner) state to build a persistable ticket. Once the cooldown matures, finish_redemption() builds the unstake(to) claim that sends NaraUSD to the receiver. has_synchronous_redemption() returns False. The receiver may differ from the owner but cannot be the zero address.

Queues and settlement

Per-owner cooldown state rather than a numbered queue: the vault keeps at most one active cooldown per owner and assigns no request id, so the ticket is identified by the request transaction hash together with the observed cooldown_end and escrowed raw_assets. Attempting a second cooldown while one is active raises. get_redemption_request_status() maps live cooldowns() state plus the latest block timestamp to pending, claimable or none (the last also covering a claimed, removed or superseded cooldown).

Lockups and cooldowns

Deposits have no lockup. Redemptions carry the live cooldown read from cooldownDuration(): estimate_redemption_delay() returns it, and NaraVault.get_estimated_lock_up() reports the same value (currently seven days on Ethereum). get_redemption_delay_over() returns the per-owner cooldown expiry when one exists.

Whitelisting / access control

Permissionless. No NaraUSD+-specific whitelist is applied beyond the inherited check_deposit_whitelist() preflight; can_create_redemption_request() simply requires a positive share balance and no cooldown already in progress.

Anvil settlement (force_settle)

Deposits settle in their originating transaction. Redemption settlement is time-based, not keeper-based: force_settle() advances an Anvil chain to the ticket’s cooldown_end and returns a hashless pending-to-claimable result. It never broadcasts unstake; callers must submit the guarded finish_redemption() claim themselves. A supplied local mock is accepted only when it is the same deployed contract as this manager’s vault, preventing it from advancing an unrelated production ticket.

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

Start the owner-specific NaraUSD+ share cooldown.

Parameters
  • owner (eth_typing.evm.HexAddress) – NaraUSD+ share owner initiating the cooldown.

  • to (Optional[eth_typing.evm.HexAddress]) – Final NaraUSD receiver, defaulting to the share owner.

  • shares (Optional[decimal.Decimal]) – Decimal NaraUSD+ share amount, exclusive with raw_shares.

  • raw_shares (Optional[int]) – Raw NaraUSD+ share amount, exclusive with shares.

  • check_max_deposit (bool) – Retained inherited argument; Nara controls redemption through cooldown state.

  • check_enough_token (bool) – Check the owner’s current NaraUSD+ balance.

Returns

One-call cooldown request to settle through finish_redemption().

Return type

eth_defi.erc_4626.vault_protocol.nara.deposit_redeem.NaraRedemptionRequest

has_synchronous_redemption()

Return whether NaraUSD+ redemptions settle immediately.

Returns

Always False because the owner must complete a cooldown first.

Return type

bool

is_redemption_in_progress(owner)

Check whether an owner has an unclaimed NaraUSD+ cooldown.

Parameters

owner (eth_typing.evm.HexAddress) – Share owner to inspect.

Returns

True when the vault records a non-zero cooldown deadline.

Return type

bool

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

Advance an Anvil Nara cooldown to the ticket’s claimable deadline.

Nara has no keeper or operator settlement transaction. Advancing the local Anvil clock is the complete settlement boundary, so the result intentionally contains no transaction hashes. The later unstake call remains a manager-owned guarded claim.

Parameters
Returns

A synchronous no-op or a pending-to-claimable, hashless cooldown settlement result.

Raises

UnsupportedVaultSimulation – If this is not Anvil, the mock is unrelated, the ticket is not a live Nara cooldown, or advancing time does not make it claimable.

Return type

eth_defi.vault.deposit_redeem.VaultForcedSettlementResult

can_create_deposit_request(owner)

Check NaraUSD+’s current ERC-4626 deposit maximum.

Parameters

owner (eth_typing.evm.HexAddress) – Prospective deposit receiver.

Returns

True when the current maximum is positive.

Return type

bool

can_create_redemption_request(owner)

Check whether the owner can start a NaraUSD+ cooldown.

Parameters

owner (eth_typing.evm.HexAddress) – Share owner to inspect.

Returns

True when the owner has shares and no active cooldown.

Return type

bool

estimate_redemption_delay()

Read the currently configured NaraUSD+ cooldown duration.

Returns

Current cooldown as a timedelta.

Return type

datetime.timedelta

fetch_cooldown(address)

Read an owner’s current NaraUSD+ cooldown state.

Parameters

address (Union[eth_typing.evm.HexAddress, str]) – NaraUSD+ share owner.

Returns

Cooldown expiry timestamp and raw escrowed NaraUSD assets.

Return type

tuple[int, int]

get_redemption_delay_over(address)

Return an owner’s cooldown expiry, when one exists.

Parameters

address (Union[eth_typing.evm.HexAddress, str]) – NaraUSD+ share owner.

Returns

Naive UTC cooldown expiry, or None when no claim is pending.

Return type

Optional[datetime.datetime]

can_finish_redeem(redemption_ticket)

Check whether a NaraUSD+ cooldown claim can now be submitted.

Parameters

redemption_ticket (eth_defi.erc_4626.vault_protocol.nara.deposit_redeem.NaraRedemptionTicket) – Persisted cooldown request.

Returns

True when the current chain timestamp has reached the deadline.

Return type

bool

reconstruct_redemption_ticket(data)

Reconstruct a NaraUSD+ cooldown ticket after a process restart.

Parameters

data (dict) – Data produced by serialize_redemption_ticket().

Returns

NaraUSD+ cooldown ticket.

Return type

eth_defi.erc_4626.vault_protocol.nara.deposit_redeem.NaraRedemptionTicket

serialize_redemption_ticket(ticket)

Serialise a NaraUSD+ ticket with its exact cooldown identity.

Parameters

ticket (eth_defi.erc_4626.vault_protocol.nara.deposit_redeem.NaraRedemptionTicket) – NaraUSD+ cooldown ticket.

Returns

JSON-compatible persistent ticket data.

Return type

dict

get_redemption_request_status(ticket)

Report whether a NaraUSD+ cooldown is pending or claimable.

Parameters

ticket (eth_defi.erc_4626.vault_protocol.nara.deposit_redeem.NaraRedemptionTicket) – NaraUSD+ cooldown ticket.

Returns

pending before maturity, claimable afterwards, or none when the cooldown was claimed, removed, or superseded by another cooldown for the same owner.

Return type

eth_defi.vault.deposit_redeem.AsyncVaultRequestStatus

finish_redemption(redemption_ticket)

Build the NaraUSD+ post-cooldown claim transaction.

Parameters

redemption_ticket (eth_defi.erc_4626.vault_protocol.nara.deposit_redeem.NaraRedemptionTicket) – Matured cooldown ticket.

Returns

unstake contract call that sends NaraUSD to the requested receiver.

Return type

web3.contract.contract.ContractFunction

__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_finish_deposit(deposit_ticket)

Synchronous deposits can be finished immediately.

Parameters

deposit_ticket (eth_defi.erc_4626.deposit_redeem.ERC4626DepositTicket) –

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_deposit_request(owner, to=None, amount=None, raw_amount=None, check_max_deposit=True, check_enough_token=True)

Build the deposit request transaction(s) for an owner.

Abstracts the ERC-4626, ERC-7540, Lagoon and other protocol deposit flows behind a common request wrapper.

Whitelisting contract: implementations must raise WhitelistingRequired before returning a request when the vault applies a deposit whitelist policy, that policy is applicable and queryable, and owner is not permitted to deposit. Call check_deposit_whitelist() at the start of the preflight to satisfy this contract. When the whitelist information cannot be obtained (the adapter’s whitelist reads raise NotImplementedError) the manager must not raise WhitelistingRequired; it either proceeds — letting any real denial surface as an onchain revert — or fails closed with a plain VaultFlowUnavailable when unknown admission is unsafe.

Parameters
  • owner (eth_typing.evm.HexAddress) – Deposit owner and controller.

  • to (eth_typing.evm.HexAddress) – Optional separate receiver, where the protocol supports it.

  • amount (decimal.Decimal) – Human-readable denomination-token amount, converted using the denomination token decimals when raw_amount is not given.

  • raw_amount (int) – Raw denomination-token amount, overriding amount.

  • check_max_deposit – Preflight the deposit against the vault’s deposit capacity.

  • check_enough_token – Preflight that owner holds enough denomination token.

Returns

Deposit request wrapper ready for signing and parsing.

Raises
Return type

eth_defi.erc_4626.deposit_redeem.ERC4626DepositRequest

create_deposit_request_for_guard_validation(owner, raw_amount)

Build ERC-4626 deposit calldata after a proven global closure.

This Anvil-only diagnostic path is available only when the selected vault’s authoritative global closure reader reports that deposits are unavailable to every account. It preserves the normal protocol admission preflight and all permanent amount constraints, while omitting the temporary closed-deposit capacity and token-balance checks needed to encode the production-equivalent deposit call.

Parameters
  • owner (eth_typing.evm.HexAddress) – Safe/SimpleVault address that would own the minted shares.

  • raw_amount (int) – Raw denomination-token amount from the rejected real-deposit attempt.

Returns

Single ERC-4626 deposit request for isolated GuardV0 validation.

Raises
Return type

eth_defi.erc_4626.deposit_redeem.ERC4626DepositRequest

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

How many shares we get for a deposit.

Parameters
Return type

decimal.Decimal

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

How many denomination tokens we get for a redeem.

Parameters
Return type

decimal.Decimal

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

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

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]

has_synchronous_deposit()

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

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

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