From d0f182db201415a3179e0c1e8877ee16cdda963a Mon Sep 17 00:00:00 2001 From: guibescos <59208140+guibescos@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:06:59 +0100 Subject: [PATCH] test: add fees overflow test (#329) * add fee overflow test * clippy * rename arg --- contracts/svm/testing/src/helpers.rs | 19 ++-- contracts/svm/testing/tests/initialize.rs | 17 +++- contracts/svm/testing/tests/set_admin.rs | 7 +- .../svm/testing/tests/set_protocol_split.rs | 9 +- contracts/svm/testing/tests/set_relayer.rs | 7 +- contracts/svm/testing/tests/set_splits.rs | 11 ++- .../testing/tests/set_swap_platform_fee.rs | 9 +- contracts/svm/testing/tests/submit_bid.rs | 61 +++++++++--- contracts/svm/testing/tests/swap.rs | 97 +++++++++++++++++-- contracts/svm/testing/tests/withdraw_fees.rs | 7 +- 10 files changed, 201 insertions(+), 43 deletions(-) diff --git a/contracts/svm/testing/src/helpers.rs b/contracts/svm/testing/src/helpers.rs index cf14de5d..5711c882 100644 --- a/contracts/svm/testing/src/helpers.rs +++ b/contracts/svm/testing/src/helpers.rs @@ -3,7 +3,7 @@ use { solana_sdk::{ instruction::{ Instruction, - InstructionError::Custom, + InstructionError, }, native_token::LAMPORTS_PER_SOL, pubkey::Pubkey, @@ -14,12 +14,10 @@ use { Transaction, TransactionError::{ self, - InstructionError, }, }, }, }; - pub const TX_FEE: u64 = 10_000; // TODO: make this programmatic? FeeStructure is currently private field within LiteSVM #[allow(clippy::result_large_err)] @@ -56,16 +54,15 @@ pub fn warp_to_unix(svm: &mut litesvm::LiteSVM, unix_timestamp: i64) { svm.set_sysvar(&clock); } -pub fn assert_custom_error(error: TransactionError, instruction_index: u8, custom_error: u32) { +pub fn assert_custom_error( + error: TransactionError, + instruction_index: u8, + instruction_error: InstructionError, +) { match error { - InstructionError(index, error_variant) => { + TransactionError::InstructionError(index, error_variant) => { assert_eq!(index, instruction_index); - match error_variant { - Custom(code) => { - assert_eq!(code, custom_error); - } - _ => panic!("Unexpected error code"), - } + assert_eq!(error_variant, instruction_error); } _ => panic!("Unexpected error variant"), } diff --git a/contracts/svm/testing/tests/initialize.rs b/contracts/svm/testing/tests/initialize.rs index 2dfc5b19..82240d96 100644 --- a/contracts/svm/testing/tests/initialize.rs +++ b/contracts/svm/testing/tests/initialize.rs @@ -3,7 +3,10 @@ use { error::ErrorCode, state::FEE_SPLIT_PRECISION, }, - solana_sdk::signer::Signer, + solana_sdk::{ + instruction::InstructionError, + signer::Signer, + }, testing::{ express_relay::helpers::get_express_relay_metadata, helpers::assert_custom_error, @@ -55,7 +58,11 @@ fn test_initialize_fail_high_split_router() { match setup_result { Ok(_) => panic!("expected setup to fail"), - Err(err) => assert_custom_error(err, 0, ErrorCode::FeeSplitLargerThanPrecision.into()), + Err(err) => assert_custom_error( + err, + 0, + InstructionError::Custom(ErrorCode::FeeSplitLargerThanPrecision.into()), + ), } } @@ -72,6 +79,10 @@ fn test_initialize_fail_high_split_relayer() { match setup_result { Ok(_) => panic!("expected setup to fail"), - Err(err) => assert_custom_error(err, 0, ErrorCode::FeeSplitLargerThanPrecision.into()), + Err(err) => assert_custom_error( + err, + 0, + InstructionError::Custom(ErrorCode::FeeSplitLargerThanPrecision.into()), + ), } } diff --git a/contracts/svm/testing/tests/set_admin.rs b/contracts/svm/testing/tests/set_admin.rs index 5daf6e54..f71001fb 100644 --- a/contracts/svm/testing/tests/set_admin.rs +++ b/contracts/svm/testing/tests/set_admin.rs @@ -1,6 +1,7 @@ use { anchor_lang::error::ErrorCode as AnchorErrorCode, solana_sdk::{ + instruction::InstructionError, signature::Keypair, signer::Signer, }, @@ -47,5 +48,9 @@ fn test_set_admin_fail_wrong_admin() { let tx_result = submit_transaction(&mut svm, &[set_admin_ix], &wrong_admin, &[&wrong_admin]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); } diff --git a/contracts/svm/testing/tests/set_protocol_split.rs b/contracts/svm/testing/tests/set_protocol_split.rs index 0e83fdf1..fffd5496 100644 --- a/contracts/svm/testing/tests/set_protocol_split.rs +++ b/contracts/svm/testing/tests/set_protocol_split.rs @@ -5,6 +5,7 @@ use { state::FEE_SPLIT_PRECISION, }, solana_sdk::{ + instruction::InstructionError, signature::Keypair, signer::Signer, }, @@ -59,7 +60,11 @@ fn test_set_router_split_fail_wrong_admin() { ) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); } #[test] @@ -78,6 +83,6 @@ fn test_set_router_split_fail_high_split() { assert_custom_error( tx_result.err, 0, - ErrorCode::FeeSplitLargerThanPrecision.into(), + InstructionError::Custom(ErrorCode::FeeSplitLargerThanPrecision.into()), ); } diff --git a/contracts/svm/testing/tests/set_relayer.rs b/contracts/svm/testing/tests/set_relayer.rs index 49a8aa61..05992904 100644 --- a/contracts/svm/testing/tests/set_relayer.rs +++ b/contracts/svm/testing/tests/set_relayer.rs @@ -1,6 +1,7 @@ use { anchor_lang::error::ErrorCode as AnchorErrorCode, solana_sdk::{ + instruction::InstructionError, signature::Keypair, signer::Signer, }, @@ -55,5 +56,9 @@ fn test_set_relayer_fail_wrong_admin() { let tx_result = submit_transaction(&mut svm, &[set_relayer_ix], &wrong_admin, &[&wrong_admin]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); } diff --git a/contracts/svm/testing/tests/set_splits.rs b/contracts/svm/testing/tests/set_splits.rs index 6eb7cc94..7ec0d695 100644 --- a/contracts/svm/testing/tests/set_splits.rs +++ b/contracts/svm/testing/tests/set_splits.rs @@ -4,6 +4,7 @@ use { error::ErrorCode, state::FEE_SPLIT_PRECISION, }, + solana_sdk::instruction::InstructionError, testing::{ express_relay::{ helpers::get_express_relay_metadata, @@ -54,7 +55,11 @@ fn test_set_splits_fail_wrong_admin() { let tx_result = submit_transaction(&mut svm, &[set_splits_ix], &wrong_admin, &[&wrong_admin]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); } #[test] @@ -73,7 +78,7 @@ fn test_set_splits_fail_high_split_router() { assert_custom_error( tx_result.err, 0, - ErrorCode::FeeSplitLargerThanPrecision.into(), + InstructionError::Custom(ErrorCode::FeeSplitLargerThanPrecision.into()), ); } @@ -93,6 +98,6 @@ fn test_set_splits_fail_high_split_relayer() { assert_custom_error( tx_result.err, 0, - ErrorCode::FeeSplitLargerThanPrecision.into(), + InstructionError::Custom(ErrorCode::FeeSplitLargerThanPrecision.into()), ); } diff --git a/contracts/svm/testing/tests/set_swap_platform_fee.rs b/contracts/svm/testing/tests/set_swap_platform_fee.rs index 2d056a6c..6760ee3a 100644 --- a/contracts/svm/testing/tests/set_swap_platform_fee.rs +++ b/contracts/svm/testing/tests/set_swap_platform_fee.rs @@ -5,6 +5,7 @@ use { state::FEE_SPLIT_PRECISION, }, litesvm::LiteSVM, + solana_sdk::instruction::InstructionError, testing::{ express_relay::{ helpers::get_express_relay_metadata, @@ -63,7 +64,11 @@ fn test_set_swap_platform_fee_wrong_admin() { ) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); } #[test] @@ -78,6 +83,6 @@ fn test_set_swap_platform_fee_fail_high_split_router() { assert_custom_error( tx_result.err, 0, - ErrorCode::FeeSplitLargerThanPrecision.into(), + InstructionError::Custom(ErrorCode::FeeSplitLargerThanPrecision.into()), ); } diff --git a/contracts/svm/testing/tests/submit_bid.rs b/contracts/svm/testing/tests/submit_bid.rs index 226bec90..112111cd 100644 --- a/contracts/svm/testing/tests/submit_bid.rs +++ b/contracts/svm/testing/tests/submit_bid.rs @@ -6,7 +6,10 @@ use { state::FEE_SPLIT_PRECISION, }, solana_sdk::{ - instruction::Instruction, + instruction::{ + Instruction, + InstructionError, + }, native_token::LAMPORTS_PER_SOL, pubkey::Pubkey, rent::Rent, @@ -175,7 +178,11 @@ fn test_bid_fail_wrong_relayer_signer() { ) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); } #[test] @@ -209,7 +216,11 @@ fn test_bid_fail_wrong_relayer_fee_receiver() { submit_transaction(&mut svm, &bid_ixs, &searcher, &[&searcher, &relayer_signer]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); } #[test] @@ -247,7 +258,7 @@ fn test_bid_fail_insufficient_searcher_rent() { assert_custom_error( tx_result.err, 0, - ErrorCode::InsufficientSearcherFunds.into(), + InstructionError::Custom(ErrorCode::InsufficientSearcherFunds.into()), ); } @@ -282,7 +293,11 @@ fn test_bid_fail_insufficient_router_rent() { submit_transaction(&mut svm, &bid_ixs, &searcher, &[&searcher, &relayer_signer]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, ErrorCode::InsufficientRent.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(ErrorCode::InsufficientRent.into()), + ); } #[test] @@ -329,7 +344,11 @@ fn test_bid_fail_insufficient_relayer_fee_receiver_rent() { submit_transaction(&mut svm, &bid_ixs, &searcher, &[&searcher, &relayer_signer]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, ErrorCode::InsufficientRent.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(ErrorCode::InsufficientRent.into()), + ); } #[test] @@ -363,7 +382,11 @@ fn test_bid_fail_passed_deadline() { submit_transaction(&mut svm, &bid_ixs, &searcher, &[&searcher, &relayer_signer]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, ErrorCode::DeadlinePassed.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(ErrorCode::DeadlinePassed.into()), + ); } #[test] @@ -397,7 +420,11 @@ fn test_bid_fail_wrong_permission_key() { submit_transaction(&mut svm, &bid_ixs, &searcher, &[&searcher, &relayer_signer]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 1, ErrorCode::MissingPermission.into()); + assert_custom_error( + tx_result.err, + 1, + InstructionError::Custom(ErrorCode::MissingPermission.into()), + ); } #[test] @@ -431,7 +458,11 @@ fn test_bid_fail_wrong_router_key() { submit_transaction(&mut svm, &bid_ixs, &searcher, &[&searcher, &relayer_signer]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 1, ErrorCode::MissingPermission.into()); + assert_custom_error( + tx_result.err, + 1, + InstructionError::Custom(ErrorCode::MissingPermission.into()), + ); } #[test] @@ -451,7 +482,11 @@ fn test_bid_fail_no_permission() { let tx_result = submit_transaction(&mut svm, &ixs, &searcher, &[&searcher]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, ErrorCode::MissingPermission.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(ErrorCode::MissingPermission.into()), + ); } #[test] @@ -494,5 +529,9 @@ fn test_bid_fail_duplicate_permission() { submit_transaction(&mut svm, &bid_ixs, &searcher, &[&searcher, &relayer_signer]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, ErrorCode::MultiplePermissions.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(ErrorCode::MultiplePermissions.into()), + ); } diff --git a/contracts/svm/testing/tests/swap.rs b/contracts/svm/testing/tests/swap.rs index 3c7dc0b7..d8d02bc7 100644 --- a/contracts/svm/testing/tests/swap.rs +++ b/contracts/svm/testing/tests/swap.rs @@ -26,6 +26,7 @@ use { litesvm::LiteSVM, solana_sdk::{ clock::Clock, + instruction::InstructionError, program_pack::Pack, pubkey::Pubkey, signature::{ @@ -618,7 +619,11 @@ fn test_swap_expired_deadline() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, ErrorCode::DeadlinePassed.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(ErrorCode::DeadlinePassed.into()), + ); } #[test] @@ -666,7 +671,59 @@ fn test_swap_invalid_referral_fee_bps() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, ErrorCode::InvalidReferralFee.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(ErrorCode::InvalidReferralFee.into()), + ); +} + +#[test] +fn test_swap_fee_calculation_overflow() { + let SwapSetupResult { + mut svm, + trader, + searcher, + input_token, + output_token, + router_output_ta, + .. + } = setup_swap(SwapSetupParams { + platform_fee_bps: 5000, // <--- high platform fee bps + input_token_program: spl_token::ID, + input_token_decimals: 6, + output_token_program: spl_token::ID, + output_token_decimals: 6, + }); + + let express_relay_metadata = get_express_relay_metadata(&mut svm); + + let swap_args = SwapArgs { + deadline: svm.get_sysvar::().unix_timestamp, + amount_input: input_token.get_amount_with_decimals(1.), + amount_output: output_token.get_amount_with_decimals(1.), + referral_fee_bps: 5001, // <--- referral fee bps + platform fee bps is more than 100% + fee_token: FeeToken::Output, + }; + + let instructions = build_swap_instructions( + searcher.pubkey(), + trader.pubkey(), + None, + None, + router_output_ta, + express_relay_metadata.fee_receiver_relayer, + input_token.mint, + output_token.mint, + Some(input_token.token_program), + Some(output_token.token_program), + swap_args, + None, + None, + ); + let result = + submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); + assert_custom_error(result.err, 4, InstructionError::ArithmeticOverflow); } #[test] @@ -714,7 +771,11 @@ fn test_swap_router_ta_has_wrong_mint() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, AnchorErrorCode::ConstraintTokenMint.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(AnchorErrorCode::ConstraintTokenMint.into()), + ); } #[test] @@ -765,7 +826,11 @@ fn test_swap_searcher_ta_wrong_mint() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, AnchorErrorCode::ConstraintTokenMint.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(AnchorErrorCode::ConstraintTokenMint.into()), + ); } #[test] @@ -813,7 +878,11 @@ fn test_swap_searcher_ta_wrong_owner() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, AnchorErrorCode::ConstraintTokenOwner.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(AnchorErrorCode::ConstraintTokenOwner.into()), + ); } #[test] @@ -859,7 +928,11 @@ fn test_swap_wrong_express_relay_fee_receiver() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, AnchorErrorCode::ConstraintTokenOwner.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(AnchorErrorCode::ConstraintTokenOwner.into()), + ); } #[test] @@ -908,7 +981,11 @@ fn test_swap_trader_output_ata_is_not_ata() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, AnchorErrorCode::ConstraintAssociated.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(AnchorErrorCode::ConstraintAssociated.into()), + ); } #[test] @@ -956,5 +1033,9 @@ fn test_swap_wrong_mint_fee() { ); let result = submit_transaction(&mut svm, &instructions, &searcher, &[&searcher, &trader]).unwrap_err(); - assert_custom_error(result.err, 4, AnchorErrorCode::ConstraintRaw.into()); + assert_custom_error( + result.err, + 4, + InstructionError::Custom(AnchorErrorCode::ConstraintRaw.into()), + ); } diff --git a/contracts/svm/testing/tests/withdraw_fees.rs b/contracts/svm/testing/tests/withdraw_fees.rs index d5dd43fc..da4fa983 100644 --- a/contracts/svm/testing/tests/withdraw_fees.rs +++ b/contracts/svm/testing/tests/withdraw_fees.rs @@ -2,6 +2,7 @@ use { anchor_lang::error::ErrorCode as AnchorErrorCode, express_relay::state::RESERVE_EXPRESS_RELAY_METADATA, solana_sdk::{ + instruction::InstructionError, native_token::LAMPORTS_PER_SOL, signature::Keypair, signer::Signer, @@ -71,5 +72,9 @@ fn test_withdraw_fees_fail_wrong_admin() { submit_transaction(&mut svm, &[withdraw_fees_ix], &wrong_admin, &[&wrong_admin]) .expect_err("Transaction should have failed"); - assert_custom_error(tx_result.err, 0, AnchorErrorCode::ConstraintHasOne.into()); + assert_custom_error( + tx_result.err, + 0, + InstructionError::Custom(AnchorErrorCode::ConstraintHasOne.into()), + ); }