Skip to main content

Instruction Builders

SDK Coming Soon

The @runner-protocol/sdk package is not yet publicly available. The instruction builders documented below will be available once the SDK is published to npm at mainnet launch. In the meantime, you can use the Indexer API to read auction state, or use the Runner Protocol app to participate in auctions.

The SDK provides functions to build CCA V2 transaction instructions without requiring the @coral-xyz/anchor package. Each builder returns a TransactionInstruction that can be added to a Solana Transaction.

All instruction builders automatically derive the required PDAs (auction state, vaults, vault authority, etc.) from the auctionConfig public key you provide.

buildPlaceBidInstruction

Place a new bid on an active auction. The bidder specifies a maximum price they are willing to pay and the amount of quote currency to commit.

interface PlaceBidParams {
/** The bidder's wallet (signer) */
bidder: PublicKey;
/** The auction config account public key */
auctionConfig: PublicKey;
/** Maximum price willing to pay, Q96-encoded (bigint) */
maxPrice: bigint;
/** Amount of quote currency in atomic units (e.g., lamports) */
amount: bigint;
/** Price of the tick just below maxPrice in the linked list.
* Pass 0n if there is no lower tick or you don't know it. */
prevTickPrice: bigint;
/** Current total_bids from the auction state (used to derive the bid PDA) */
totalBids: bigint;
/** Latest checkpoint auction block from the auction state */
latestCheckpointAuctionBlock: bigint;
/** Bidder's quote token account (ATA) */
bidderQuoteAccount: PublicKey;
/** Token program for the quote mint (defaults to TOKEN_PROGRAM_ID) */
tokenProgram?: PublicKey;
}

function buildPlaceBidInstruction(
params: PlaceBidParams
): TransactionInstruction

Accounts (auto-derived)

AccountDerived FromWritableSigner
bidderprovidedyesyes
auctionConfigprovidednono
auctionStatefindAuctionStatePda(auctionConfig)yesno
bidfindBidPda(auctionConfig, totalBids)yesno
latestCheckpointfindCheckpointPda(auctionConfig, latestCheckpointAuctionBlock)yesno
bidderQuoteAccountprovidedyesno
quoteVaultfindQuoteVaultPda(auctionConfig)yesno
tokenProgramprovided or defaultnono
systemProgramSystemProgram.programIdnono
tickAtMaxPricefindTickPda(auctionConfig, maxPrice) (remaining account)yesno
prevTickfindTickPda(auctionConfig, prevTickPrice) (remaining account, if prevTickPrice > 0)yesno

Example

import {
buildPlaceBidInstruction,
encodeQ96,
getAssociatedTokenAddress,
TOKEN_PROGRAM_ID,
} from "@runner-protocol/sdk";
import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";

const ix = buildPlaceBidInstruction({
bidder: wallet.publicKey,
auctionConfig: auctionConfigPubkey,
maxPrice: encodeQ96("0.10"),
amount: BigInt(5_000_000), // 5 USDC (6 decimals)
prevTickPrice: BigInt(0),
totalBids: BigInt(currentTotalBids),
latestCheckpointAuctionBlock: BigInt(currentCheckpointBlock),
bidderQuoteAccount: getAssociatedTokenAddress(quoteMint, wallet.publicKey),
tokenProgram: TOKEN_PROGRAM_ID,
});

const tx = new Transaction().add(ix);
await sendAndConfirmTransaction(connection, tx, [wallet]);
caution

The totalBids value must match the current on-chain value exactly. Each bid increments this counter, so fetch it fresh from the indexer or deserialize the auction state account right before building the transaction.


buildClaimTokensInstruction

Claim filled base tokens from a bid after the auction has ended.

interface ClaimTokensParams {
/** The bidder's wallet (signer, must be the bid owner) */
bidder: PublicKey;
/** The auction config account public key */
auctionConfig: PublicKey;
/** The bid account public key to claim from */
bidPubkey: PublicKey;
/** Bidder's base token account for receiving claimed tokens */
bidderBaseAccount: PublicKey;
/** Token program for the base mint. Use TOKEN_2022_PROGRAM_ID for
* tokens launched via the Launchpad (Token-2022 mints). */
tokenProgram: PublicKey;
}

function buildClaimTokensInstruction(
params: ClaimTokensParams
): TransactionInstruction

Accounts (auto-derived)

AccountDerived FromWritableSigner
bidderprovidednoyes
auctionConfigprovidednono
auctionStatefindAuctionStatePda(auctionConfig)nono
bidprovidedyesno
baseVaultfindBaseVaultPda(auctionConfig)yesno
bidderBaseAccountprovidedyesno
vaultAuthorityfindVaultAuthorityPda(auctionConfig)nono
tokenProgramprovidednono

Example

import {
buildClaimTokensInstruction,
getAssociatedTokenAddress,
createAssociatedTokenAccountIdempotentInstruction,
TOKEN_2022_PROGRAM_ID,
} from "@runner-protocol/sdk";

// Ensure the base token ATA exists (Token-2022 for launched tokens)
const bidderBaseAccount = getAssociatedTokenAddress(
baseMint,
wallet.publicKey,
TOKEN_2022_PROGRAM_ID
);

