Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jsonschema = "0.30"
litesvm = "0.15"
litesvm-token = "0.15"
num_enum = "0.7"
pastey = "0.2"
pinocchio = "0.11"
pinocchio-system = "0.6"
pinocchio-token = "0.7"
Expand Down
21 changes: 21 additions & 0 deletions bench-report.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@
"add_solver/add_with_many_existing_solvers": 5,
"add_solver/adds_a_solver": 5,
"create_buffers/happy_path_creates_initialized_buffer_token_account": 6,
"create_buffers/happy_path_creates_initialized_buffer_token_account_token_2022": 6,
"create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 10,
"create_buffers/happy_path_creates_multiple_buffers_in_one_instruction_token_2022": 10,
"create_buffers/max_buffers_in_one_instruction": 64,
"create_buffers/max_buffers_in_one_instruction_token_2022": 46,
"create_order/happy_path_creates_order_pda_with_expected_body": 4,
"initialize/happy_path_initializes_state_pda_with_expected_data": 4,
"reclaim_buffer/funded_buffer_is_skipped": 7,
"reclaim_buffer/funded_buffer_is_skipped_token_2022": 7,
"reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 7,
"reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself_token_2022": 7,
"reclaim_buffer/max_buffers_in_one_instruction": 64,
"reclaim_buffer/max_buffers_in_one_instruction_token_2022": 64,
"reclaim_buffer/reclaims_multiple_buffers_skipping_funded": 9,
"reclaim_buffer/reclaims_multiple_buffers_skipping_funded_token_2022": 9,
"reclaim_order/happy_path_expired_returns_lamports_and_closes_pda": 4,
"reclaim_order/happy_path_on_chain_order_cancelled_is_reclaimable_before_expiry": 3,
"reclaim_order/happy_path_on_chain_order_fully_filled_is_reclaimable_before_expiry": 3,
Expand All @@ -35,14 +42,21 @@
"add_solver/add_with_many_existing_solvers": 5074,
"add_solver/adds_a_solver": 4622,
"create_buffers/happy_path_creates_initialized_buffer_token_account": 7373,
"create_buffers/happy_path_creates_initialized_buffer_token_account_token_2022": 12188,
"create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 17283,
"create_buffers/happy_path_creates_multiple_buffers_in_one_instruction_token_2022": 31720,
"create_buffers/max_buffers_in_one_instruction": 169742,
"create_buffers/max_buffers_in_one_instruction_token_2022": 214956,
"create_order/happy_path_creates_order_pda_with_expected_body": 4986,
"initialize/happy_path_initializes_state_pda_with_expected_data": 4530,
"reclaim_buffer/funded_buffer_is_skipped": 4860,
"reclaim_buffer/funded_buffer_is_skipped_token_2022": 4868,
"reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 6010,
"reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself_token_2022": 7499,
"reclaim_buffer/max_buffers_in_one_instruction": 124824,
"reclaim_buffer/max_buffers_in_one_instruction_token_2022": 169378,
"reclaim_buffer/reclaims_multiple_buffers_skipping_funded": 7612,
"reclaim_buffer/reclaims_multiple_buffers_skipping_funded_token_2022": 9105,
"reclaim_order/happy_path_expired_returns_lamports_and_closes_pda": 2203,
"reclaim_order/happy_path_on_chain_order_cancelled_is_reclaimable_before_expiry": 2072,
"reclaim_order/happy_path_on_chain_order_fully_filled_is_reclaimable_before_expiry": 2080,
Expand All @@ -67,14 +81,21 @@
"add_solver/add_with_many_existing_solvers": 366,
"add_solver/adds_a_solver": 366,
"create_buffers/happy_path_creates_initialized_buffer_token_account": 303,
"create_buffers/happy_path_creates_initialized_buffer_token_account_token_2022": 303,
"create_buffers/happy_path_creates_multiple_buffers_in_one_instruction": 435,
"create_buffers/happy_path_creates_multiple_buffers_in_one_instruction_token_2022": 435,
"create_buffers/max_buffers_in_one_instruction": 331,
"create_buffers/max_buffers_in_one_instruction_token_2022": 295,
"create_order/happy_path_creates_order_pda_with_expected_body": 452,
"initialize/happy_path_initializes_state_pda_with_expected_data": 301,
"reclaim_buffer/funded_buffer_is_skipped": 400,
"reclaim_buffer/funded_buffer_is_skipped_token_2022": 400,
"reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself": 400,
"reclaim_buffer/happy_path_reclaims_empty_buffer_to_the_authority_itself_token_2022": 400,
"reclaim_buffer/max_buffers_in_one_instruction": 332,
"reclaim_buffer/max_buffers_in_one_instruction_token_2022": 332,
"reclaim_buffer/reclaims_multiple_buffers_skipping_funded": 466,
"reclaim_buffer/reclaims_multiple_buffers_skipping_funded_token_2022": 466,
"reclaim_order/happy_path_expired_returns_lamports_and_closes_pda": 236,
"reclaim_order/happy_path_on_chain_order_cancelled_is_reclaimable_before_expiry": 204,
"reclaim_order/happy_path_on_chain_order_fully_filled_is_reclaimable_before_expiry": 204,
Expand Down
2 changes: 2 additions & 0 deletions programs/settlement/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jsonschema.workspace = true
bincode.workspace = true
litesvm.workspace = true
litesvm-token.workspace = true
pastey.workspace = true
proptest.workspace = true
serde_json.workspace = true
cow-settlement-client.workspace = true
Expand All @@ -42,6 +43,7 @@ solana-program-pack.workspace = true
solana-sdk.workspace = true
solana-sha256-hasher.workspace = true
solana-system-interface.workspace = true
spl-associated-token-account-interface.workspace = true
spl-token-2022-interface.workspace = true
syn.workspace = true

