From 2681c25a05d82074f7b25cd42f145832c2c42140 Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Tue, 28 Dec 2021 09:31:09 -0800 Subject: [PATCH 01/10] Migrate assertions to Vipers --- Cargo.lock | 13 +++++++++++ stable-swap-program/program/Cargo.toml | 2 ++ .../program/src/processor/checks.rs | 22 +++++++++---------- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c9677607..708e8f4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1723,11 +1723,13 @@ dependencies = [ name = "stable-swap" version = "1.6.0" dependencies = [ + "anchor-lang", "solana-program", "solana-sdk", "spl-token", "stable-swap-client", "stable-swap-math", + "vipers", ] [[package]] @@ -1935,6 +1937,17 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "vipers" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "344716a0a37af7570a1402541e48b42a09def1d453c031aefcd05944d2c4488c" +dependencies = [ + "anchor-lang", + "anchor-spl", + "spl-associated-token-account", +] + [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/stable-swap-program/program/Cargo.toml b/stable-swap-program/program/Cargo.toml index 2932cc73..b54bf965 100644 --- a/stable-swap-program/program/Cargo.toml +++ b/stable-swap-program/program/Cargo.toml @@ -15,10 +15,12 @@ no-entrypoint = [] fuzz = ["stable-swap-client/fuzz"] [dependencies] +anchor-lang = ">=0.17" solana-program = "^1.6.10" spl-token = { version = "3.1.1", features = ["no-entrypoint"] } stable-swap-client = { path = "../../stable-swap-client", version = "^1" } stable-swap-math = { path = "../../stable-swap-math", version = "^1" } +vipers = "^1.5.5" [dev-dependencies] solana-sdk = "^1.6.10" diff --git a/stable-swap-program/program/src/processor/checks.rs b/stable-swap-program/program/src/processor/checks.rs index e1ffb482..d74e4f9f 100644 --- a/stable-swap-program/program/src/processor/checks.rs +++ b/stable-swap-program/program/src/processor/checks.rs @@ -5,11 +5,13 @@ use crate::{ processor::utils, state::{SwapInfo, SwapTokenInfo}, }; +use anchor_lang::prelude::*; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, }; +use vipers::assert_keys_eq; use super::logging::log_slippage_error; @@ -78,10 +80,9 @@ pub fn check_withdraw_token_accounts( admin_fee_dest_key: &Pubkey, ) -> ProgramResult { check_reserves_match(token, reserves_info_key)?; - check_keys_equal!( - *admin_fee_dest_key, + assert_keys_eq!( + admin_fee_dest_key, token.admin_fees, - "Admin fee dest", SwapError::InvalidAdmin ); Ok(()) @@ -94,10 +95,9 @@ pub fn check_swap_authority( swap_authority_key: &Pubkey, ) -> ProgramResult { let swap_authority = utils::authority_id(program_id, swap_info_key, token_swap.nonce)?; - check_keys_equal!( - *swap_authority_key, + assert_keys_eq!( + swap_authority_key, swap_authority, - "Swap authority", SwapError::InvalidProgramAddress ); Ok(()) @@ -109,16 +109,14 @@ pub fn check_swap_token_destination_accounts( swap_destination_info_key: &Pubkey, admin_destination_info_key: &Pubkey, ) -> ProgramResult { - check_keys_equal!( - *swap_destination_info_key, + assert_keys_eq!( + swap_destination_info_key, token.reserves, - "Incorrect destination, expected", SwapError::IncorrectSwapAccount ); - check_keys_equal!( - *admin_destination_info_key, + assert_keys_eq!( + admin_destination_info_key, token.admin_fees, - "Admin fee", SwapError::InvalidAdmin ); Ok(()) From 34e1f267ea868afea4eb090e3e3cff91f2698f96 Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Tue, 28 Dec 2021 09:46:48 -0800 Subject: [PATCH 02/10] migrate more stuff to vipers --- .../program/src/processor/checks.rs | 77 +++++++++++++------ .../program/src/processor/macros.rs | 26 ------- .../program/src/processor/swap.rs | 22 +++--- 3 files changed, 65 insertions(+), 60 deletions(-) diff --git a/stable-swap-program/program/src/processor/checks.rs b/stable-swap-program/program/src/processor/checks.rs index d74e4f9f..92e91baa 100644 --- a/stable-swap-program/program/src/processor/checks.rs +++ b/stable-swap-program/program/src/processor/checks.rs @@ -1,29 +1,42 @@ //! Checks for processing instructions. +use anchor_lang::prelude::*; + +use super::logging::log_slippage_error; use crate::{ error::SwapError, processor::utils, state::{SwapInfo, SwapTokenInfo}, }; -use anchor_lang::prelude::*; - use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, }; -use vipers::assert_keys_eq; - -use super::logging::log_slippage_error; +use vipers::{assert_keys_eq, assert_keys_neq}; /// Checks if the reserve of the swap is the given key. fn check_reserves_match(token: &SwapTokenInfo, reserves_info_key: &Pubkey) -> ProgramResult { - check_token_keys_equal!( - token, - *reserves_info_key, - token.reserves, - "Reserves", - SwapError::IncorrectSwapAccount - ); + if token.index == 0 { + assert_keys_eq!( + reserves_info_key, + token.reserves, + SwapError::IncorrectSwapAccount, + "reserves A" + ); + } else if token.index == 1 { + assert_keys_eq!( + reserves_info_key, + token.reserves, + SwapError::IncorrectSwapAccount, + "reserves B" + ); + } else { + assert_keys_eq!( + reserves_info_key, + token.reserves, + SwapError::IncorrectSwapAccount + ); + } Ok(()) } @@ -32,10 +45,9 @@ pub fn check_has_admin_signer( expected_admin_key: &Pubkey, admin_account_info: &AccountInfo, ) -> ProgramResult { - check_keys_equal!( - *expected_admin_key, - *admin_account_info.key, - "Admin signer", + assert_keys_eq!( + expected_admin_key, + admin_account_info.key, SwapError::Unauthorized ); if !admin_account_info.is_signer { @@ -49,13 +61,32 @@ pub fn check_deposit_token_accounts( source_key: &Pubkey, reserves_info_key: &Pubkey, ) -> ProgramResult { - check_token_keys_not_equal!( - token, - *source_key, - token.reserves, - "Source account cannot be swap token account of token", - SwapError::InvalidInput - ); + match token.index { + 0 => { + assert_keys_neq!( + source_key, + token.reserves, + SwapError::InvalidInput, + "Source account cannot be one of swap's token accounts for token A", + ); + } + 1 => { + assert_keys_neq!( + source_key, + token.reserves, + SwapError::InvalidInput, + "Source account cannot be one of swap's token accounts for token B", + ); + } + _ => { + assert_keys_neq!( + source_key, + token.reserves, + SwapError::InvalidInput, + "Source account cannot be one of swap's token accounts", + ); + } + }; check_reserves_match(token, reserves_info_key)?; Ok(()) } diff --git a/stable-swap-program/program/src/processor/macros.rs b/stable-swap-program/program/src/processor/macros.rs index 386b23f6..192ed655 100644 --- a/stable-swap-program/program/src/processor/macros.rs +++ b/stable-swap-program/program/src/processor/macros.rs @@ -47,29 +47,3 @@ macro_rules! check_keys_condition_optional { } }; } - -/// Checks if two pubkeys are equal, and if not, throws an error. -macro_rules! check_token_keys_equal { - ($token: expr,$left:expr, $right:expr, $msg:expr, $err:expr) => { - check_token_keys_condition!($token, $left, $right, $msg, $err, $left == $right); - }; -} - -/// Checks if two pubkeys are not equal, and if not, throws an error. -macro_rules! check_token_keys_not_equal { - ($token: expr,$left:expr, $right:expr, $msg:expr, $err:expr) => { - check_token_keys_condition!($token, $left, $right, $msg, $err, $left != $right); - }; -} - -macro_rules! check_token_keys_condition { - ($token:expr, $left:expr, $right:expr, $msg:expr, $err:expr, $continueExpr:expr) => { - if ($token.index == 0) { - check_keys_condition!($left, $right, concat!($msg, " A"), $err, $continueExpr); - } else if ($token.index == 1) { - check_keys_condition!($left, $right, concat!($msg, " B"), $err, $continueExpr); - } else { - check_keys_condition!($left, $right, concat!($msg), $err, $continueExpr); - } - }; -} diff --git a/stable-swap-program/program/src/processor/swap.rs b/stable-swap-program/program/src/processor/swap.rs index 02a0b90f..91aa0329 100644 --- a/stable-swap-program/program/src/processor/swap.rs +++ b/stable-swap-program/program/src/processor/swap.rs @@ -1,5 +1,7 @@ //! Module for processing non-admin pool instructions. +use anchor_lang::prelude::*; + use crate::{ error::SwapError, fees::Fees, @@ -23,6 +25,7 @@ use solana_program::{ pubkey::Pubkey, sysvar::{clock::Clock, Sysvar}, }; +use vipers::assert_keys_neq; use super::checks::*; use super::logging::*; @@ -311,20 +314,17 @@ fn process_swap( return Err(SwapError::IsPaused.into()); } - check_token_keys_not_equal!( - token_swap.token_a, - *source_info.key, + assert_keys_neq!( + source_info.key, token_swap.token_a.reserves, - "Source account cannot be one of swap's token accounts for token", - SwapError::InvalidInput + SwapError::InvalidInput, + "Source account cannot be one of swap's token accounts for token A", ); - - check_token_keys_not_equal!( - token_swap.token_b, - *source_info.key, + assert_keys_neq!( + source_info.key, token_swap.token_b.reserves, - "Source account cannot be one of swap's token accounts for token", - SwapError::InvalidInput + SwapError::InvalidInput, + "Source account cannot be one of swap's token accounts for token B", ); check_swap_authority( From 18444f896fe18cc3f1887e044f235180f00ebe9d Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Tue, 28 Dec 2021 09:53:37 -0800 Subject: [PATCH 03/10] migrate more stuff to assert_keys --- .../program/src/processor/logging.rs | 25 ----- .../program/src/processor/macros.rs | 49 ---------- .../program/src/processor/mod.rs | 3 - .../program/src/processor/swap.rs | 98 +++++++++---------- 4 files changed, 48 insertions(+), 127 deletions(-) delete mode 100644 stable-swap-program/program/src/processor/macros.rs diff --git a/stable-swap-program/program/src/processor/logging.rs b/stable-swap-program/program/src/processor/logging.rs index a7bdac8f..856402fc 100644 --- a/stable-swap-program/program/src/processor/logging.rs +++ b/stable-swap-program/program/src/processor/logging.rs @@ -2,7 +2,6 @@ use solana_program::log::sol_log_64; use solana_program::msg; -use solana_program::pubkey::Pubkey; /// Event enum #[derive(Debug)] @@ -47,30 +46,6 @@ pub fn log_event( ); } -pub fn log_keys_mismatch(msg: &str, left: Pubkey, right: Pubkey) { - msg!(msg); - msg!("Left:"); - left.log(); - msg!("Right:"); - right.log(); -} - -pub fn log_keys_mismatch_optional(msg: &str, left: Option, right: Option) { - msg!(msg); - msg!("Left:"); - if let Some(left_inner) = left { - left_inner.log(); - } else { - msg!("left: missing"); - } - msg!("Right:"); - if let Some(right_inner) = right { - right_inner.log(); - } else { - msg!("right: missing"); - } -} - /// Log slippage error pub fn log_slippage_error(minimum_amount: u64, computed_amount: u64) { sol_log_64(0, 0, 0, minimum_amount, computed_amount); diff --git a/stable-swap-program/program/src/processor/macros.rs b/stable-swap-program/program/src/processor/macros.rs deleted file mode 100644 index 192ed655..00000000 --- a/stable-swap-program/program/src/processor/macros.rs +++ /dev/null @@ -1,49 +0,0 @@ -/// Checks if two pubkeys are equal, and if not, throws an error. -macro_rules! check_keys_equal { - ($left:expr, $right:expr, $msg:expr, $err:expr) => { - check_keys_condition!($left, $right, $msg, $err, $left == $right); - }; -} - -/// Checks if two pubkeys are not equal, and if not, throws an error. -macro_rules! check_keys_not_equal { - ($left:expr, $right:expr, $msg:expr, $err:expr) => { - check_keys_condition!($left, $right, $msg, $err, $left != $right); - }; -} - -macro_rules! check_keys_condition { - ($left:expr, $right:expr, $msg:expr, $err:expr, $continueExpr:expr) => { - if !$continueExpr { - $crate::processor::logging::log_keys_mismatch( - concat!($msg, " mismatch:"), - $left, - $right, - ); - return Err::<(), ProgramError>($err.into()); - } - }; -} - -/// Checks if two pubkeys are equal and exist, and if not, throws an error. -macro_rules! check_keys_equal_optional { - ($left:expr, $right:expr, $msg:expr, $err:expr) => { - check_keys_condition_optional!($left, $right, $msg, $err, $left == $right); - }; -} - -macro_rules! check_keys_condition_optional { - ($left:expr, $right:expr, $msg:expr, $err:expr, $continueExpr:expr) => { - match ($left.into(), $right.into()) { - (Some(_), Some(_)) if $continueExpr => (), - (left, right) => { - $crate::processor::logging::log_keys_mismatch_optional( - concat!($msg, " mismatch:"), - left, - right, - ); - return Err::<(), ProgramError>($err.into()); - } - } - }; -} diff --git a/stable-swap-program/program/src/processor/mod.rs b/stable-swap-program/program/src/processor/mod.rs index daa2da2d..d3e0ad02 100644 --- a/stable-swap-program/program/src/processor/mod.rs +++ b/stable-swap-program/program/src/processor/mod.rs @@ -1,8 +1,5 @@ //! Program state processor -#[macro_use] -mod macros; - mod admin; mod checks; mod logging; diff --git a/stable-swap-program/program/src/processor/swap.rs b/stable-swap-program/program/src/processor/swap.rs index 91aa0329..1fe98328 100644 --- a/stable-swap-program/program/src/processor/swap.rs +++ b/stable-swap-program/program/src/processor/swap.rs @@ -19,13 +19,11 @@ use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, msg, - program_error::ProgramError, - program_option::COption, program_pack::Pack, pubkey::Pubkey, sysvar::{clock::Clock, Sysvar}, }; -use vipers::assert_keys_neq; +use vipers::{assert_keys_eq, assert_keys_neq}; use super::checks::*; use super::logging::*; @@ -128,34 +126,34 @@ fn process_initialize( return Err(SwapError::AlreadyInUse.into()); } let swap_authority = utils::authority_id(program_id, swap_info.key, nonce)?; - check_keys_equal!( - *authority_info.key, + assert_keys_eq!( + authority_info, swap_authority, + SwapError::InvalidProgramAddress, "Swap authority", - SwapError::InvalidProgramAddress ); let destination = utils::unpack_token_account(&destination_info.data.borrow())?; let token_a = utils::unpack_token_account(&token_a_info.data.borrow())?; let token_b = utils::unpack_token_account(&token_b_info.data.borrow())?; - check_keys_equal!( - *authority_info.key, + assert_keys_eq!( + authority_info, token_a.owner, + SwapError::InvalidOwner, "Token A authority", - SwapError::InvalidOwner ); - check_keys_equal!( - *authority_info.key, + assert_keys_eq!( + authority_info, token_b.owner, + SwapError::InvalidOwner, "Token B authority", - SwapError::InvalidOwner ); - check_keys_not_equal!( - *authority_info.key, + assert_keys_neq!( + authority_info, destination.owner, + SwapError::InvalidOutputOwner, "Initial LP destination authority", - SwapError::InvalidOutputOwner ); if token_a.mint == token_b.mint { @@ -173,17 +171,17 @@ fn process_initialize( if token_b.delegate.is_some() { return Err(SwapError::InvalidDelegate.into()); } - check_keys_equal!( + assert_keys_eq!( token_a.mint, - *token_a_mint_info.key, + token_a_mint_info, + SwapError::IncorrectMint, "Mint A", - SwapError::IncorrectMint ); - check_keys_equal!( + assert_keys_eq!( token_b.mint, - *token_b_mint_info.key, + token_b_mint_info, + SwapError::IncorrectMint, "Mint B", - SwapError::IncorrectMint ); if token_a.close_authority.is_some() { return Err(SwapError::InvalidCloseAuthority.into()); @@ -192,11 +190,11 @@ fn process_initialize( return Err(SwapError::InvalidCloseAuthority.into()); } let pool_mint = utils::unpack_mint(&pool_mint_info.data.borrow())?; - check_keys_equal_optional!( - pool_mint.mint_authority, - COption::Some(*authority_info.key), + assert_keys_eq!( + pool_mint.mint_authority.unwrap(), + authority_info, + SwapError::InvalidOwner, "LP mint authority", - SwapError::InvalidOwner ); if pool_mint.freeze_authority.is_some() { return Err(SwapError::InvalidFreezeAuthority.into()); @@ -215,17 +213,17 @@ fn process_initialize( let admin_fee_key_a = utils::unpack_token_account(&admin_fee_a_info.data.borrow())?; let admin_fee_key_b = utils::unpack_token_account(&admin_fee_b_info.data.borrow())?; - check_keys_equal!( + assert_keys_eq!( token_a.mint, admin_fee_key_a.mint, + SwapError::InvalidAdmin, "Mint A", - SwapError::InvalidAdmin ); - check_keys_equal!( + assert_keys_eq!( token_b.mint, admin_fee_key_b.mint, + SwapError::InvalidAdmin, "Mint B", - SwapError::InvalidAdmin ); // amp_factor == initial_amp_factor == target_amp_factor on init @@ -453,11 +451,11 @@ fn process_deposit( check_deposit_token_accounts(&token_swap.token_a, source_a_info.key, token_a_info.key)?; check_deposit_token_accounts(&token_swap.token_b, source_b_info.key, token_b_info.key)?; - check_keys_equal!( - *pool_mint_info.key, + assert_keys_eq!( + pool_mint_info, token_swap.pool_mint, + SwapError::IncorrectMint, "Mint A", - SwapError::IncorrectMint ); let clock = Clock::get()?; @@ -608,11 +606,11 @@ fn process_withdraw( admin_fee_dest_b_info.key, )?; - check_keys_equal!( - *pool_mint_info.key, + assert_keys_eq!( + pool_mint_info, token_swap.pool_mint, + SwapError::IncorrectMint, "Pool mint", - SwapError::IncorrectMint ); let pool_mint = utils::unpack_mint(&pool_mint_info.data.borrow())?; @@ -717,30 +715,30 @@ fn process_withdraw_one( )?; if *base_token_info.key == token_swap.token_a.reserves { - check_keys_equal!( - *quote_token_info.key, + assert_keys_eq!( + quote_token_info, token_swap.token_b.reserves, + SwapError::IncorrectSwapAccount, "Swap A -> B reserves", - SwapError::IncorrectSwapAccount ); - check_keys_equal!( - *admin_destination_info.key, + assert_keys_eq!( + admin_destination_info, token_swap.token_a.admin_fees, + SwapError::InvalidAdmin, "Swap A -> B admin fee destination", - SwapError::InvalidAdmin ); } else if *base_token_info.key == token_swap.token_b.reserves { - check_keys_equal!( - *quote_token_info.key, + assert_keys_eq!( + quote_token_info, token_swap.token_a.reserves, + SwapError::IncorrectSwapAccount, "Swap B -> A reserves", - SwapError::IncorrectSwapAccount ); - check_keys_equal!( - *admin_destination_info.key, + assert_keys_eq!( + admin_destination_info, token_swap.token_b.admin_fees, + SwapError::InvalidAdmin, "Swap B -> A admin fee destination", - SwapError::InvalidAdmin ); } else { msg!("Unknown base token:"); @@ -748,11 +746,11 @@ fn process_withdraw_one( return Err(SwapError::IncorrectSwapAccount.into()); } - check_keys_equal!( - *pool_mint_info.key, + assert_keys_eq!( + pool_mint_info, token_swap.pool_mint, + SwapError::IncorrectMint, "Pool mint", - SwapError::IncorrectMint ); let pool_mint = utils::unpack_mint(&pool_mint_info.data.borrow())?; From ca96cd4ab00590ff7525c431825ae8db6dc0ee98 Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Tue, 28 Dec 2021 09:56:41 -0800 Subject: [PATCH 04/10] More vipers checks --- .../program/src/processor/checks.rs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/stable-swap-program/program/src/processor/checks.rs b/stable-swap-program/program/src/processor/checks.rs index 92e91baa..435fe8ef 100644 --- a/stable-swap-program/program/src/processor/checks.rs +++ b/stable-swap-program/program/src/processor/checks.rs @@ -21,20 +21,21 @@ fn check_reserves_match(token: &SwapTokenInfo, reserves_info_key: &Pubkey) -> Pr reserves_info_key, token.reserves, SwapError::IncorrectSwapAccount, - "reserves A" + "Reserves A" ); } else if token.index == 1 { assert_keys_eq!( reserves_info_key, token.reserves, SwapError::IncorrectSwapAccount, - "reserves B" + "Reserves B" ); } else { assert_keys_eq!( reserves_info_key, token.reserves, - SwapError::IncorrectSwapAccount + SwapError::IncorrectSwapAccount, + "Reserves", ); } Ok(()) @@ -48,7 +49,8 @@ pub fn check_has_admin_signer( assert_keys_eq!( expected_admin_key, admin_account_info.key, - SwapError::Unauthorized + SwapError::Unauthorized, + "Admin signer", ); if !admin_account_info.is_signer { return Err(ProgramError::MissingRequiredSignature); @@ -114,7 +116,8 @@ pub fn check_withdraw_token_accounts( assert_keys_eq!( admin_fee_dest_key, token.admin_fees, - SwapError::InvalidAdmin + SwapError::InvalidAdmin, + "Admin fee dest", ); Ok(()) } @@ -129,7 +132,8 @@ pub fn check_swap_authority( assert_keys_eq!( swap_authority_key, swap_authority, - SwapError::InvalidProgramAddress + SwapError::InvalidProgramAddress, + "Swap authority", ); Ok(()) } @@ -143,12 +147,14 @@ pub fn check_swap_token_destination_accounts( assert_keys_eq!( swap_destination_info_key, token.reserves, - SwapError::IncorrectSwapAccount + SwapError::IncorrectSwapAccount, + "Incorrect destination, expected", ); assert_keys_eq!( admin_destination_info_key, token.admin_fees, - SwapError::InvalidAdmin + SwapError::InvalidAdmin, + "Admin fee", ); Ok(()) } From afc0790961978ce2fd800cd5f239635e11397bf5 Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Tue, 28 Dec 2021 10:26:11 -0800 Subject: [PATCH 05/10] More checks --- Cargo.lock | 1 + stable-swap-program/program/Cargo.toml | 1 + stable-swap-program/program/src/lib.rs | 2 + .../program/src/processor/swap.rs | 71 +++++++++---------- 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30a24233..87133b72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1724,6 +1724,7 @@ name = "stable-swap" version = "1.6.1" dependencies = [ "anchor-lang", + "anchor-spl", "solana-program", "solana-sdk", "spl-token", diff --git a/stable-swap-program/program/Cargo.toml b/stable-swap-program/program/Cargo.toml index 1a0c3c5e..95d1d4cc 100644 --- a/stable-swap-program/program/Cargo.toml +++ b/stable-swap-program/program/Cargo.toml @@ -16,6 +16,7 @@ fuzz = ["stable-swap-client/fuzz"] [dependencies] anchor-lang = ">=0.17" +anchor-spl = ">=0.17" solana-program = "^1.6.10" spl-token = { version = "3.1.1", features = ["no-entrypoint"] } stable-swap-client = { path = "../../stable-swap-client", version = "^1" } diff --git a/stable-swap-program/program/src/lib.rs b/stable-swap-program/program/src/lib.rs index 3bbb3963..8bf2db62 100644 --- a/stable-swap-program/program/src/lib.rs +++ b/stable-swap-program/program/src/lib.rs @@ -11,6 +11,8 @@ pub mod processor; pub use stable_swap_client::{error, fees, instruction, state}; pub use stable_swap_math::{curve, math, pool_converter}; +pub use stable_swap_client::error::SwapError as ErrorCode; + /// Export current solana-program types for downstream users who may also be /// building with a different solana-program version pub use solana_program; diff --git a/stable-swap-program/program/src/processor/swap.rs b/stable-swap-program/program/src/processor/swap.rs index 1fe98328..99390e9d 100644 --- a/stable-swap-program/program/src/processor/swap.rs +++ b/stable-swap-program/program/src/processor/swap.rs @@ -23,7 +23,7 @@ use solana_program::{ pubkey::Pubkey, sysvar::{clock::Clock, Sysvar}, }; -use vipers::{assert_keys_eq, assert_keys_neq}; +use vipers::{assert_keys_eq, assert_keys_neq, invariant}; use super::checks::*; use super::logging::*; @@ -122,9 +122,7 @@ fn process_initialize( } let token_swap = SwapInfo::unpack_unchecked(&swap_info.data.borrow())?; - if token_swap.is_initialized { - return Err(SwapError::AlreadyInUse.into()); - } + invariant!(token_swap.is_initialized, AlreadyInUse); let swap_authority = utils::authority_id(program_id, swap_info.key, nonce)?; assert_keys_eq!( authority_info, @@ -133,6 +131,16 @@ fn process_initialize( "Swap authority", ); + assert_keys_eq!(admin_fee_a_info.owner, anchor_spl::token::ID); + assert_keys_eq!(admin_fee_b_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_a_mint_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_a_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_b_mint_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_b_info.owner, anchor_spl::token::ID); + assert_keys_eq!(pool_mint_info.owner, anchor_spl::token::ID); + assert_keys_eq!(destination_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_program_info, anchor_spl::token::ID); + let destination = utils::unpack_token_account(&destination_info.data.borrow())?; let token_a = utils::unpack_token_account(&token_a_info.data.borrow())?; let token_b = utils::unpack_token_account(&token_b_info.data.borrow())?; @@ -156,39 +164,28 @@ fn process_initialize( "Initial LP destination authority", ); - if token_a.mint == token_b.mint { - return Err(SwapError::RepeatedMint.into()); - } - if token_b.amount == 0 { - return Err(SwapError::EmptySupply.into()); - } - if token_a.amount == 0 { - return Err(SwapError::EmptySupply.into()); - } - if token_a.delegate.is_some() { - return Err(SwapError::InvalidDelegate.into()); - } - if token_b.delegate.is_some() { - return Err(SwapError::InvalidDelegate.into()); - } + invariant!(token_a.mint != token_b.mint, RepeatedMint); + + invariant!(token_a.amount != 0, EmptySupply); + invariant!(token_a.delegate.is_none(), InvalidDelegate); assert_keys_eq!( token_a.mint, token_a_mint_info, SwapError::IncorrectMint, "Mint A", ); + invariant!(token_a.close_authority.is_none(), InvalidCloseAuthority); + + invariant!(token_b.amount != 0, EmptySupply); + invariant!(token_b.delegate.is_none(), InvalidDelegate); assert_keys_eq!( token_b.mint, token_b_mint_info, SwapError::IncorrectMint, "Mint B", ); - if token_a.close_authority.is_some() { - return Err(SwapError::InvalidCloseAuthority.into()); - } - if token_b.close_authority.is_some() { - return Err(SwapError::InvalidCloseAuthority.into()); - } + invariant!(token_b.close_authority.is_none(), InvalidCloseAuthority); + let pool_mint = utils::unpack_mint(&pool_mint_info.data.borrow())?; assert_keys_eq!( pool_mint.mint_authority.unwrap(), @@ -196,20 +193,20 @@ fn process_initialize( SwapError::InvalidOwner, "LP mint authority", ); - if pool_mint.freeze_authority.is_some() { - return Err(SwapError::InvalidFreezeAuthority.into()); - } - if pool_mint.supply != 0 { - return Err(SwapError::InvalidSupply.into()); - } + invariant!(pool_mint.freeze_authority.is_none(), InvalidFreezeAuthority); + invariant!(pool_mint.supply == 0, InvalidSupply); + let token_a_mint = utils::unpack_mint(&token_a_mint_info.data.borrow())?; let token_b_mint = utils::unpack_mint(&token_b_mint_info.data.borrow())?; - if token_a_mint.decimals != token_b_mint.decimals { - return Err(SwapError::MismatchedDecimals.into()); - } - if pool_mint.decimals != token_a_mint.decimals { - return Err(SwapError::MismatchedDecimals.into()); - } + invariant!( + token_a_mint.decimals == token_b_mint.decimals, + MismatchedDecimals + ); + invariant!( + pool_mint.decimals == token_b_mint.decimals, + MismatchedDecimals + ); + let admin_fee_key_a = utils::unpack_token_account(&admin_fee_a_info.data.borrow())?; let admin_fee_key_b = utils::unpack_token_account(&admin_fee_b_info.data.borrow())?; From 1ad5f1d4159de83472044fce293d9f20042236df Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Tue, 28 Dec 2021 10:33:23 -0800 Subject: [PATCH 06/10] not init --- stable-swap-program/program/src/processor/swap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stable-swap-program/program/src/processor/swap.rs b/stable-swap-program/program/src/processor/swap.rs index 99390e9d..c7c8475f 100644 --- a/stable-swap-program/program/src/processor/swap.rs +++ b/stable-swap-program/program/src/processor/swap.rs @@ -122,7 +122,7 @@ fn process_initialize( } let token_swap = SwapInfo::unpack_unchecked(&swap_info.data.borrow())?; - invariant!(token_swap.is_initialized, AlreadyInUse); + invariant!(!token_swap.is_initialized, AlreadyInUse); let swap_authority = utils::authority_id(program_id, swap_info.key, nonce)?; assert_keys_eq!( authority_info, From 4a8fefcfb9bd0f2ae42ddd0f6d9c761f63f101aa Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Sat, 5 Feb 2022 22:39:34 -0800 Subject: [PATCH 07/10] Upgrade deps --- Cargo.lock | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a44c3bae..610d4021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,6 +179,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "anchor-spl" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea94b04fc9a0aaae4d4473b0595fb5f55b6c9b38e0d6f596df8c8060f95f096" +dependencies = [ + "anchor-lang", + "solana-program", + "spl-associated-token-account", + "spl-token", +] + [[package]] name = "anchor-syn" version = "0.20.1" @@ -1682,6 +1694,16 @@ dependencies = [ "syn", ] +[[package]] +name = "spl-associated-token-account" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "393e2240d521c3dd770806bff25c2c00d761ac962be106e14e22dd912007f428" +dependencies = [ + "solana-program", + "spl-token", +] + [[package]] name = "spl-token" version = "3.3.0" @@ -1916,9 +1938,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vipers" -version = "1.5.5" +version = "1.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "344716a0a37af7570a1402541e48b42a09def1d453c031aefcd05944d2c4488c" +checksum = "aa8c839e2a15777b45909d05ab3eeaaa3035b43241b404345c9cd999ed30df09" dependencies = [ "anchor-lang", "anchor-spl", From 6ec570f779ece4a4ea276b6b13002888c249d48b Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Sat, 5 Feb 2022 23:19:01 -0800 Subject: [PATCH 08/10] Move ownership checks lower in the program --- .../program/src/processor/checks.rs | 9 +++--- .../program/src/processor/mod.rs | 2 -- .../program/src/processor/swap.rs | 28 ++++++++----------- .../program/src/processor/test_utils.rs | 2 ++ 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/stable-swap-program/program/src/processor/checks.rs b/stable-swap-program/program/src/processor/checks.rs index 435fe8ef..fcc6652a 100644 --- a/stable-swap-program/program/src/processor/checks.rs +++ b/stable-swap-program/program/src/processor/checks.rs @@ -12,7 +12,7 @@ use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, }; -use vipers::{assert_keys_eq, assert_keys_neq}; +use vipers::{assert_keys_eq, assert_keys_neq, invariant}; /// Checks if the reserve of the swap is the given key. fn check_reserves_match(token: &SwapTokenInfo, reserves_info_key: &Pubkey) -> ProgramResult { @@ -52,9 +52,10 @@ pub fn check_has_admin_signer( SwapError::Unauthorized, "Admin signer", ); - if !admin_account_info.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } + invariant!( + admin_account_info.is_signer, + ProgramError::MissingRequiredSignature + ); Ok(()) } diff --git a/stable-swap-program/program/src/processor/mod.rs b/stable-swap-program/program/src/processor/mod.rs index d3e0ad02..1cf91515 100644 --- a/stable-swap-program/program/src/processor/mod.rs +++ b/stable-swap-program/program/src/processor/mod.rs @@ -7,8 +7,6 @@ mod swap; mod token; mod utils; -#[cfg(test)] -#[allow(clippy::unwrap_used)] mod test_utils; use crate::instruction::AdminInstruction; diff --git a/stable-swap-program/program/src/processor/swap.rs b/stable-swap-program/program/src/processor/swap.rs index c7c8475f..04689ca8 100644 --- a/stable-swap-program/program/src/processor/swap.rs +++ b/stable-swap-program/program/src/processor/swap.rs @@ -131,16 +131,6 @@ fn process_initialize( "Swap authority", ); - assert_keys_eq!(admin_fee_a_info.owner, anchor_spl::token::ID); - assert_keys_eq!(admin_fee_b_info.owner, anchor_spl::token::ID); - assert_keys_eq!(token_a_mint_info.owner, anchor_spl::token::ID); - assert_keys_eq!(token_a_info.owner, anchor_spl::token::ID); - assert_keys_eq!(token_b_mint_info.owner, anchor_spl::token::ID); - assert_keys_eq!(token_b_info.owner, anchor_spl::token::ID); - assert_keys_eq!(pool_mint_info.owner, anchor_spl::token::ID); - assert_keys_eq!(destination_info.owner, anchor_spl::token::ID); - assert_keys_eq!(token_program_info, anchor_spl::token::ID); - let destination = utils::unpack_token_account(&destination_info.data.borrow())?; let token_a = utils::unpack_token_account(&token_a_info.data.borrow())?; let token_b = utils::unpack_token_account(&token_b_info.data.borrow())?; @@ -223,6 +213,16 @@ fn process_initialize( "Mint B", ); + assert_keys_eq!(admin_fee_a_info.owner, anchor_spl::token::ID); + assert_keys_eq!(admin_fee_b_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_a_mint_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_a_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_b_mint_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_b_info.owner, anchor_spl::token::ID); + assert_keys_eq!(pool_mint_info.owner, anchor_spl::token::ID); + assert_keys_eq!(destination_info.owner, anchor_spl::token::ID); + assert_keys_eq!(token_program_info, anchor_spl::token::ID); + // amp_factor == initial_amp_factor == target_amp_factor on init let invariant = StableSwap::new(amp_factor, amp_factor, ZERO_TS, ZERO_TS, ZERO_TS); // Compute amount of LP tokens to mint for bootstrapper @@ -696,14 +696,10 @@ fn process_withdraw_one( let admin_destination_info = next_account_info(account_info_iter)?; let token_program_info = next_account_info(account_info_iter)?; - if *base_token_info.key == *quote_token_info.key { - return Err(SwapError::InvalidInput.into()); - } + assert_keys_neq!(base_token_info, quote_token_info, SwapError::InvalidInput); let token_swap = SwapInfo::unpack(&swap_info.data.borrow())?; - if token_swap.is_paused { - return Err(SwapError::IsPaused.into()); - } + invariant!(!token_swap.is_paused, SwapError::IsPaused); check_swap_authority( &token_swap, swap_info.key, diff --git a/stable-swap-program/program/src/processor/test_utils.rs b/stable-swap-program/program/src/processor/test_utils.rs index d6608068..e1794060 100644 --- a/stable-swap-program/program/src/processor/test_utils.rs +++ b/stable-swap-program/program/src/processor/test_utils.rs @@ -1,4 +1,6 @@ //! Test utility methods +#![cfg(test)] +#![allow(clippy::unwrap_used)] #![allow(clippy::too_many_arguments)] use crate::{curve::ZERO_TS, fees::Fees, instruction::*, processor::Processor, state::SwapInfo}; From bda08f7b091f63d5a4ff1d40e56d822fddc8cfdf Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Sat, 5 Feb 2022 23:35:18 -0800 Subject: [PATCH 09/10] more cleanup --- stable-swap-program/program/src/processor/swap.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stable-swap-program/program/src/processor/swap.rs b/stable-swap-program/program/src/processor/swap.rs index 04689ca8..2a90cac7 100644 --- a/stable-swap-program/program/src/processor/swap.rs +++ b/stable-swap-program/program/src/processor/swap.rs @@ -300,14 +300,14 @@ fn process_swap( let admin_destination_info = next_account_info(account_info_iter)?; let token_program_info = next_account_info(account_info_iter)?; - if *swap_source_info.key == *swap_destination_info.key { - return Err(SwapError::InvalidInput.into()); - } + assert_keys_neq!( + swap_source_info, + swap_destination_info, + SwapError::InvalidInput + ); let token_swap = SwapInfo::unpack(&swap_info.data.borrow())?; - if token_swap.is_paused { - return Err(SwapError::IsPaused.into()); - } + invariant!(!token_swap.is_paused, SwapError::IsPaused); assert_keys_neq!( source_info.key, From 4f637c6967339c504c379dfe74f0decff246d87f Mon Sep 17 00:00:00 2001 From: Ian Macalinao Date: Sat, 5 Feb 2022 23:38:07 -0800 Subject: [PATCH 10/10] more vipers --- stable-swap-program/program/src/processor/swap.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/stable-swap-program/program/src/processor/swap.rs b/stable-swap-program/program/src/processor/swap.rs index 2a90cac7..b889496e 100644 --- a/stable-swap-program/program/src/processor/swap.rs +++ b/stable-swap-program/program/src/processor/swap.rs @@ -435,9 +435,7 @@ fn process_deposit( let token_program_info = next_account_info(account_info_iter)?; let token_swap = SwapInfo::unpack(&swap_info.data.borrow())?; - if token_swap.is_paused { - return Err(SwapError::IsPaused.into()); - } + invariant!(!token_swap.is_paused, SwapError::IsPaused); check_swap_authority( &token_swap, swap_info.key,