Transfers
How it works
Transfers move balances and positions between subaccounts owned by the same authority. Each subaccount is its own cross-margin account: the deposits inside one back only that subaccount's positions. Collateral is never shared across subaccounts under the same wallet, which is why moving it between them takes an explicit instruction rather than happening implicitly when one runs short.
That separation is what makes subaccounts useful, and what makes transfers necessary. Integrations typically move value to isolate a market-making book from a directional one, to sweep profits into an account that is not trading, to top up a subaccount that needs more margin, or to fund a new strategy from a limited allocation.
Transfers between subaccounts under one authority never touch a wallet token account. For that, see Deposits and Withdrawals.
SDK Usage
Transfer a spot deposit between subaccounts
const marketIndex = 0; // the quote-asset spot market
const amount = velocityClient.convertToSpotPrecision(marketIndex, 100);
// transferDeposit(amount, marketIndex, fromSubAccountId, toSubAccountId)
await velocityClient.transferDeposit(amount, marketIndex, 0, 1);Transfer a perp position between subaccounts
// transferPerpPosition(fromSubAccountId, toSubAccountId, marketIndex, amount)
const amount = velocityClient.convertToPerpPrecision(1); // 1 base unit
await velocityClient.transferPerpPosition(0, 1, 0, amount);Transfer a deposit and a borrow together
transferPools moves a deposit position and a borrow position between two subaccounts under the same authority in a single instruction, so collateral and debt rebalance together instead of through two separate transfers that leave the account unbalanced in between.
const depositAmount = velocityClient.convertToSpotPrecision(0, 100); // 100 quote-asset units
const borrowAmount = velocityClient.convertToSpotPrecision(1, 1); // 1 unit of spot market 1
// transferPools(depositFromMarketIndex, depositToMarketIndex, borrowFromMarketIndex,
// borrowToMarketIndex, depositAmount, borrowAmount, fromSubAccountId, toSubAccountId)
await velocityClient.transferPools(0, 0, 1, 1, depositAmount, borrowAmount, 0, 1);Delegate transfers
A delegate is an address authorized via updateUserDelegate to trade on the owner's behalf. Internal transfers by a delegate are gated separately: transferDepositByDelegate is rejected unless the owner has opted in. The opt-in is a single bit in delegatePermissions on the owner's UserStats account, set through updateUserAllowDelegateTransfer, and it applies to every subaccount under that authority at once. It does not affect direct deposits, withdrawals, or trading.
// Run with a VelocityClient whose wallet is the account owner (authority),
// not the delegate. Once, before any delegate transfer.
await velocityClient.updateUserAllowDelegateTransfer(true);Once opted in, the delegate can call transferDepositByDelegate. It takes the same first four arguments as transferDeposit, plus an equityFloorDelta (BN | 'auto', defaulting to zero) before the trailing txParams. Do not pass txParams positionally in that fifth slot.
// Run with a VelocityClient constructed with `authority: <OWNER_PUBKEY>` and a
// delegate wallet, per the delegated-accounts note in Setup.
const marketIndex = 0; // the quote-asset spot market
const amount = velocityClient.convertToSpotPrecision(marketIndex, 100);
// transferDepositByDelegate(amount, marketIndex, fromSubAccountId, toSubAccountId, equityFloorDelta?, txParams?)
await velocityClient.transferDepositByDelegate(amount, marketIndex, 0, 1);If the owner has not opted in, the onchain instruction rejects the transfer regardless of which subaccounts the delegate is otherwise authorized to trade on. A delegate can never withdraw out of the protocol at all: see Users.