Expand Down
2 changes: 1 addition & 1 deletion programs/settlement/src/create_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ mod tests {
}

#[test]
fn process_create_buffer_rejects_wrong_token_program() {
fn process_create_buffer_rejects_unsupported_token_program() {
let data = create_buffer_data();
// The three shared accounts plus one (buffer_pda, mint) pair so parsing
// succeeds and reaches the token-program check. The third account (token
Expand Down
2 changes: 1 addition & 1 deletion programs/settlement/src/reclaim_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ mod tests {
}

#[test]
fn process_reclaim_buffer_rejects_wrong_token_program() {
fn process_reclaim_buffer_rejects_unsupported_token_program() {
let mut accounts = base_accounts();
accounts[TOKEN_PROGRAM] = fake_account(UNRELATED);
assert_rejects(accounts, ProgramError::IncorrectProgramId);
Expand Down
18 changes: 6 additions & 12 deletions programs/settlement/tests/common/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,17 @@ pub fn ensure_buffer_exists(
program_id: &Pubkey,
payer: &Keypair,
mint: &Pubkey,
) -> Pubkey {
ensure_buffer_exists_for(svm, program_id, payer, mint, TokenProgram::SplToken)
}

/// [`ensure_buffer_exists`] under a token program of the caller's choosing, for
/// the tests that need a buffer belonging to Token-2022.
pub fn ensure_buffer_exists_for(
svm: &mut LiteSVM,
program_id: &Pubkey,
payer: &Keypair,
mint: &Pubkey,
token_program: TokenProgram,
) -> Pubkey {
let pda = buffer_pda(program_id, mint);
if svm.get_account(&pda).is_some() {
return pda;
}
// A buffer is a token account of its mint, so it has to be created under the
// mint's own program. Read off the mint rather than left to
// `super::aim_at_active_token_program`, because a test may build buffers
// under both programs at once.
let token_program = TokenProgram::try_from(&token::program_of(svm, mint))
.expect("the mint lives under a supported token program");
let ix = Instruction::from(CreateBuffers {
program_id: *program_id,
payer: payer.pubkey(),
Expand Down
70 changes: 64 additions & 6 deletions programs/settlement/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,6 @@ pub const PROGRAM_SO: &str = concat!(
"/../../target/deploy/cow_settlement.so"
);

/// The legacy SPL Token program, which the tests create their buffers and
/// token accounts under unless they exercise Token-2022 specifically.
pub const SPL_TOKEN_PROGRAM_ID: Pubkey = TokenProgram::SplToken.address();

pub const CPI_CALLER_SO: &str = concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../target/deploy/test_cpi_caller.so"
Expand Down Expand Up @@ -265,8 +261,10 @@ pub fn signed_tx(
owner: &Keypair,
ix: impl Into<Instruction>,
) -> Transaction {
let mut instructions = [ix.into()];
aim_at_active_token_program(&mut instructions);
Transaction::new_signed_with_payer(
&[ix.into()],
&instructions,
Some(&fee_payer.pubkey()),
&[fee_payer, owner],
svm.latest_blockhash(),
Expand All @@ -293,8 +291,9 @@ pub fn replace_first_matching_account(instruction: &mut Instruction, from: &Pubk
pub fn payer_signed_tx(
svm: &LiteSVM,
payer: &Keypair,
instructions: Vec<Instruction>,
mut instructions: Vec<Instruction>,
) -> Transaction {
aim_at_active_token_program(&mut instructions);
Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
Expand All @@ -303,6 +302,65 @@ pub fn payer_signed_tx(
)
}

/// Repoint every legacy-SPL-Token account of `instructions` at
/// [`token::active`], so a test written against the legacy program submits the
/// same transaction aimed at whichever program it is being run under.
fn aim_at_active_token_program(instructions: &mut [Instruction]) {
let active = token::active();
if active == TokenProgram::SplToken {
return;
}
for account in instructions
.iter_mut()
.flat_map(|instruction| &mut instruction.accounts)
{
if account.pubkey == TokenProgram::SplToken.address() {
account.pubkey = active.address();
}
}
}

/// Also run `$test` against Token-2022, as `<test>_token_2022`.
///
/// Written in front of the test it applies to:
///
/// ```ignore
/// common::also_under_token_2022!(settles_a_single_order);
/// #[test]
/// fn settles_a_single_order() { .. }
/// ```
///
/// The test keeps its own `#[test]`, so it runs twice: once under the legacy SPL
/// Token program, which is what [`token::active`] reports by default, and once
/// under Token-2022. Nothing in the body changes — the token helpers and
/// [`payer_signed_tx`] follow the active program on their own. Naming the test
/// rather than wrapping it keeps the body's indentation, and a stale name is a
/// compile error rather than a test that quietly stopped being generated.
#[allow(
unused_macros,
reason = "only the suites whose instructions name a token program generate the pair"
)]
macro_rules! also_under_token_2022 {
($($test:ident),+ $(,)?) => {
$(
pastey::paste! {
#[test]
fn [<$test _token_2022>]() {
$crate::common::token::under_token_program(
cow_settlement_interface::token_program::TokenProgram::Token2022,
$test,
);
}
}
)+
};
}
#[allow(
unused_imports,
reason = "re-exported for the suites that use the macro; the others never name it"
)]
pub(crate) use also_under_token_2022;

/// Assemble `instructions` into a transaction signed by `payer` and submit it,
/// surfacing only the transaction-level error on failure (dropping the success
/// metadata's error wrapper).
Expand Down
Loading