Skip to main content

Savings-vault request-withdraw · claim · deposit · cancel

GET 

/v1/actions/vaults/savings

Build calldata for savings-vault exits — the cooldown / async-redeem mechanics that plain ERC-4626 withdraw can't express (Ethena sUSDe, erc7540 async vaults, instant erc4626 redeem, and Native Credit Pool wNLP) — plus the deposit leg for the one family that isn't ERC-4626. The protocol is resolved from the calldata-sdk savings registry by (chainId, shareToken), or forced with kind=ethena|erc7540|erc4626|native-wnlp.

ERC-4626 savings deposits go through /v1/actions/vaults/deposit. Native wNLP works on either route — /vaults/deposit auto-detects it from the registry and needs no underlying.

Actions​

  • request-withdraw (default) — start the exit:
    • Ethena → cooldownShares(amount) (or cooldownAssets when byAssets=true); escrows the assets for the cooldown window.
    • erc7540 → requestRedeem.
    • erc4626 → instant redeem (no cooldown).
    • native-wnlp → WithdrawQueue.requestWithdrawal(amount) — the free leg; the payout is snapshotted at request time and claimable after the queue window. Pass instant=true for instantRedeem instead: one transaction, no wait, but it costs the vault's withdrawFeeBps (100 bps by default) — deducted from the underlying paid out, never charged as a separate transfer — and is capped by its reported liquidity.
  • claim — settle a matured exit:
    • Ethena → unstake(receiver).
    • erc7540 / erc4626 → redeem (pass shares).
    • native-wnlp → claimWithdrawalTo(receiver); no shares — the queue always pays the full request.
  • deposit — native-wnlp only (depositAndWrap(receiver, amount)). Every other kind is a plain ERC-4626 mint and is rejected here with a pointer to /vaults/deposit.
  • cancel — native-wnlp only; withdraw an open queued request and take the shares back. One request per address, so cancel is also how you resize one.
  • Apyx (apyx: apyUSD, aptUSD) has no cancel: the redeem mints an unlock receipt whose contract has no cancel function (only claim), so the exit is irreversible. Claim with tokenId (from the redeem's ERC-721 Transfer log) once it unlocks.

Superform SuperVaults (kind=superform)​

Deposits are plain synchronous ERC-4626 and go through /vaults/deposit (or action=deposit here). The exit is ERC-7540, settled by Superform's manager (typically within an hour, not guaranteed):

  • request-withdraw → requestRedeem(amount, owner, owner). The request is booked under the owner, and no share approval is needed. Pass redeemSlippageBps to set your own fulfilment floor first (default 50 bps below the request price).
  • claim → redeem(shares, receiver, controller). Omit shares to claim the whole fulfilled balance (maxRedeem, read on-chain), or pass assets to claim by assets.
  • cancel → cancelRedeemRequest. After the manager fulfils the cancel, call cancel again with step=claim to take the shares back.
  • claim-rewards → Merkl claim for the sUP/USDC incentives. They are paid on Base for every chain's vaults, so call it with chainId=8453 and any Base SuperVault.

Track pending requests via /v1/data/vaults/withdrawals.

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

Parameters

ParameterInTypeRequiredDescription
chainIdquerystringyesChain ID See the ChainId schema for the full set of supported chains.
actionqueryrequest-withdraw, claim, claim-rewards, deposit, cancelnoOperation to build. deposit and cancel are Native wNLP only.
shareTokenquerystringyesSavings share-token address (e.g. sUSDe 0x9d39a5de30e57443bff2a8307a4256c8797a3497).
operatorquerystringyesUser wallet executing the action (share holder). receiver defaults to it.
receiverquerystringnoRecipient of the claimed assets. Defaults to operator. Kinds whose on-chain calls act on msg.sender only (wren, bitway, zerobase, and any leg whose calldata does not depend on the receiver — probed per request, e.g. an Ethena-style cooldown or a sUSDai request) reject a value that differs from operator with a 400. On a request leg pass the receiver on the claim step instead.
kindqueryethena, erc7540, erc4626, native-wnlp, susd3, strata, … (35 values)noProtocol override. Otherwise resolved from (chainId, shareToken).
maxPriceAgequerystringnodeposit / request-withdraw, aera — oldest vault price (seconds) an Aera solver may fill the request at. Default 86400. Aera requests also REQUIRE minShares (deposit: least units minted) or minAssets (request-withdraw: least USDC paid) — non-zero, the only bound on the fill price — and take deadline (unix seconds, default now + 7 days). There is no claim (the solver pays the receiver); action=cancel refunds an EXPIRED unsolved request and needs request = URI-encoded JSON of the booked Request {requestType, user, receiver, units, tokens, solverTip, deadline, maxPriceAge} (bigints as strings; receiver only on the ProvisionerV2 vault, Base). The Ethereum and Arbitrum vaults run the V1 provisioner: no receiver anywhere — the units / USDC go to the sender, and a receiver other than operator is refused.
positionIdquerystringnorequest-withdraw, alchemix-transmuter (REQUIRED) — the Transmuter position NFT to claim (lots[].id on /v1/data/vaults/user or /v1/data/earn/positions). A Transmuter exit burns one whole position: route=early before it has fully vested (the vested part at par, the rest back as alAsset less the exit fee) or route=maturity after; both are the same claimRedemption and pay the sender only. amount is ignored. No claim step, no cancel.
payoutquerystringnorequest-withdraw, alchemix-transmuter — what the vested part arrives as. underlying (default): the Alchemist router takes the position by ERC-721 approval (emitted as the first transaction), claims it and redeems the MYT shares to the underlying, bounded by minAssetOut (omitted ⇒ the vested amount less redeemSlippageBps, default 50) and deadline; receiveAsset=<native> unwraps to ETH on the alETH markets. myt: the Transmuter's own claim — no approval, no floor, the vested part as MYT shares (mixUSDC / mixWETH).
timestampsquerystringnoclaim, mellow — comma-separated request timestamps (unix seconds) of the async redemptions to claim (RedeemQueue.claim(receiver, uint32[])). Optional: omitted, the route reads requestsOf(operator, 0, 64) on the queue and claims every request it reports isClaimable; NOTHING_TO_CLAIM when none is. action=claim&step=deposit instead mints the shares of a settled async DEPOSIT (DepositQueue.claim(account)), and action=cancel&step=deposit refunds a pending one. Mellow redemptions have no cancel. async=true on action=deposit books an async deposit (priced at the next oracle report) instead of the default sync mint.
requestNoncequerystringnoclaim / cancel, Royco only — the RoycoEntryPoint nonce of the redemption request (its RedemptionRequested event; getLastRequestNonce() right after the request). Claim executes it (executeRedemption) once the 24 h delay has passed — Royco keepers usually do this unasked; cancel returns the escrowed shares.
requestIdquerystringnoclaim / cancel, eva — the vault's request id from its RequestSubmitted event. Claim (claimRequest) pays the request's receiver once eva has queued it into a Wildcat batch and the 24 h cycle has passed — anyone may send it; cancel (cancelRequest) is the requester's, and only before the request is queued. cancel, ember — the sequence number from the vault's RequestRedeemed (cancelPendingWithdrawalRequest; the shares return at the operator's next processing), or with step=deposit from the parking lot's DepositRequested (cancelDeposit, refunded at once). Ember has no claim: the operator pays the receiver; action=claim&step=deposit-refund pulls a parking-lot refund the token could not push. claim / cancel, yuzu (yzSyrup) — the order id from CreatedRedeemOrder: claim = finalizeRedeemOrder once Yuzu has filled it (owner or the order's controller), cancel = cancelRedeemOrder, only for an UNFILLED order past its due time (24 h). claim, makina — the request NFT id from the AsyncRedeemer's RedeemRequestCreated: claimAssets(requestId), sent by the NFT's owner once finalised; no cancel. usdai (sUSDai) needs no id: claim sizes itself off maxRedeem(controller) (serviced shares) unless shares is given; no cancel.
epochIdsquerystringnoclaim, Concrete only — comma-separated PROCESSED epoch ids the redemption was booked in (QueuedWithdrawal event / getUserEpochRequest(receiver, epoch)); claimWithdrawal(epochIDs) pays msg.sender, which must be the receiver the redeem named.
epochIdquerystringnocancel, Concrete only — the still-OPEN epoch whose request to cancel (cancelRequest(epochID); refused once the epoch is closed or when the vault has cancellation disabled).
maxWithdrawDelayquerystringnorequest-withdraw, Inverse jrDOLA only — the longest queue delay in seconds the request accepts (the escrow assigns 1–10 days, rising with the queue); queueWithdrawal reverts above it. Default: accept any. Complete with action=claim inside the 48 h window that follows; action=cancel works only once that window has opened.
executorBonusWADquerystringnorequest-withdraw, Royco only — share of the payout (WAD) a third-party executor may keep. Default 0 = free keeper execution; 18446744073709551615 restricts execution to the requester.
queueIdquerystringnoclaim / cancel, Bitway and Zerobase — the claim-queue id returned by requestClaim (also in its RequestClaim event). Zerobase claim: omitted ⇒ the oldest matured request of operator (read via getClaimQueueIDs).
amountquerystringnorequest-withdraw: shares to cool down (or underlying assets when byAssets=true). deposit: underlying to wrap. Wei.
byAssetsquerybooleannorequest-withdraw, Ethena only — interpret amount as the underlying asset amount (cooldownAssets) instead of shares (cooldownShares).
instantquerybooleannorequest-withdraw, native-wnlp only — take the immediate instantRedeem instead of the free queue. Costs the vault’s withdrawFeeBps (deducted from the underlying paid out) and is capped by its liquidity; check both on /v1/data/vaults first.
underlyingquerystringnodeposit, native-wnlp only — the asset to wrap, when the pool is too new to be in the registry. Otherwise resolved automatically.
sharesqueryintegernoclaim: share amount to redeem (erc7540 / erc4626).
ownerquerystringnorequest-withdraw: share owner, when not operator (erc4626 redeem).
controllerquerystringnoerc7540 / superform controller, when not operator.
stepqueryclaimnocancel, superform only — claim takes back the shares of a cancel Superform has fulfilled (claimCancelRedeemRequest). Omit to ask for the cancel.
redeemSlippageBpsqueryintegernorequest-withdraw, superform only — set your fulfilment floor (bps below the request price) before requesting. Default on-chain is 50.
assetsqueryintegernoclaim, superform — claim by asset amount (withdraw) instead of shares. request-withdraw route=queued, origin (wOETH / wsuperOETHb → WETH) — the OToken amount to request from Origin's vault after the unwrap; omitted, the route reads the wrapper's previewRedeem(amount) less 2 wei. The queued exit and its action=claim&requestId=<id> (from WithdrawalRequested, claimable after the vault's withdrawalClaimDelay once the queue holds the WETH) are msg.sender-only; route=instant (the default) is the plain unwrap to the OToken. No cancel.
accountquerystringnoclaim-rewards — whose rewards to claim. Defaults to operator. Superform (Merkl) claims must be sent by the account itself, so it must equal operator.

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[].chainTypeevmWhich VM executes this step. ABSENT means evm, which is the only value any endpoint returns today — every EVM response is unchanged. A non-EVM chain would return a different shape (a serialized, PERISHABLE transaction rather than to/data/value) carrying its own chainType, so a client that wants to stay forward-compatible should branch on this field rather than assume to is present.
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[].chainTypeevmWhich VM executes this step. ABSENT means evm, which is the only value any endpoint returns today — every EVM response is unchanged. A non-EVM chain would return a different shape (a serialized, PERISHABLE transaction rather than to/data/value) carrying its own chainType, so a client that wants to stay forward-compatible should branch on this field rather than assume to is present.
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.
actions.permissions[].typeERC20, LenderWhat kind of grant this is. A signatures[] offer names the type it replaces via its replaces field.
actions.signaturesobject[]EIP-712 payloads the user can sign INSTEAD of sending the corresponding permissions entry. Only present when the caller opted in with permit=auto|required and a permit path exists. Sign with eth_signTypedData_v4, then POST the same endpoint with {permits: [{permitId, signature}]} (plus builds where the response issued buildIds) — the permit executes inside the main transaction.
actions.signatures[].permitIdstringOpaque, self-describing handle for this permit. Round-trip it verbatim: POST {permits: [{permitId, signature}]} back to the same endpoint.
actions.signatures[].kinderc2612, dai, permit2, aaveCredit, morphoAuth, cometAuthPermit flavour
actions.signatures[].typedDataobjectReady for eth_signTypedData_v4 (domain, types incl. EIP712Domain, primaryType, message). All numeric fields are decimal strings.
actions.signatures[].replacesstringWhich permissions entry this signature replaces (ERC20 | Lender)
actions.signatures[].spenderstringWho the signature authorises. Match against the permission / quote (approvalTarget) it replaces — on multi-spender responses (x-chain) only routes whose approvalTarget equals this spender are covered by the signature.
actions.signatures[].descriptionstringHuman-readable label for this entry.
actions.signatures[].unscopedbooleanTrue when the grant is NOT amount-scoped (full position control until revoked) — surface this to the user.
actions.signatures[].deadlinestringUnix seconds after which the signature is worthless (default: 30 minutes).
actions.signsobject[]Messages that MUST be signed before anything can be sent (unlike signatures, which are optional permit offers). When non-empty, transactions and alternatives are EMPTY: sign typedData with eth_signTypedData_v4 (or, for format: personal_sign, message with personal_sign), then call submit with the signature under submit.signatureField, and execute what that answers. permissions may still be present and can be sent before or after signing. Produced by Inverse FiRM loops (/loop/leverage, /loop/close), Morpho Midnight make, Term order-sign and the Lista leg of /rewards/claim.
actions.signs[].purposeauthorize-action, publish-order, authenticateauthorize-action: the signature authorises an on-chain action and submit answers its transaction (Inverse ALE loops). publish-order: the signature makes an off-chain order and submit publishes it (Midnight make, Term order-sign). authenticate: the signature is a LOGIN to a third-party API that serves the data the transaction needs (Lista reward proofs); it authorises nothing on-chain.
actions.signs[].formateip712, personal_signeip712: sign typedData with eth_signTypedData_v4. personal_sign: sign the UTF-8 message with personal_sign.
actions.signs[].typedDataobjectformat: eip712 only. Ready for eth_signTypedData_v4. All numeric fields are decimal strings.
actions.signs[].messagestringformat: personal_sign only. The exact text to sign; send it back verbatim (it is part of submit.params).
actions.signs[].signerstringThe address that must sign. The server recovers the signer and refuses any other.
actions.signs[].deadlinestringUnix seconds after which the signature is worthless.
actions.signs[].spenderstringWho the signature authorises.
actions.signs[].descriptionstringHuman-readable label for this entry.

Example response

{
"success": true,
"data": {},
"actions": {
"transactions": [
{
"chainType": "evm",
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"alternatives": [
{
"chainType": "evm",
"to": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string"
}
],
"permissions": [
{
"to": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"data": "0x617ba037000000000000000000000000c02aaa39b2",
"value": "0",
"description": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"type": "ERC20"
}
],
"signatures": [
{
"permitId": "string",
"kind": "erc2612",
"typedData": {},
"replaces": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"description": "string",
"unscoped": true,
"deadline": "string"
}
],
"signs": [
{
"purpose": "authorize-action",
"format": "eip712",
"typedData": {},
"message": "string",
"signer": "string",
"deadline": "string",
"spender": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"description": "string",
"submit": {
"method": "GET",
"endpoint": "string",
"params": {},
"body": {},
"signatureField": "string"
}
}
],
"permitSkipped": [
{
"replaces": "string",
"reason": "string"
}
]
}
}

Request​

Responses​

Ordered transactions[] + ERC-20 permissions[] (run first). Each step carries its builder label as description.