Skip to main content

Vault withdraw

GET 

/v1/actions/vaults/withdraw

Build calldata for an ERC-4626 vault withdraw. When the user wants the underlying back (no native unwrap), the dispatcher calls vault.withdraw / vault.redeem directly with no approval needed. When the receiver wants native ETH, falls back to the composer (unwraps WETH).

This endpoint targets passive ERC-4626 vaults. For Fluid's NFT-position margin vaults use /v1/actions/lending/withdraw.

Pendle Principal Tokens exit the same way they are entered — by SELLING on Pendle's AMM, since a PT has no redeem leg before maturity. Detected from the token list and built as a trade through the spot meta-aggregator: amount is the PT amount, receiveAsset is free (defaults to the PT's underlying), and slippage (bps) is REQUIRED because the fill is priced by pool depth, not by the protocol.

Execution mode (mode query param)

  • auto (default) — direct when receiveAsset === underlying and not native, otherwise composer.
  • direct — force direct; returns 400 when not eligible.
  • proxy — force the composer (legacy behavior).

Direct withdraw skips the share-token approval entirely — the user calls vault.withdraw(assets, receiver, owner=operator) themselves and the vault uses its own balance check (msg.sender == owner).

Assets vs shares

  • isShares=false (default): amount is assets — issues vault.withdraw(assets, receiver, owner).
  • isShares=true: amount is shares — issues vault.redeem(shares, receiver, owner).

Withdraw all (isAll=true)

Two ways to drive a full-balance withdraw — both resolve the operator's vault-share balance and encode vault.redeem(shares, …). They differ only in where the share balance is read:

  • GET + isAll=true — worker reads balanceOf(vault, operator) against its own configured RPC. Simple but only works when the worker can see the state (i.e. live mainnet, not fork). Returns BALANCE_READ_FAILED on RPC failure.
  • POST + isAll=true + body { "sharesRaw": "<uint string>" } — caller pre-reads the balance against any RPC (fork, custom, premium) and supplies it. The worker just trusts and encodes. Mirrors the lending simulation pattern (SimulationBody).

The POST body's amount query param is ignored when isAll=true.


Non-4626 interfaces (interface query param)

Default erc4626 is the composer/direct path above. Otherwise:

  • erc7540async redeem. action=requestRedeem (default) burns shares into the queue; claimRedeem / claimWithdraw settle once fulfilled. controller when not operator.
  • erc7575multi-asset. Requires share (the share-token address); isShares toggles assets-out vs shares-in.
  • hypercoreHyperLiquid multi-leg withdraw route. amount is uint64 micro-USD; destination picks where the funds land. Returns a structured legs[] array. (Withdrawing out to Arbitrum is an L1 signed action, not EVM calldata — not offered here.)

Savings cooldowns (sUSDe) use /v1/actions/vaults/savings; LST exits use /v1/actions/vaults/lst.

Plain-text reference — GET /v1/actions/vaults/withdraw

Parameters

ParameterInTypeRequiredDescription
chainIdquerystringyesChain ID See the ChainId schema for the full set of supported chains.
vaultquerystringyesERC-4626 share-token address
underlyingquerystringyesVault's asset()
amountquerystringyesAmount in wei. Interpreted as assets unless isShares=true.
operatorquerystringyesUser wallet executing the withdraw (share holder)
receiveAssetquerystringnoWhat the user receives. Defaults to underlying. Zero address or the 0xEEEE… sentinel = native ETH (composer unwraps before forwarding).
slippagequerystringnoBasis points. Pendle PTs only, and REQUIRED there — the exit is a sale into the PT pool, priced by its depth. Ignored by every other vault family.
receiverquerystringnoFinal recipient of the withdrawn assets. Defaults to operator.
isSharesquerybooleannoWhen true, amount is treated as shares (the composer calls vault.redeem).
isAllquerybooleannoFull-balance withdraw. Relaxes the sweep tolerance so dust does not revert the tx.
modequeryauto, direct, proxynoExecution mode. auto (default) routes direct when no unwrap is needed, composer otherwise. direct forces vault-direct (returns 400 if ineligible). proxy forces the composer.
interfacequeryerc4626, erc7540, erc7575, hypercore, native-wnlpnoVault interface. erc4626 (default) = composer/direct path. Others dispatch to dedicated builders (see description). Native wNLP is auto-detected from the savings registry, which also fills in underlying.
instantquerybooleannointerface=native-wnlp only — take the immediate instantRedeem instead of the free queue. Costs the vault’s withdrawFeeBps (100 bps by default), deducted from the underlying paid out rather than charged separately, and is capped by its reported liquidity. The default is the free queued leg: a fee is never charged unless asked for.
actionqueryrequestRedeem, claimRedeem, claimWithdrawnointerface=erc7540 only — requestRedeem (default), claimRedeem, or claimWithdraw. interface=native-wnlprequestWithdraw (default), claim, or cancel.
controllerquerystringnointerface=erc7540 controller, when not operator.
sharequerystringnointerface=erc7575 — the share-token address (required for that interface).
destinationqueryhypercore-perp, hypercore-spot, hyperevmnointerface=hypercore withdraw destination.

Response 200

FieldTypeDescription
successTrue
dataobjectInformational data (quotes, simulation results, etc.)
actionsobjectTransaction calldata and approvals. Null for quote-only responses (no account provided).
actions.transactionsobject[]Pre-trade setup transactions (e.g. e-mode switch, collateral enable). Execute these before the main swap. Empty when no setup is needed.
actions.transactions[].tostringTarget contract address
actions.transactions[].datastringEncoded calldata
actions.transactions[].valuestringETH value to send with the transaction
actions.transactions[].descriptionstringHuman-readable label. For alternatives, this is the aggregator name (e.g. "Paraswap"). For transactions, describes the setup action (e.g. "Switch e-mode to 1").
actions.alternativesobject[]DEX aggregator swap transactions sorted by best output (descending). Each entry's description is the aggregator name. The client should pick one to execute. Present on loop action endpoints.
actions.alternatives[].tostringTarget contract address
actions.alternatives[].datastringEncoded calldata
actions.alternatives[].valuestringETH value to send with the transaction
actions.alternatives[].descriptionstringHuman-readable label. For alternatives, this is the aggregator name (e.g. "Paraswap"). For transactions, describes the setup action (e.g. "Switch e-mode to 1").
actions.permissionsobject[]Approval/delegation transactions that must execute before both transactions and alternatives. Includes ERC20 allowances (targeting the composer contract) and lender borrow/withdrawal delegations (targeting the lending protocol contract directly). Filtered against on-chain state so only missing approvals are returned. Null when no approvals are needed.
actions.permissions[].tostringTarget contract address
actions.permissions[].datastringEncoded calldata
actions.permissions[].valuestringETH value
actions.permissions[].descriptionstringHuman-readable description of the approval (e.g. "Approve borrow for AAVE_V3", "Approve ERC20")
actions.permissions[].spenderstringERC-20 approve spender (x-chain permissions). Match it against the selected quote's approvalTarget — several bridges can share one spender, so do not match by description.

Example response

{
"success": true,
"data": {},
"actions": {
"transactions": [
{
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"alternatives": [
{
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"permissions": [
{
"to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
}
]
}
}

Request

Responses

Transaction calldata + any approval(s) needed for the vault withdraw. interface=hypercore returns a structured legs[] route instead of transactions[].