CCA V2 Program Reference
Complete reference for the CCA V2 (Continuous Clearing Auction) program on Solana.
Program ID: F5DdorN9nawhpQJZkfLYx2SpcWxSTZnwh3ZRyNtXP5do
Framework: Anchor 0.29.0
Account Types
The program manages six on-chain account types, plus three additional PDAs for vaults and signing authority.
AuctionConfig
Immutable auction parameters set at creation time.
PDA Seeds: ["cca_auction", authority, auctionId (u64 LE)]
Size: 386 bytes (378 + 8 discriminator)
| Field | Rust Type | Description |
|---|---|---|
authority | Pubkey | Auction creator/admin |
auction_id | u64 | Unique auction identifier |
base_mint | Pubkey | Base token mint (tokens being auctioned) |
quote_mint | Pubkey | Quote token mint (currency used to bid) |
total_supply | u64 | Total base tokens offered |
floor_price | u128 | Minimum clearing price (Q96) |
max_bid_price | u128 | Maximum allowed bid price (Q96) |
tick_spacing | u32 | Minimum gap between tick prices |
start_slot | u64 | Solana slot when auction starts |
end_slot | u64 | Solana slot when auction ends |
claim_slot | u64 | Solana slot when token claims begin |
required_currency_raised | u128 | Currency threshold for graduation |
funds_recipient | Pubkey | Receives swept quote currency |
tokens_recipient | Pubkey | Receives unsold base tokens |
supply_schedule_account | Pubkey | Associated SupplySchedule PDA |
auction_block_interval | u64 | Solana slots per auction block |
min_bid_amount | u64 | Minimum bid size |
min_new_tick_amount | u64 | Minimum bid to create a new tick |
max_ticks | u32 | Max initialized ticks for this auction |
tokens_received | bool | Whether base tokens deposited |
graduated | bool | Deprecated -- graduation is computed dynamically |
bump | u8 | PDA bump seed |
AuctionState
Mutable singleton tracking live auction state.
PDA Seeds: ["cca_state", auctionConfig]
Size: 357 bytes (349 + 8 discriminator)
| Field | Rust Type | Description |
|---|---|---|
auction_config | Pubkey | Parent AuctionConfig reference |
clearing_price | u128 | Current clearing price (Q96) |
sum_currency_demand_above_clearing | [u8; 32] (U256) | Total demand from ticks above clearing |
next_active_tick_price | u128 | Price of next initialized tick above clearing |
current_step_index | u32 | Current supply schedule step index |
cumulative_mps | u32 | Cumulative supply issued (milli-basis-points) |
currency_raised_q96_x7 | [u8; 32] (U256) | X7-scaled total currency raised (graduation check) |
currency_raised | [u8; 32] (U256) | Total currency raised |
currency_raised_at_clearing_price | [u8; 32] (U256) | Cumulative currency at clearing price tick |
latest_checkpoint_slot | u64 | Solana slot of most recent checkpoint |
latest_checkpoint_auction_block | u64 | Auction block of most recent checkpoint |
checkpoint_count | u64 | Total checkpoints created |
total_bids | u64 | Total bids placed |
total_ticks | u32 | Total initialized ticks |
total_cleared_q96_x7 | [u8; 32] (U256) | X7-scaled total base tokens cleared |
currency_swept | bool | Whether quote currency has been swept |
tokens_swept | bool | Whether unsold tokens have been swept |
bump | u8 | PDA bump seed |
SupplySchedule
Defines token release rate over time via step-based issuance.
PDA Seeds: ["cca_supply", auctionConfig]
Size: Dynamic (depends on number of steps)
| Field | Rust Type | Description |
|---|---|---|
auction_config | Pubkey | Parent AuctionConfig reference |
num_steps | u16 | Number of supply steps |
total_blocks | u64 | Total auction blocks across all steps |
steps | Vec<SupplyStep> | Supply step definitions |
bump | u8 | PDA bump seed |
SupplyStep fields: mps (u32), block_delta (u64), start_block (u64), end_block (u64).
Tick
Price-level node in a singly-linked list (descending by price).
PDA Seeds: ["cca_tick", auctionConfig, price (u128 LE)]
Size: 146 bytes (138 + 8 discriminator)
| Field | Rust Type | Description |
|---|---|---|
auction_config | Pubkey | Parent AuctionConfig reference |
price | u128 | Price level (Q96) |
currency_demand_q96 | [u8; 32] (U256) | Total effective currency demand at this price |
next_tick_price | u128 | Price of next lower tick (0 = end of list) |
is_initialized | bool | Whether this tick has been activated |
bump | u8 | PDA bump seed |
Checkpoint
Epoch snapshot tracking cumulative supply, clearing price, and accumulators.
PDA Seeds: ["cca_checkpoint", auctionConfig, auctionBlock (u64 LE)]
Size: 324 bytes (316 + 8 discriminator)
| Field | Rust Type | Description |
|---|---|---|
auction_config | Pubkey | Parent AuctionConfig reference |
auction_block | u64 | Auction block number |
slot | u64 | Solana slot when created |
clearing_price | u128 | Clearing price at this checkpoint (Q96) |
cumulative_mps | u32 | Cumulative supply MPS at this checkpoint |
cumulative_mps_per_price | [u8; 32] (U256) | Settlement accumulator: sum of (delta_mps / clearing_price) |
currency_raised_at_clearing_price_q96_x7 | [u8; 32] (U256) | X7-scaled cumulative currency raised at clearing tick |
prev_checkpoint_auction_block | u64 | Previous checkpoint (doubly-linked list) |
next_checkpoint_auction_block | u64 | Next checkpoint (0 = last/final) |
status | CheckpointStatus | Uninitialized / Resolving / Finalized |
bump | u8 | PDA bump seed |
Bid
Individual bid receipt tracking position, fill status, and settlement.
PDA Seeds: ["cca_bid", auctionConfig, bidId (u64 LE)]
Size: 226 bytes (218 + 8 discriminator)
| Field | Rust Type | Description |
|---|---|---|
auction_config | Pubkey | Parent AuctionConfig reference |
bid_id | u64 | Sequential bid identifier |
owner | Pubkey | Bidder's wallet address |
max_price | u128 | Maximum price willing to pay (Q96) |
amount_q96 | [u8; 32] (U256) | Effective bid amount (Q96-scaled, MPS-adjusted) |
raw_amount | u64 | Original quote currency amount deposited |
start_slot | u64 | Solana slot when bid was placed |
start_auction_block | u64 | Auction block when bid was placed |
start_cumulative_mps | u32 | Cumulative MPS at time of bid placement |
exited_slot | u64 | Solana slot when exited (0 = not exited) |
tokens_filled | u64 | Base tokens earned (set at exit, zeroed at claim) |
currency_spent | u64 | Quote currency actually spent (set at exit) |
bump | u8 | PDA bump seed |
Instructions (13 total)
initialize_auction
Creates the auction configuration, state singleton, and supply schedule.
| Property | Value |
|---|---|
| Discriminator | sha256("global:initialize_auction")[..8] |
| Access Control | Authority (signer) |
| Prerequisites | None (first instruction in lifecycle) |
Accounts:
| Account | Type | Mut | Signer | Seeds |
|---|---|---|---|---|
authority | Signer | Yes | Yes | -- |
auction_config | AuctionConfig | Yes | No | ["cca_auction", authority, auction_id LE] |
auction_state | AuctionState | Yes | No | ["cca_state", auction_config] |
supply_schedule | SupplySchedule | Yes | No | ["cca_supply", auction_config] |
system_program | System | No | No | -- |
Arguments:
| Field | Type | Description |
|---|---|---|
auction_id | u64 | Unique auction identifier |
total_supply | u64 | Total base tokens to auction |
floor_price | u128 | Minimum clearing price (Q96) |
max_bid_price | u128 | Maximum bid price allowed (Q96) |
tick_spacing | u32 | Minimum gap between tick prices |
start_slot | u64 | Auction start slot |
end_slot | u64 | Auction end slot |
claim_slot | u64 | Token claim start slot |
required_currency_raised | u128 | Graduation threshold |
auction_block_interval | u64 | Slots per auction block |
min_bid_amount | u64 | Minimum bid size |
min_new_tick_amount | u64 | Minimum bid for new tick creation |
max_ticks | u32 | Max ticks for this auction |
supply_steps | Vec<SupplyStepInput> | Supply schedule step definitions (mps: u32, block_delta: u64) |
Validation: total_supply > 0, floor_price > 0, max_bid_price > floor_price, tick_spacing >= 2, start_slot < end_slot, claim_slot >= end_slot, sum of all mps * block_delta must equal 10,000,000 (MPS).
initialize_vaults
Creates the base token vault PDA and sets mint references on AuctionConfig.
| Property | Value |
|---|---|
| Discriminator | sha256("global:initialize_vaults")[..8] |
| Access Control | Authority only (has_one = authority) |
| Prerequisites | initialize_auction |
Accounts:
| Account | Type | Mut | Signer | Seeds |
|---|---|---|---|---|
authority | Signer | Yes | Yes | -- |
auction_config | AuctionConfig | Yes | No | has_one = authority |
base_mint | Mint (Interface) | No | No | -- |
quote_mint | Mint (Interface) | No | No | -- |
vault_authority | UncheckedAccount | No | No | ["cca_vault_auth", auction_config] |
base_vault | TokenAccount (Interface) | Yes | No | ["cca_base_vault", auction_config] |
system_program | System | No | No | -- |
token_program | TokenInterface | No | No | -- |
rent | Rent | No | No | -- |
initialize_quote_vault
Creates the quote currency vault PDA.
| Property | Value |
|---|---|
| Discriminator | sha256("global:initialize_quote_vault")[..8] |
| Access Control | Authority only |
| Prerequisites | initialize_auction, initialize_vaults |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
authority | Signer | Yes | -- |
auction_config | AuctionConfig | No | has_one = authority |
quote_mint | Mint (Interface) | No | -- |
vault_authority | UncheckedAccount | No | ["cca_vault_auth", auction_config] |
quote_vault | TokenAccount (Interface) | Yes | ["cca_quote_vault", auction_config] |
system_program | System | No | -- |
token_program | TokenInterface | No | -- |
rent | Rent | No | -- |
initialize_floor_tick
Creates the floor price tick (sentinel node at the bottom of the tick linked list).
| Property | Value |
|---|---|
| Discriminator | sha256("global:initialize_floor_tick")[..8] |
| Access Control | Authority only |
| Prerequisites | initialize_auction |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
authority | Signer | Yes | -- |
auction_config | AuctionConfig | No | has_one = authority |
floor_tick | Tick | Yes | ["cca_tick", auction_config, floor_price LE] |
system_program | System | No | -- |
receive_tokens
Transfers total_supply base tokens from authority's token account to the base vault.
| Property | Value |
|---|---|
| Discriminator | sha256("global:receive_tokens")[..8] |
| Access Control | Authority only |
| Prerequisites | initialize_vaults |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
authority | Signer | Yes | -- |
auction_config | AuctionConfig | Yes | has_one = authority |
authority_token_account | TokenAccount (Interface) | Yes | -- |
base_vault | TokenAccount (Interface) | Yes | ["cca_base_vault", auction_config] |
token_program | TokenInterface | No | -- |
Errors: TokensAlreadyReceived (6118), InsufficientVaultBalance (6117).
create_checkpoint
Core clearing price discovery. Creates or resumes a checkpoint for a given auction block, walking the tick linked list to find the clearing price.
| Property | Value |
|---|---|
| Discriminator | sha256("global:create_checkpoint")[..8] |
| Access Control | Permissionless -- anyone can call |
| Prerequisites | All setup complete (initialize_*, receive_tokens) |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
payer | Signer | Yes | -- |
auction_config | AuctionConfig | No | -- |
auction_state | AuctionState | Yes | ["cca_state", auction_config] |
supply_schedule | SupplySchedule | No | ["cca_supply", auction_config] |
checkpoint | Checkpoint | Yes | ["cca_checkpoint", auction_config, auction_block LE] |
system_program | System | No | -- |
remaining_accounts: Previous checkpoint PDA (for linking) + Tick PDAs for linked-list traversal.
Arguments: auction_block: u64 -- the auction block number to checkpoint.
Checkpoints must be created in strictly contiguous order. The instruction walks up to 10 ticks per transaction call. If more ticks exist, the checkpoint enters Resolving status and must be resumed with additional calls.
place_bid
Places a new bid at a specified max price, transferring quote currency to the vault.
| Property | Value |
|---|---|
| Discriminator | sha256("global:place_bid")[..8] |
| Access Control | Any signer (permissionless) |
| Prerequisites | At least one checkpoint must exist |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
bidder | Signer | Yes | -- |
auction_config | AuctionConfig | No | -- |
auction_state | AuctionState | Yes | ["cca_state", auction_config] |
bid | Bid | Yes | ["cca_bid", auction_config, total_bids LE] |
latest_checkpoint | Checkpoint | Yes | -- |
bidder_quote_account | TokenAccount (Interface) | Yes | -- |
quote_vault | TokenAccount (Interface) | Yes | ["cca_quote_vault", auction_config] |
token_program | TokenInterface | No | -- |
system_program | System | No | -- |
remaining_accounts: Tick at max_price + previous tick PDA (for linked-list insertion).
Arguments:
| Field | Type | Description |
|---|---|---|
max_price | u128 | Maximum price bidder will pay (Q96) |
amount | u64 | Quote currency to deposit |
prev_tick_price | u128 | Price of preceding tick (insertion hint) |
Key Logic: The effective bid amount is amount_q96 = raw_amount * Q96 * MPS / mps_remaining, where mps_remaining = MPS - cumulative_mps. This adjusts for the remaining supply fraction.
exit_bid
Exits a fully-filled or completely-unfilled bid. Computes settlement and refunds unspent quote currency.
| Property | Value |
|---|---|
| Discriminator | sha256("global:exit_bid")[..8] |
| Access Control | Permissionless -- anyone can trigger |
| Prerequisites | Auction ended, end checkpoint finalized |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
signer | Signer | No | -- |
auction_config | AuctionConfig | No | -- |
auction_state | AuctionState | Yes | ["cca_state", auction_config] |
bid | Bid | Yes | has_one = auction_config |
start_checkpoint | Checkpoint | No | -- |
end_checkpoint | Checkpoint | No | -- |
quote_vault | TokenAccount (Interface) | Yes | ["cca_quote_vault", auction_config] |
bidder_quote_account | TokenAccount (Interface) | Yes | -- |
vault_authority | UncheckedAccount | No | ["cca_vault_auth", auction_config] |
token_program | TokenInterface | No | -- |
Settlement formula:
tokens_filled = amount_q96 * cumulative_mps_per_price_delta / (Q96^2 * mps_remaining)currency_spent = amount_q96 * cumulative_mps_delta / mps_remainingrefund = raw_amount - currency_spent
If the auction did not graduate, the bidder receives a full refund with zero tokens.
exit_partially_filled_bid
Exits a bid that was partially filled (at the clearing price for some period). Uses three-period settlement.
| Property | Value |
|---|---|
| Discriminator | sha256("global:exit_partially_filled_bid")[..8] |
| Access Control | Permissionless |
| Prerequisites | Auction ended, end checkpoint finalized |
Accounts:
| Account | Type | Mut | Description |
|---|---|---|---|
signer | Signer | No | -- |
auction_config | AuctionConfig | No | -- |
auction_state | AuctionState | Yes | -- |
bid | Bid | Yes | -- |
start_checkpoint | Checkpoint | No | Covers bid start |
last_fully_filled_checkpoint | Checkpoint | No | Last CP where bid was fully above clearing |
next_of_last_fully_filled_checkpoint | Checkpoint | No | Transition to marginal |
end_checkpoint | Checkpoint | No | Final checkpoint |
tick_at_max_price | Tick | No | For demand share calculation |
quote_vault | TokenAccount (Interface) | Yes | -- |
bidder_quote_account | TokenAccount (Interface) | Yes | -- |
vault_authority | UncheckedAccount | No | -- |
token_program | TokenInterface | No | -- |
Three periods:
- Fully Filled (start to last_fully_filled): same formula as
exit_bid - At Clearing Price (marginal period): pro-rata based on
bid.amount_q96 / tick.currency_demand_q96 - Outbid (if applicable): zero allocation
claim_tokens
Transfers earned base tokens from vault to bidder after bid exit.
| Property | Value |
|---|---|
| Discriminator | sha256("global:claim_tokens")[..8] |
| Access Control | Bid owner only |
| Prerequisites | Bid exited, claim_slot reached, auction graduated |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
bidder | Signer | No | -- |
auction_config | AuctionConfig | No | -- |
auction_state | AuctionState | No | ["cca_state", auction_config] |
bid | Bid | Yes | has_one = auction_config, owner = bidder |
base_vault | TokenAccount (Interface) | Yes | ["cca_base_vault", auction_config] |
bidder_base_account | TokenAccount (Interface) | Yes | -- |
vault_authority | UncheckedAccount | No | ["cca_vault_auth", auction_config] |
token_program | TokenInterface | No | -- |
For tokens launched via the Launchpad, always use TOKEN_2022_PROGRAM_ID as the token program.
set_claim_slot
Allows the authority to update the claim_slot on AuctionConfig.
| Property | Value |
|---|---|
| Discriminator | sha256("global:set_claim_slot")[..8] |
| Access Control | Authority only |
Arguments: new_claim_slot: u64 -- must be >= clock.slot and >= end_slot.
sweep_currency
Transfers all raised quote currency from the vault to the funds_recipient after a graduated auction ends.
| Property | Value |
|---|---|
| Discriminator | sha256("global:sweep_currency")[..8] |
| Access Control | Permissionless |
| Prerequisites | Auction ended, graduated, end checkpoint finalized |
Accounts:
| Account | Type | Mut | Seeds |
|---|---|---|---|
signer | Signer | No | -- |
auction_config | AuctionConfig | No | -- |
auction_state | AuctionState | Yes | ["cca_state", auction_config] |
end_checkpoint | Checkpoint | No | Must be finalized + final |
quote_vault | TokenAccount (Interface) | Yes | ["cca_quote_vault", auction_config] |
funds_recipient_account | TokenAccount (Interface) | Yes | owner = funds_recipient |
vault_authority | UncheckedAccount | No | ["cca_vault_auth", auction_config] |
token_program | TokenInterface | No | -- |
Sets currency_swept = true to prevent replay.
sweep_unsold_tokens
Transfers unsold base tokens to the tokens_recipient. If graduated, sweeps total_supply - total_cleared. If not graduated, sweeps all tokens.
| Property | Value |
|---|---|
| Discriminator | sha256("global:sweep_unsold_tokens")[..8] |
| Access Control | Permissionless |
| Prerequisites | Auction ended, end checkpoint finalized |
Sets tokens_swept = true to prevent replay.
Instruction Lifecycle
1. initialize_auction -- Authority creates config, state, supply schedule
2. initialize_vaults -- Authority creates base vault, sets mints
3. initialize_quote_vault -- Authority creates quote vault
4. initialize_floor_tick -- Authority creates floor tick (sentinel)
5. receive_tokens -- Authority deposits base tokens to vault
---- AUCTION ACTIVE (after start_slot) ----
6. create_checkpoint (ongoing) -- Anyone (keeper) creates epoch checkpoints
7. place_bid (ongoing) -- Bidders place bids
---- AUCTION ENDED (after end_slot) ----
8. create_checkpoint (final) -- Final checkpoint must be finalized
9. exit_bid / exit_partially_filled_bid -- Exit bids (settlement)
10. sweep_currency -- Anyone sweeps raised funds (graduated only)
sweep_unsold_tokens -- Anyone sweeps unsold tokens
---- CLAIM PERIOD (after claim_slot) ----
11. claim_tokens -- Bidders claim earned base tokens (graduated only)
Optional (any time after init):
set_claim_slot -- Authority updates claim slot
Key Invariants
- Clearing price monotonicity --
clearing_pricenever decreases across checkpoints - Checkpoint contiguity -- Checkpoints must be created for consecutive auction blocks
- Graduation is computed, not stored --
graduated = currency_raised_q96_x7 >= required_currency_raised - State-before-transfer --
claim_tokenszerostokens_filledbefore CPI transfer - Replay protection --
currency_sweptandtokens_sweptbooleans prevent double-sweep - Supply conservation --
sum(tokens_filled) + unsold_tokens == total_supply - Currency conservation --
sum(currency_spent) + sum(refunds) == sum(raw_amount deposited)
Discriminators
Anchor instruction discriminators are sha256("global:<snake_case_name>")[..8]. Account discriminators are sha256("account:<PascalCaseName>")[..8].
| Instruction | Discriminator Bytes |
|---|---|
place_bid | [238, 77, 148, 91, 200, 151, 92, 146] |
claim_tokens | [108, 216, 210, 231, 0, 212, 42, 64] |
exit_bid | [146, 67, 26, 184, 83, 215, 78, 14] |
exit_partially_filled_bid | [86, 66, 82, 227, 228, 7, 213, 147] |
Anchor uses the exact Rust function name in snake_case for discriminators. Using camelCase (placeBid instead of place_bid) produces a completely different discriminator and will cause the transaction to fail.
Error Codes
Configuration Errors (6000-6014)
| Code | Name | Message |
|---|---|---|
| 6000 | TotalSupplyExceedsMax | Total supply exceeds maximum |
| 6001 | FloorPriceBelowMin | Floor price below minimum |
| 6002 | TickSpacingBelowMin | Tick spacing below minimum |
| 6003 | InvalidSlotRange | Start must be before end |
| 6004 | SupplyScheduleExceedsMps | Steps sum exceeds MPS |
| 6012 | InvalidClaimSlot | Claim slot must be after end slot |
| 6013 | TooManySupplySteps | Too many supply steps (max 50) |
Bid Errors (6030-6040)
| Code | Name | Message |
|---|---|---|
| 6030 | BidAmountBelowMin | Bid amount below minimum |
| 6032 | MaxPriceExceedsLimit | Max price exceeds auction max bid price |
| 6033 | MaxPriceBelowClearing | Max price must be above clearing price |
| 6035 | MaxTicksReached | Max ticks reached for this auction |
| 6036 | BidAlreadyExited | Bid already exited |
| 6037 | BidNotExited | Bid not exited yet |
| 6039 | NoTokensToClaim | No tokens to claim |
Phase Errors (6050-6053)
| Code | Name | Message |
|---|---|---|
| 6050 | AuctionNotGraduated | Auction not graduated |
| 6051 | AuctionNotEnded | Auction not ended |
| 6052 | ClaimPeriodNotStarted | Claim period not started |
| 6053 | TokensNotReceived | Tokens not yet received |
Arithmetic Errors (6095-6099)
| Code | Name | Message |
|---|---|---|
| 6095 | ArithmeticOverflow | Arithmetic overflow |
| 6096 | ArithmeticUnderflow | Arithmetic underflow |
| 6097 | DivisionByZero | Division by zero |
| 6098 | MonotonicityViolated | Clearing price monotonicity violated |