Velocity Builder Codes
Overview
Velocity’s Builder Code (VBC) system enables any builder to build on top of Velocity while earning fees for routing trades.
Velocity Builder Codes establish the financial infrastructure layer on Solana, by allowing anyone to tap into Velocity’s deep liquidity and efficient execution, without building their own backend. This enables a fully open, composable UI layer for perps on Solana.
This Builder Code system makes Velocity an on-chain DEX on Solana that offers per order monetization for third-party frontends, setting the stage for a permissionless ecosystem of apps, bots, and aggregators all aligned with Velocity and Solana’s liquidity growth.
Builder fees are denominated in tenths of a basis point throughout the protocol (1 unit = 0.001%, so 100 = 10 bps = 0.1%, and 1_000 = 100 bps = 1%). Fields like builderFeeTenthBps, feeTenthBps, and maxFeeTenthBps all use this unit.
How the builder fee is calculated
builder_fee = notional × fee_tenth_bps / 100_000The builder fee is charged on top of the taker fee. It does not reduce the taker’s fee tier or the protocol’s cut of the fill, and it accrues to the builder’s RevenueShareEscrow rather than to any protocol pool. fee_tenth_bps is capped at 1000 tenth-bps (1% of the filled notional); a builder code above that cap is rejected. See Trading Fees for the taker fee it sits on top of.
A fill that carries a builder code also requires the taker’s RevenueShareEscrow to be passed into the instruction, or the fill fails.
Getting started with VBC
-
Builder Registration: In order to receive fees, builders are required to have an existing Velocity account, as well as set up a
RevenueShareAccount(via theinitializeRevenueShareinstruction). -
User Onboarding: Before approving any builder, users must first create a
RevenueShareEscrowaccount with theinitializeRevenueShareEscrowinstruction (numOrdersmust be at least 1). Then, before placing any orders, users approve the builder and the maximum allowed builder fee via thechangeApprovedBuilderinstruction (builder pubkey,maxFeeTenthBps). This approval is stored directly inside the user’sRevenueShareEscrowaccount.
How Builder Codes Work
Order Placement
The builder’s app places an order (e.g. placePerpOrder) with builderIdx and builderFeeTenthBps set in the order params. The RevenueShareEscrow account is included in the transaction so the program can validate and record the order.
Fee Accrual (Per Order)
When an order is filled, the fee is credited to the user’s RevenueShareEscrow as a RevenueShareOrder, unless the taker is below initial margin at fill time (or a required oracle is invalid), in which case the fee is waived for that fill. The fill itself still goes through; the builder simply is not paid for it.
- The accrued row tracks the builder index (
builderIdx),feesAccrued,orderId,feeTenthBps, market index and type, and completion status. - Fees remain in escrow until settlement.
Settlement
Accrued fees in the escrow are swept to the builder’s RevenueShareAccount when the user calls settlePnl. They can also be swept permissionlessly via the settleRevenueShare instruction, without the user settling PnL. Rows that can never be paid (for example, a delisted market) are cleared with forfeitRevenueShareOrder.
Code examples can be found in Builder Codes (SDK).
A builder-coded order cannot be modified
modifyOrder (and modifyOrderByUserOrderId) reject any order that carries a builder code, with CannotModifyBuilderOrder (error 6366 / 0x18de, “Cannot modify a builder-coded order; cancel and re-place instead”).
The reason is that the fee attribution for a builder-coded order lives in a RevenueShareEscrow row keyed to that order’s orderId. A modify cancels and re-places under a new order id, which would leave the row behind and silently drop the builder fee. To change a builder-coded order, cancel it and place a new order with the builder params set again. The SDK’s cancelAndPlaceOrders does both in one transaction.
Payout paths for accrued fees
Three instructions move an accrued row out of escrow and into the builder’s RevenueShare account. All three pay out of the perp market’s PnL pool.
| Instruction | Who can call it | What it does |
|---|---|---|
settlePnl | The escrow owner | Runs the sweep after it settles the owner’s PnL on that market. Only sweeps while the owner still has PnL to settle. |
settleRevenueShare | Anyone | Pays the accrued builder and referrer rows in one escrow for one perp market, without the owner settling PnL. Takes marketIndex and the number of owner sub-accounts to pass. Rejects a delisted market, and requires builder codes to be enabled and settle-PnL not paused. |
forfeitRevenueShareOrder | Anyone | Writes off one row the program cannot pay, so PerpMarket.pendingRevenueShare can reach zero and the delist is not blocked. Moves no tokens. |
settleRevenueShare exists because settlePnl is not a reliable payout path for a builder: once the escrow owner closes the position and stops trading, nobody can collect through PnL settlement, and pendingRevenueShare holds PnL-pool value against the claim indefinitely. Builders and keepers should treat settleRevenueShare as their own collection path rather than waiting on the user.
forfeitRevenueShareOrder needs proof that the row is unpayable, and the market must be in settlement or delisted. One of these must hold: the beneficiary has no payout User account, the closed pool is smaller than the row, or the row names no beneficiary the program can reach. A row that can still be paid is rejected with RevenueShareOrderNotForfeitable.
Notes on builder codes for MMs
RevenueShareEscrow Inclusion: Fillers and market makers must include the user’s RevenueShareEscrow account in every order fill transaction. This PDA is derived from the user’s pubkey (requires no additional RPC calls).
Multi Builder Support: Users can approve multiple builders, so MMs may see multiple builder accounts to sweep to during settlePnl. The protocol will not throw if a specific builder is omitted, but all filled rewards must eventually be swept.
Error Handling: If a required escrow account isn’t included, the program will throw. Therefore it’s recommended to always include it.
FAQ
Q: Can a user approve multiple builders?
A: Yes. Each builder entry in approvedBuilders has its own maxFeeTenthBps.
Q: How are builder fees capped?
A: Two caps apply. The user-approved max fee per builder (maxFeeTenthBps) is the first. The protocol also enforces a global ceiling of MAX_BUILDER_FEE_TENTH_BPS (1000 tenth-bps, i.e. 1% of notional), independent of the user’s approval. Attempts to exceed either cap result in a transaction failure.
Q: Do MMs or fillers need to know the builder/referrer accounts for every order?
A: No, but including the RevenueShareEscrow is required to process reward accrual and settlement.
Q: Is a builder fee charged on every fill?
A: No. The fee is charged only when the taker meets initial margin at fill time, under strict oracle rules (each price is the more conservative of the live price and the TWAP, and every liability oracle must be valid). Below that, and on liquidation fills, the fill still happens and still reports the builder in its OrderActionRecord, but the builder fee for that fill is zero. A builder fee is a transfer out of the taker’s account, so it clears the same gate a withdrawal clears.
Q: Can I modify a builder-coded order?
A: No. modifyOrder rejects it with CannotModifyBuilderOrder (6366 / 0x18de). Cancel the order and place a new one with the builder params set.
Q: When are builder rewards paid?
A: On the user’s settlePnl call, or permissionlessly via settleRevenueShare, which any caller can send without the user. Both sweep escrowed fees to the builder. Unpayable rows in a settlement or delisted market are written off with forfeitRevenueShareOrder.