Skip to Content
DevelopersVelocity SDKSwift (off-chain signed orders)

Swift (off-chain signed orders)

Swift is an extension to Velocity that lets users place orders without submitting a transaction to the Solana network. Instead of paying gas and waiting for confirmation, users sign an order message off-chain and submit it to the Swift API. Keepers and market makers then bundle the signed message with their own transaction to fill the order on-chain.

This enables faster order placement, lower latency, and a gas-free experience for takers, while market makers still settle on-chain.

The Swift API host below (swift.velocity.exchange) is a provisional placeholder — Velocity’s production Swift endpoint had not been finalized/verified at the time this page was written. Confirm the real host before shipping against it.

Order flow:

  1. Define order parameters
  2. Sign the order message off-chain
  3. Submit to the Swift API (https://swift.velocity.exchange/orders)
  4. Keepers/market makers pick up the order and fill it on-chain

Step 1: Define order parameters

Set up a standard Velocity order. For Swift, you typically use a market order with auction parameters that give market makers a window to fill:

import { getMarketOrderParams, MarketType, PositionDirection, isVariant } from "@velocity-exchange/sdk"; const marketIndex = 0; // SOL-PERP const oracleInfo = velocityClient.getOracleDataForPerpMarket(marketIndex); const direction = PositionDirection.LONG; // Set auction price range around the current oracle price const highPrice = oracleInfo.price.muln(101).divn(100); // oracle + 1% const lowPrice = oracleInfo.price; const orderParams = getMarketOrderParams({ marketIndex, marketType: MarketType.PERP, direction, baseAssetAmount: velocityClient.convertToPerpPrecision(0.1), // 0.1 SOL auctionStartPrice: isVariant(direction, "long") ? lowPrice : highPrice, auctionEndPrice: isVariant(direction, "long") ? highPrice : lowPrice, auctionDuration: 50, // slots for market makers to compete });
Example Swift order setupReference ↗
TypeScript docs unavailable for Swift order setup.

Step 2: Sign the order message

Sign the order parameters off-chain using your VelocityClient’s wallet. This produces a serialized message and signature that the Swift API will verify:

import { generateSignedMsgUuid } from "@velocity-exchange/sdk"; const slot = await velocityClient.connection.getSlot(); const orderMessage = { signedMsgOrderParams: orderParams, subAccountId: velocityClient.activeSubAccountId, slot: new BN(slot), uuid: generateSignedMsgUuid(), // unique ID for deduplication stopLossOrderParams: null, takeProfitOrderParams: null, }; const { orderParams: message, signature } = velocityClient.signSignedMsgOrderParamsMessage(orderMessage);
Method VelocityClient.signSignedMsgOrderParamsMessageReference ↗
ParameterTypeRequired
orderParamsMessage
SignedMsgOrderParamsMessage | SignedMsgOrderParamsDelegateMessage
The order message to sign; use `SignedMsgOrderParamsDelegateMessage` when signing on behalf of a delegated authority (`delegateSigner: true`), otherwise `SignedMsgOrderParamsMessage`.
Yes
delegateSigner
boolean
Whether `orderParamsMessage` is the delegate-signer variant; must match the message's actual shape or encoding/decoding elsewhere will misinterpret the buffer.
No
Returns
SignedMsgOrderParams

Builder Codes attach the same way here: add builderIdx / builderFeeTenthBps to orderMessage alongside signedMsgOrderParams. Builder codes are not exclusive to Swift — the same fields also work on regular on-chain order placement.

Step 3: Submit to the Swift API

POST the signed message to the Swift endpoint. The API validates the signature and queues the order for keepers and market makers:

const swiftUrl = "https://swift.velocity.exchange/orders"; const response = await fetch(swiftUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ market_index: orderParams.marketIndex, market_type: "perp", message: message.toString("hex"), signature: signature.toString("hex"), taker_authority: velocityClient.wallet.publicKey.toBase58(), // signing_authority: delegatePublicKey.toBase58(), // only needed for delegate flows }), }); if (!response.ok) { const errorText = await response.text(); throw new Error("Swift error: " + response.status + " " + errorText); }
Example Swift API submitReference ↗
TypeScript docs unavailable for Swift API submit.

Delegate flows: When signing as a delegate for another account, also pass signing_authority (the delegate’s public key) and ensure taker_authority is the account owner’s public key. Initialize the VelocityClient with authority set to the owner’s key.

Signed message accounts (delegate flow)

For delegate accounts, you can initialize a SignedMsgUserOrders account to allow a delegate to place Swift orders on behalf of the owner:

import { getSignedMsgUserAccountPublicKey } from "@velocity-exchange/sdk"; // Derive the PDA for the signed message orders account const pda = getSignedMsgUserAccountPublicKey(velocityClient.program.programId, authority);
Function getSignedMsgUserAccountPublicKeyReference ↗

Derives the `SignedMsgUserOrders` PDA (holds a user's pending swift/signed-message orders) from seeds `["SIGNED_MSG", authority]`.

ParameterTypeRequired
programId
PublicKey
Deployed velocity program id.
Yes
authority
PublicKey
Wallet pubkey (the user's main authority, not the sub-account) that owns the account.
Yes

The `SignedMsgUserOrders` account's public key.

Returns
PublicKey
// Initialize the signed message orders account for `authority` // with space for 8 concurrent orders const [txSig, signedMsgUserAccount] = await velocityClient.initializeSignedMsgUserOrders(authority, 8);
Method VelocityClient.initializeSignedMsgUserOrdersReference ↗
ParameterTypeRequired
authority
PublicKey
Authority the account is created for.
Yes
numOrders
number
Number of order-message slots to allocate; determines account rent/size.
Yes
txParams
TxParams
Optional compute-unit/priority-fee overrides for the transaction.
No
Returns
Promise<[string, PublicKey]>

Decode signed messages

Decode a raw signed message (e.g., received from the Swift API or another source) back into structured order params:

const signedMessage = velocityClient.decodeSignedMsgOrderParamsMessage( Buffer.from(orderMessageHex, "hex"), isDelegateSigner // true if the message was signed by a delegate );
Method VelocityClient.decodeSignedMsgOrderParamsMessageReference ↗
ParameterTypeRequired
encodedMessage
Buffer
The borsh-encoded message bytes (as produced by `encodeSignedMsgOrderParamsMessage`).
Yes
delegateSigner
boolean
Whether to decode as the delegate-signer variant (`SignedMsgOrderParamsDelegateMessage`) rather than the regular one.
No
Returns
SignedMsgOrderParamsMessage | SignedMsgOrderParamsDelegateMessage

Instruction builders (Swift taker + maker)

For advanced use cases (such as building keeper or market-maker bots), you can construct Swift fill instructions directly instead of using the HTTP API flow.

Build the taker-side instructions for placing a Swift order on-chain:

// Used by keeper bots to submit a taker's signed Swift order on-chain const ixs = await velocityClient.getPlaceSignedMsgTakerPerpOrderIxs( { orderParams: orderMessageHex, signature }, marketIndex, takerInfo );
Method VelocityClient.getPlaceSignedMsgTakerPerpOrderIxsReference ↗
ParameterTypeRequired
signedSignedMsgOrderParams
SignedMsgOrderParams
The signed order payload.
Yes
marketIndex
number
Perp market index the signed order targets.
Yes
takerInfo
{ taker: PublicKey; takerStats: PublicKey; takerUserAccount: UserAccount; signingAuthority: PublicKey; }
Taker's account/authority info; see `placeSignedMsgTakerOrder`.
Yes
precedingIxs
TransactionInstruction[]
Instructions preceding these two in the final transaction (used only to compute the ed25519-verify instruction's sysvar index).
No
overrideCustomIxIndex
number
Explicit sysvar-instructions index override.
No
Returns
Promise<TransactionInstruction[]>

Build instructions to place a maker order and simultaneously fill a pending Swift taker order (atomic maker fill). If the taker’s order carries a builder code or the taker is referred with an escrow, pass their decoded RevenueShareEscrow as the trailing takerEscrow argument — see Fill-time enforcement:

// Used by market makers to fill a taker's Swift order with their own maker quote const ixs = await velocityClient.getPlaceAndMakeSignedMsgPerpOrderIxs( signedMsgOrderParams, signedMsgOrderUuid, takerInfo, makerOrderParams, subAccountId, [], // precedingIxs undefined, // overrideCustomIxIndex takerEscrow // optional; required when the taker's order needs it );
Method VelocityClient.getPlaceAndMakeSignedMsgPerpOrderIxsReference ↗
ParameterTypeRequired
signedSignedMsgOrderParams
SignedMsgOrderParams
The taker's signed order payload.
Yes
signedMsgOrderUuid
Uint8Array<ArrayBufferLike>
UUID identifying the signed-msg order.
Yes
takerInfo
{ taker: PublicKey; takerStats: PublicKey; takerUserAccount: UserAccount; signingAuthority: PublicKey; }
Taker's account/authority info.
Yes
orderParams
OptionalOrderParams
Maker order to place; see `placeAndMakeSignedMsgPerpOrder` for field precisions and required order shape.
Yes
subAccountId
number
Sub-account placing the maker order; defaults to the active sub-account.
No
precedingIxs
TransactionInstruction[]
Instructions preceding these in the final transaction (used only to compute the ed25519-verify instruction's sysvar index).
No
overrideCustomIxIndex
number
Explicit sysvar-instructions index override.
No
takerEscrow
RevenueShareEscrowAccount
See `placeAndMakeSignedMsgPerpOrder`.
No
Returns
Promise<TransactionInstruction[]>

Helper functions

Generate a unique UUID for a Swift order message. Each order must have a distinct UUID to prevent replay attacks:

import { generateSignedMsgUuid } from "@velocity-exchange/sdk"; const uuid = generateSignedMsgUuid();
Function generateSignedMsgUuidReference ↗

Generates a random 8-character uuid for tagging a signed-message (swift) order, matching the `uuid: Uint8Array` field expected on-chain/by the swift server.

8 raw bytes (the ASCII/UTF-8 encoding of an 8-character nanoid string).

Returns
Uint8Array<ArrayBufferLike>

Hash a signature for use in order deduplication or indexing:

import { digestSignature } from "@velocity-exchange/sdk"; const hash = digestSignature(Uint8Array.from(signature));
Function digestSignatureReference ↗

Computes a base64-encoded SHA-256 digest of a signature, used as a compact, collision-resistant dedup/lookup key for signed messages (e.g. swift/signed-message orders) without storing the full signature.

ParameterTypeRequired
signature
Uint8Array<ArrayBufferLike>
Raw signature bytes to hash.
Yes

The base64-encoded SHA-256 digest.

Returns
string

Derive the user stats account PDA for an authority:

import { getUserStatsAccountPublicKey } from "@velocity-exchange/sdk"; const userStats = getUserStatsAccountPublicKey( velocityClient.program.programId, authority );
Function getUserStatsAccountPublicKeyReference ↗

Derives the `UserStats` PDA (one per authority, shared across all of that authority's sub-accounts) from seeds `["user_stats", authority]`.

ParameterTypeRequired
programId
PublicKey
Deployed velocity program id.
Yes
authority
PublicKey
Wallet pubkey the stats account tracks.
Yes

The `UserStats` account's public key.

Returns
PublicKey
Last updated on