Velocity ProtocolDevelopers
Velocity SDK

Swift (offchain signed orders)

Swift lets a taker place an order without sending a transaction. The taker signs an order message offchain and posts it to the Swift API; keepers and market makers then bundle that signed message into their own transaction and land the fill onchain. The taker pays no transaction fee and waits on no confirmation, and settlement is still fully onchain.

This page covers the taker side: building, signing, and submitting the message. For the maker side, receiving and filling signed orders over websocket, see the Swift API guide.

The flow is four steps: build the order params, sign the message offchain, POST it to the Swift API, and let keepers land it. The first three are the taker's.

The SDK's own websocket defaults are wss://swift.velocity.exchange/ws on mainnet and wss://swift.master.velocity.exchange/ws on devnet. The HTTP submit endpoint below uses the same mainnet host.

Step 1: Define order parameters

A Swift order is an ordinary Velocity order. In practice it is a market order carrying auction parameters, because the auction window is what gives market makers time to compete for the fill. auctionDuration is counted in 400 ms wall-clock units, so 50 is 20 seconds.

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, // 400ms wall-clock units, 20s for market makers to compete
});

Step 2: Sign the order message

Sign the order params offchain with the client's wallet. The result is a borsh-encoded, hex-serialized message plus its signature, both Buffers, which is what the Swift API verifies. uuid must be unique per order: it is what prevents a replay.

import { BN, 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);

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 onchain order placement.

Step 3: Submit to the Swift API

POST the message and signature to the Swift endpoint. The API validates the signature and queues the order for keepers and market makers. The message is already hex-encoded text, so do not encode it again.

const swiftUrl = "https://swift.velocity.exchange/orders";

const response = await fetch(swiftUrl, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    message: message.toString(), // already hex-encoded text; do not re-encode
    signature: signature.toString("base64"),
    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);
}

Delegate flows. When signing as a delegate for another account, pass signing_authority as the delegate's public key and keep taker_authority as the account owner's. Construct the VelocityClient with authority set to the owner's key, per the delegated-accounts note in Setup.

Signed message accounts (delegate flow)

For delegate accounts, initializing a SignedMsgUserOrders account allows 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);

// Initialize the signed message orders account for `authority`
// with space for 8 concurrent orders
const [txSig, signedMsgUserAccount] =
  await velocityClient.initializeSignedMsgUserOrders(authority, 8);

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
);

Instruction builders (taker and maker)

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

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

// Used by keeper bots to submit a taker's signed Swift order onchain
const ixs = await velocityClient.getPlaceSignedMsgTakerPerpOrderIxs(
  { orderParams: orderMessageHex, signature },
  marketIndex,
  takerInfo
);

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
);

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();

Hash a signature for use in order deduplication or indexing:

import { digestSignature } from "@velocity-exchange/sdk";

const hash = digestSignature(Uint8Array.from(signature));

Derive the user stats account PDA for an authority:

import { getUserStatsAccountPublicKey } from "@velocity-exchange/sdk";

const userStats = getUserStatsAccountPublicKey(
  velocityClient.program.programId,
  authority
);