Deposit & Redeem Flows¶
This guide covers the write hooks that drive the ERC-7540 lifecycle. If you have not read the request → epoch → claim lifecycle, start there.
The transaction-hook state machine¶
Every write hook returns the same shape:
{
txState: TxState; // 'idle' | 'approving' | 'pending' | 'confirming' | 'success' | 'error'
txHash: Hash | undefined; // set once the wallet returns a hash
error: string | null; // human-readable; from err.shortMessage ?? err.message
reset: () => void; // back to 'idle', clears txHash and error
// ...plus the action function(s), documented per hook
}
State transitions:
'approving'occurs only inuseDepositwhen an ERC-20 approval is needed.'confirming'is set after the wallet returns a hash, whilewaitForTransactionReceiptruns.
Action functions never throw
Failures land in error + txState: 'error' — you do not need try/catch around hook actions. Precondition failures (wallet not connected, zero amount, wrong vault type) also set error without sending anything. This is the opposite of the standalone VaultActions methods, which throw — see the Core Reference.
Deposit¶
useDeposit(vaultAddress, depositAssetAddress, depositAssetDecimals, vaultType) orchestrates the full flow in one call: it checks the ERC-20 allowance → if insufficient, sends approve (txState: 'approving') and waits for the receipt → then sends the deposit transaction. For 'ASYNC' vaults the deposit tx is requestDeposit(assets, controller, owner); for 'SYNC_DEPOSIT_ASYNC_REDEEM' it is the ERC-4626 deposit(assets, receiver).
The amount is human-readable (e.g. "100"), parsed with assetDecimals — take the decimals from metadata, never hard-code them.
import { useDeposit, useVaultMetadata } from "@arkonix.xyz/arkonix-vault-sdk";
function DepositForm({ vaultAddress }: { vaultAddress: `0x${string}` }) {
const { data: meta } = useVaultMetadata(vaultAddress);
const [amount, setAmount] = useState("");
const { deposit, txState, error, reset } = useDeposit(
vaultAddress, meta?.asset, meta?.assetDecimals, meta?.vaultType,
);
return (
<div>
<input value={amount} onChange={(e) => setAmount(e.target.value)} placeholder="100" />
<button onClick={() => deposit(amount)} disabled={txState !== "idle"}>
{txState === "approving" ? "Approving…" :
txState === "pending" ? "Depositing…" :
txState === "confirming" ? "Confirming…" :
txState === "success" ? "Done!" : "Deposit"}
</button>
{error && <p style={{ color: "red" }}>{error}</p>}
</div>
);
}
On ASYNC vaults, a deposit request must be claimed after the epoch: pass state.claimableDepositAssets (a raw bigint from useVaultUserState) to useClaimDeposit(vaultAddress)'s claimDeposit(assets) action. On-chain the claim is the ERC-4626 deposit(assets, receiver) call that mints the shares.
Redeem: request → wait → claim¶
Redeem is asynchronous on both vault types:
useRequestRedeem(vaultAddress)—requestRedeem(shares, shareDecimals)with a human-readable share amount (passmeta.shareDecimals).- Wait for the epoch —
useVaultUserStatepolls every 10 s and flipshasPendingRedeem→hasClaimableRedeem. useClaimRedeem(vaultAddress)—claimRedeem(shares)with the rawbigintstate.claimableRedeemShares.
import { useRequestRedeem, useClaimRedeem, useVaultUserState } from "@arkonix.xyz/arkonix-vault-sdk";
function RedeemFlow({ vaultAddress, meta }) {
const state = useVaultUserState(vaultAddress, meta.share, meta.assetDecimals, meta.vaultType);
const { requestRedeem } = useRequestRedeem(vaultAddress);
const { claimRedeem } = useClaimRedeem(vaultAddress);
return (
<div>
{state.shareBalance > 0n && (
<button onClick={() => requestRedeem("10", meta.shareDecimals)}>
Request Redeem 10 Shares
</button>
)}
{state.hasPendingRedeem && (
<p>Pending redeem: ~{state.pendingRedeemAssetsFormatted} {meta.assetSymbol}</p>
)}
{state.hasClaimableRedeem && (
<button onClick={() => claimRedeem(state.claimableRedeemShares)}>
Claim {state.claimableRedeemAssetsFormatted} {meta.assetSymbol}
</button>
)}
</div>
);
}
Pending vs claimable valuations differ
pendingRedeemAssetsFormatted is estimated at the current price; claimableRedeemAssetsFormatted is locked at the epoch price (via maxWithdraw).
Cancel flows¶
A pending (not yet epoch-processed) request can be cancelled. Cancellation is itself asynchronous: cancel → wait for the epoch → claim back the funds.
- Cancel redeem (both vault types):
useCancelRedeem(vaultAddress)exposescancelRedeem()andclaimCancelRedeem()(returns the user's shares), sharing onetxState. - Cancel deposit (ASYNC vaults only):
useCancelDeposit(vaultAddress, vaultType)exposescancelDeposit()andclaimCancelDeposit()(returns the assets).
useCancelDeposit requires the vaultType argument
The second argument is required — pass meta?.vaultType. If vaultType is not exactly 'ASYNC' (including undefined while metadata loads), both actions fail early with:
"Cancel deposit is not supported on this vault: SYNC_DEPOSIT_ASYNC_REDEEM deposits settle immediately."
Sync deposits settle immediately, so there is nothing to cancel; the guard exists to prevent a confusing on-chain revert.
import { useCancelRedeem, useCancelDeposit, useVaultMetadata, useVaultUserState } from "@arkonix.xyz/arkonix-vault-sdk";
function CancelControls({ vaultAddress }) {
const { data: meta } = useVaultMetadata(vaultAddress);
const state = useVaultUserState(vaultAddress, meta?.share, meta?.assetDecimals, meta?.vaultType);
const { cancelRedeem, claimCancelRedeem } = useCancelRedeem(vaultAddress);
const { cancelDeposit, claimCancelDeposit } = useCancelDeposit(vaultAddress, meta?.vaultType);
return (
<div>
{state.hasPendingRedeem && <button onClick={cancelRedeem}>Cancel Redeem</button>}
{state.hasClaimableCancelRedeem && (
<button onClick={claimCancelRedeem}>Claim Cancelled Shares</button>
)}
{/* ASYNC vaults only — the hook fails early with a clear error otherwise */}
{state.hasPendingDeposit && <button onClick={cancelDeposit}>Cancel Deposit</button>}
{state.hasClaimableCancelDeposit && (
<button onClick={claimCancelDeposit}>Claim Cancelled Assets</button>
)}
</div>
);
}
Related pages¶
- Hooks Reference — full signatures, query keys, refetch cadences
- Core Reference — the same flows without React (
VaultActions,VaultTxBuilder) - Concepts — lifecycle rules, decimals, vault types