Action envelope, and the protocol re-verifies the signature before applying it. Four methods cover the distinct destinations:
private/transfer_spot
Move collateral between two subaccounts you own.
private/transfer_spot_external
Send collateral to a subaccount owned by a different wallet, bounded by your recipient allow-list.
private/withdraw
Withdraw collateral on-chain to an L1 recipient address.
private/transfer_positions
Move open positions between subaccounts, booked as an RFQ trade.
nonce, signer, signature, and signature_expiry_sec built exactly as described in Action signing. The action module for each flow is filled in by the server and does not appear on the wire — it is part of the signed struct hash and must match the deployment’s module address in Action signing.
Amounts, prices, and fees are human decimals on the wire — decimal strings (e.g.
"100.5") or JSON numbers. The
exception is private/withdraw, whose on-chain amount uses the asset’s native ERC-20 decimals (see below).Transfer collateral between your subaccounts
private/transfer_spot moves a spot balance from one of your subaccounts to another subaccount you own (existing, or a new one created in the same call). Positions are not moved by this method — use private/transfer_positions for that.
Required scope: transfer:existing_subaccount or transfer:new_subaccount (see Access scopes).
Transfer positions between subaccounts
private/transfer_positions moves open positions from one subaccount to another. It is booked as an RFQ-module trade: both sides sign a transfer quote over the same legs, and the transfer clears at the agreed prices. The signing key needs a transfer:* scope covering the destination (transfer:existing_subaccount, transfer:new_subaccount, or transfer:different_owner_subaccount).
Both quotes sign the same legs hash; each side authorizes its own max_fee. The response returns the resulting maker_quote and taker_quote.
Transfer collateral to another wallet
private/transfer_spot_external sends collateral to a subaccount owned by a different wallet. The destination owner must be on the sender’s whitelisted-recipient allow-list, and the signing key needs transfer:different_owner_subaccount.
Managing the recipient allow-list
Withdrawals or transfers to external subaccounts can only reach wallets you have explicitly whitelisted. Manage the list withprivate/update_whitelisted_recipients. Permissions that can modify this list are:
- owner
adminscoped session key
Withdraw
private/withdraw removes collateral from a subaccount and settles it to an Ethereum L1 recipient. It is a signed action requiring the withdraw scope.
Withdrawals signed by a session key must go to an address on the owner’s whitelisted-recipient allow-list, including the owner wallet itself. See Managing the recipient allow-list above.
recipient is optional and defaults to the account’s owner wallet — not the signer, and not the subaccount. The
signature commits to it, so the server cannot redirect a payout: a recipient disagreeing with what you signed fails
signature verification rather than being honored.A session key may only pay out to an address on the owner’s whitelist — including the owner wallet itself, which is
not whitelisted implicitly. Whitelist it before granting a key the withdraw scope, or the withdrawal is rejected with
RecipientNotWhitelisted. The owner and admin-scoped keys bound to no subaccounts skip the whitelist entirely.Forwarded withdrawals
Some tokens come to Derive over a bridge from their home chain. You can withdraw these tokens back to the home chain in one step. The withdrawal goes to a withdraw address: an Ethereum address bound to one recipient on one destination chain. When the payout lands there, Derive bridges it to the recipient. Derive pays the bridge fee. You pay only the normal withdrawal fee.chain_id is the destination’s chain id. Solana uses 101 (mainnet-beta) and 103 (devnet); every other
chain_id is an EVM chain. A withdraw address forwards only the tokens in the routes list that
private/register_withdraw_address returns. Send it nothing else: private/withdraw does not check the token,
and a token the address doesn’t forward is held there (held, no_route) until Derive recovers it and
refunds your wallet by hand.
Flow
1
Get the withdraw address
Call
private/register_withdraw_address with the destination chain_id and recipient.2
Whitelist it (scoped session keys only)
Skip this step if the owner wallet, or an
admin key bound to no subaccounts, signs the withdrawal. A scoped
session key can only withdraw to a whitelisted address, for EVM and Solana destinations alike. The owner first
adds the withdraw address with private/update_whitelisted_recipients, or private/withdraw fails with 11033
transfer_not_whitelisted (the protocol’s RecipientNotWhitelisted).3
Withdraw to it
Call
private/withdraw with recipient set to the withdraw address. The signature commits to the recipient,
so get the address before you sign.4
Track it
private/get_withdrawal_history shows the forward under forward (see below).Withdrawals signed by the owner wallet or by an
admin key bound to no subaccounts need no whitelisting. Every
other session key needs the withdraw address on whitelisted_recipients first. The whitelisted flag in
private/get_withdraw_addresses shows which addresses are ready.Register a withdraw address
private/register_withdraw_address returns the withdraw address for one (recipient, chain_id) and starts
forwarding for it. It is idempotent: the same params return the same address. The caller must be authenticated
as wallet.
string
required
Your account wallet. It must match the authenticated wallet.
integer
required
Destination chain, from the table above.
string
required
Recipient on the destination chain.
- EVM chain: must be your account
wallet. Forwards go to the same address on the home chain. - Solana: a base58 wallet public key. It must be on the ed25519 curve, so a program address or a token account is rejected: the bridge credits the recipient’s token account itself. Derive screens the address before it returns a withdraw address.
recipient is the 32-byte form the bridge uses. recipient_display is the address as you gave it, normalised.
List your withdraw addresses
private/get_withdraw_addresses {wallet} returns your EVM withdraw addresses (their recipient is your wallet)
and every withdraw address on your whitelisted_recipients, oldest first. A Solana withdraw address you
registered but never whitelisted is not listed: keep the register_withdraw_address result for it, or call
register_withdraw_address again (it returns the same address).
Response
address_kind is evm or solana. whitelisted tells you whether a scoped session key can withdraw to the
address now. Owner and unbounded admin signatures don’t need it.
Track a forwarded withdrawal
Eachprivate/get_withdrawal_history entry whose recipient is a withdraw address has a forward object:
A token amount below the bridge’s smallest unit stays at the withdraw address and goes with the next
forward, so the amount delivered can be slightly lower than
amount_forwarded.
Explorer links: LayerZero https://layerzeroscan.com/tx/<forward_tx> (testnet
https://testnet.layerzeroscan.com/tx/<forward_tx>), CCIP https://ccip.chain.link/msg/<message_id>.