Skip to content

Commit a6e91cf

Browse files
author
Edward (OpenClaw)
committed
test(defi/clob): replace JS tests with LiteSVM Rust, align with repo conventions
Bring the CLOB example in line with the repo's Anchor 1.0 + LiteSVM Rust test convention established by tokens/escrow and defi/asset-leasing. Previously CLOB shipped a TypeScript suite backed by Codama-generated clients and @solana/kit, which was a one-off in the current examples set. Why: - Every other defi/*/anchor and the tokens/escrow example now uses LiteSVM-driven Rust tests that `include_bytes!` the built .so, share a common test-stack (litesvm + solana-kite + solana-signer), and run under a plain `cargo test`. Contributors moving between examples had to relearn the CLOB harness (Codama client generation, surfpool-vs-legacy validator selection, tsx + node:test runner). - The JS suite also required `anchor test --validator legacy` because Anchor 1.0's default surfpool validator does not expose the websocket RPC methods Kit uses for confirmation. Switching to LiteSVM Rust sidesteps that entirely — no validator at all. Changes: - Anchor.toml: drop anchor_version, package_manager, [hooks], [test] blocks. Set `[scripts] test = "cargo test"` matching asset-leasing. Keep solana_version = 3.1.8 pinned so BPF toolchain stays in lock-step. - programs/clob/Cargo.toml: add [dev-dependencies] for litesvm 0.11, solana-signer 3.0, solana-keypair 3.0.1, solana-kite 0.3. Versions match the rest of the repo to avoid drift. - programs/clob/tests/test_clob.rs: 13 LiteSVM tests covering initialize_market (happy path + zero-tick + oversized-fee rejection), create_user_account, place_order (bid locks quote, ask locks base, zero-price / unaligned-tick / below-min rejections), cancel_order (owner refund credited to unsettled, non-owner rejected), and settle_funds (drains unsettled balance from vault to user ATA for both an ask cancel and a bid cancel + full refund round-trip). - programs/clob/src/lib.rs and instructions/*.rs: rename the verbose `*AccountConstraints` struct suffix to the plain `InitializeMarket`, `PlaceOrder`, etc. naming that every other Anchor example in the repo uses. Rename handler functions from bare `initialize_market` to `handle_initialize_market` to match escrow and asset-leasing. - README.md: replace the TS/Codama test instructions with the Rust LiteSVM flow; drop the surfpool caveat (no longer relevant). - Remove tests/clob.test.ts, package.json, tsconfig.json: no JS/TS scaffolding needed now that testing is pure Rust. - .gitignore: drop the now-unused `dist` entry (the Codama client output directory). Scope note carried into the tests: the program does not run a matching engine, it only keeps the book and escrow the funds. The test file's header comment calls this out so the reader does not look for cross-order settlement tests that cannot exist yet. Test result: `anchor build && cargo test` → 13 passed, 0 failed.
1 parent ee1c84f commit a6e91cf

14 files changed

Lines changed: 946 additions & 556 deletions

File tree

defi/clob/anchor/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ target
55
node_modules
66
test-ledger
77
.yarn
8-
dist

defi/clob/anchor/Anchor.toml

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
[toolchain]
2-
anchor_version = "1.0.0"
2+
# Pin Solana to the version used across the repo's Anchor 1.0 examples so the
3+
# bundled test validator and BPF toolchain stay in lock-step.
34
solana_version = "3.1.8"
4-
# pnpm matches the program-examples root package manager.
5-
package_manager = "pnpm"
65

76
[features]
87
resolution = true
@@ -12,23 +11,10 @@ skip-lint = false
1211
clob = "C69UJ8irfmHq5ysyLek7FKApHR86FBeupiz4JnoyPzzx"
1312

1413
[provider]
15-
cluster = "localnet"
14+
cluster = "Localnet"
1615
wallet = "~/.config/solana/id.json"
1716

1817
[scripts]
19-
# Generate the Codama TS client from the built IDL, then run node:test tests
20-
# via tsx. Run with: `anchor test --validator legacy`
21-
#
22-
# The `legacy` flag selects `solana-test-validator`, which Kit can talk to.
23-
# Anchor 1.0's default "surfpool" validator does not accept the websocket RPC
24-
# methods Kit uses for confirmation, so tests time out waiting for their
25-
# first transaction. Revisit when surfpool adds full Kit support.
26-
test = "npx create-codama-clients && npx tsx --test --test-reporter=spec tests/*.ts"
27-
28-
[hooks]
29-
30-
[test]
31-
# Give the local validator enough time to become reachable before tests start.
32-
startup_wait = 10000
33-
shutdown_wait = 2000
34-
upgradeable = false
18+
# LiteSVM Rust tests live under `programs/clob/tests/` and include the built
19+
# `.so` via `include_bytes!`, so a fresh `anchor build` must run first.
20+
test = "cargo test"

defi/clob/anchor/README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,11 @@ anchor build
3434
## Test
3535

3636
```shell
37-
anchor test --validator legacy
37+
anchor test
3838
```
3939

40-
The `--validator legacy` flag is required: Anchor 1.0's default "surfpool" validator does not yet accept the websocket RPC methods Solana Kit uses for transaction confirmation, so tests hang waiting for their first transaction. `solana-test-validator` works.
41-
42-
The test script (defined in `Anchor.toml`) first runs `npx create-codama-clients` to generate a TypeScript client from the built IDL into `dist/clob-client/`, then executes the `node:test` suite with `tsx`.
40+
Tests are pure Rust, running against [LiteSVM](https://github.com/LiteSVM/litesvm). They live in `programs/clob/tests/test_clob.rs` and include the built `.so` via `include_bytes!`, so a fresh `anchor build` must run first. `anchor test` does this automatically; alternatively run `anchor build && cargo test`.
4341

4442
## Credit
4543

46-
Ported and modernised from [anchor-decentralized-exchange-clob](https://github.com/mikemaccana/anchor-decentralized-exchange-clob). Migrated from Anchor 0.32.1 to Anchor 1.0.0 and conformed to the [Solana Anchor coding skill](https://github.com/mikemaccana/solana-anchor-claude-skill) (Kit + Kite + Codama, `node:test`, no `@coral-xyz/anchor`, no magic numbers, `Box`-ed interface accounts to keep BPF stack size within budget).
44+
Ported and modernised from [anchor-decentralized-exchange-clob](https://github.com/mikemaccana/anchor-decentralized-exchange-clob). Migrated from Anchor 0.32.1 to Anchor 1.0.0 and conformed to the repo's LiteSVM-Rust-tests convention (no magic numbers, `Box`-ed interface accounts to keep BPF stack size within budget).

defi/clob/anchor/package.json

Lines changed: 0 additions & 24 deletions
This file was deleted.

defi/clob/anchor/programs/clob/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,14 @@ custom-panic = []
2323
anchor-lang = "1.0.0"
2424
anchor-spl = "1.0.0"
2525

26+
[dev-dependencies]
27+
# Match the test stack used by tokens/escrow, defi/asset-leasing, and the
28+
# other LiteSVM-based Anchor examples so contributors can move between them
29+
# without version drift.
30+
litesvm = "0.11.0"
31+
solana-signer = "3.0.0"
32+
solana-keypair = "3.0.1"
33+
solana-kite = "0.3.0"
34+
2635
[lints.rust]
2736
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] }

defi/clob/anchor/programs/clob/src/instructions/cancel_order.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::state::{
66
OrderStatus, UserAccount, ORDER_BOOK_SEED, ORDER_SEED, USER_ACCOUNT_SEED,
77
};
88

9-
pub fn cancel_order(context: Context<CancelOrderAccountConstraints>) -> Result<()> {
9+
pub fn handle_cancel_order(context: Context<CancelOrder>) -> Result<()> {
1010
let order = &mut context.accounts.order;
1111

1212
require!(
@@ -58,7 +58,7 @@ pub fn cancel_order(context: Context<CancelOrderAccountConstraints>) -> Result<(
5858
}
5959

6060
#[derive(Accounts)]
61-
pub struct CancelOrderAccountConstraints<'info> {
61+
pub struct CancelOrder<'info> {
6262
pub market: Account<'info, Market>,
6363

6464
#[account(

defi/clob/anchor/programs/clob/src/instructions/create_user_account.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use anchor_lang::prelude::*;
22

33
use crate::state::{Market, UserAccount, USER_ACCOUNT_SEED};
44

5-
pub fn create_user_account(context: Context<CreateUserAccountAccountConstraints>) -> Result<()> {
5+
pub fn handle_create_user_account(context: Context<CreateUserAccount>) -> Result<()> {
66
let user_account = &mut context.accounts.user_account;
77
user_account.market = context.accounts.market.key();
88
user_account.owner = context.accounts.owner.key();
@@ -15,7 +15,7 @@ pub fn create_user_account(context: Context<CreateUserAccountAccountConstraints>
1515
}
1616

1717
#[derive(Accounts)]
18-
pub struct CreateUserAccountAccountConstraints<'info> {
18+
pub struct CreateUserAccount<'info> {
1919
#[account(
2020
init,
2121
payer = owner,

defi/clob/anchor/programs/clob/src/instructions/initialize_market.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use crate::state::{Market, OrderBook, MARKET_SEED, ORDER_BOOK_SEED};
88
// would be nonsensical, so we cap here.
99
const MAX_FEE_BASIS_POINTS: u16 = 10_000;
1010

11-
pub fn initialize_market(
12-
context: Context<InitializeMarketAccountConstraints>,
11+
pub fn handle_initialize_market(
12+
context: Context<InitializeMarket>,
1313
fee_basis_points: u16,
1414
tick_size: u64,
1515
min_order_size: u64,
@@ -46,7 +46,7 @@ pub fn initialize_market(
4646
}
4747

4848
#[derive(Accounts)]
49-
pub struct InitializeMarketAccountConstraints<'info> {
49+
pub struct InitializeMarket<'info> {
5050
#[account(
5151
init,
5252
payer = authority,

defi/clob/anchor/programs/clob/src/instructions/place_order.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use crate::state::{
1313
// PlaceOrder check reads clearly and the limit is documented in one place.
1414
const MAX_OPEN_ORDERS_PER_USER: usize = 20;
1515

16-
pub fn place_order(
17-
context: Context<PlaceOrderAccountConstraints>,
16+
pub fn handle_place_order(
17+
context: Context<PlaceOrder>,
1818
side: OrderSide,
1919
price: u64,
2020
quantity: u64,
@@ -105,7 +105,7 @@ pub fn place_order(
105105

106106
#[derive(Accounts)]
107107
#[instruction(side: OrderSide, price: u64, quantity: u64)]
108-
pub struct PlaceOrderAccountConstraints<'info> {
108+
pub struct PlaceOrder<'info> {
109109
#[account(mut)]
110110
pub market: Account<'info, Market>,
111111

defi/clob/anchor/programs/clob/src/instructions/settle_funds.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use anchor_spl::token_interface::{
55

66
use crate::state::{Market, UserAccount, MARKET_SEED, USER_ACCOUNT_SEED};
77

8-
pub fn settle_funds(context: Context<SettleFundsAccountConstraints>) -> Result<()> {
8+
pub fn handle_settle_funds(context: Context<SettleFunds>) -> Result<()> {
99
let user_account = &mut context.accounts.user_account;
1010
let market = &context.accounts.market;
1111

@@ -63,7 +63,7 @@ pub fn settle_funds(context: Context<SettleFundsAccountConstraints>) -> Result<(
6363
}
6464

6565
#[derive(Accounts)]
66-
pub struct SettleFundsAccountConstraints<'info> {
66+
pub struct SettleFunds<'info> {
6767
#[account(mut)]
6868
pub market: Account<'info, Market>,
6969

@@ -74,7 +74,7 @@ pub struct SettleFundsAccountConstraints<'info> {
7474
)]
7575
pub user_account: Account<'info, UserAccount>,
7676

77-
// Boxed for the same reason as in PlaceOrderAccountConstraints
77+
// Boxed for the same reason as in PlaceOrder
7878
// InterfaceAccount is too large to keep on the BPF stack in bulk.
7979
#[account(mut)]
8080
pub base_vault: Box<InterfaceAccount<'info, TokenAccount>>,

0 commit comments

Comments
 (0)