const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(
wallet.publicKey,
bidderBaseAccount,
wallet.publicKey,
baseMint,
TOKEN_2022_PROGRAM_ID
);

const claimIx = buildClaimTokensInstruction({
bidder: wallet.publicKey,
auctionConfig: auctionConfigPubkey,
bidPubkey: bidAccountPubkey,
bidderBaseAccount,
tokenProgram: TOKEN_2022_PROGRAM_ID,
});

const tx = new Transaction().add(createAtaIx, claimIx);
await sendAndConfirmTransaction(connection, tx, [wallet]);
tip

Tokens launched via the Launchpad use Token-2022 mints. Always pass TOKEN_2022_PROGRAM_ID as the tokenProgram when claiming tokens from these auctions.


buildExitBidInstruction

Exit (withdraw from) a fully out-of-range bid and reclaim the deposited quote currency. The bid must have maxPrice < clearing_price.

interface ExitBidParams {
/** The signer (bid owner or permissionless after auction ends) */
signer: PublicKey;
/** The auction config account public key */
auctionConfig: PublicKey;
/** The bid account public key to exit */
bidPubkey: PublicKey;
/** The checkpoint at the bid's start auction block */
startCheckpoint: PublicKey;
/** The checkpoint at the current/end auction block */
endCheckpoint: PublicKey;
/** Bidder's quote token account for receiving refund */
bidderQuoteAccount: PublicKey;
/** Token program (defaults to TOKEN_PROGRAM_ID) */
tokenProgram?: PublicKey;
}

function buildExitBidInstruction(
params: ExitBidParams
): TransactionInstruction

Accounts (auto-derived)

AccountDerived FromWritableSigner
signerprovidednoyes
auctionConfigprovidednono
auctionStatefindAuctionStatePda(auctionConfig)yesno
bidprovidedyesno
startCheckpointprovidednono
endCheckpointprovidednono
quoteVaultfindQuoteVaultPda(auctionConfig)yesno
bidderQuoteAccountprovidedyesno
vaultAuthorityfindVaultAuthorityPda(auctionConfig)nono
tokenProgramprovided or defaultnono
note

You must provide the startCheckpoint and endCheckpoint accounts. Use findCheckpointPda to derive them from the bid's startAuctionBlock and the current or latest checkpoint auction block.


buildExitPartiallyFilledBidInstruction

Exit a bid that was partially filled. This is used when a bid was at the clearing price and received a pro-rata fill. It requires additional checkpoint accounts to calculate the partial fill.

interface ExitPartiallyFilledBidParams {
/** The signer (bid owner or permissionless after auction ends) */
signer: PublicKey;
/** The auction config account public key */
auctionConfig: PublicKey;
/** The bid account public key */
bidPubkey: PublicKey;
/** Checkpoint at the bid's start auction block */
startCheckpoint: PublicKey;
/** The last checkpoint where the bid was fully filled */
lastFullyFilledCheckpoint: PublicKey;
/** The checkpoint immediately after lastFullyFilledCheckpoint */
nextOfLastFullyFilledCheckpoint: PublicKey;
/** Checkpoint at the current/end auction block */
endCheckpoint: PublicKey;
/** Tick account at the bid's max price */
tickAtMaxPrice: PublicKey;
/** Bidder's quote token account for receiving refund */
bidderQuoteAccount: PublicKey;
/** Token program (defaults to TOKEN_PROGRAM_ID) */
tokenProgram?: PublicKey;
}

function buildExitPartiallyFilledBidInstruction(
params: ExitPartiallyFilledBidParams
): TransactionInstruction

Accounts (auto-derived)

AccountDerived FromWritableSigner
signerprovidednoyes
auctionConfigprovidednono
auctionStatefindAuctionStatePda(auctionConfig)yesno
bidprovidedyesno
startCheckpointprovidednono
lastFullyFilledCheckpointprovidednono
nextOfLastFullyFilledCheckpointprovidednono
endCheckpointprovidednono
tickAtMaxPriceprovidednono
quoteVaultfindQuoteVaultPda(auctionConfig)yesno
bidderQuoteAccountprovidedyesno
vaultAuthorityfindVaultAuthorityPda(auctionConfig)nono
tokenProgramprovided or defaultnono
caution

Determining the correct checkpoint accounts for partial exit requires knowledge of which auction blocks the bid was fully filled through. This typically requires querying checkpoint data from the indexer API (GET /api/auctions/:pubkey/checkpoints).


Building and Sending Transactions

All instruction builders return a standard TransactionInstruction. Combine them with @solana/web3.js transaction primitives:

import { Transaction, sendAndConfirmTransaction } from "@solana/web3.js";

// Build instructions
const ix1 = buildPlaceBidInstruction({ ... });
const ix2 = buildClaimTokensInstruction({ ... });

// Combine into a transaction
const tx = new Transaction().add(ix1);
// or multiple instructions:
const tx2 = new Transaction().add(ix1, ix2);

// Send and confirm
const signature = await sendAndConfirmTransaction(
connection,
tx,
[wallet] // signers array
);
tip

Always fetch a fresh blockhash before sending. If you build the transaction and then wait before sending, the blockhash may expire. Use connection.getLatestBlockhash() right before sendAndConfirmTransaction.