2025-11-14 20:59:42 -05:00
use nssa_core ::{
account ::{ Account , AccountId , AccountWithMetadata , Data } ,
2025-11-21 19:17:50 -05:00
program ::{ ProgramId , ProgramInput , ChainedCall , read_nssa_inputs , write_nssa_outputs_with_chained_call } ,
2025-11-14 20:59:42 -05:00
} ;
use bytemuck ;
2025-12-02 17:21:49 -05:00
// The AMM program has five functions (four directly accessible via instructions):
2025-11-20 21:02:18 -05:00
// 1. New AMM definition.
2025-11-14 20:59:42 -05:00
// Arguments to this function are:
2025-11-20 21:02:18 -05:00
// * Seven **default** accounts: [amm_pool, vault_holding_a, vault_holding_b, pool_lp, user_holding_a, user_holding_b, user_holding_lp].
// amm_pool is a default account that will initiate the amm definition account values
// vault_holding_a is a token holding account for token a
// vault_holding_b is a token holding account for token b
// pool_lp is a token holding account for the pool's lp token
// user_holding_a is a token holding account for token a
// user_holding_b is a token holding account for token b
// user_holding_lp is a token holding account for lp token
// TODO: ideally, vault_holding_a, vault_holding_b, pool_lp and user_holding_lp are uninitated.
2025-11-21 19:17:50 -05:00
// * An instruction data of 65-bytes, indicating the initial amm reserves' balances and token_program_id with
2025-11-14 20:59:42 -05:00
// the following layout:
2025-11-20 21:02:18 -05:00
// [0x00 || array of balances (little-endian 16 bytes) || TOKEN_PROGRAM_ID)]
// 2. Swap assets
2025-11-14 20:59:42 -05:00
// Arguments to this function are:
2025-11-20 21:02:18 -05:00
// * Two accounts: [amm_pool, vault_holding_1, vault_holding_2, user_holding_a, user_holding_b].
// * An instruction data byte string of length 49, indicating which token type to swap and maximum amount with the following layout
// [0x01 || amount (little-endian 16 bytes) || TOKEN_DEFINITION_ID].
// 3. Add liquidity
// Arguments to this function are:
// * Two accounts: [amm_pool, vault_holding_a, vault_holding_b, pool_lp, user_holding_a, user_holding_b, user_holding_lp].
// * An instruction data byte string of length 65, amounts to add
// [0x02 || array of max amounts (little-endian 16 bytes) || TOKEN_DEFINITION_ID (for primary)].
// 4. Remove liquidity
// * Input instruction set [0x03].
2025-12-02 17:21:49 -05:00
// - Swap logic
// Arguments of this function are:
// * Four accounts: [user_deposit_tx, vault_deposit_tx, vault_withdraw_tx, user_withdraw_tx].
// user_deposit_tx and vault_deposit_tx define deposit transaction.
// vault_withdraw_tx and user_withdraw_tx define withdraw transaction.
// * deposit_amount is the amount for user_deposit_tx -> vault_deposit_tx transfer.
// * reserve_amounts is the pool's reserves; used to compute the withdraw amount.
// * Outputs the token transfers as a Vec<ChainedCall> and the withdraw amount.
2025-11-14 20:59:42 -05:00
2025-12-04 21:53:58 -05:00
const POOL_DEFINITION_DATA_SIZE : usize = 209 ;
const MAX_NUMBER_POOLS : usize = 32 ;
const AMM_DEFINITION_DATA_SIZE : usize = 1024 ;
struct AMMDefinition {
pool_ids : Vec < AccountId > ,
}
impl AMMDefinition {
fn into_data ( self ) -> Vec < u8 > {
let size_of_pool : usize = self . pool_ids . len ( ) ;
let mut bytes = [ 0 ; AMM_DEFINITION_DATA_SIZE ] ;
for i in 0 .. size_of_pool - 1 {
bytes [ 32 * i .. 32 * ( i + 1 ) ] . copy_from_slice ( & self . pool_ids [ i ] . to_bytes ( ) )
}
for i in size_of_pool .. MAX_NUMBER_POOLS {
bytes [ 32 * i .. 32 * ( i + 1 ) ] . copy_from_slice ( & AccountId ::default ( ) . to_bytes ( ) )
}
bytes . into ( )
}
fn parse ( data : & [ u8 ] ) -> Option < Self > {
if data . len ( ) % 32 ! = 0 {
panic! ( " AMM data should be divisible by 32 (number of bytes per of AccountId " ) ;
}
let size_of_pool = data . len ( ) / 32 ;
let mut pool_ids = Vec ::< AccountId > ::new ( ) ;
for i in 0 .. size_of_pool {
pool_ids . push (
AccountId ::new ( data [ i * 32 .. ( i + 1 ) * 32 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid AccountIds " ) )
) ;
}
for _ in size_of_pool .. MAX_NUMBER_POOLS {
pool_ids . push ( AccountId ::default ( ) ) ;
}
Some ( Self {
pool_ids
} )
}
}
2025-11-14 20:59:42 -05:00
struct PoolDefinition {
2025-12-02 15:20:16 -05:00
definition_token_a_id : AccountId ,
definition_token_b_id : AccountId ,
2025-11-14 20:59:42 -05:00
vault_a_addr : AccountId ,
vault_b_addr : AccountId ,
liquidity_pool_id : AccountId ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply : u128 ,
2025-11-14 20:59:42 -05:00
reserve_a : u128 ,
reserve_b : u128 ,
2025-12-04 21:53:58 -05:00
active : bool
2025-11-14 20:59:42 -05:00
}
impl PoolDefinition {
fn into_data ( self ) -> Vec < u8 > {
let mut bytes = [ 0 ; POOL_DEFINITION_DATA_SIZE ] ;
bytes [ 0 .. 32 ] . copy_from_slice ( & self . definition_token_a_id . to_bytes ( ) ) ;
bytes [ 32 .. 64 ] . copy_from_slice ( & self . definition_token_b_id . to_bytes ( ) ) ;
bytes [ 64 .. 96 ] . copy_from_slice ( & self . vault_a_addr . to_bytes ( ) ) ;
bytes [ 96 .. 128 ] . copy_from_slice ( & self . vault_b_addr . to_bytes ( ) ) ;
bytes [ 128 .. 160 ] . copy_from_slice ( & self . liquidity_pool_id . to_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
bytes [ 160 .. 176 ] . copy_from_slice ( & self . liquidity_pool_supply . to_le_bytes ( ) ) ;
2025-11-14 20:59:42 -05:00
bytes [ 176 .. 192 ] . copy_from_slice ( & self . reserve_a . to_le_bytes ( ) ) ;
bytes [ 192 .. 208 ] . copy_from_slice ( & self . reserve_b . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
bytes [ 208 ] = self . active as u8 ;
2025-11-14 20:59:42 -05:00
bytes . into ( )
}
fn parse ( data : & [ u8 ] ) -> Option < Self > {
if data . len ( ) ! = POOL_DEFINITION_DATA_SIZE {
None
} else {
2025-12-04 21:53:58 -05:00
let definition_token_a_id = AccountId ::new ( data [ 0 .. 32 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid AccountId for Token A definition " ) ) ;
let definition_token_b_id = AccountId ::new ( data [ 32 .. 64 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid AccountId for Vault B definition " ) ) ;
let vault_a_addr = AccountId ::new ( data [ 64 .. 96 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid AccountId for Vault A " ) ) ;
let vault_b_addr = AccountId ::new ( data [ 96 .. 128 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid AccountId for Vault B " ) ) ;
let liquidity_pool_id = AccountId ::new ( data [ 128 .. 160 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid AccountId for Token liquidity pool definition " ) ) ;
let liquidity_pool_supply = u128 ::from_le_bytes ( data [ 160 .. 176 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid u128 for liquidity cap " ) ) ;
let reserve_a = u128 ::from_le_bytes ( data [ 176 .. 192 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid u128 for reserve A balance " ) ) ;
let reserve_b = u128 ::from_le_bytes ( data [ 192 .. 208 ] . try_into ( ) . expect ( " Parse data: The AMM program must be provided a valid u128 for reserve B balance " ) ) ;
let active = match data [ 208 ] {
0 = > false ,
1 = > true ,
_ = > panic! ( " Parse data: The AMM program must be provided a valid bool for active " ) ,
} ;
2025-11-14 20:59:42 -05:00
Some ( Self {
definition_token_a_id ,
definition_token_b_id ,
vault_a_addr ,
vault_b_addr ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-14 20:59:42 -05:00
reserve_a ,
reserve_b ,
2025-12-04 21:53:58 -05:00
active ,
2025-11-14 20:59:42 -05:00
} )
}
}
}
//TODO: remove repeated code for Token_Definition and TokenHoldling
2025-12-04 21:53:58 -05:00
const TOKEN_DEFINITION_TYPE : u8 = 0 ;
const TOKEN_DEFINITION_DATA_SIZE : usize = 23 ;
2025-11-14 20:59:42 -05:00
const TOKEN_HOLDING_TYPE : u8 = 1 ;
const TOKEN_HOLDING_DATA_SIZE : usize = 49 ;
2025-12-04 21:53:58 -05:00
struct TokenDefinition {
account_type : u8 ,
name : [ u8 ; 6 ] ,
total_supply : u128 ,
}
2025-11-14 20:59:42 -05:00
struct TokenHolding {
account_type : u8 ,
definition_id : AccountId ,
balance : u128 ,
}
2025-12-04 21:53:58 -05:00
impl TokenDefinition {
fn into_data ( self ) -> Vec < u8 > {
let mut bytes = [ 0 ; TOKEN_DEFINITION_DATA_SIZE ] ;
bytes [ 0 ] = self . account_type ;
bytes [ 1 .. 7 ] . copy_from_slice ( & self . name ) ;
bytes [ 7 .. ] . copy_from_slice ( & self . total_supply . to_le_bytes ( ) ) ;
bytes . into ( )
}
fn parse ( data : & [ u8 ] ) -> Option < Self > {
if data . len ( ) ! = TOKEN_DEFINITION_DATA_SIZE | | data [ 0 ] ! = TOKEN_DEFINITION_TYPE {
None
} else {
let account_type = data [ 0 ] ;
let name = data [ 1 .. 7 ] . try_into ( ) . unwrap ( ) ;
let total_supply = u128 ::from_le_bytes (
data [ 7 .. ]
. try_into ( )
. expect ( " Total supply must be 16 bytes little-endian " ) ,
) ;
Some ( Self {
account_type ,
name ,
total_supply ,
} )
}
}
}
2025-11-14 20:59:42 -05:00
impl TokenHolding {
2025-12-04 21:53:58 -05:00
fn new ( definition_id : & AccountId ) -> Self {
Self {
account_type : TOKEN_HOLDING_TYPE ,
definition_id : definition_id . clone ( ) ,
balance : 0 ,
}
}
2025-11-14 20:59:42 -05:00
fn parse ( data : & [ u8 ] ) -> Option < Self > {
if data . len ( ) ! = TOKEN_HOLDING_DATA_SIZE | | data [ 0 ] ! = TOKEN_HOLDING_TYPE {
None
} else {
let account_type = data [ 0 ] ;
2025-12-04 21:53:58 -05:00
let definition_id = AccountId ::new (
data [ 1 .. 33 ]
. try_into ( )
. expect ( " Defintion ID must be 32 bytes long " ) ,
) ;
let balance = u128 ::from_le_bytes (
data [ 33 .. ]
. try_into ( )
. expect ( " balance must be 16 bytes little-endian " ) ,
) ;
2025-11-14 20:59:42 -05:00
Some ( Self {
definition_id ,
balance ,
account_type ,
} )
}
}
fn into_data ( self ) -> Data {
let mut bytes = [ 0 ; TOKEN_HOLDING_DATA_SIZE ] ;
bytes [ 0 ] = self . account_type ;
bytes [ 1 .. 33 ] . copy_from_slice ( & self . definition_id . to_bytes ( ) ) ;
bytes [ 33 .. ] . copy_from_slice ( & self . balance . to_le_bytes ( ) ) ;
bytes . into ( )
}
}
2025-11-24 19:44:08 -05:00
2025-11-25 23:06:47 -05:00
type Instruction = Vec < u8 > ;
fn main ( ) {
let ProgramInput {
pre_states ,
instruction ,
} = read_nssa_inputs ::< Instruction > ( ) ;
match instruction [ 0 ] {
0 = > {
2025-12-04 21:53:58 -05:00
let balance_a : u128 = u128 ::from_le_bytes ( instruction [ 1 .. 17 ] . try_into ( ) . expect ( " New definition: AMM Program expects u128 for balance a " ) ) ;
let balance_b : u128 = u128 ::from_le_bytes ( instruction [ 17 .. 33 ] . try_into ( ) . expect ( " New definition: AMM Program expects u128 for balance b " ) ) ;
2025-11-25 23:06:47 -05:00
2025-12-04 21:53:58 -05:00
// Convert Vec<u8> to ProgramId ([u32;8])
2025-11-25 23:06:47 -05:00
let mut token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
2025-12-04 21:53:58 -05:00
token_program_id [ 0 ] = u32 ::from_le_bytes ( instruction [ 33 .. 37 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
token_program_id [ 1 ] = u32 ::from_le_bytes ( instruction [ 37 .. 41 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
token_program_id [ 2 ] = u32 ::from_le_bytes ( instruction [ 41 .. 45 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
token_program_id [ 3 ] = u32 ::from_le_bytes ( instruction [ 45 .. 49 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
token_program_id [ 4 ] = u32 ::from_le_bytes ( instruction [ 49 .. 53 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
token_program_id [ 5 ] = u32 ::from_le_bytes ( instruction [ 53 .. 57 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
token_program_id [ 6 ] = u32 ::from_le_bytes ( instruction [ 57 .. 61 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
token_program_id [ 7 ] = u32 ::from_le_bytes ( instruction [ 61 .. 65 ] . try_into ( ) . expect ( " New definition: AMM Program expects valid u32 " ) ) ;
2025-11-25 23:06:47 -05:00
let ( post_states , chained_call ) = new_definition ( & pre_states ,
& [ balance_a , balance_b ] ,
token_program_id
) ;
write_nssa_outputs_with_chained_call ( pre_states , post_states , chained_call ) ;
}
1 = > {
let mut token_addr : [ u8 ; 32 ] = [ 0 ; 32 ] ;
token_addr [ 0 .. ] . copy_from_slice ( & instruction [ 17 .. 49 ] ) ;
2025-12-02 15:20:16 -05:00
let token_addr = AccountId ::new ( token_addr ) ;
2025-11-25 23:06:47 -05:00
2025-12-04 21:53:58 -05:00
let amount = u128 ::from_le_bytes ( instruction [ 1 .. 17 ] . try_into ( ) . expect ( " Swap: AMM Program expects valid u128 for balance to move " ) ) ;
2025-11-25 23:06:47 -05:00
let ( post_states , chained_call ) = swap ( & pre_states , amount , token_addr ) ;
write_nssa_outputs_with_chained_call ( pre_states , post_states , chained_call ) ;
}
2 = > {
2025-12-04 21:53:58 -05:00
let balance_a = u128 ::from_le_bytes ( instruction [ 1 .. 17 ] . try_into ( ) . expect ( " Add liquidity: AMM Program expects valid u128 for balance a " ) ) ;
let balance_b = u128 ::from_le_bytes ( instruction [ 17 .. 33 ] . try_into ( ) . expect ( " Add liquidity: AMM Program expects valid u128 for balance b " ) ) ;
2025-11-25 23:06:47 -05:00
let mut token_addr : [ u8 ; 32 ] = [ 0 ; 32 ] ;
token_addr [ 0 .. ] . copy_from_slice ( & instruction [ 33 .. 65 ] ) ;
2025-12-02 15:20:16 -05:00
let token_addr = AccountId ::new ( token_addr ) ;
2025-11-25 23:06:47 -05:00
let ( post_states , chained_call ) = add_liquidity ( & pre_states ,
& [ balance_a , balance_b ] , token_addr . clone ( ) ) ;
write_nssa_outputs_with_chained_call ( pre_states , post_states , chained_call ) ;
}
3 = > {
2025-12-04 21:53:58 -05:00
let balance_lp = u128 ::from_le_bytes ( instruction [ 1 .. 17 ] . try_into ( ) . expect ( " Remove liquidity: AMM Program expects valid u128 for balance liquidity " ) ) ;
let balance_a = u128 ::from_le_bytes ( instruction [ 17 .. 33 ] . try_into ( ) . expect ( " Remove liquidity: AMM Program expects valid u128 for balance a " ) ) ;
let balance_b = u128 ::from_le_bytes ( instruction [ 33 .. 49 ] . try_into ( ) . expect ( " Remove liquidity: AMM Program expects valid u128 for balance b " ) ) ;
let ( post_states , chained_call ) = remove_liquidity ( & pre_states , & [ balance_lp , balance_a , balance_b ] ) ;
2025-11-25 23:06:47 -05:00
write_nssa_outputs_with_chained_call ( pre_states , post_states , chained_call ) ;
}
_ = > panic! ( " Invalid instruction " ) ,
} ;
2025-11-24 19:44:08 -05:00
}
2025-11-14 20:59:42 -05:00
fn new_definition (
pre_states : & [ AccountWithMetadata ] ,
balance_in : & [ u128 ] ,
token_program : ProgramId ,
) -> ( Vec < Account > , Vec < ChainedCall > ) {
//Pool accounts: pool itself, and its 2 vaults and LP token
//2 accounts for funding tokens
//initial funder's LP account
if pre_states . len ( ) ! = 7 {
2025-11-18 14:43:40 -05:00
panic! ( " Invalid number of input accounts " )
2025-11-14 20:59:42 -05:00
}
if balance_in . len ( ) ! = 2 {
panic! ( " Invalid number of balance " )
}
2025-11-17 18:48:17 -05:00
let pool = & pre_states [ 0 ] ;
let vault_a = & pre_states [ 1 ] ;
let vault_b = & pre_states [ 2 ] ;
let pool_lp = & pre_states [ 3 ] ;
2025-12-04 21:53:58 -05:00
let user_holding_a = & pre_states [ 4 ] ;
let user_holding_b = & pre_states [ 5 ] ;
let user_holding_lp = & pre_states [ 6 ] ;
2025-11-14 20:59:42 -05:00
2025-11-25 23:06:47 -05:00
if pool . account ! = Account ::default ( ) | | ! pool . is_authorized {
panic! ( " Pool account is initiated or not authorized " ) ;
2025-11-14 20:59:42 -05:00
}
// TODO: temporary band-aid to prevent vault's from being
// owned by the amm program.
if vault_a . account = = Account ::default ( ) | | vault_b . account = = Account ::default ( ) {
2025-11-18 14:43:40 -05:00
panic! ( " Vault accounts uninitialized " )
2025-11-14 20:59:42 -05:00
}
let amount_a = balance_in [ 0 ] ;
let amount_b = balance_in [ 1 ] ;
// Prevents pool constant coefficient (k) from being 0.
2025-11-18 14:43:40 -05:00
if amount_a = = 0 | | amount_b = = 0 {
panic! ( " Balances must be nonzero " )
}
2025-11-14 20:59:42 -05:00
// Verify token_a and token_b are different
2025-12-04 21:53:58 -05:00
let definition_token_a_id = TokenHolding ::parse ( & user_holding_a . account . data ) . expect ( " New definition: AMM Program expects valid Token Holding account for Token A " ) . definition_id ;
let definition_token_b_id = TokenHolding ::parse ( & user_holding_b . account . data ) . expect ( " New definition: AMM Program expects valid Token Holding account for Token B " ) . definition_id ;
2025-11-21 19:17:50 -05:00
2025-11-17 18:48:17 -05:00
if definition_token_a_id = = definition_token_b_id {
2025-11-25 23:06:47 -05:00
panic! ( " Cannot set up a swap for a token with itself. " )
2025-11-17 18:48:17 -05:00
}
2025-11-14 20:59:42 -05:00
// 5. Update pool account
let mut pool_post = Account ::default ( ) ;
let pool_post_definition = PoolDefinition {
definition_token_a_id ,
definition_token_b_id ,
2025-11-17 18:48:17 -05:00
vault_a_addr : vault_a . account_id . clone ( ) ,
vault_b_addr : vault_b . account_id . clone ( ) ,
2025-12-04 21:53:58 -05:00
liquidity_pool_id : TokenHolding ::parse ( & pool_lp . account . data ) . expect ( " New definition: AMM Program expects valid Token Holding account for liquidity pool " ) . definition_id ,
liquidity_pool_supply : amount_a ,
2025-11-14 20:59:42 -05:00
reserve_a : amount_a ,
reserve_b : amount_b ,
2025-12-04 21:53:58 -05:00
active : true ,
2025-11-14 20:59:42 -05:00
} ;
pool_post . data = pool_post_definition . into_data ( ) ;
let mut chained_call = Vec ::new ( ) ;
2025-11-25 09:02:43 -05:00
2025-12-04 21:53:58 -05:00
//Chain call for Token A (user_holding_a -> Vault_A)
2025-11-25 09:02:43 -05:00
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & amount_a . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . expect ( " New definition: AMM Program expects valid instruction_data " ) ;
2025-11-25 23:06:47 -05:00
2025-11-14 20:59:42 -05:00
let call_token_a = ChainedCall {
2025-11-25 23:06:47 -05:00
program_id : token_program ,
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_a . clone ( ) , vault_a . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
2025-11-25 09:02:43 -05:00
2025-12-04 21:53:58 -05:00
//Chain call for Token B (user_holding_b -> Vault_B)
2025-11-25 23:06:47 -05:00
instruction [ 1 .. 17 ] . copy_from_slice ( & amount_b . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . expect ( " New definition: AMM Program expects valid instruction_data " ) ;
2025-11-25 23:06:47 -05:00
2025-11-14 20:59:42 -05:00
let call_token_b = ChainedCall {
program_id : token_program ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_b . clone ( ) , vault_b . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
2025-12-04 21:53:58 -05:00
//Chain call for LP (Pool_LP -> user_holding_lp)
2025-11-25 23:06:47 -05:00
instruction [ 1 .. 17 ] . copy_from_slice ( & amount_a . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . expect ( " New definition: AMM Program expects valid instruction_data " ) ;
2025-11-25 23:06:47 -05:00
2025-11-14 20:59:42 -05:00
let call_token_lp = ChainedCall {
program_id : token_program ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ pool_lp . clone ( ) , user_holding_lp . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
chained_call . push ( call_token_lp ) ;
chained_call . push ( call_token_b ) ;
chained_call . push ( call_token_a ) ;
2025-11-25 23:06:47 -05:00
let post_states = vec! [
pool_post . clone ( ) ,
2025-11-14 20:59:42 -05:00
pre_states [ 1 ] . account . clone ( ) ,
pre_states [ 2 ] . account . clone ( ) ,
pre_states [ 3 ] . account . clone ( ) ,
pre_states [ 4 ] . account . clone ( ) ,
pre_states [ 5 ] . account . clone ( ) ,
pre_states [ 6 ] . account . clone ( ) ] ;
( post_states . clone ( ) , chained_call )
}
fn swap (
pre_states : & [ AccountWithMetadata ] ,
2025-11-18 14:43:40 -05:00
amount : u128 ,
2025-12-02 15:20:16 -05:00
token_id : AccountId ,
2025-11-14 20:59:42 -05:00
) -> ( Vec < Account > , Vec < ChainedCall > ) {
if pre_states . len ( ) ! = 5 {
panic! ( " Invalid number of input accounts " ) ;
}
let pool = & pre_states [ 0 ] ;
2025-12-02 17:21:49 -05:00
let vault_a = & pre_states [ 1 ] ;
let vault_b = & pre_states [ 2 ] ;
2025-12-04 21:53:58 -05:00
let user_holding_a = & pre_states [ 3 ] ;
let user_holding_b = & pre_states [ 4 ] ;
2025-11-14 20:59:42 -05:00
// Verify vaults are in fact vaults
2025-12-04 21:53:58 -05:00
let pool_def_data = PoolDefinition ::parse ( & pool . account . data ) . expect ( " Swap: AMM Program expects a valid Pool Definition Account " ) ;
if ! pool_def_data . active {
panic! ( " Pool is inactive " ) ;
}
2025-11-20 21:02:18 -05:00
2025-12-02 17:21:49 -05:00
if vault_a . account_id ! = pool_def_data . vault_a_addr {
2025-11-21 19:17:50 -05:00
panic! ( " Vault A was not provided " ) ;
2025-12-02 17:21:49 -05:00
}
2025-11-14 20:59:42 -05:00
2025-12-02 17:21:49 -05:00
if vault_b . account_id ! = pool_def_data . vault_b_addr {
2025-11-18 14:43:40 -05:00
panic! ( " Vault B was not provided " ) ;
2025-12-02 17:21:49 -05:00
}
2025-11-14 20:59:42 -05:00
2025-12-02 17:21:49 -05:00
// fetch pool reserves
2025-11-14 20:59:42 -05:00
//validates reserves is at least the vaults' balances
2025-12-04 21:53:58 -05:00
assert! ( TokenHolding ::parse ( & vault_a . account . data ) . expect ( " Swap: AMM Program expects a valid Token Holding Account for Vault A " ) . balance > = pool_def_data . reserve_a ) ;
assert! ( TokenHolding ::parse ( & vault_b . account . data ) . expect ( " Swap: AMM Program expects a valid Token Holding Account for Vault B " ) . balance > = pool_def_data . reserve_b ) ;
2025-11-14 20:59:42 -05:00
//Cannot swap if a reserve is 0
assert! ( pool_def_data . reserve_a > 0 ) ;
assert! ( pool_def_data . reserve_b > 0 ) ;
2025-12-02 17:21:49 -05:00
let ( chained_call , [ deposit_a , withdraw_a ] , [ deposit_b , withdraw_b ] )
= if token_id = = pool_def_data . definition_token_a_id {
2025-12-04 21:53:58 -05:00
let ( chained_call , withdraw_b ) = swap_logic ( & [ user_holding_a . clone ( ) , vault_a . clone ( ) , vault_b . clone ( ) , user_holding_b . clone ( ) ] ,
2025-12-02 17:21:49 -05:00
amount ,
& [ pool_def_data . reserve_a , pool_def_data . reserve_b ] ) ;
( chained_call , [ amount , 0 ] , [ 0 , withdraw_b ] )
} else if token_id = = pool_def_data . definition_token_b_id {
2025-12-04 21:53:58 -05:00
let ( chained_call , withdraw_a ) = swap_logic ( & [ user_holding_b . clone ( ) , vault_b . clone ( ) , vault_a . clone ( ) , user_holding_a . clone ( ) ] ,
2025-12-02 17:21:49 -05:00
amount ,
& [ pool_def_data . reserve_b , pool_def_data . reserve_a ] ) ;
( chained_call , [ 0 , withdraw_a ] , [ amount , 0 ] )
} else {
panic! ( " AccountId is not a token type for the pool " ) ;
} ;
2025-11-14 20:59:42 -05:00
2025-12-02 17:21:49 -05:00
// Update pool account
2025-11-14 20:59:42 -05:00
let mut pool_post = pool . account . clone ( ) ;
let pool_post_definition = PoolDefinition {
definition_token_a_id : pool_def_data . definition_token_a_id . clone ( ) ,
definition_token_b_id : pool_def_data . definition_token_b_id . clone ( ) ,
vault_a_addr : pool_def_data . vault_a_addr . clone ( ) ,
vault_b_addr : pool_def_data . vault_b_addr . clone ( ) ,
liquidity_pool_id : pool_def_data . liquidity_pool_id . clone ( ) ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply : pool_def_data . liquidity_pool_supply . clone ( ) ,
2025-11-14 20:59:42 -05:00
reserve_a : pool_def_data . reserve_a + deposit_a - withdraw_a ,
reserve_b : pool_def_data . reserve_b + deposit_b - withdraw_b ,
2025-12-04 21:53:58 -05:00
active : true ,
2025-11-14 20:59:42 -05:00
} ;
pool_post . data = pool_post_definition . into_data ( ) ;
2025-11-25 23:06:47 -05:00
2025-12-02 17:21:49 -05:00
let post_states = vec! [
pool_post . clone ( ) ,
pre_states [ 1 ] . account . clone ( ) ,
pre_states [ 2 ] . account . clone ( ) ,
pre_states [ 3 ] . account . clone ( ) ,
pre_states [ 4 ] . account . clone ( ) ] ;
2025-11-25 23:06:47 -05:00
2025-12-02 17:21:49 -05:00
( post_states . clone ( ) , chained_call )
}
2025-11-25 23:06:47 -05:00
2025-12-02 17:21:49 -05:00
fn swap_logic (
pre_states : & [ AccountWithMetadata ] ,
deposit_amount : u128 ,
reserve_amounts : & [ u128 ] ,
) -> ( Vec < ChainedCall > , u128 )
{
let user_deposit_tx = pre_states [ 0 ] . clone ( ) ;
let vault_deposit_tx = pre_states [ 1 ] . clone ( ) ;
let vault_withdraw_tx = pre_states [ 2 ] . clone ( ) ;
let user_withdraw_tx = pre_states [ 3 ] . clone ( ) ;
let reserve_deposit_vault_amount = reserve_amounts [ 0 ] ;
let reserve_withdraw_vault_amount = reserve_amounts [ 1 ] ;
// Compute withdraw amount
// Compute pool's exchange constant
// let k = pool_def_data.reserve_a * pool_def_data.reserve_b;
let withdraw_amount = ( reserve_withdraw_vault_amount * deposit_amount ) / ( reserve_deposit_vault_amount + deposit_amount ) ;
//Slippage check
assert! ( withdraw_amount ! = 0 ) ;
let mut chained_call = Vec ::new ( ) ;
2025-11-26 21:44:57 -05:00
2025-12-02 17:21:49 -05:00
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
instruction_data [ 1 .. 17 ] . copy_from_slice ( & deposit_amount . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . expect ( " Swap Logic: AMM Program expects valid transaction instruction data " ) ;
2025-12-02 17:21:49 -05:00
chained_call . push (
2025-11-26 21:44:57 -05:00
ChainedCall {
2025-12-02 17:21:49 -05:00
program_id : vault_deposit_tx . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-02 17:21:49 -05:00
pre_states : vec ! [ user_deposit_tx . clone ( ) , vault_deposit_tx . clone ( ) ]
2025-11-26 21:44:57 -05:00
}
2025-12-02 17:21:49 -05:00
) ;
2025-11-14 20:59:42 -05:00
2025-12-02 17:21:49 -05:00
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
instruction_data [ 1 .. 17 ] . copy_from_slice ( & withdraw_amount . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . expect ( " Swap Logic: AMM Program expects valid transaction instruction data " ) ;
2025-12-02 17:21:49 -05:00
chained_call . push (
2025-11-26 21:44:57 -05:00
ChainedCall {
2025-12-02 17:21:49 -05:00
program_id : vault_deposit_tx . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-02 17:21:49 -05:00
pre_states : vec ! [ vault_withdraw_tx . clone ( ) , user_withdraw_tx . clone ( ) ]
2025-11-26 21:44:57 -05:00
}
2025-12-02 17:21:49 -05:00
) ;
2025-11-26 21:44:57 -05:00
2025-12-02 17:21:49 -05:00
( chained_call , withdraw_amount )
2025-11-14 20:59:42 -05:00
}
fn add_liquidity ( pre_states : & [ AccountWithMetadata ] ,
max_balance_in : & [ u128 ] ,
2025-12-02 15:20:16 -05:00
main_token : AccountId ) -> ( Vec < Account > , Vec < ChainedCall > ) {
2025-11-14 20:59:42 -05:00
if pre_states . len ( ) ! = 7 {
panic! ( " Invalid number of input accounts " ) ;
}
2025-12-04 21:53:58 -05:00
//TODO: add logic for re-initialized
2025-11-14 20:59:42 -05:00
let pool = & pre_states [ 0 ] ;
2025-12-04 21:53:58 -05:00
let vault_a = & pre_states [ 1 ] ;
let vault_b = & pre_states [ 2 ] ;
2025-11-14 20:59:42 -05:00
let pool_lp = & pre_states [ 3 ] ;
2025-12-04 21:53:58 -05:00
let user_holding_a = & pre_states [ 4 ] ;
let user_holding_b = & pre_states [ 5 ] ;
let user_holding_lp = & pre_states [ 6 ] ;
2025-11-14 20:59:42 -05:00
2025-11-28 08:11:04 -05:00
// Verify vaults are in fact vaults
2025-12-04 21:53:58 -05:00
let pool_def_data = PoolDefinition ::parse ( & pool . account . data ) . expect ( " Add liquidity: AMM Program expects valid Pool Definition Account " ) ;
if vault_a . account_id ! = pool_def_data . vault_a_addr {
2025-11-28 08:11:04 -05:00
panic! ( " Vault A was not provided " ) ;
2025-12-04 21:53:58 -05:00
}
if vault_b . account_id ! = pool_def_data . vault_b_addr {
2025-11-18 14:43:40 -05:00
panic! ( " Vault B was not provided " ) ;
2025-12-04 21:53:58 -05:00
}
2025-11-20 21:02:18 -05:00
if max_balance_in . len ( ) ! = 2 {
panic! ( " Invalid number of input balances " ) ;
}
let max_amount_a = max_balance_in [ 0 ] ;
let max_amount_b = max_balance_in [ 1 ] ;
2025-11-14 20:59:42 -05:00
2025-11-20 21:02:18 -05:00
if max_amount_a = = 0 | | max_amount_b = = 0 {
panic! ( " Both max-balances must be nonzero " ) ;
}
2025-11-26 21:44:57 -05:00
// 2. Determine deposit amount
2025-12-04 21:53:58 -05:00
let vault_b_balance = TokenHolding ::parse ( & vault_b . account . data ) . expect ( " Add liquidity: AMM Program expects valid Token Holding Account for Vault B " ) . balance ;
let vault_a_balance = TokenHolding ::parse ( & vault_a . account . data ) . expect ( " Add liquidity: AMM Program expects valid Token Holding Account for Vault A " ) . balance ;
2025-11-25 23:06:47 -05:00
if vault_a_balance = = 0 | | vault_b_balance = = 0 {
2025-11-20 21:02:18 -05:00
panic! ( " Vaults must have nonzero balances " ) ;
}
2025-11-25 23:06:47 -05:00
2025-11-20 21:02:18 -05:00
if pool_def_data . reserve_a = = 0 | | pool_def_data . reserve_b = = 0 {
panic! ( " Reserves must be nonzero " ) ;
}
2025-11-26 21:44:57 -05:00
//Calculate actual_amounts
let actual_amount_a = if main_token = = pool_def_data . definition_token_a_id {
max_amount_a
2025-11-14 20:59:42 -05:00
} else if main_token = = pool_def_data . definition_token_b_id {
2025-11-26 21:44:57 -05:00
( pool_def_data . reserve_a * max_amount_b ) / pool_def_data . reserve_b
2025-11-14 20:59:42 -05:00
} else {
panic! ( " Mismatch of token types " ) ; //main token does not match with vaults.
2025-11-26 21:44:57 -05:00
} ;
let actual_amount_b = if main_token = = pool_def_data . definition_token_a_id {
( pool_def_data . reserve_b * max_amount_a ) / pool_def_data . reserve_a
} else if main_token = = pool_def_data . definition_token_b_id {
max_amount_b
} else {
panic! ( " Mismatch of token types " ) ; //main token does not match with vaults.
} ;
2025-11-14 20:59:42 -05:00
// 3. Validate amounts
2025-12-04 21:53:58 -05:00
let user_holding_a_balance = TokenHolding ::parse ( & user_holding_a . account . data ) . expect ( " Add liquidity: AMM Program expects a valid Token Holding Account for User A " ) . balance ;
let user_holding_b_balance = TokenHolding ::parse ( & user_holding_b . account . data ) . expect ( " Add liquidity: AMM Program expects a valid Token Holding Account for User B " ) . balance ;
2025-11-18 14:43:40 -05:00
assert! ( max_amount_a > = actual_amount_a & & max_amount_b > = actual_amount_b ) ;
2025-12-04 21:53:58 -05:00
if user_holding_a_balance < actual_amount_a {
2025-11-20 21:02:18 -05:00
panic! ( " Insufficient balance " ) ;
}
2025-12-04 21:53:58 -05:00
if user_holding_b_balance < actual_amount_b {
2025-11-20 21:02:18 -05:00
panic! ( " Insufficient balance " ) ;
}
if actual_amount_a = = 0 | | actual_amount_b = = 0 {
panic! ( " A trade amount is 0 " ) ;
}
2025-11-14 20:59:42 -05:00
// 4. Calculate LP to mint
2025-12-04 21:53:58 -05:00
let delta_lp = ( pool_def_data . liquidity_pool_supply * actual_amount_b ) / pool_def_data . reserve_b ;
2025-11-14 20:59:42 -05:00
// 5. Update pool account
let mut pool_post = pool . account . clone ( ) ;
let pool_post_definition = PoolDefinition {
definition_token_a_id : pool_def_data . definition_token_a_id . clone ( ) ,
definition_token_b_id : pool_def_data . definition_token_b_id . clone ( ) ,
vault_a_addr : pool_def_data . vault_a_addr . clone ( ) ,
vault_b_addr : pool_def_data . vault_b_addr . clone ( ) ,
liquidity_pool_id : pool_def_data . liquidity_pool_id . clone ( ) ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply : pool_def_data . liquidity_pool_supply + delta_lp ,
2025-11-14 20:59:42 -05:00
reserve_a : pool_def_data . reserve_a + actual_amount_a ,
reserve_b : pool_def_data . reserve_b + actual_amount_b ,
2025-12-04 21:53:58 -05:00
active : true ,
2025-11-14 20:59:42 -05:00
} ;
2025-11-25 23:06:47 -05:00
2025-11-14 20:59:42 -05:00
pool_post . data = pool_post_definition . into_data ( ) ;
let mut chained_call = Vec ::new ( ) ;
2025-12-04 21:53:58 -05:00
// Chain call for Token A (user_holding_a -> Vault_A)
2025-11-14 20:59:42 -05:00
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
instruction_data [ 1 .. 17 ] . copy_from_slice ( & actual_amount_a . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . expect ( " Add liquidity: AMM Program expects valid token transfer instruction data " ) ;
2025-11-14 20:59:42 -05:00
let call_token_a = ChainedCall {
2025-12-04 21:53:58 -05:00
program_id : vault_a . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_a . clone ( ) , vault_a . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
2025-12-04 21:53:58 -05:00
// Chain call for Token B (user_holding_b -> Vault_B)
2025-11-25 23:06:47 -05:00
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
2025-11-14 20:59:42 -05:00
instruction_data [ 1 .. 17 ] . copy_from_slice ( & actual_amount_b . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . expect ( " Add liquidity: AMM Program expects valid token transfer instruction data " ) ;
2025-11-14 20:59:42 -05:00
let call_token_b = ChainedCall {
2025-12-04 21:53:58 -05:00
program_id : vault_b . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_b . clone ( ) , vault_b . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
2025-11-26 21:44:57 -05:00
2025-12-04 21:53:58 -05:00
// Chain call for LP (user_holding_lp -> Pool_LP)
2025-11-25 23:06:47 -05:00
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
2025-11-14 20:59:42 -05:00
instruction_data [ 1 .. 17 ] . copy_from_slice ( & delta_lp . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . expect ( " Add liquidity: AMM Program expects valid token transfer instruction data " ) ;
2025-11-14 20:59:42 -05:00
let call_token_lp = ChainedCall {
2025-12-04 21:53:58 -05:00
program_id : pool_lp . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ pool_lp . clone ( ) , user_holding_lp . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
2025-11-25 23:06:47 -05:00
2025-11-14 20:59:42 -05:00
chained_call . push ( call_token_lp ) ;
chained_call . push ( call_token_b ) ;
chained_call . push ( call_token_a ) ;
2025-11-25 09:02:43 -05:00
let post_states = vec! [
pool_post . clone ( ) ,
2025-11-14 20:59:42 -05:00
pre_states [ 1 ] . account . clone ( ) ,
pre_states [ 2 ] . account . clone ( ) ,
pre_states [ 3 ] . account . clone ( ) ,
pre_states [ 4 ] . account . clone ( ) ,
pre_states [ 5 ] . account . clone ( ) ,
pre_states [ 6 ] . account . clone ( ) , ] ;
( post_states . clone ( ) , chained_call )
}
2025-12-04 21:53:58 -05:00
fn remove_liquidity ( pre_states : & [ AccountWithMetadata ] ,
amounts : & [ u128 ]
) -> ( Vec < Account > , Vec < ChainedCall > )
{
2025-11-14 20:59:42 -05:00
if pre_states . len ( ) ! = 7 {
panic! ( " Invalid number of input accounts " ) ;
}
let pool = & pre_states [ 0 ] ;
2025-12-04 21:53:58 -05:00
let vault_a = & pre_states [ 1 ] ;
let vault_b = & pre_states [ 2 ] ;
2025-11-14 20:59:42 -05:00
let pool_lp = & pre_states [ 3 ] ;
2025-12-04 21:53:58 -05:00
let user_holding_a = & pre_states [ 4 ] ;
let user_holding_b = & pre_states [ 5 ] ;
let user_holding_lp = & pre_states [ 6 ] ;
if amounts . len ( ) ! = 3 {
panic! ( " Invalid number of balances " ) ;
}
let amount_lp = amounts [ 0 ] ;
let amount_min_a = amounts [ 1 ] ;
let amount_min_b = amounts [ 2 ] ;
2025-11-28 08:11:04 -05:00
// Verify vaults are in fact vaults
2025-12-04 21:53:58 -05:00
let pool_def_data = PoolDefinition ::parse ( & pool . account . data ) . expect ( " Remove liquidity: AMM Program expects a valid Pool Definition Account " ) ;
2025-11-14 20:59:42 -05:00
2025-12-04 21:53:58 -05:00
if ! pool_def_data . active {
panic! ( " Pool is inactive " ) ;
}
if vault_a . account_id ! = pool_def_data . vault_a_addr {
2025-11-28 08:11:04 -05:00
panic! ( " Vault A was not provided " ) ;
2025-12-04 21:53:58 -05:00
}
if vault_b . account_id ! = pool_def_data . vault_b_addr {
2025-11-18 14:43:40 -05:00
panic! ( " Vault B was not provided " ) ;
2025-12-04 21:53:58 -05:00
}
// 2. Compute withdrawal amounts
let user_holding_lp_data = TokenHolding ::parse ( & user_holding_lp . account . data ) . expect ( " Remove liquidity: AMM Program expects a valid Token Account for liquidity token " ) ;
if user_holding_lp_data . balance > pool_def_data . liquidity_pool_supply | | user_holding_lp_data . definition_id ! = pool_def_data . liquidity_pool_id {
panic! ( " Invalid liquidity account provided " ) ;
}
if user_holding_lp_data . balance < amount_lp {
panic! ( " Invalid liquidity amount provided " ) ;
}
2025-11-14 20:59:42 -05:00
2025-12-04 21:53:58 -05:00
let withdraw_amount_a = pool_def_data . reserve_a * ( amount_lp / pool_def_data . liquidity_pool_supply ) ;
let withdraw_amount_b = pool_def_data . reserve_b * ( amount_lp / pool_def_data . liquidity_pool_supply ) ;
2025-11-14 20:59:42 -05:00
2025-12-04 21:53:58 -05:00
// 3. Validate and slippage check
if withdraw_amount_a < amount_min_a {
panic! ( " Insufficient minimal withdraw amount (Token A) provided for liquidity amount " ) ;
}
if withdraw_amount_b < amount_min_b {
panic! ( " Insufficient minimal withdraw amount (Token B) provided for liquidity amount " ) ;
}
2025-11-14 20:59:42 -05:00
// 4. Calculate LP to reduce cap by
2025-12-04 21:53:58 -05:00
let delta_lp : u128 = ( pool_def_data . liquidity_pool_supply * amount_lp ) / pool_def_data . liquidity_pool_supply ;
2025-11-14 20:59:42 -05:00
// 5. Update pool account
let mut pool_post = pool . account . clone ( ) ;
let pool_post_definition = PoolDefinition {
definition_token_a_id : pool_def_data . definition_token_a_id . clone ( ) ,
definition_token_b_id : pool_def_data . definition_token_b_id . clone ( ) ,
vault_a_addr : pool_def_data . vault_a_addr . clone ( ) ,
vault_b_addr : pool_def_data . vault_b_addr . clone ( ) ,
liquidity_pool_id : pool_def_data . liquidity_pool_id . clone ( ) ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply : pool_def_data . liquidity_pool_supply - delta_lp ,
2025-11-14 20:59:42 -05:00
reserve_a : pool_def_data . reserve_a - withdraw_amount_a ,
reserve_b : pool_def_data . reserve_b - withdraw_amount_b ,
2025-12-04 21:53:58 -05:00
active : true ,
2025-11-14 20:59:42 -05:00
} ;
pool_post . data = pool_post_definition . into_data ( ) ;
let mut chained_call = Vec ::new ( ) ;
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
2025-11-26 21:44:57 -05:00
//Chaincall for Token A withdraw
2025-11-25 23:06:47 -05:00
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & withdraw_amount_a . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . expect ( " Remove liquidity: AMM Program expects valid token transfer instruction data " ) ;
2025-11-14 20:59:42 -05:00
let call_token_a = ChainedCall {
2025-12-04 21:53:58 -05:00
program_id : vault_a . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ vault_a . clone ( ) , user_holding_a . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
2025-11-26 21:44:57 -05:00
//Chaincall for Token B withdraw
2025-11-25 23:06:47 -05:00
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & withdraw_amount_b . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . expect ( " Remove liquidity: AMM Program expects valid token transfer instruction data " ) ;
2025-11-14 20:59:42 -05:00
let call_token_b = ChainedCall {
2025-12-04 21:53:58 -05:00
program_id : vault_b . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ vault_b . clone ( ) , user_holding_b . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
2025-12-04 21:53:58 -05:00
//TODO: make this a call for burn once implemented in
// Token Program
2025-11-26 21:44:57 -05:00
//Chaincall for LP adjustment
2025-11-25 23:06:47 -05:00
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & delta_lp . to_le_bytes ( ) ) ;
2025-12-04 21:53:58 -05:00
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . expect ( " Remove liquidity: AMM Program expects valid token transfer instruction data " ) ;
2025-11-14 20:59:42 -05:00
let call_token_lp = ChainedCall {
2025-12-04 21:53:58 -05:00
program_id : pool_lp . account . program_owner ,
2025-11-25 23:06:47 -05:00
instruction_data : instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_lp . clone ( ) , pool_lp . clone ( ) ]
2025-11-14 20:59:42 -05:00
} ;
chained_call . push ( call_token_lp ) ;
chained_call . push ( call_token_b ) ;
chained_call . push ( call_token_a ) ;
2025-11-25 09:02:43 -05:00
let post_states = vec !
[ pool_post . clone ( ) ,
2025-11-14 20:59:42 -05:00
pre_states [ 1 ] . account . clone ( ) ,
pre_states [ 2 ] . account . clone ( ) ,
pre_states [ 3 ] . account . clone ( ) ,
pre_states [ 4 ] . account . clone ( ) ,
pre_states [ 5 ] . account . clone ( ) ,
pre_states [ 6 ] . account . clone ( ) ] ;
2025-11-26 21:44:57 -05:00
( post_states , chained_call )
2025-11-17 18:48:17 -05:00
}
2025-11-18 14:43:40 -05:00
#[ cfg(test) ]
mod tests {
2025-12-04 21:53:58 -05:00
use nssa_core ::{ { account ::{ Account , AccountId , AccountWithMetadata , Data } , program ::ChainedCall } , program ::ProgramId } ;
2025-11-18 14:43:40 -05:00
2025-11-21 19:17:50 -05:00
use crate ::{ PoolDefinition , TOKEN_HOLDING_TYPE , TokenHolding , add_liquidity , new_definition , remove_liquidity , swap } ;
2025-11-18 14:43:40 -05:00
2025-12-04 21:53:58 -05:00
const TOKEN_PROGRAM_ID : ProgramId = [ 15 ; 8 ] ;
enum AccountEnum {
user_holding_b ,
user_holding_a ,
vault_a_uninit ,
vault_b_uninit ,
vault_a_init ,
vault_b_init ,
vault_a_wrong_acc_id ,
vault_b_wrong_acc_id ,
pool_lp_uninit ,
pool_lp_init ,
pool_lp_wrong_acc_id ,
user_holding_lp_uninit ,
user_holding_lp_init ,
pool_definition_uninit ,
pool_definition_init ,
pool_definition_unauth ,
}
enum BalanceEnum {
vault_a_reserve_init ,
vault_b_reserve_init ,
user_token_a_bal ,
user_token_b_bal ,
user_token_lp_bal ,
remove_min_amount_a ,
remove_min_amount_b ,
remove_amount_lp ,
remove_amount_lp_too_large ,
add_amount_a ,
add_amount_b ,
}
fn helper_balance_constructor ( selection : BalanceEnum ) -> u128 {
match selection {
BalanceEnum ::vault_a_reserve_init = > 1000 ,
BalanceEnum ::vault_b_reserve_init = > 250 ,
BalanceEnum ::user_token_a_bal = > 1000 ,
BalanceEnum ::user_token_b_bal = > 500 ,
BalanceEnum ::user_token_lp_bal = > 100 ,
BalanceEnum ::remove_min_amount_a = > 50 ,
BalanceEnum ::remove_min_amount_b = > 50 ,
BalanceEnum ::remove_amount_lp = > 50 ,
BalanceEnum ::remove_amount_lp_too_large = > 150 ,
BalanceEnum ::add_amount_a = > 500 ,
BalanceEnum ::add_amount_b = > 200 ,
_ = > panic! ( " Invalid selection " )
}
}
enum IdEnum {
token_a_definition_id ,
token_b_definition_id ,
token_lp_definition_id ,
user_token_a_id ,
user_token_b_id ,
user_token_lp_id ,
pool_definition_id ,
vault_a_id ,
vault_b_id ,
pool_lp_id ,
}
fn helper_id_constructor ( selection : IdEnum ) -> AccountId {
match selection {
IdEnum ::token_a_definition_id = > AccountId ::new ( [ 42 ; 32 ] ) ,
IdEnum ::token_b_definition_id = > AccountId ::new ( [ 43 ; 32 ] ) ,
IdEnum ::token_lp_definition_id = > AccountId ::new ( [ 44 ; 32 ] ) ,
IdEnum ::user_token_a_id = > AccountId ::new ( [ 45 ; 32 ] ) ,
IdEnum ::user_token_b_id = > AccountId ::new ( [ 46 ; 32 ] ) ,
IdEnum ::user_token_lp_id = > AccountId ::new ( [ 47 ; 32 ] ) ,
IdEnum ::pool_definition_id = > AccountId ::new ( [ 48 ; 32 ] ) ,
IdEnum ::vault_a_id = > AccountId ::new ( [ 45 ; 32 ] ) ,
IdEnum ::vault_b_id = > AccountId ::new ( [ 46 ; 32 ] ) ,
IdEnum ::pool_lp_id = > AccountId ::new ( [ 47 ; 32 ] ) ,
_ = > panic! ( " Invalid selection " )
}
}
fn helper_account_constructor ( selection : AccountEnum ) -> AccountWithMetadata {
let amm_program_id : ProgramId = [ 16 ; 8 ] ;
match selection {
AccountEnum ::user_holding_a = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_a_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::user_token_a_id ) ,
} ,
AccountEnum ::user_holding_b = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_b_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::user_token_b_id ) ,
} ,
AccountEnum ::vault_a_uninit = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_a_definition_id ) ,
balance : 0 ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::vault_a_id ) ,
} ,
AccountEnum ::vault_b_uninit = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_b_definition_id ) ,
balance : 0 ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::vault_b_id ) ,
} ,
AccountEnum ::vault_a_init = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_a_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::vault_a_id ) ,
} ,
AccountEnum ::vault_b_init = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_b_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::vault_b_reserve_init ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::vault_b_id ) ,
} ,
AccountEnum ::vault_a_wrong_acc_id = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_a_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::vault_b_id ) ,
} ,
AccountEnum ::vault_b_wrong_acc_id = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_b_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::vault_b_reserve_init ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::vault_a_id ) ,
} ,
AccountEnum ::pool_lp_uninit = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_lp_definition_id ) ,
balance : 0 ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::pool_lp_id ) ,
} ,
AccountEnum ::pool_lp_init = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_lp_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::pool_lp_id ) ,
} ,
AccountEnum ::pool_lp_wrong_acc_id = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_lp_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::vault_a_id ) ,
} ,
AccountEnum ::user_holding_lp_uninit = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_lp_definition_id ) ,
balance : 0 ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::user_token_lp_id ) ,
} ,
AccountEnum ::user_holding_lp_init = > AccountWithMetadata {
account : Account {
program_owner : TOKEN_PROGRAM_ID ,
balance : 0 u128 ,
data : TokenHolding ::into_data (
TokenHolding {
account_type : 1 u8 ,
definition_id : helper_id_constructor ( IdEnum ::token_lp_definition_id ) ,
balance : helper_balance_constructor ( BalanceEnum ::user_token_lp_bal ) ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::user_token_lp_id ) ,
} ,
AccountEnum ::pool_definition_uninit = > AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::pool_definition_id ) ,
} ,
AccountEnum ::pool_definition_init = > AccountWithMetadata {
account : Account {
program_owner : amm_program_id ,
balance : 0 u128 ,
data : PoolDefinition ::into_data (
PoolDefinition {
definition_token_a_id : helper_id_constructor ( IdEnum ::token_a_definition_id ) ,
definition_token_b_id : helper_id_constructor ( IdEnum ::token_b_definition_id ) ,
vault_a_addr : helper_id_constructor ( IdEnum ::vault_a_id ) ,
vault_b_addr : helper_id_constructor ( IdEnum ::vault_b_id ) ,
liquidity_pool_id : helper_id_constructor ( IdEnum ::token_lp_definition_id ) ,
liquidity_pool_supply : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
reserve_a : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
reserve_b : helper_balance_constructor ( BalanceEnum ::vault_b_reserve_init ) ,
active : true ,
} ) ,
nonce : 0 ,
} ,
is_authorized : true ,
account_id : helper_id_constructor ( IdEnum ::pool_definition_id ) ,
} ,
AccountEnum ::pool_definition_unauth = > AccountWithMetadata {
account : Account {
program_owner : amm_program_id ,
balance : 0 u128 ,
data : PoolDefinition ::into_data (
PoolDefinition {
definition_token_a_id : helper_id_constructor ( IdEnum ::token_a_definition_id ) ,
definition_token_b_id : helper_id_constructor ( IdEnum ::token_b_definition_id ) ,
vault_a_addr : helper_id_constructor ( IdEnum ::vault_a_id ) ,
vault_b_addr : helper_id_constructor ( IdEnum ::vault_b_id ) ,
liquidity_pool_id : helper_id_constructor ( IdEnum ::token_lp_definition_id ) ,
liquidity_pool_supply : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
reserve_a : helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) ,
reserve_b : helper_balance_constructor ( BalanceEnum ::vault_b_reserve_init ) ,
active : true ,
} ) ,
nonce : 0 ,
} ,
is_authorized : false ,
account_id : helper_id_constructor ( IdEnum ::pool_definition_id ) ,
} ,
_ = > panic! ( " Invalid selection " ) ,
}
}
/*
TODO : delete
fn helper_account_constructor ( selection : AccountEnum ) -> AccountWithMetadata {
let amm_program_id : ProgramId = [ 15 ; 8 ] ;
let token_program_id : ProgramId = [ 16 ; 8 ] ;
let helper_id_constructor ( IdEnum ::token_a_definition_id ) = AccountId ::new ( [ 42 ; 32 ] ) ;
let helper_id_constructor ( IdEnum ::token_b_definition_id ) = AccountId ::new ( [ 43 ; 32 ] ) ;
let helper_id_constructor ( IdEnum ::token_lp_definition_id ) = AccountId ::new ( [ 44 ; 32 ] ) ;
let user_token_a_id = AccountId ::new ( [ 45 ; 32 ] ) ;
let user_token_b_id = AccountId ::new ( [ 46 ; 32 ] ) ;
let user_token_lp_id = AccountId ::new ( [ 47 ; 32 ] ) ;
let pool_definition_id = AccountId ::new ( [ 48 ; 32 ] ) ;
let vault_a_id = AccountId ::new ( [ 45 ; 32 ] ) ;
let vault_b_id = AccountId ::new ( [ 46 ; 32 ] ) ;
let pool_lp_id = AccountId ::new ( [ 47 ; 32 ] ) ;
let helper_balance_constructor ( BalanceEnum ::vault_a_reserve_init ) : u128 = 1000 ;
let helper_balance_constructor ( BalanceEnum ::vault_b_reserve_init ) : u128 = 250 ;
let user_token_a_bal : u128 = 500 ;
let helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) : u128 = 250 ;
let helper_balance_constructor ( BalanceEnum ::user_token_lp_bal ) : u128 = 100 ;
enum AccountEnum {
account_a_holding ,
account_b_holding ,
vault_a_uninit ,
vault_b_uninit ,
vault_a_init ,
vault_b_init ,
vault_a_wrong_acc_id ,
vault_b_wrong_acc_id ,
pool_lp_uninit ,
pool_lp_init ,
pool_lp_wrong_acc_id ,
account_lp_holding_uninit ,
account_lp_holding_init ,
pool_definition_uninit ,
pool_definition_init ,
enum BalanceEnum {
uninit_balance ,
vault_a_reserve_init ,
vault_b_reserve_init ,
user_token_a_bal ,
user_token_b_bal ,
user_token_lp_bal
}
}
// amm_pool is a default account that will initiate the amm definition account values
// vault_holding_a is a token holding account for token a
// vault_holding_b is a token holding account for token b
// pool_lp is a token holding account for the pool's lp token
// user_holding_a is a token holding account for token a
// user_holding_b is a token holding account for token b
// user_holding_lp is a token holding account for lp token
* /
2025-11-18 14:43:40 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_new_definition_with_invalid_number_of_accounts_1 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [ helper_account_constructor ( AccountEnum ::pool_definition_uninit ) , ]
;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_new_definition_with_invalid_number_of_accounts_2 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_new_definition_with_invalid_number_of_accounts_3 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
}
2025-11-18 14:43:40 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_new_definition_with_invalid_number_of_accounts_4 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_new_definition_with_invalid_number_of_accounts_5 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_new_definition_with_invalid_number_of_accounts_6 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of balance " ) ]
#[ test ]
fn test_call_new_definition_with_invalid_number_of_balances_1 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
2025-11-25 23:06:47 -05:00
#[ should_panic(expected = " Pool account is initiated or not authorized " ) ]
2025-11-18 14:43:40 -05:00
#[ test ]
2025-11-25 23:06:47 -05:00
fn test_call_new_definition_with_initiated_pool ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
2025-11-25 23:06:47 -05:00
#[ should_panic(expected = " Pool account is initiated or not authorized " ) ]
2025-11-18 14:43:40 -05:00
#[ test ]
fn test_call_new_definition_with_unauthorized_pool ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_unauth ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Balances must be nonzero " ) ]
#[ test ]
fn test_call_new_definition_with_balance_zero_1 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ 0 ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
}
2025-11-18 14:43:40 -05:00
#[ should_panic(expected = " Balances must be nonzero " ) ]
#[ test ]
fn test_call_new_definition_with_balance_zero_2 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
0 ] ,
TOKEN_PROGRAM_ID ) ;
2025-11-18 14:43:40 -05:00
}
2025-12-04 21:53:58 -05:00
2025-11-25 23:06:47 -05:00
#[ should_panic(expected = " Cannot set up a swap for a token with itself. " ) ]
2025-11-18 14:43:40 -05:00
#[ test ]
fn test_call_new_definition_same_token ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_uninit ) ,
] ;
let _post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
}
//TODO: fix this
#[ test ]
fn test_call_new_definition_chain_call_success ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_a_uninit ) ,
helper_account_constructor ( AccountEnum ::vault_b_uninit ) ,
helper_account_constructor ( AccountEnum ::pool_lp_uninit ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_uninit ) ,
] ;
let post_states = new_definition ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::user_token_a_bal ) ,
helper_balance_constructor ( BalanceEnum ::user_token_b_bal ) ] ,
TOKEN_PROGRAM_ID ) ;
}
2025-11-18 14:43:40 -05:00
2025-11-26 21:44:57 -05:00
2025-12-04 21:53:58 -05:00
/* TODO: ^^^ need to chain call checks
let user_holding_a = AccountWithMetadata {
account : user_holding_a . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ;
let user_holding_b = AccountWithMetadata {
account : user_holding_b . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ;
2025-11-26 21:44:57 -05:00
2025-12-04 21:53:58 -05:00
let user_holding_lp = AccountWithMetadata {
account : user_holding_lp . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] )
} ;
2025-11-25 23:06:47 -05:00
2025-12-04 21:53:58 -05:00
let vault_a = AccountWithMetadata {
account : vault_a . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 2 ; 32 ] ) } ;
2025-11-25 23:06:47 -05:00
2025-12-04 21:53:58 -05:00
let vault_b = AccountWithMetadata {
account : vault_b . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 3 ; 32 ] ) } ;
let pool_lp = AccountWithMetadata {
account : pool_lp . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ;
2025-11-18 14:43:40 -05:00
let pre_states = vec! [ AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : pool . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-11-26 21:44:57 -05:00
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
vault_a . clone ( ) ,
vault_b . clone ( ) ,
pool_lp . clone ( ) ,
2025-12-04 21:53:58 -05:00
user_holding_a . clone ( ) ,
user_holding_b . clone ( ) ,
user_holding_lp . clone ( ) ,
2025-11-18 14:43:40 -05:00
] ;
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
2025-11-26 21:44:57 -05:00
let ( post_states , chained_calls ) = new_definition ( & pre_states , & [ balance_a , balance_b ] , token_program_id ) ;
let chained_call_lp = chained_calls [ 0 ] . clone ( ) ;
let chained_call_b = chained_calls [ 1 ] . clone ( ) ;
let chained_call_a = chained_calls [ 2 ] . clone ( ) ;
//Expected chain_call for Token A
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & balance_a . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_a = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_a . clone ( ) , vault_a . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
//Expected chain call for Token B
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & balance_b . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_b = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_b . clone ( ) , vault_b . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
//Expected chain call for LP
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & balance_a . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_lp = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ pool_lp . clone ( ) , user_holding_lp . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
assert! ( chained_call_a . program_id = = expected_chained_call_a . program_id ) ;
assert! ( chained_call_a . instruction_data = = expected_chained_call_a . instruction_data ) ;
assert! ( chained_call_a . pre_states [ 0 ] . account = = expected_chained_call_a . pre_states [ 0 ] . account ) ;
assert! ( chained_call_a . pre_states [ 1 ] . account = = expected_chained_call_a . pre_states [ 1 ] . account ) ;
assert! ( chained_call_b . program_id = = expected_chained_call_b . program_id ) ;
assert! ( chained_call_b . instruction_data = = expected_chained_call_b . instruction_data ) ;
assert! ( chained_call_b . pre_states [ 0 ] . account = = expected_chained_call_b . pre_states [ 0 ] . account ) ;
assert! ( chained_call_b . pre_states [ 1 ] . account = = expected_chained_call_b . pre_states [ 1 ] . account ) ;
assert! ( chained_call_lp . program_id = = expected_chained_call_lp . program_id ) ;
assert! ( chained_call_lp . instruction_data = = expected_chained_call_lp . instruction_data ) ;
assert! ( chained_call_lp . pre_states [ 0 ] . account = = expected_chained_call_lp . pre_states [ 0 ] . account ) ;
assert! ( chained_call_lp . pre_states [ 1 ] . account = = expected_chained_call_lp . pre_states [ 1 ] . account ) ;
2025-12-04 21:53:58 -05:00
} * /
2025-11-18 14:43:40 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_remove_liquidity_with_invalid_number_of_accounts_1 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_remove_liquidity_with_invalid_number_of_accounts_2 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_remove_liquidity_with_invalid_number_of_accounts_3 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
2025-11-18 14:43:40 -05:00
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_remove_liquidity_with_invalid_number_of_accounts_4 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
}
2025-11-18 14:43:40 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_remove_liquidity_with_invalid_number_of_accounts_5 ( ) {
2025-12-04 21:53:58 -05:00
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
2025-11-18 14:43:40 -05:00
}
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
2025-11-18 14:43:40 -05:00
#[ test ]
2025-12-04 21:53:58 -05:00
fn test_call_remove_liquidity_with_invalid_number_of_accounts_6 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
}
#[ should_panic(expected = " Vault A was not provided " ) ]
#[ test ]
fn test_call_remove_liquidity_vault_a_omitted ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_wrong_acc_id ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
}
#[ should_panic(expected = " Vault B was not provided " ) ]
#[ test ]
fn test_call_remove_liquidity_vault_b_omitted ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_wrong_acc_id ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) ,
] ;
let _post_states = remove_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::remove_min_amount_b ) ] ,
) ;
}
#[ test ]
//TODO: need to fix this test
fn test_call_remove_liquidity_chain_call_success ( ) {
2025-11-18 14:43:40 -05:00
let mut pool = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut vault_a = Account ::default ( ) ;
let mut vault_b = Account ::default ( ) ;
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-18 14:43:40 -05:00
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_lp = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
vault_a . data = TokenHolding ::into_data (
2025-11-20 21:02:18 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-12-04 21:53:58 -05:00
vault_b . data = TokenHolding ::into_data (
2025-11-20 21:02:18 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-11-18 14:43:40 -05:00
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
let reserve_a : u128 = 30 ;
2025-11-18 14:43:40 -05:00
let reserve_b : u128 = 20 ;
2025-12-04 21:53:58 -05:00
let user_holding_lp_amt : u128 = 10 ;
2025-11-18 14:43:40 -05:00
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
2025-11-20 21:02:18 -05:00
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
2025-11-18 14:43:40 -05:00
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply : reserve_a ,
2025-11-18 14:43:40 -05:00
reserve_a ,
reserve_b ,
2025-12-04 21:53:58 -05:00
active : true ,
2025-11-18 14:43:40 -05:00
} ) ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 5 u128 }
) ;
2025-11-18 14:43:40 -05:00
2025-12-04 21:53:58 -05:00
user_holding_b . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 5 u128 }
) ;
user_holding_lp . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id : AccountId ::new ( [ 3 ; 32 ] ) ,
balance : user_holding_lp_amt }
) ;
let user_holding_a = AccountWithMetadata {
account : user_holding_a . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-12-04 21:53:58 -05:00
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ;
let user_holding_b = AccountWithMetadata {
account : user_holding_b . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-12-04 21:53:58 -05:00
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ;
let user_holding_lp = AccountWithMetadata {
account : user_holding_lp . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-12-04 21:53:58 -05:00
account_id : AccountId ::new ( [ 7 ; 32 ] )
} ;
let vault_a = AccountWithMetadata {
account : vault_a . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-12-04 21:53:58 -05:00
account_id : vault_a_addr . clone ( ) , } ;
let vault_b = AccountWithMetadata {
account : vault_b . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-12-04 21:53:58 -05:00
account_id : vault_b_addr . clone ( ) } ;
let pool_lp = AccountWithMetadata {
account : pool_lp . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-12-04 21:53:58 -05:00
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ;
let pre_states = vec! [ AccountWithMetadata {
account : pool . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-12-04 21:53:58 -05:00
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
vault_a . clone ( ) ,
vault_b . clone ( ) ,
pool_lp . clone ( ) ,
user_holding_a . clone ( ) ,
user_holding_b . clone ( ) ,
user_holding_lp . clone ( ) ,
2025-11-18 14:43:40 -05:00
] ;
2025-12-04 21:53:58 -05:00
let amount_lp = 5 ;
let amount_min_a = 2 ;
let amount_min_b = 2 ;
let ( post_states , chained_calls ) = remove_liquidity ( & pre_states , & [ amount_lp , amount_min_a , amount_min_b ] ) ;
let chained_call_lp = chained_calls [ 0 ] . clone ( ) ;
let chained_call_b = chained_calls [ 1 ] . clone ( ) ;
let chained_call_a = chained_calls [ 2 ] . clone ( ) ;
//Expected withdraw
let withdraw_amount_a = reserve_a * ( amount_lp / reserve_a ) ;
let withdraw_amount_b = reserve_b * ( amount_lp / reserve_a ) ;
//Expected chain_call for Token A
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & withdraw_amount_a . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_a = ChainedCall {
program_id : token_program_id ,
instruction_data ,
pre_states : vec ! [ vault_a . clone ( ) , user_holding_a . clone ( ) ] ,
} ;
//Expected chain call for Token B
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & withdraw_amount_b . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_b = ChainedCall {
program_id : token_program_id ,
instruction_data ,
pre_states : vec ! [ vault_b . clone ( ) , user_holding_b . clone ( ) ] ,
} ;
//Expected chain call for LP
let mut instruction : [ u8 ; 32 ] = [ 0 ; 32 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & user_holding_lp_amt . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_lp = ChainedCall {
program_id : token_program_id ,
instruction_data ,
pre_states : vec ! [ user_holding_lp . clone ( ) , pool_lp . clone ( ) ] ,
} ;
assert! ( chained_call_a . program_id = = expected_chained_call_a . program_id ) ;
assert! ( chained_call_a . instruction_data = = expected_chained_call_a . instruction_data ) ;
assert! ( chained_call_a . pre_states [ 0 ] . account = = expected_chained_call_a . pre_states [ 0 ] . account ) ;
assert! ( chained_call_a . pre_states [ 1 ] . account = = expected_chained_call_a . pre_states [ 1 ] . account ) ;
assert! ( chained_call_b . program_id = = expected_chained_call_b . program_id ) ;
assert! ( chained_call_b . instruction_data = = expected_chained_call_b . instruction_data ) ;
assert! ( chained_call_b . pre_states [ 0 ] . account = = expected_chained_call_b . pre_states [ 0 ] . account ) ;
assert! ( chained_call_b . pre_states [ 1 ] . account = = expected_chained_call_b . pre_states [ 1 ] . account ) ;
assert! ( chained_call_lp . program_id = = expected_chained_call_lp . program_id ) ;
assert! ( chained_call_lp . instruction_data = = expected_chained_call_lp . instruction_data ) ;
assert! ( chained_call_lp . pre_states [ 0 ] . account = = expected_chained_call_lp . pre_states [ 0 ] . account ) ;
assert! ( chained_call_lp . pre_states [ 1 ] . account = = expected_chained_call_lp . pre_states [ 1 ] . account ) ;
2025-11-18 14:43:40 -05:00
}
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_add_liquidity_with_invalid_number_of_accounts_1 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
2025-11-18 14:43:40 -05:00
#[ test ]
2025-12-04 21:53:58 -05:00
fn test_call_add_liquidity_with_invalid_number_of_accounts_2 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
2025-11-18 14:43:40 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_add_liquidity_with_invalid_number_of_accounts_3 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
2025-11-20 21:02:18 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_add_liquidity_with_invalid_number_of_accounts_4 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
2025-11-20 21:02:18 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_add_liquidity_with_invalid_number_of_accounts_5 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
2025-11-20 21:02:18 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_add_liquidity_with_invalid_number_of_accounts_6 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::remove_amount_lp ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
/*
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) , * /
2025-11-20 21:02:18 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Invalid number of input balances " ) ]
#[ test ]
fn test_call_add_liquidity_invalid_number_of_balances_1 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::add_amount_a ) , ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
2025-11-18 14:43:40 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Vault A was not provided " ) ]
#[ test ]
fn test_call_add_liquidity_vault_a_omitted ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_wrong_acc_id ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) , ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
2025-11-18 14:43:40 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Vault B was not provided " ) ]
#[ test ]
fn test_call_add_liquidity_vault_b_omitted ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_wrong_acc_id ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) , ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
2025-11-18 14:43:40 -05:00
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Both max-balances must be nonzero " ) ]
#[ test ]
fn test_call_add_liquidity_zero_balance_1 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ 0 ,
helper_balance_constructor ( BalanceEnum ::add_amount_b ) , ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
2025-11-18 14:43:40 -05:00
}
2025-12-04 21:53:58 -05:00
#[ should_panic(expected = " Both max-balances must be nonzero " ) ]
#[ test ]
fn test_call_add_liquidity_zero_balance_2 ( ) {
let pre_states = vec! [
helper_account_constructor ( AccountEnum ::pool_definition_init ) ,
helper_account_constructor ( AccountEnum ::vault_a_init ) ,
helper_account_constructor ( AccountEnum ::vault_b_init ) ,
helper_account_constructor ( AccountEnum ::pool_lp_init ) ,
helper_account_constructor ( AccountEnum ::user_holding_a ) ,
helper_account_constructor ( AccountEnum ::user_holding_b ) ,
helper_account_constructor ( AccountEnum ::user_holding_lp_init ) ,
] ;
let _post_states = add_liquidity ( & pre_states ,
& [ helper_balance_constructor ( BalanceEnum ::add_amount_a ) ,
0 , ] ,
helper_id_constructor ( IdEnum ::vault_a_id ) ,
) ;
}
/*
2025-11-20 21:02:18 -05:00
#[ should_panic(expected = " Mismatch of token types " ) ]
2025-11-18 14:43:40 -05:00
#[ test ]
2025-11-20 21:02:18 -05:00
fn test_call_add_liquidity_incorrect_token_type ( ) {
2025-11-18 14:43:40 -05:00
let mut pool = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
2025-11-18 14:43:40 -05:00
let mut pool_lp = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
2025-11-25 23:06:47 -05:00
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
2025-11-20 21:02:18 -05:00
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-11-18 14:43:40 -05:00
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-18 14:43:40 -05:00
let reserve_a : u128 = 10 ;
let reserve_b : u128 = 20 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id ,
definition_token_b_id ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-18 14:43:40 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
2025-11-20 21:02:18 -05:00
account : vault1 ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
2025-11-20 21:02:18 -05:00
account_id : vault_a_addr . clone ( ) } ,
2025-11-18 14:43:40 -05:00
AccountWithMetadata {
2025-11-20 21:02:18 -05:00
account : vault2 ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
2025-11-20 21:02:18 -05:00
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
2025-11-18 14:43:40 -05:00
] ;
2025-11-20 21:02:18 -05:00
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
let main_token = AccountId ::new ( [ 9 ; 32 ] ) ;
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
2025-11-18 14:43:40 -05:00
}
2025-11-20 21:02:18 -05:00
#[ should_panic(expected = " Vaults must have nonzero balances " ) ]
2025-11-18 14:43:40 -05:00
#[ test ]
2025-11-20 21:02:18 -05:00
fn test_call_add_liquidity_zero_vault_balance_1 ( ) {
2025-11-18 14:43:40 -05:00
let mut pool = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
2025-11-18 14:43:40 -05:00
let mut pool_lp = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
2025-11-25 23:06:47 -05:00
balance : 0 u128 }
2025-11-20 21:02:18 -05:00
) ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-20 21:02:18 -05:00
let reserve_a : u128 = 10 ;
let reserve_b : u128 = 20 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
] ;
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
let main_token = definition_token_a_id . clone ( ) ;
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
}
#[ should_panic(expected = " Vaults must have nonzero balances " ) ]
#[ test ]
fn test_call_add_liquidity_zero_vault_balance_2 ( ) {
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
2025-11-25 23:06:47 -05:00
balance : 0 u128 }
2025-11-20 21:02:18 -05:00
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-20 21:02:18 -05:00
let reserve_a : u128 = 10 ;
let reserve_b : u128 = 20 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
] ;
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
let main_token = definition_token_a_id . clone ( ) ;
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
}
#[ should_panic(expected = " Insufficient balance " ) ]
#[ test ]
fn test_call_add_liquidity_insufficient_balance_1 ( ) {
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = TokenHolding ::into_data (
2025-11-25 23:06:47 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 10 u128 }
) ;
2025-11-20 21:02:18 -05:00
vault1 . balance = 15 u128 ;
vault2 . balance = 15 u128 ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = TokenHolding ::into_data (
2025-11-25 23:06:47 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 40 u128 }
) ;
2025-11-20 21:02:18 -05:00
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-20 21:02:18 -05:00
let reserve_a : u128 = 15 ;
let reserve_b : u128 = 15 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
] ;
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
let main_token = definition_token_a_id . clone ( ) ;
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
}
#[ should_panic(expected = " Insufficient balance " ) ]
#[ test ]
fn test_call_add_liquidity_insufficient_balance_2 ( ) {
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = TokenHolding ::into_data (
2025-11-25 23:06:47 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 40 u128 }
) ;
2025-11-20 21:02:18 -05:00
vault1 . balance = 15 u128 ;
vault2 . balance = 15 u128 ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = TokenHolding ::into_data (
2025-11-25 23:06:47 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 10 u128 }
) ;
2025-11-20 21:02:18 -05:00
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-25 23:06:47 -05:00
let reserve_a : u128 = 15 ;
let reserve_b : u128 = 15 ;
2025-11-20 21:02:18 -05:00
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
] ;
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
2025-11-25 23:06:47 -05:00
let main_token = definition_token_a_id . clone ( ) ;
2025-11-20 21:02:18 -05:00
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
}
#[ should_panic(expected = " A trade amount is 0 " ) ]
#[ test ]
fn test_call_add_liquidity_actual_trade_insufficient ( ) {
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
2025-11-25 23:06:47 -05:00
balance : 1500 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = TokenHolding ::into_data (
2025-11-25 23:06:47 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 40 u128 }
2025-11-20 21:02:18 -05:00
) ;
2025-11-25 23:06:47 -05:00
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 1500 u128 }
) ;
2025-11-20 21:02:18 -05:00
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
2025-11-25 23:06:47 -05:00
balance : 2000 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = TokenHolding ::into_data (
2025-11-25 23:06:47 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 40 u128 }
2025-11-20 21:02:18 -05:00
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-20 21:02:18 -05:00
let reserve_a : u128 = 1500 u128 ;
let reserve_b : u128 = 2000 u128 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
] ;
let balance_a = 1 u128 ;
let balance_b = 1 u128 ;
let main_token = definition_token_b_id . clone ( ) ;
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
}
#[ should_panic(expected = " Reserves must be nonzero " ) ]
#[ test ]
2025-11-25 23:06:47 -05:00
fn test_call_add_liquidity_reserves_zero_1 ( ) {
2025-11-20 21:02:18 -05:00
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-20 21:02:18 -05:00
let reserve_a : u128 = 15 ;
let reserve_b : u128 = 0 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
2025-11-25 23:06:47 -05:00
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a ,
2025-11-25 23:06:47 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b ,
2025-11-25 23:06:47 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
] ;
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
let main_token = definition_token_b_id . clone ( ) ;
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
}
2025-11-26 21:44:57 -05:00
2025-11-25 23:06:47 -05:00
#[ should_panic(expected = " Reserves must be nonzero " ) ]
#[ test ]
fn test_call_add_liquidity_reserves_zero ( ) {
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-25 23:06:47 -05:00
let reserve_a : u128 = 0 ;
let reserve_b : u128 = 15 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-25 23:06:47 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] ) } ,
] ;
let balance_a = 15 u128 ;
let balance_b = 15 u128 ;
let main_token = definition_token_b_id . clone ( ) ;
let _post_states = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
}
2025-11-25 23:06:47 -05:00
#[ test ]
2025-11-26 21:44:57 -05:00
fn test_call_add_liquidity_chain_call_success_1 ( ) {
let mut pool = Account ::default ( ) ;
let mut vault_a = Account ::default ( ) ;
let mut vault_b = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-26 21:44:57 -05:00
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_lp = Account ::default ( ) ;
2025-11-25 23:06:47 -05:00
2025-11-26 21:44:57 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
2025-11-28 08:11:04 -05:00
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
2025-11-26 21:44:57 -05:00
vault_a . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault_b . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-26 21:44:57 -05:00
let reserve_a : u128 = 30 ;
let reserve_b : u128 = 20 ;
2025-12-04 21:53:58 -05:00
let user_holding_lp_amt : u128 = 10 ;
2025-11-26 21:44:57 -05:00
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
2025-11-28 08:11:04 -05:00
let vault_a_addr = AccountId ::new ( [ 7 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 9 ; 32 ] ) ;
2025-11-26 21:44:57 -05:00
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply : reserve_a ,
2025-11-26 21:44:57 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = TokenHolding ::into_data (
2025-11-26 21:44:57 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 50 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = TokenHolding ::into_data (
2025-11-26 21:44:57 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 50 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_lp . data = TokenHolding ::into_data (
2025-11-26 21:44:57 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id : AccountId ::new ( [ 3 ; 32 ] ) ,
2025-12-04 21:53:58 -05:00
balance : user_holding_lp_amt }
2025-11-26 21:44:57 -05:00
) ;
2025-12-04 21:53:58 -05:00
let user_holding_a = AccountWithMetadata {
account : user_holding_a . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ;
2025-12-04 21:53:58 -05:00
let user_holding_b = AccountWithMetadata {
account : user_holding_b . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ;
2025-12-04 21:53:58 -05:00
let user_holding_lp = AccountWithMetadata {
account : user_holding_lp . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] )
} ;
let vault_a = AccountWithMetadata {
account : vault_a . clone ( ) ,
is_authorized : true ,
2025-11-28 08:11:04 -05:00
account_id : vault_a_addr . clone ( ) } ;
2025-11-26 21:44:57 -05:00
let vault_b = AccountWithMetadata {
account : vault_b . clone ( ) ,
is_authorized : true ,
2025-11-28 08:11:04 -05:00
account_id : vault_b_addr . clone ( ) } ;
2025-11-26 21:44:57 -05:00
let pool_lp = AccountWithMetadata {
account : pool_lp . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ;
let pre_states = vec! [ AccountWithMetadata {
account : pool . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
vault_a . clone ( ) ,
vault_b . clone ( ) ,
pool_lp . clone ( ) ,
2025-12-04 21:53:58 -05:00
user_holding_a . clone ( ) ,
user_holding_b . clone ( ) ,
user_holding_lp . clone ( ) ,
2025-11-26 21:44:57 -05:00
] ;
let balance_a = 10 u128 ;
let balance_b = 30 u128 ;
let main_token = definition_token_a_id . clone ( ) ;
let ( post_states , chained_calls ) = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
let chained_call_lp = chained_calls [ 0 ] . clone ( ) ;
let chained_call_b = chained_calls [ 1 ] . clone ( ) ;
let chained_call_a = chained_calls [ 2 ] . clone ( ) ;
//Expected amounts
let expected_actual_amount_a = balance_a ;
//Uses: (pool_def_data.reserve_b*actual_amount_a)/pool_def_data.reserve_a
let expected_actual_amount_b = ( reserve_b * expected_actual_amount_a ) / reserve_a ;
let expected_delta_lp = ( reserve_a * expected_actual_amount_b ) / reserve_b ;
//Expected chain_call for Token A
let mut instruction : [ u8 ; 23 ] = [ 0 ; 23 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & expected_actual_amount_a . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_a = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_a . clone ( ) , vault_a . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
//Expected chain call for Token B
let mut instruction : [ u8 ; 23 ] = [ 0 ; 23 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & expected_actual_amount_b . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_b = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_b . clone ( ) , vault_b . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
//Expected chain call for LP
let mut instruction : [ u8 ; 23 ] = [ 0 ; 23 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & expected_delta_lp . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_lp = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ pool_lp . clone ( ) , user_holding_lp . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
assert! ( chained_call_a . program_id = = expected_chained_call_a . program_id ) ;
assert! ( chained_call_a . instruction_data = = expected_chained_call_a . instruction_data ) ;
assert! ( chained_call_a . pre_states [ 0 ] . account = = expected_chained_call_a . pre_states [ 0 ] . account ) ;
assert! ( chained_call_a . pre_states [ 1 ] . account = = expected_chained_call_a . pre_states [ 1 ] . account ) ;
assert! ( chained_call_b . program_id = = expected_chained_call_b . program_id ) ;
assert! ( chained_call_b . instruction_data = = expected_chained_call_b . instruction_data ) ;
assert! ( chained_call_b . pre_states [ 0 ] . account = = expected_chained_call_b . pre_states [ 0 ] . account ) ;
assert! ( chained_call_b . pre_states [ 1 ] . account = = expected_chained_call_b . pre_states [ 1 ] . account ) ;
assert! ( chained_call_lp . program_id = = expected_chained_call_lp . program_id ) ;
assert! ( chained_call_lp . instruction_data = = expected_chained_call_lp . instruction_data ) ;
assert! ( chained_call_lp . pre_states [ 0 ] . account = = expected_chained_call_lp . pre_states [ 0 ] . account ) ;
assert! ( chained_call_lp . pre_states [ 1 ] . account = = expected_chained_call_lp . pre_states [ 1 ] . account ) ;
}
#[ test ]
fn test_call_add_liquidity_chain_call_success_2 ( ) {
let mut pool = Account ::default ( ) ;
let mut vault_a = Account ::default ( ) ;
let mut vault_b = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-26 21:44:57 -05:00
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_lp = Account ::default ( ) ;
2025-11-26 21:44:57 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault_a . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault_b . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-26 21:44:57 -05:00
let reserve_a : u128 = 30 ;
let reserve_b : u128 = 20 ;
2025-12-04 21:53:58 -05:00
let user_holding_lp_amt : u128 = 10 ;
2025-11-26 21:44:57 -05:00
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
2025-11-28 08:11:04 -05:00
let vault_a_addr = AccountId ::new ( [ 2 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 3 ; 32 ] ) ;
2025-11-26 21:44:57 -05:00
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply : reserve_a ,
2025-11-26 21:44:57 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = TokenHolding ::into_data (
2025-11-26 21:44:57 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 50 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = TokenHolding ::into_data (
2025-11-26 21:44:57 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 50 u128 }
) ;
2025-12-04 21:53:58 -05:00
user_holding_lp . data = TokenHolding ::into_data (
2025-11-26 21:44:57 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id : AccountId ::new ( [ 3 ; 32 ] ) ,
2025-12-04 21:53:58 -05:00
balance : user_holding_lp_amt }
2025-11-26 21:44:57 -05:00
) ;
2025-12-04 21:53:58 -05:00
let user_holding_a = AccountWithMetadata {
account : user_holding_a . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) } ;
2025-12-04 21:53:58 -05:00
let user_holding_b = AccountWithMetadata {
account : user_holding_b . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 6 ; 32 ] ) } ;
2025-12-04 21:53:58 -05:00
let user_holding_lp = AccountWithMetadata {
account : user_holding_lp . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 7 ; 32 ] )
} ;
let vault_a = AccountWithMetadata {
account : vault_a . clone ( ) ,
is_authorized : true ,
2025-11-28 08:11:04 -05:00
account_id : vault_a_addr . clone ( ) } ;
2025-11-26 21:44:57 -05:00
let vault_b = AccountWithMetadata {
account : vault_b . clone ( ) ,
is_authorized : true ,
2025-11-28 08:11:04 -05:00
account_id : vault_b_addr . clone ( ) } ;
2025-11-26 21:44:57 -05:00
let pool_lp = AccountWithMetadata {
account : pool_lp . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ;
let pre_states = vec! [ AccountWithMetadata {
account : pool . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
vault_a . clone ( ) ,
vault_b . clone ( ) ,
pool_lp . clone ( ) ,
2025-12-04 21:53:58 -05:00
user_holding_a . clone ( ) ,
user_holding_b . clone ( ) ,
user_holding_lp . clone ( ) ,
2025-11-26 21:44:57 -05:00
] ;
let balance_a = 40 u128 ;
let balance_b = 20 u128 ;
let main_token = definition_token_b_id . clone ( ) ;
let ( post_states , chained_calls ) = add_liquidity ( & pre_states , & [ balance_a , balance_b ] , main_token ) ;
let chained_call_lp = chained_calls [ 0 ] . clone ( ) ;
let chained_call_b = chained_calls [ 1 ] . clone ( ) ;
let chained_call_a = chained_calls [ 2 ] . clone ( ) ;
//Expected amounts
let expected_actual_amount_b = balance_b ;
//Uses: (pool_def_data.reserve_b*actual_amount_a)/pool_def_data.reserve_a
let expected_actual_amount_a = ( reserve_a * expected_actual_amount_b ) / reserve_b ;
let expected_delta_lp = ( reserve_a * expected_actual_amount_b ) / reserve_b ;
//Expected chain_call for Token A
let mut instruction : [ u8 ; 23 ] = [ 0 ; 23 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & expected_actual_amount_a . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_a = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_a . clone ( ) , vault_a . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
//Expected chain call for Token B
let mut instruction : [ u8 ; 23 ] = [ 0 ; 23 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & expected_actual_amount_b . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_b = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ user_holding_b . clone ( ) , vault_b . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
//Expected chain call for LP
let mut instruction : [ u8 ; 23 ] = [ 0 ; 23 ] ;
instruction [ 0 ] = 1 ;
instruction [ 1 .. 17 ] . copy_from_slice ( & expected_delta_lp . to_le_bytes ( ) ) ;
let instruction_data = risc0_zkvm ::serde ::to_vec ( & instruction ) . unwrap ( ) ;
let expected_chained_call_lp = ChainedCall {
program_id : token_program_id ,
instruction_data ,
2025-12-04 21:53:58 -05:00
pre_states : vec ! [ pool_lp . clone ( ) , user_holding_lp . clone ( ) ] ,
2025-11-26 21:44:57 -05:00
} ;
assert! ( chained_call_a . program_id = = expected_chained_call_a . program_id ) ;
assert! ( chained_call_a . instruction_data = = expected_chained_call_a . instruction_data ) ;
assert! ( chained_call_a . pre_states [ 0 ] . account = = expected_chained_call_a . pre_states [ 0 ] . account ) ;
assert! ( chained_call_a . pre_states [ 1 ] . account = = expected_chained_call_a . pre_states [ 1 ] . account ) ;
assert! ( chained_call_b . program_id = = expected_chained_call_b . program_id ) ;
assert! ( chained_call_b . instruction_data = = expected_chained_call_b . instruction_data ) ;
assert! ( chained_call_b . pre_states [ 0 ] . account = = expected_chained_call_b . pre_states [ 0 ] . account ) ;
assert! ( chained_call_b . pre_states [ 1 ] . account = = expected_chained_call_b . pre_states [ 1 ] . account ) ;
assert! ( chained_call_lp . program_id = = expected_chained_call_lp . program_id ) ;
assert! ( chained_call_lp . instruction_data = = expected_chained_call_lp . instruction_data ) ;
assert! ( chained_call_lp . pre_states [ 0 ] . account = = expected_chained_call_lp . pre_states [ 0 ] . account ) ;
assert! ( chained_call_lp . pre_states [ 1 ] . account = = expected_chained_call_lp . pre_states [ 1 ] . account ) ;
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_swap_with_invalid_number_of_accounts_1 ( ) {
let pre_states = vec! [ AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 1 ; 32 ] ) ,
} ] ;
let amount = 15 u128 ;
let vault_addr = AccountId ::new ( [ 1 ; 32 ] ) ;
let _post_states = swap ( & pre_states , amount , vault_addr ) ;
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_swap_with_invalid_number_of_accounts_2 ( ) {
let pre_states = vec! [ AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 1 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 2 ; 32 ] ) } ,
] ;
let amount = 15 u128 ;
let vault_addr = AccountId ::new ( [ 1 ; 32 ] ) ;
let _post_states = swap ( & pre_states , amount , vault_addr ) ;
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_swap_with_invalid_number_of_accounts_3 ( ) {
let pre_states = vec! [ AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 1 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
2025-11-25 23:06:47 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 2 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 3 ; 32 ] ) } ,
] ;
let amount = 15 u128 ;
let vault_addr = AccountId ::new ( [ 1 ; 32 ] ) ;
let _post_states = swap ( & pre_states , amount , vault_addr ) ;
}
#[ should_panic(expected = " Invalid number of input accounts " ) ]
#[ test ]
fn test_call_swap_with_invalid_number_of_accounts_4 ( ) {
let pre_states = vec! [ AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 1 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 2 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 3 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
] ;
let amount = 15 u128 ;
let vault_addr = AccountId ::new ( [ 1 ; 32 ] ) ;
let _post_states = swap ( & pre_states , amount , vault_addr ) ;
}
2025-12-02 15:20:16 -05:00
#[ should_panic(expected = " AccountId is not a token type for the pool " ) ]
2025-11-20 21:02:18 -05:00
#[ test ]
fn test_call_swap_incorrect_token_type ( ) {
let mut pool = Account ::default ( ) ;
2025-12-02 17:21:49 -05:00
let mut vault_a = Account ::default ( ) ;
let mut vault_b = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-20 21:02:18 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
2025-12-02 17:21:49 -05:00
vault_a . data = TokenHolding ::into_data (
2025-11-20 21:02:18 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
2025-12-02 17:21:49 -05:00
vault_b . data = TokenHolding ::into_data (
2025-11-20 21:02:18 -05:00
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
2025-12-02 17:21:49 -05:00
balance : 20 u128 }
2025-11-20 21:02:18 -05:00
) ;
2025-11-18 14:43:40 -05:00
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = vec! [
2025-12-02 17:21:49 -05:00
0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = vec! [
2025-12-02 17:21:49 -05:00
1 , 1 , 1 , 1 , 1 , 1 , 1 , 12 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
2025-11-18 14:43:40 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-12-02 17:21:49 -05:00
let reserve_a : u128 = 15 ;
2025-11-18 14:43:40 -05:00
let reserve_b : u128 = 20 ;
2025-12-02 17:21:49 -05:00
let token_program_id : [ u32 ; 8 ] = [ 5 ; 8 ] ;
2025-11-18 14:43:40 -05:00
2025-12-02 17:21:49 -05:00
pool . data = PoolDefinition ::into_data (
PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-12-02 17:21:49 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
}
) ;
2025-11-18 14:43:40 -05:00
let pre_states = vec! [ AccountWithMetadata {
2025-12-02 17:21:49 -05:00
account : pool . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-02 17:21:49 -05:00
account : vault_a . clone ( ) ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
2025-12-02 17:21:49 -05:00
account : vault_b . clone ( ) ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a . clone ( ) ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b . clone ( ) ,
2025-11-20 21:02:18 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) }
] ;
let amount = 15 u128 ;
2025-12-02 17:21:49 -05:00
let token_addr = AccountId ::new ( [ 42 ; 32 ] ) ;
let ( post_accounts , chain_calls ) = swap ( & pre_states , amount , token_addr ) ;
2025-11-20 21:02:18 -05:00
}
#[ should_panic(expected = " Vault A was not provided " ) ]
#[ test ]
fn test_call_swap_vault_a_omitted ( ) {
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
2025-11-28 08:11:04 -05:00
definition_id :definition_token_a_id . clone ( ) ,
2025-11-20 21:02:18 -05:00
balance : 15 u128 }
) ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 15 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-20 21:02:18 -05:00
let reserve_a : u128 = 10 ;
let reserve_b : u128 = 20 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id ,
definition_token_b_id ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) }
] ;
let amount = 15 u128 ;
let vault_addr = AccountId ::new ( [ 0 ; 32 ] ) ;
let _post_states = swap ( & pre_states , amount , vault_addr ) ;
}
#[ should_panic(expected = " Vault B was not provided " ) ]
#[ test ]
fn test_call_swap_vault_b_omitted ( ) {
let mut pool = Account ::default ( ) ;
let mut vault1 = Account ::default ( ) ;
let mut vault2 = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
2025-11-28 08:11:04 -05:00
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
2025-11-20 21:02:18 -05:00
vault1 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault2 . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
2025-11-28 08:11:04 -05:00
definition_id :definition_token_b_id . clone ( ) ,
2025-11-20 21:02:18 -05:00
balance : 15 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-20 21:02:18 -05:00
let reserve_a : u128 = 10 ;
let reserve_b : u128 = 20 ;
let token_program_id : [ u32 ; 8 ] = [ 0 ; 8 ] ;
pool . data = PoolDefinition ::into_data ( PoolDefinition {
definition_token_a_id ,
definition_token_b_id ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-20 21:02:18 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
} ) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault1 ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault2 ,
2025-11-18 14:43:40 -05:00
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : pool_lp ,
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
account : Account ::default ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) }
] ;
let amount = 15 u128 ;
let vault_addr = AccountId ::new ( [ 0 ; 32 ] ) ;
let _post_states = swap ( & pre_states , amount , vault_addr ) ;
}
2025-11-26 21:44:57 -05:00
#[ test ]
fn test_call_swap_successful_chain_call_1 ( ) {
let mut pool = Account ::default ( ) ;
let mut vault_a = Account ::default ( ) ;
let mut vault_b = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-26 21:44:57 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault_a . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault_b . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 20 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = vec! [
2025-11-26 21:44:57 -05:00
0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = vec! [
2025-11-26 21:44:57 -05:00
1 , 1 , 1 , 1 , 1 , 1 , 1 , 12 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-26 21:44:57 -05:00
let reserve_a : u128 = 15 ;
let reserve_b : u128 = 20 ;
let token_program_id : [ u32 ; 8 ] = [ 5 ; 8 ] ;
2025-12-04 21:53:58 -05:00
user_holding_a . program_owner = token_program_id ;
user_holding_b . program_owner = token_program_id ;
2025-12-02 17:21:49 -05:00
vault_a . program_owner = token_program_id ;
vault_b . program_owner = token_program_id ;
2025-11-26 21:44:57 -05:00
pool . data = PoolDefinition ::into_data (
PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-26 21:44:57 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
}
) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault_a . clone ( ) ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault_b . clone ( ) ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) }
] ;
let amount = 15 u128 ;
2025-12-02 17:21:49 -05:00
2025-11-26 21:44:57 -05:00
let token_addr = definition_token_a_id ;
let ( post_accounts , chain_calls ) = swap ( & pre_states , amount , token_addr ) ;
let pool_post = post_accounts [ 0 ] . clone ( ) ;
let pool_pre_data = PoolDefinition ::parse ( & pool . data ) . unwrap ( ) ;
let pool_post_data = PoolDefinition ::parse ( & pool_post . data ) . unwrap ( ) ;
assert! ( pool_post_data . reserve_a = = pool_pre_data . reserve_a + amount ) ;
2025-12-02 17:21:49 -05:00
2025-11-26 21:44:57 -05:00
let expected_withdraw = ( pool_pre_data . reserve_b * amount ) / ( pool_pre_data . reserve_a + amount ) ;
assert! ( pool_post_data . reserve_b = = pool_pre_data . reserve_b - expected_withdraw ) ;
2025-12-02 17:21:49 -05:00
2025-11-26 21:44:57 -05:00
let chain_call_a = chain_calls [ 0 ] . clone ( ) ;
let chain_call_b = chain_calls [ 1 ] . clone ( ) ;
2025-12-02 17:21:49 -05:00
2025-11-26 21:44:57 -05:00
assert! ( chain_call_b . program_id = = token_program_id ) ;
assert! ( chain_call_a . program_id = = token_program_id ) ;
2025-12-02 17:21:49 -05:00
2025-11-26 21:44:57 -05:00
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
instruction_data [ 1 .. 17 ] . copy_from_slice ( & amount . to_le_bytes ( ) ) ;
let expected_instruction_data_0 = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . unwrap ( ) ;
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
instruction_data [ 1 .. 17 ] . copy_from_slice ( & expected_withdraw . to_le_bytes ( ) ) ;
let expected_instruction_data_1 = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . unwrap ( ) ;
let chain_call_a_account0 = chain_call_a . pre_states [ 0 ] . account . clone ( ) ;
let chain_call_a_account1 = chain_call_a . pre_states [ 1 ] . account . clone ( ) ;
let chain_call_b_account0 = chain_call_b . pre_states [ 0 ] . account . clone ( ) ;
let chain_call_b_account1 = chain_call_b . pre_states [ 1 ] . account . clone ( ) ;
assert! ( chain_call_a . instruction_data = = expected_instruction_data_0 ) ;
2025-12-04 21:53:58 -05:00
assert! ( chain_call_a_account0 = = user_holding_a ) ;
2025-11-26 21:44:57 -05:00
assert! ( chain_call_a_account1 = = vault_a ) ;
assert! ( chain_call_b . instruction_data = = expected_instruction_data_1 ) ;
assert! ( chain_call_b_account0 = = vault_b ) ;
2025-12-04 21:53:58 -05:00
assert! ( chain_call_b_account1 = = user_holding_b ) ;
2025-11-26 21:44:57 -05:00
}
#[ test ]
fn test_call_swap_successful_chain_call_2 ( ) {
let mut pool = Account ::default ( ) ;
let mut vault_a = Account ::default ( ) ;
let mut vault_b = Account ::default ( ) ;
let mut pool_lp = Account ::default ( ) ;
2025-12-04 21:53:58 -05:00
let mut user_holding_a = Account ::default ( ) ;
let mut user_holding_b = Account ::default ( ) ;
2025-11-26 21:44:57 -05:00
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
vault_a . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_a_id . clone ( ) ,
balance : 15 u128 }
) ;
vault_b . data = TokenHolding ::into_data (
TokenHolding { account_type : TOKEN_HOLDING_TYPE ,
definition_id :definition_token_b_id . clone ( ) ,
balance : 20 u128 }
) ;
pool_lp . data = vec! [
1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
2025-12-04 21:53:58 -05:00
user_holding_a . data = vec! [
2025-11-26 21:44:57 -05:00
0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
2025-12-04 21:53:58 -05:00
user_holding_b . data = vec! [
2025-11-26 21:44:57 -05:00
1 , 1 , 1 , 1 , 1 , 1 , 1 , 12 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,
1 , 1 , 1 , 1 , 1 , 10 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
] ;
let definition_token_a_id = AccountId ::new ( [ 1 ; 32 ] ) ;
let definition_token_b_id = AccountId ::new ( [ 2 ; 32 ] ) ;
let vault_a_addr = AccountId ::new ( [ 5 ; 32 ] ) ;
let vault_b_addr = AccountId ::new ( [ 6 ; 32 ] ) ;
let liquidity_pool_id = AccountId ::new ( [ 7 ; 32 ] ) ;
2025-12-04 21:53:58 -05:00
let liquidity_pool_supply : u128 = 30 u128 ;
2025-11-26 21:44:57 -05:00
let reserve_a : u128 = 15 ;
let reserve_b : u128 = 20 ;
let token_program_id : [ u32 ; 8 ] = [ 5 ; 8 ] ;
2025-12-04 21:53:58 -05:00
user_holding_a . program_owner = token_program_id ;
user_holding_b . program_owner = token_program_id ;
2025-12-02 17:21:49 -05:00
vault_a . program_owner = token_program_id ;
vault_b . program_owner = token_program_id ;
2025-11-26 21:44:57 -05:00
pool . data = PoolDefinition ::into_data (
PoolDefinition {
definition_token_a_id : definition_token_a_id . clone ( ) ,
definition_token_b_id : definition_token_b_id . clone ( ) ,
vault_a_addr : vault_a_addr . clone ( ) ,
vault_b_addr : vault_b_addr . clone ( ) ,
liquidity_pool_id ,
2025-12-04 21:53:58 -05:00
liquidity_pool_supply ,
2025-11-26 21:44:57 -05:00
reserve_a ,
reserve_b ,
token_program_id ,
}
) ;
let pre_states = vec! [ AccountWithMetadata {
account : pool . clone ( ) ,
is_authorized : true ,
account_id : AccountId ::new ( [ 0 ; 32 ] ) } ,
AccountWithMetadata {
account : vault_a . clone ( ) ,
is_authorized : true ,
account_id : vault_a_addr . clone ( ) } ,
AccountWithMetadata {
account : vault_b . clone ( ) ,
is_authorized : true ,
account_id : vault_b_addr . clone ( ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_a . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 4 ; 32 ] ) } ,
AccountWithMetadata {
2025-12-04 21:53:58 -05:00
account : user_holding_b . clone ( ) ,
2025-11-26 21:44:57 -05:00
is_authorized : true ,
account_id : AccountId ::new ( [ 5 ; 32 ] ) }
] ;
let amount = 15 u128 ;
let token_addr = definition_token_b_id ;
let ( post_accounts , chain_calls ) = swap ( & pre_states , amount , token_addr ) ;
let pool_post = post_accounts [ 0 ] . clone ( ) ;
let pool_pre_data = PoolDefinition ::parse ( & pool . data ) . unwrap ( ) ;
let pool_post_data = PoolDefinition ::parse ( & pool_post . data ) . unwrap ( ) ;
assert! ( pool_post_data . reserve_b = = pool_pre_data . reserve_b + amount ) ;
let expected_withdraw = ( pool_pre_data . reserve_a * amount ) / ( pool_pre_data . reserve_b + amount ) ;
assert! ( pool_post_data . reserve_a = = pool_pre_data . reserve_a - expected_withdraw ) ;
2025-12-02 17:21:49 -05:00
let chain_call_b = chain_calls [ 0 ] . clone ( ) ;
let chain_call_a = chain_calls [ 1 ] . clone ( ) ;
2025-11-26 21:44:57 -05:00
assert! ( chain_call_b . program_id = = token_program_id ) ;
assert! ( chain_call_a . program_id = = token_program_id ) ;
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
instruction_data [ 1 .. 17 ] . copy_from_slice ( & expected_withdraw . to_le_bytes ( ) ) ;
let expected_instruction_data_0 = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . unwrap ( ) ;
let mut instruction_data = [ 0 ; 23 ] ;
instruction_data [ 0 ] = 1 ;
instruction_data [ 1 .. 17 ] . copy_from_slice ( & amount . to_le_bytes ( ) ) ;
let expected_instruction_data_1 = risc0_zkvm ::serde ::to_vec ( & instruction_data ) . unwrap ( ) ;
let chain_call_a_account0 = chain_call_a . pre_states [ 0 ] . account . clone ( ) ;
let chain_call_a_account1 = chain_call_a . pre_states [ 1 ] . account . clone ( ) ;
let chain_call_b_account0 = chain_call_b . pre_states [ 0 ] . account . clone ( ) ;
let chain_call_b_account1 = chain_call_b . pre_states [ 1 ] . account . clone ( ) ;
assert! ( chain_call_a . instruction_data = = expected_instruction_data_0 ) ;
assert! ( chain_call_a_account0 = = vault_a ) ;
2025-12-04 21:53:58 -05:00
assert! ( chain_call_a_account1 = = user_holding_a ) ;
2025-11-26 21:44:57 -05:00
assert! ( chain_call_b . instruction_data = = expected_instruction_data_1 ) ;
2025-12-04 21:53:58 -05:00
assert! ( chain_call_b_account0 = = user_holding_b ) ;
2025-11-26 21:44:57 -05:00
assert! ( chain_call_b_account1 = = vault_b ) ;
}
2025-12-04 21:53:58 -05:00
* /
2025-11-26 21:44:57 -05:00
}