diff --git a/packages/solana-contracts/Anchor.toml b/packages/solana-contracts/Anchor.toml index 72cd8fa6a..c61d216e8 100644 --- a/packages/solana-contracts/Anchor.toml +++ b/packages/solana-contracts/Anchor.toml @@ -32,6 +32,7 @@ initialize_pool = "yarn run ts-node scripts/initializePool.ts" initialize_propeller = "yarn run ts-node scripts/initializePropeller.ts" scratch = "yarn run ts-node scripts/scratch.ts" add_to_pool = "yarn run ts-node scripts/addToPool.ts" +parse_account = "yarn run ts-node scripts/parseAccount.ts" [test] startup_wait = 100000 diff --git a/packages/solana-contracts/programs/propeller/src/constants.rs b/packages/solana-contracts/programs/propeller/src/constants.rs index 2b1c88f90..e71a1263f 100644 --- a/packages/solana-contracts/programs/propeller/src/constants.rs +++ b/packages/solana-contracts/programs/propeller/src/constants.rs @@ -1,24 +1,15 @@ -use {num_traits::FromPrimitive, rust_decimal::Decimal}; +use {rust_decimal::Decimal}; pub const CURRENT_SWIM_PAYLOAD_VERSION: u8 = 1; pub const TOKEN_COUNT: usize = 2; +pub const ENACT_DELAY: i64 = 3 * 86400; -// seed prefixes -// pub const SEED_PREFIX_CUSTODIAN: &str = "icco-custodian"; -// pub const SEED_PREFIX_SALE: &str = "icco-sale"; -// pub const SEED_PREFIX_BUYER: &str = "icco-buyer"; - -pub const CHAIN_ID_SOL: u16 = 1; -pub const CHAIN_ID_ETH: u16 = 2; - -pub const TOKEN_BRIDGE_MINT_OUTPUT_TOKEN_INDEX: usize = 0; -pub const SWAP_EXACT_INPUT_OUTPUT_TOKEN_INDEX: u8 = 0; +pub const METAPOOL_SWIM_USD_INDEX: u8 = 0; pub const REMOVE_EXACT_BURN_OUTPUT_TOKEN_INDEX: u8 = 0; pub const SWAP_EXACT_OUTPUT_INPUT_TOKEN_INDEX: u8 = 1; /// Pool IXs for sending ignore slippage pub const PROPELLER_MINIMUM_OUTPUT_AMOUNT: u64 = 0u64; -// pub const LAMPORTS_PER_SOL_DECIMAL: Decimal = Decimal::from_u64(1_000_000_000u64).unwrap(); pub const LAMPORTS_PER_SOL_DECIMAL: Decimal = Decimal::from_parts(1_000_000_000u32, 0, 0, false, 0u32); #[cfg(test)] @@ -33,41 +24,3 @@ mod test { // println!("token_bridge_id2: {}", ID); } } -// // vaa payload types -// pub const PAYLOAD_SALE_INIT_SOLANA: u8 = 5; // 1 for everyone else -// pub const PAYLOAD_ATTEST_CONTRIBUTIONS: u8 = 2; -// pub const PAYLOAD_SALE_SEALED: u8 = 3; -// pub const PAYLOAD_SALE_ABORTED: u8 = 4; -// -// // universal -// pub const PAYLOAD_HEADER_LEN: usize = 33; // payload + sale id -// pub const INDEX_SALE_ID: usize = 1; -// -// // for sale init -// pub const INDEX_SALE_INIT_TOKEN_ADDRESS: usize = 33; -// pub const INDEX_SALE_INIT_TOKEN_CHAIN: usize = 65; -// pub const INDEX_SALE_INIT_TOKEN_DECIMALS: usize = 67; -// pub const INDEX_SALE_INIT_SALE_START: usize = 68; -// pub const INDEX_SALE_INIT_SALE_END: usize = 100; -// pub const INDEX_SALE_INIT_ACCEPTED_TOKENS_START: usize = 132; -// -// pub const ACCEPTED_TOKEN_NUM_BYTES: usize = 33; -// pub const ACCEPTED_TOKENS_MAX: usize = 8; -// pub const INDEX_ACCEPTED_TOKEN_INDEX: usize = 0; -// pub const INDEX_ACCEPTED_TOKEN_ADDRESS: usize = 1; -// pub const INDEX_ACCEPTED_TOKEN_END: usize = 33; -// -// // for attest contributions -// pub const ATTEST_CONTRIBUTIONS_ELEMENT_LEN: usize = 33; // token index + amount -// -// // for sale sealed -// pub const INDEX_SALE_SEALED_ALLOCATIONS_START: usize = 33; -// -// pub const ALLOCATION_NUM_BYTES: usize = 65; -// pub const INDEX_ALLOCATIONS_AMOUNT: usize = 1; -// pub const INDEX_ALLOCATIONS_EXCESS: usize = 33; -// pub const INDEX_ALLOCATIONS_END: usize = 65; - -// misc -// pub const PAD_U8: usize = 31; -// pub const PAD_U64: usize = 24; diff --git a/packages/solana-contracts/programs/propeller/src/error.rs b/packages/solana-contracts/programs/propeller/src/error.rs index 9e4599a29..86ad8be8c 100644 --- a/packages/solana-contracts/programs/propeller/src/error.rs +++ b/packages/solana-contracts/programs/propeller/src/error.rs @@ -36,6 +36,9 @@ pub enum PropellerError { #[msg("TransferNotAllowed")] TransferNotAllowed, + #[msg("Invalid Pool for Init To SwimUSD")] + InvalidPoolForInitToSwimUsd, + #[msg("Incorrect ProgramId for CPI return value")] InvalidCpiReturnProgramId, @@ -72,7 +75,7 @@ pub enum PropellerError { #[msg("Not a valid Switchboard account")] InvalidSwitchboardAccount, - #[msg("Switchboard feed has not been updated in 5 minutes")] + #[msg("Switchboard feed value is stale ")] StaleFeed, #[msg("Switchboard feed exceeded provided confidence interval")] @@ -81,33 +84,42 @@ pub enum PropellerError { #[msg("Insufficient Amount being transferred")] InsufficientAmount, + #[msg("Invalid Wormhole Claim Account")] + InvalidWormholeClaimAccount, + #[msg("Invalid claim data")] InvalidClaimData, #[msg("Claim Account not claimed")] ClaimNotClaimed, - #[msg("Invalid Propeller Admin")] - InvalidPropellerAdmin, + #[msg("Invalid Propeller GovernanceKey")] + InvalidPropellerGovernanceKey, + + #[msg("Invalid Propeller Pause Key")] + InvalidPropellerPauseKey, - #[msg("Invalid Pool for Token Id Map")] - InvalidTokenIdMapPool, + #[msg("Invalid Pool for Token Number Map")] + InvalidTokenNumberMapPool, #[msg("Invalid Output Token Index")] InvalidOutputTokenIndex, - #[msg("Invalid Pool Token Index for Token Id Map")] - InvalidTokenIdMapPoolTokenIndex, + #[msg("Invalid Pool Token Index for Token Number Map")] + InvalidTokenNumberMapPoolTokenIndex, - #[msg("Invalid Pool Token Mint for Token Id Map")] - InvalidTokenIdMapPoolTokenMint, + #[msg("Invalid Pool Token Mint for Token Number Map")] + InvalidTokenNumberMapPoolTokenMint, - #[msg("Invalid Pool Ix for Token Id Map")] - InvalidTokenIdMapPoolIx, + #[msg("Invalid To Token Step for Token Number Map")] + InvalidTokenNumberMapToTokenStep, #[msg("Invalid Gas Kickstart parameter in Swim Payload")] InvalidSwimPayloadGasKickstart, + #[msg("Invalid Marginal Price Pool")] + InvalidMarginalPricePool, + #[msg("Invalid Marginal Price Pool Accounts")] InvalidMarginalPricePoolAccounts, @@ -141,15 +153,48 @@ pub enum PropellerError { #[msg("Owner of token account != swimPayload.owner")] IncorrectOwnerForCreateTokenAccount, - #[msg("TokenIdMap exists. Please use the correct instruction")] - TokenIdMapExists, - - #[msg("Invalid address for TokenIdMap account")] - InvalidTokenIdMapAccountAddress, + #[msg("TokenNumberMap exists. Please use the correct instruction")] + TokenNumberMapExists, #[msg("Invalid Swim Payload version")] InvalidSwimPayloadVersion, #[msg("Invalid Aggregator")] InvalidAggregator, + + #[msg("Invalid Fee Vault")] + InvalidFeeVault, + + #[msg("Invalid Memo")] + InvalidMemo, + + #[msg("ToTokenNumber does not match SwimPayload.to_tokenNumber")] + ToTokenNumberMismatch, + + #[msg("Routing Contract is paused")] + IsPaused, + + #[msg("Target Chain is paused")] + TargetChainIsPaused, + + #[msg("Invalid Target Chain Map")] + InvalidTargetChainMap, + + #[msg("Invalid SwimPayloadMessagePayer")] + InvalidSwimPayloadMessagePayer, + + #[msg("Invalid New Pause Key for UpdatePauseKey")] + InvalidNewPauseKey, + + #[msg("Invalid Upcoming Governance Key for Prepare Governance Transition")] + InvalidUpcomingGovernanceKey, + + #[msg("Invalid Enact Governance Transition")] + InvalidEnact, + + #[msg("Insufficient Delay for Enact Governance Transition")] + InsufficientDelay, + + #[msg("Invalid Fee Tracker")] + InvalidFeeTracker, } diff --git a/packages/solana-contracts/programs/propeller/src/fees.rs b/packages/solana-contracts/programs/propeller/src/fees.rs new file mode 100644 index 000000000..28452b9a9 --- /dev/null +++ b/packages/solana-contracts/programs/propeller/src/fees.rs @@ -0,0 +1,131 @@ +use { + crate::{constants::LAMPORTS_PER_SOL_DECIMAL, marginal_price_pool::*, FeeTracker, Propeller, PropellerError}, + anchor_lang::prelude::*, + anchor_spl::token::TokenAccount, + num_traits::{FromPrimitive, ToPrimitive}, + rust_decimal::Decimal, + switchboard_v2::{AggregatorAccountData, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID}, +}; + +pub trait Fees<'info> { + /// Calculate the fees, including txn and rent exemptions, in lamports. + fn calculate_fees_in_lamports(&self) -> Result; + // fn get_marginal_prices(&self) -> Result<[BorshDecimal; TOKEN_COUNT]>; + // fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64) -> Result; + fn transfer_fees_to_vault(&mut self, fees_in_swim_usd: u64) -> Result<()>; +} + +#[derive(Accounts)] +pub struct FeeTracking<'info> { + #[account( + constraint = + *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount, + )] + pub aggregator: AccountLoader<'info, AggregatorAccountData>, + + #[account(mut)] + /// this is "to_fees" + /// recipient of fees for executing complete transfer (e.g. relayer) + pub fee_vault: Box>, + + #[account(mut)] + pub fee_tracker: Box>, + pub marginal_price_pool: MarginalPricePool<'info>, +} + +impl<'info> FeeTracking<'info> { + pub fn validate(&self, propeller: &Propeller, payer: &Pubkey, program_id: &Pubkey) -> Result<()> { + require_keys_eq!( + self.marginal_price_pool.pool.key(), + propeller.marginal_price_pool, + PropellerError::InvalidMarginalPricePool + ); + require_keys_eq!(self.fee_vault.key(), propeller.fee_vault, PropellerError::InvalidFeeVault); + require_keys_eq!(self.aggregator.key(), propeller.aggregator, PropellerError::InvalidAggregator); + let expected_fee_tracker = Pubkey::create_program_address( + &[ + b"propeller".as_ref(), + b"fee".as_ref(), + propeller.swim_usd_mint.as_ref(), + payer.as_ref(), + &[self.fee_tracker.bump], + ], + program_id, + ) + .map_err(|_| PropellerError::InvalidFeeTracker)?; + require_keys_eq!(self.fee_tracker.key(), expected_fee_tracker, PropellerError::InvalidFeeTracker); + msg!("finished fees tracking validation"); + self.marginal_price_pool.validate(propeller) + } + + pub fn two_pool_program_key(&self) -> Pubkey { + self.marginal_price_pool.two_pool_program.key() + } + + pub fn track_fees(&mut self, fees_in_lamports: u64, propeller: &Propeller) -> Result { + let fee_in_swim_usd_atomic = self.convert_fees_to_swim_usd_atomic(fees_in_lamports, propeller)?; + self.update_fee_tracker(fee_in_swim_usd_atomic) + } + + fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64, propeller: &Propeller) -> Result { + let intermediate_token_price_decimal: Decimal = + self.marginal_price_pool.get_marginal_price_decimal(propeller)?; + + msg!("intermediate_token_price_decimal: {:?}", intermediate_token_price_decimal); + + let fee_in_lamports_decimal = Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::ConversionError)?; + msg!("fee_in_lamports(u64): {:?} fee_in_lamports_decimal: {:?}", fee_in_lamports, fee_in_lamports_decimal); + + let lamports_intermediate_token_price = self.get_lamports_intermediate_token_price(propeller)?; + let fee_in_swim_usd_decimal = lamports_intermediate_token_price + .checked_mul(fee_in_lamports_decimal) + .and_then(|x| x.checked_div(intermediate_token_price_decimal)) + .ok_or(PropellerError::IntegerOverflow)?; + + // let swim_usd_decimals = + // get_swim_usd_mint_decimals(&swim_usd_mint_key, &self.marginal_price_pool, &marginal_price_pool_lp_mint)?; + let swim_usd_decimals = self.marginal_price_pool.get_swim_usd_mint_decimals(propeller)?; + msg!("swim_usd_decimals: {:?}", swim_usd_decimals); + + let ten_pow_decimals = + Decimal::from_u64(10u64.pow(swim_usd_decimals as u32)).ok_or(PropellerError::IntegerOverflow)?; + let fee_in_swim_usd_atomic = fee_in_swim_usd_decimal + .checked_mul(ten_pow_decimals) + .and_then(|v| v.to_u64()) + .ok_or(PropellerError::ConversionError)?; + + msg!( + "fee_in_swim_usd_decimal: {:?} fee_in_swim_usd_atomic: {:?}", + fee_in_swim_usd_decimal, + fee_in_swim_usd_atomic + ); + Ok(fee_in_swim_usd_atomic) + } + + fn get_lamports_intermediate_token_price(&self, propeller: &Propeller) -> Result { + let feed = self.aggregator.load()?; + feed.check_staleness(Clock::get().unwrap().unix_timestamp, propeller.max_staleness) + .map_err(|_| error!(PropellerError::StaleFeed))?; + + // check feed does not exceed max_confidence_interval + // let max_confidence_interval = f64::MAX; + // // let max_confidence_interval = propeller.max_confidence_interval; + // feed.check_confidence_interval(SwitchboardDecimal::from_f64(max_confidence_interval)) + // .map_err(|_| error!(PropellerError::ConfidenceIntervalExceeded))?; + + let sol_usd_price: Decimal = feed.get_result()?.try_into()?; + + let lamports_usd_price = + sol_usd_price.checked_div(LAMPORTS_PER_SOL_DECIMAL).ok_or(PropellerError::IntegerOverflow)?; + msg!("sol_usd_price:{},lamports_usd_price: {}", sol_usd_price, lamports_usd_price); + Ok(lamports_usd_price) + // check whether the feed has been updated in the last 300 seconds + } + + fn update_fee_tracker(&mut self, fees_in_swim_usd_atomic: u64) -> Result { + let fee_tracker = &mut self.fee_tracker; + fee_tracker.fees_owed = + fee_tracker.fees_owed.checked_add(fees_in_swim_usd_atomic).ok_or(PropellerError::IntegerOverflow)?; + Ok(fees_in_swim_usd_atomic) + } +} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/create_owner_token_accounts.rs b/packages/solana-contracts/programs/propeller/src/instructions/create_owner_token_accounts.rs index 65442a0d6..db809f6de 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/create_owner_token_accounts.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/create_owner_token_accounts.rs @@ -1,44 +1,18 @@ pub use switchboard_v2::{AggregatorAccountData, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID}; use { crate::{ - constants::LAMPORTS_PER_SOL_DECIMAL, convert_fees_to_swim_usd_atomic, get_lamports_intermediate_token_price, - get_marginal_price_decimal, get_marginal_prices, get_swim_usd_mint_decimals, FeeTracker, - }, - anchor_spl::{ - associated_token::{get_associated_token_address, AssociatedToken}, - token::Transfer, - }, - num_traits::{FromPrimitive, ToPrimitive}, - rust_decimal::Decimal, - solana_program::{instruction::Instruction, program::invoke_signed}, - two_pool::BorshDecimal, -}; -use { - crate::{ - deserialize_message_payload, - // env::*, error::*, - get_message_data, - get_transfer_with_payload_from_message_account, - hash_vaa, - state::{SwimClaim, SwimPayloadMessage, *}, - token_bridge::TokenBridge, - token_id_map::{PoolInstruction, TokenIdMap}, - ClaimData, - PayloadTransferWithPayload, - PostVAAData, - PostedVAAData, - Propeller, - RawSwimPayload, - TOKEN_COUNT, + fees::*, + get_memo_as_utf8, + state::{SwimPayloadMessage}, + token_number_map::{TokenNumberMap}, Fees, Propeller, }, anchor_lang::{prelude::*, solana_program::program::invoke}, anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, + associated_token::{get_associated_token_address, AssociatedToken}, + token::{self, Mint, Token, TokenAccount, Transfer}, }, - std::convert::TryInto, - two_pool::state::TwoPool, + two_pool::{state::TwoPool}, }; #[derive(Accounts)] @@ -46,7 +20,7 @@ pub struct PropellerCreateOwnerTokenAccounts<'info> { #[account( seeds = [ b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], bump = propeller.bump, - has_one = aggregator @ PropellerError::InvalidAggregator + constraint = !propeller.is_paused @ PropellerError::IsPaused, )] pub propeller: Box>, #[account(mut)] @@ -63,28 +37,9 @@ pub struct PropellerCreateOwnerTokenAccounts<'info> { pub redeemer: SystemAccount<'info>, #[account( mut, - token::mint = propeller.swim_usd_mint, - token::authority = redeemer, + address = get_associated_token_address(&redeemer.key(), &propeller.swim_usd_mint) )] pub redeemer_escrow: Box>, - #[account( - mut, - token::mint = propeller.swim_usd_mint, - token::authority = propeller, - )] - pub fee_vault: Box>, - - #[account( - mut, - seeds = [ - b"propeller".as_ref(), - b"fee".as_ref(), - propeller.swim_usd_mint.as_ref(), - payer.key().as_ref() - ], - bump = fee_tracker.bump - )] - pub fee_tracker: Account<'info, FeeTracker>, #[account( seeds = [ @@ -93,7 +48,7 @@ pub struct PropellerCreateOwnerTokenAccounts<'info> { swim_payload_message.vaa_sequence.to_be_bytes().as_ref(), ], bump, - seeds::program = propeller.token_bridge().unwrap() + seeds::program = propeller.token_bridge(), )] /// CHECK: WH Claim account pub claim: UncheckedAccount<'info>, @@ -105,7 +60,8 @@ pub struct PropellerCreateOwnerTokenAccounts<'info> { b"swim_payload".as_ref(), claim.key().as_ref(), ], - bump = swim_payload_message.bump + bump = swim_payload_message.bump, + has_one = claim, )] pub swim_payload_message: Box>, @@ -116,9 +72,10 @@ pub struct PropellerCreateOwnerTokenAccounts<'info> { propeller.key().as_ref(), &swim_payload_message.target_token_id.to_le_bytes() ], - bump = token_id_map.bump, + bump = token_number_map.bump, + has_one = pool @ PropellerError::InvalidTokenNumberMapPool, )] - pub token_id_map: Box>, + pub token_number_map: Box>, #[account( mut, @@ -129,52 +86,41 @@ pub struct PropellerCreateOwnerTokenAccounts<'info> { pool_lp_mint.key().as_ref(), ], bump = pool.bump, - seeds::program = two_pool_program.key() + seeds::program = fee_tracking.two_pool_program_key(), )] pub pool: Box>, + #[account(address = pool.token_mint_keys[0])] pub pool_token_0_mint: Box>, + #[account(address = pool.token_mint_keys[1])] pub pool_token_1_mint: Box>, + #[account(address = pool.lp_mint_key)] pub pool_lp_mint: Box>, #[account(address = swim_payload_message.owner)] pub user: SystemAccount<'info>, - #[account(mut)] + #[account( + mut, + address = get_associated_token_address(&user.key(), &pool_token_0_mint.key()), + )] /// CHECK: may possibly need to initialize pub user_pool_token_0_account: UncheckedAccount<'info>, - #[account(mut)] + #[account( + mut, + address = get_associated_token_address(&user.key(), &pool_token_1_mint.key()), + )] /// CHECK: may possibly need to initialize pub user_pool_token_1_account: UncheckedAccount<'info>, - #[account(mut)] + #[account( + mut, + address = get_associated_token_address(&user.key(), &pool_lp_mint.key()), + )] /// CHECK: may possibly need to initialize pub user_lp_token_account: UncheckedAccount<'info>, pub associated_token_program: Program<'info, AssociatedToken>, pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, - /* for sol -> token_bridge_mint conversion */ - #[account( - constraint = - *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount - )] - pub aggregator: AccountLoader<'info, AggregatorAccountData>, - - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - marginal_price_pool_token_0_account.mint.as_ref(), - marginal_price_pool_token_1_account.mint.as_ref(), - marginal_price_pool_lp_mint.key().as_ref(), - ], - bump = marginal_price_pool.bump, - seeds::program = two_pool_program.key() - )] - pub marginal_price_pool: Box>, - pub marginal_price_pool_token_0_account: Box>, - pub marginal_price_pool_token_1_account: Box>, - pub marginal_price_pool_lp_mint: Box>, - - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, + pub fee_tracking: FeeTracking<'info>, #[account(executable, address = spl_memo::id())] ///CHECK: memo program pub memo: UncheckedAccount<'info>, @@ -183,132 +129,117 @@ pub struct PropellerCreateOwnerTokenAccounts<'info> { impl<'info> PropellerCreateOwnerTokenAccounts<'info> { pub fn accounts(ctx: &Context) -> Result<()> { require_keys_eq!(ctx.accounts.user.key(), ctx.accounts.swim_payload_message.owner); - require_keys_eq!(ctx.accounts.pool.key(), ctx.accounts.token_id_map.pool); + require_keys_eq!(ctx.accounts.pool.key(), ctx.accounts.token_number_map.pool); let propeller = &ctx.accounts.propeller; - validate_marginal_prices_pool_accounts( - &propeller, - &ctx.accounts.marginal_price_pool.key(), - &[ - ctx.accounts.marginal_price_pool_token_0_account.mint, - ctx.accounts.marginal_price_pool_token_1_account.mint, - ], - )?; - let expected_user_token_0_ata = - get_associated_token_address(&ctx.accounts.user.key(), &ctx.accounts.pool_token_0_mint.key()); - require_keys_eq!(expected_user_token_0_ata, ctx.accounts.user_pool_token_0_account.key()); - let expected_user_token_1_ata = - get_associated_token_address(&ctx.accounts.user.key(), &ctx.accounts.pool_token_1_mint.key()); - require_keys_eq!(expected_user_token_1_ata, ctx.accounts.user_pool_token_1_account.key()); - let expected_user_lp_ata = - get_associated_token_address(&ctx.accounts.user.key(), &ctx.accounts.pool_lp_mint.key()); - require_keys_eq!(expected_user_lp_ata, ctx.accounts.user_lp_token_account.key()); + let payer = &ctx.accounts.payer.key(); + ctx.accounts.fee_tracking.validate(propeller, payer, ctx.program_id)?; + // ctx.accounts.marginal_price_pool.validate(&propeller)?; + // validate_marginal_prices_pool_accounts( + // &propeller, + // &ctx.accounts.marginal_price_pool, + // &[&ctx.accounts.marginal_price_pool_token_0_account, &ctx.accounts.marginal_price_pool_token_1_account], + // )?; msg!("Passed PropellerCreateOwnerTokenAccounts::accounts() check"); Ok(()) } - pub fn validate(&self) -> Result<()> { - require_keys_eq!(self.user.key(), self.swim_payload_message.owner); - let expected_user_token_0_ata = get_associated_token_address(&self.user.key(), &self.pool_token_0_mint.key()); - require_keys_eq!(expected_user_token_0_ata, self.user_pool_token_0_account.key()); - let expected_user_token_1_ata = get_associated_token_address(&self.user.key(), &self.pool_token_1_mint.key()); - require_keys_eq!(expected_user_token_1_ata, self.user_pool_token_1_account.key()); - let expected_user_lp_ata = get_associated_token_address(&self.user.key(), &self.pool_lp_mint.key()); - require_keys_eq!(expected_user_lp_ata, self.user_lp_token_account.key()); + + fn initialize_user_ata_and_get_fees( + &self, + user_unchecked_token_account: &UncheckedAccount<'info>, + mint: &Account<'info, Mint>, + ) -> Result { + let ata_data_len = user_unchecked_token_account.data_len(); + + let payer = &self.payer; + let user = &self.user; + let system_program = &self.system_program; + let token_program = &self.token_program; + + if ata_data_len == TokenAccount::LEN { + let token_account = + TokenAccount::try_deserialize(&mut &**user_unchecked_token_account.data.try_borrow_mut().unwrap())?; + require_keys_eq!(token_account.owner, user.key(), PropellerError::IncorrectOwnerForCreateTokenAccount); + Ok(0u64) + } else if ata_data_len != 0 { + //TODO: spl_token_2022? + // panic!("data_len != 0 && != TokenAcount::LEN"); + err!(PropellerError::InvalidTokenAccountDataLen) + } else { + let ix = spl_associated_token_account::instruction::create_associated_token_account( + &payer.key(), + &user.key(), + &mint.key(), + ); + invoke( + &ix, + &[ + payer.to_account_info(), + user_unchecked_token_account.to_account_info(), + user.to_account_info(), + mint.to_account_info(), + system_program.to_account_info(), + token_program.to_account_info(), + ], + )?; + let fees = Rent::get()?.minimum_balance(TokenAccount::LEN) + self.propeller.init_ata_fee; + Ok(fees) + } + } + + fn log_memo(&self) -> Result<()> { + let memo = self.swim_payload_message.memo; + if memo != [0u8; 16] { + let memo_ix = spl_memo::build_memo(get_memo_as_utf8(memo)?.as_ref(), &[]); + invoke(&memo_ix, &[self.memo.to_account_info()])?; + } Ok(()) } +} + +impl<'info> Fees<'info> for PropellerCreateOwnerTokenAccounts<'info> { + fn calculate_fees_in_lamports(&self) -> Result { + let mut create_owner_token_account_total_fees_in_lamports = 0u64; + + let init_token_account_0_fees = + self.initialize_user_ata_and_get_fees(&self.user_pool_token_0_account, &self.pool_token_0_mint)?; + let init_token_account_1_fees = + self.initialize_user_ata_and_get_fees(&self.user_pool_token_1_account, &self.pool_token_1_mint)?; + let init_lp_token_account_fees = + self.initialize_user_ata_and_get_fees(&self.user_lp_token_account, &self.pool_lp_mint)?; + create_owner_token_account_total_fees_in_lamports = init_token_account_0_fees + .checked_add(init_token_account_1_fees) + .and_then(|f| f.checked_add(init_lp_token_account_fees)) + .ok_or(PropellerError::IntegerOverflow)?; - fn into_marginal_prices(&self) -> CpiContext<'_, '_, '_, 'info, two_pool::cpi::accounts::MarginalPrices<'info>> { - let program = self.two_pool_program.to_account_info(); - let accounts = two_pool::cpi::accounts::MarginalPrices { - pool: self.marginal_price_pool.to_account_info(), - pool_token_account_0: self.marginal_price_pool_token_0_account.to_account_info(), - pool_token_account_1: self.marginal_price_pool_token_1_account.to_account_info(), - lp_mint: self.marginal_price_pool_lp_mint.to_account_info(), - }; - CpiContext::new(program, accounts) + msg!( + " + {}(init_token_account_0_fees) + + {}(init_token_account_1_fees) + + {}(init_lp_token_account_fees) + = {}(create_owner_token_account_total_fees_in_lamports) + ", + init_token_account_0_fees, + init_token_account_1_fees, + init_lp_token_account_fees, + create_owner_token_account_total_fees_in_lamports + ); + Ok(create_owner_token_account_total_fees_in_lamports) } - // pub fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64) -> Result { - // let propeller = &self.propeller; - // - // msg!("fee_in_lamports: {:?}", fee_in_lamports); - // let marginal_price_pool_lp_mint = &self.marginal_price_pool_lp_mint; - // - // let token_bridge_mint_key = propeller.token_bridge_mint; - // let marginal_prices = get_marginal_prices(self.into_marginal_prices())?; - // - // let intermediate_token_price_decimal: Decimal = get_marginal_price_decimal( - // &self.marginal_price_pool, - // &marginal_prices, - // &propeller, - // &marginal_price_pool_lp_mint.key(), - // )?; - // - // msg!("intermediate_token_price_decimal: {:?}", intermediate_token_price_decimal); - // - // let fee_in_lamports_decimal = Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::ConversionError)?; - // msg!("fee_in_lamports(u64): {:?} fee_in_lamports_decimal: {:?}", fee_in_lamports, fee_in_lamports_decimal); - // - // let mut res = 0u64; - // - // let lamports_intermediate_token_price = get_lamports_intermediate_token_price(&self.aggregator, i64::MAX)?; - // let fee_in_swim_usd_decimal = lamports_intermediate_token_price - // .checked_mul(fee_in_lamports_decimal) - // .and_then(|x| x.checked_div(intermediate_token_price_decimal)) - // .ok_or(PropellerError::IntegerOverflow)?; - // - // let swim_usd_decimals = get_token_bridge_mint_decimals( - // &token_bridge_mint_key, - // &self.marginal_price_pool, - // &marginal_price_pool_lp_mint, - // )?; - // msg!("swim_usd_decimals: {:?}", swim_usd_decimals); - // - // let ten_pow_decimals = - // Decimal::from_u64(10u64.pow(swim_usd_decimals as u32)).ok_or(PropellerError::IntegerOverflow)?; - // let fee_in_swim_usd_atomic = fee_in_swim_usd_decimal - // .checked_mul(ten_pow_decimals) - // .and_then(|v| v.to_u64()) - // .ok_or(PropellerError::ConversionError)?; - // - // msg!( - // "fee_in_swim_usd_decimal: {:?} fee_in_swim_usd_atomic: {:?}", - // fee_in_swim_usd_decimal, - // fee_in_swim_usd_atomic - // ); - // res = fee_in_swim_usd_atomic; - // Ok(res) - // } - - fn track_and_transfer_fees(&mut self, fees_in_swim_usd: u64) -> Result<()> { - let fee_tracker = &mut self.fee_tracker; - let updated_fees_owed = - fee_tracker.fees_owed.checked_add(fees_in_swim_usd).ok_or(PropellerError::IntegerOverflow)?; - fee_tracker.fees_owed = updated_fees_owed; - - let cpi_accounts = Transfer { - from: self.redeemer_escrow.to_account_info(), - to: self.fee_vault.to_account_info(), - authority: self.redeemer.to_account_info(), - }; + fn transfer_fees_to_vault(&mut self, fees_in_swim_usd: u64) -> Result<()> { token::transfer( CpiContext::new_with_signer( self.token_program.to_account_info(), - cpi_accounts, - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], + Transfer { + from: self.redeemer_escrow.to_account_info(), + to: self.fee_tracking.fee_vault.to_account_info(), + authority: self.redeemer.to_account_info(), + }, + &[&[(b"redeemer".as_ref()), &[self.propeller.redeemer_bump]]], ), fees_in_swim_usd, ) } - - fn log_memo(&self) -> Result<()> { - let memo = self.swim_payload_message.memo; - if memo != [0u8; 16] { - let memo_ix = - spl_memo::build_memo(std::str::from_utf8(hex::encode(memo).as_bytes()).unwrap().as_ref(), &[]); - invoke(&memo_ix, &[self.memo.to_account_info()])?; - } - Ok(()) - } } //TODO: allow this regardless of gasKickstart or only if gasKickstart? @@ -317,259 +248,39 @@ impl<'info> PropellerCreateOwnerTokenAccounts<'info> { /// we penalize the engine by not reimbursing them anything in that situation so that they are incentivized to /// check if any of the require token accounts don't exist. pub fn handle_propeller_create_owner_token_accounts(ctx: Context) -> Result<()> { - let mut create_owner_token_account_total_fees_in_lamports = 0u64; - //TODO: enforce that this step can only be done after CompleteNativeWithPayload is done? - // - // let claim_data = ClaimData::try_from_slice(&mut ctx.accounts.claim.data.borrow()) - // .map_err(|_| error!(PropellerError::InvalidClaimData))?; - // require!(claim_data.claimed, PropellerError::ClaimNotClaimed); - - //TODO: check `vaa.to` is this program's address? - // let payload_transfer_with_payload = - // get_transfer_with_payload_from_message_account(&ctx.accounts.message.to_account_info())?; - // msg!("message_data_payload: {:?}", payload_transfer_with_payload); - // let PayloadTransferWithPayload { - // message_type, - // amount, - // token_address, - // token_chain, - // to, - // to_chain, - // from_address, - // payload, - // } = payload_transfer_with_payload; - // //TODO: do i need to re-check this? - // // any issue in doing so? - // msg!("payload_transfer_with_payload.to: {:?}", to); - // let to_pubkey = Pubkey::new_from_array(to); - // require_keys_eq!(to_pubkey, crate::ID); - - let init_ata_fee = ctx.accounts.propeller.init_ata_fee; - let token_program = ctx.accounts.token_program.to_account_info(); - let payer = ctx.accounts.payer.to_account_info(); - let user = ctx.accounts.user.to_account_info(); - let system_program = ctx.accounts.system_program.to_account_info(); - let init_token_account_0_fees = initialize_user_ata_and_get_fees( - ctx.accounts.user_pool_token_0_account.to_account_info().clone(), - payer.clone(), - user.clone(), - ctx.accounts.pool_token_0_mint.to_account_info().clone(), - system_program.clone(), - token_program.clone(), - init_ata_fee, - )?; - msg!("init_token_account_0_fees: {}", init_token_account_0_fees); - let init_token_account_1_fees = initialize_user_ata_and_get_fees( - ctx.accounts.user_pool_token_1_account.to_account_info().clone(), - payer.clone(), - user.clone(), - ctx.accounts.pool_token_1_mint.to_account_info().clone(), - system_program.clone(), - token_program.clone(), - init_ata_fee, - )?; - let init_lp_token_account_fees = initialize_user_ata_and_get_fees( - ctx.accounts.user_lp_token_account.to_account_info().clone(), - payer.clone(), - user.clone(), - ctx.accounts.pool_lp_mint.to_account_info().clone(), - system_program.clone(), - token_program.clone(), - init_ata_fee, - )?; - create_owner_token_account_total_fees_in_lamports = init_token_account_0_fees - .checked_add(init_token_account_1_fees) - .and_then(|f| f.checked_add(init_lp_token_account_fees)) - .ok_or(PropellerError::IntegerOverflow)?; - - msg!( - " - {}(init_token_account_0_fees) + - {}(init_token_account_1_fees) + - {}(init_lp_token_account_fees) - = {}(create_owner_token_account_total_fees_in_lamports) - ", - init_token_account_0_fees, - init_token_account_1_fees, - init_lp_token_account_fees, - create_owner_token_account_total_fees_in_lamports - ); - if create_owner_token_account_total_fees_in_lamports == 0 { + let create_owner_atas_total_fees_in_lamports = ctx.accounts.calculate_fees_in_lamports()?; + if create_owner_atas_total_fees_in_lamports == 0 { //TODO: log memo still? msg!("No accounts need to be initialized. Returning early"); ctx.accounts.log_memo()?; return Ok(()); } - //let fees_in_swim_usd = convert_fees_to_swim_usd_atomic() - // self.handle_fees(fees_in_swim_usd) - - // let create_owner_token_account_total_fees_in_token_bridge_mint = - // get_fees_in_token_bridge_mint(create_owner_token_account_total_fees_in_lamports, &ctx)?; - - // let fee_tracker = &mut ctx.accounts.fee_tracker; - // fee_tracker.fees_owed = fee_tracker - // .fees_owed - // .checked_add(create_owner_token_account_total_fees_in_token_bridge_mint) - // .ok_or(PropellerError::IntegerOverflow)?; - // let cpi_accounts = Transfer { - // from: ctx.accounts.redeemer_escrow.to_account_info(), - // to: ctx.accounts.fee_vault.to_account_info(), - // authority: ctx.accounts.redeemer.to_account_info(), - // }; - // token::transfer( - // CpiContext::new_with_signer( - // token_program.clone(), - // cpi_accounts, - // &[&[&b"redeemer".as_ref(), &[ctx.accounts.propeller.redeemer_bump]]], - // ), - // create_owner_token_account_total_fees_in_token_bridge_mint, - // )?; - - let create_owner_token_account_total_fees_in_swim_usd = convert_fees_to_swim_usd_atomic( - create_owner_token_account_total_fees_in_lamports, - &ctx.accounts.propeller, - &ctx.accounts.marginal_price_pool_lp_mint, - ctx.accounts.into_marginal_prices(), - &ctx.accounts.marginal_price_pool, - &ctx.accounts.aggregator, - i64::MAX, - )?; - ctx.accounts.track_and_transfer_fees(create_owner_token_account_total_fees_in_swim_usd)?; - - let transfer_amount = ctx.accounts.swim_payload_message.transfer_amount; - let new_transfer_amount = transfer_amount - .checked_sub(create_owner_token_account_total_fees_in_swim_usd) - .ok_or(error!(PropellerError::InsufficientFunds))?; - - msg!( - "transfer_amount: {} - fees_in_swim_usd: {} = {}", - transfer_amount, - create_owner_token_account_total_fees_in_swim_usd, - new_transfer_amount - ); - ctx.accounts.swim_payload_message.transfer_amount = new_transfer_amount; - ctx.accounts.log_memo()?; - Ok(()) -} + // owner bypass check - we don't track fees if swim_payload.user == payer + // normally they should just initialize the needed ATAs on their own but have this check just in case. + let swim_payload_owner = ctx.accounts.swim_payload_message.owner; + if swim_payload_owner != ctx.accounts.payer.key() { + let propeller = &ctx.accounts.propeller; + let create_owner_atas_total_fees_in_swim_usd = + ctx.accounts.fee_tracking.track_fees(create_owner_atas_total_fees_in_lamports, propeller)?; + + ctx.accounts.transfer_fees_to_vault(create_owner_atas_total_fees_in_swim_usd)?; -fn initialize_user_ata_and_get_fees<'info>( - user_unchecked_token_account: AccountInfo<'info>, - payer: AccountInfo<'info>, - user: AccountInfo<'info>, - mint: AccountInfo<'info>, - system_program: AccountInfo<'info>, - token_program: AccountInfo<'info>, - create_ata_fee: u64, -) -> Result { - //TODO: figure out actual cost of create ata txn. - let create_ata_fee = 10000u64; - let ata_data_len = user_unchecked_token_account.data_len(); - if ata_data_len == TokenAccount::LEN { - let token_account = - TokenAccount::try_deserialize(&mut &**user_unchecked_token_account.data.try_borrow_mut().unwrap())?; - require_keys_eq!(token_account.owner, user.key(), PropellerError::IncorrectOwnerForCreateTokenAccount); - return Ok(0u64); - } else if ata_data_len != 0 { - //TODO: spl_token_2022? - // panic!("data_len != 0 && != TokenAcount::LEN"); - return err!(PropellerError::InvalidTokenAccountDataLen); - } else { - let ix = spl_associated_token_account::instruction::create_associated_token_account( - &payer.key(), - &user.key(), - &mint.key(), + let transfer_amount = ctx.accounts.swim_payload_message.transfer_amount; + let new_transfer_amount = transfer_amount + .checked_sub(create_owner_atas_total_fees_in_swim_usd) + .ok_or(error!(PropellerError::InsufficientFunds))?; + + msg!( + "transfer_amount: {} - fees_in_swim_usd: {} = {}", + transfer_amount, + create_owner_atas_total_fees_in_swim_usd, + new_transfer_amount ); - invoke(&ix, &[payer, user_unchecked_token_account, user, mint, system_program, token_program])?; - let fees = Rent::get()?.minimum_balance(TokenAccount::LEN) + create_ata_fee; - Ok(fees) + ctx.accounts.swim_payload_message.transfer_amount = new_transfer_amount; } -} -fn get_fees_in_swim_usd(fee_in_lamports: u64, ctx: &Context) -> Result { - msg!("fee_in_lamports: {:?}", fee_in_lamports); - - let propeller = &ctx.accounts.propeller; - let lp_mint_key = ctx.accounts.marginal_price_pool_lp_mint.key(); - - let swim_usd_mint_key = propeller.swim_usd_mint; - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::MarginalPrices { - pool: ctx.accounts.marginal_price_pool.to_account_info(), - pool_token_account_0: ctx.accounts.marginal_price_pool_token_0_account.to_account_info(), - pool_token_account_1: ctx.accounts.marginal_price_pool_token_1_account.to_account_info(), - lp_mint: ctx.accounts.marginal_price_pool_lp_mint.to_account_info(), - }, - ); - let result = two_pool::cpi::marginal_prices(cpi_ctx)?; - // let marginal_prices = result.get().marginal_prices; - let marginal_prices = result.get(); - - msg!("marginal_prices: {:?}", marginal_prices); - - let marginal_price: Decimal = get_marginal_price_decimal( - &ctx.accounts.marginal_price_pool, - &marginal_prices, - &propeller, - // propeller.marginal_price_pool_token_index as usize, - &ctx.accounts.marginal_price_pool_lp_mint.key(), - // &token_bridge_mint_key, - )?; - msg!("marginal_price: {}", marginal_price); - - //swimUSD is lp token of marginal price pool - let mut res = 0u64; - let feed = &ctx.accounts.aggregator.load()?; - - // get result - // note - for tests this is currently hardcoded to 100 - // this val is SOL/USD price - // 100 => 1 SOL/100 USD (usdc) - // let v2 = feed.get_result()?.try_into()?; - let sol_usd_price: Decimal = feed.get_result()?.try_into()?; - let name = feed.name; - - let lamports_usd_price = - sol_usd_price.checked_div(LAMPORTS_PER_SOL_DECIMAL).ok_or(PropellerError::IntegerOverflow)?; - msg!("sol_usd_price:{},lamports_usd_price: {}", sol_usd_price, lamports_usd_price); - // check whether the feed has been updated in the last 300 seconds - feed.check_staleness( - Clock::get().unwrap().unix_timestamp, - // 300 - i64::MAX, - ) - .map_err(|_| error!(PropellerError::StaleFeed))?; - // check feed does not exceed max_confidence_interval - // if let Some(max_confidence_interval) = params.max_confidence_interval { - // feed.check_confidence_interval(SwitchboardDecimal::from_f64(max_confidence_interval)) - // .map_err(|_| error!(PropellerError::ConfidenceIntervalExceeded))?; - // } - - let fee_in_lamports_decimal = Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::ConversionError)?; - msg!("fee_in_lamports(u64): {:?} fee_in_lamports_decimal: {:?}", fee_in_lamports, fee_in_lamports_decimal); - let fee_in_swim_usd_decimal = marginal_price - .checked_mul(lamports_usd_price) - .and_then(|v| v.checked_mul(fee_in_lamports_decimal)) - .ok_or(PropellerError::IntegerOverflow)?; - // .checked_mul(Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::IntegerOverflow)?) - // .ok_or(PropellerError::IntegerOverflow)?; - let swim_usd_mint_decimals = get_swim_usd_mint_decimals( - &swim_usd_mint_key, - &ctx.accounts.marginal_price_pool, - &ctx.accounts.marginal_price_pool_lp_mint, - )?; - msg!("swim_usd_mint_decimals: {:?}", swim_usd_mint_decimals); - - let ten_pow_decimals = - Decimal::from_u64(10u64.pow(swim_usd_mint_decimals as u32)).ok_or(PropellerError::IntegerOverflow)?; - let fee_in_swim_usd_atomic = fee_in_swim_usd_decimal - .checked_mul(ten_pow_decimals) - .and_then(|v| v.to_u64()) - .ok_or(PropellerError::ConversionError)?; - - msg!("fee_in_swim_usd_decimal: {:?} fee_in_swim_usd_atomic: {:?}", fee_in_swim_usd_decimal, fee_in_swim_usd_atomic); - res = fee_in_swim_usd_atomic; - Ok(res) + ctx.accounts.log_memo()?; + Ok(()) } #[derive(Accounts)] @@ -578,7 +289,7 @@ pub struct PropellerCreateOwnerSwimUsdAta<'info> { seeds = [ b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], bump = propeller.bump, has_one = swim_usd_mint @ PropellerError::InvalidSwimUsdMint, - has_one = aggregator @ PropellerError::InvalidAggregator, + constraint = !propeller.is_paused @ PropellerError::IsPaused, )] pub propeller: Box>, #[account(mut)] @@ -595,28 +306,9 @@ pub struct PropellerCreateOwnerSwimUsdAta<'info> { pub redeemer: SystemAccount<'info>, #[account( mut, - token::mint = propeller.swim_usd_mint, - token::authority = redeemer, + address = get_associated_token_address(&redeemer.key(), &propeller.swim_usd_mint) )] pub redeemer_escrow: Box>, - #[account( - mut, - token::mint = propeller.swim_usd_mint, - token::authority = propeller, - )] - pub fee_vault: Box>, - - #[account( - mut, - seeds = [ - b"propeller".as_ref(), - b"fee".as_ref(), - propeller.swim_usd_mint.as_ref(), - payer.key().as_ref() - ], - bump = fee_tracker.bump - )] - pub fee_tracker: Account<'info, FeeTracker>, #[account( seeds = [ @@ -625,7 +317,7 @@ pub struct PropellerCreateOwnerSwimUsdAta<'info> { swim_payload_message.vaa_sequence.to_be_bytes().as_ref(), ], bump, - seeds::program = propeller.token_bridge().unwrap() + seeds::program = propeller.token_bridge() )] /// CHECK: WH Claim account pub claim: UncheckedAccount<'info>, @@ -641,17 +333,17 @@ pub struct PropellerCreateOwnerSwimUsdAta<'info> { )] pub swim_payload_message: Box>, - // #[account( - // seeds = [ - // b"propeller".as_ref(), - // b"token_id".as_ref(), - // propeller.key().as_ref(), - // &swim_payload_message.target_token_id.to_le_bytes() - // ], - // bump, - // )] + #[account( + seeds = [ + b"propeller".as_ref(), + b"token_id".as_ref(), + propeller.key().as_ref(), + &swim_payload_message.target_token_id.to_le_bytes() + ], + bump, + )] /// CHECK: Unchecked b/c if target_token_id is invalid then this account should not exist/be able to be - /// deseraizlied as a `TokenIdMap`. if it does exist, then engine should have called + /// deserialized as a `TokenIdMap`. if it does exist, then engine should have called /// propeller_create_owner_token_accounts instead pub token_id_map: UncheckedAccount<'info>, pub swim_usd_mint: Box>, @@ -670,30 +362,8 @@ pub struct PropellerCreateOwnerSwimUsdAta<'info> { pub system_program: Program<'info, System>, pub token_program: Program<'info, Token>, - /* for sol -> token_bridge_mint conversion */ - #[account( - constraint = - *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount - )] - pub aggregator: AccountLoader<'info, AggregatorAccountData>, + pub fee_tracking: FeeTracking<'info>, - // pub marginal_price_pool: MarginalPricePool<'info>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - marginal_price_pool_token_0_account.mint.as_ref(), - marginal_price_pool_token_1_account.mint.as_ref(), - marginal_price_pool_lp_mint.key().as_ref(), - ], - bump = marginal_price_pool.bump, - seeds::program = two_pool_program.key() - )] - pub marginal_price_pool: Box>, - pub marginal_price_pool_token_0_account: Box>, - pub marginal_price_pool_token_1_account: Box>, - pub marginal_price_pool_lp_mint: Box>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, #[account(executable, address = spl_memo::id())] ///CHECK: memo program pub memo: UncheckedAccount<'info>, @@ -705,161 +375,73 @@ pub struct PropellerCreateOwnerSwimUsdAta<'info> { impl<'info> PropellerCreateOwnerSwimUsdAta<'info> { pub fn accounts(ctx: &Context) -> Result<()> { require_keys_eq!(ctx.accounts.owner.key(), ctx.accounts.swim_payload_message.owner); - let (expected_token_id_map_address, _bump) = Pubkey::find_program_address( - &[ - b"propeller".as_ref(), - b"token_id".as_ref(), - ctx.accounts.propeller.key().as_ref(), - ctx.accounts.swim_payload_message.target_token_id.to_le_bytes().as_ref(), - ], - ctx.program_id, - ); - //Note: the address should at least be valid even though it doesn't exist. - require_keys_eq!(expected_token_id_map_address, ctx.accounts.token_id_map.key()); + TokenNumberMap::assert_is_invalid(&ctx.accounts.token_id_map.to_account_info())?; let propeller = &ctx.accounts.propeller; - validate_marginal_prices_pool_accounts( - &propeller, - &ctx.accounts.marginal_price_pool.key(), - &[ - ctx.accounts.marginal_price_pool_token_0_account.mint, - ctx.accounts.marginal_price_pool_token_1_account.mint, - ], - )?; + let payer = &ctx.accounts.payer.key(); + ctx.accounts.fee_tracking.validate(propeller, payer, ctx.program_id)?; msg!("Passed PropellerCreateOwnerTokenAccounts::accounts() check"); Ok(()) } - fn into_marginal_prices(&self) -> CpiContext<'_, '_, '_, 'info, two_pool::cpi::accounts::MarginalPrices<'info>> { - let program = self.two_pool_program.to_account_info(); - let accounts = two_pool::cpi::accounts::MarginalPrices { - pool: self.marginal_price_pool.to_account_info(), - pool_token_account_0: self.marginal_price_pool_token_0_account.to_account_info(), - pool_token_account_1: self.marginal_price_pool_token_1_account.to_account_info(), - lp_mint: self.marginal_price_pool_lp_mint.to_account_info(), - }; - CpiContext::new(program, accounts) + fn log_memo(&self) -> Result<()> { + let memo = self.swim_payload_message.memo; + if memo != [0u8; 16] { + let memo_ix = spl_memo::build_memo(get_memo_as_utf8(memo)?.as_ref(), &[]); + invoke(&memo_ix, &[self.memo.to_account_info()])?; + } + Ok(()) } +} - pub fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64) -> Result { - let propeller = &self.propeller; - - msg!("fee_in_lamports: {:?}", fee_in_lamports); - let marginal_price_pool_lp_mint = &self.marginal_price_pool_lp_mint; - - let swim_usd_mint_key = propeller.swim_usd_mint; - let marginal_prices = get_marginal_prices(self.into_marginal_prices())?; - - let intermediate_token_price_decimal: Decimal = get_marginal_price_decimal( - &self.marginal_price_pool, - &marginal_prices, - &propeller, - &marginal_price_pool_lp_mint.key(), - )?; - - msg!("intermediate_token_price_decimal: {:?}", intermediate_token_price_decimal); - - let fee_in_lamports_decimal = Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::ConversionError)?; - msg!("fee_in_lamports(u64): {:?} fee_in_lamports_decimal: {:?}", fee_in_lamports, fee_in_lamports_decimal); - - let mut res = 0u64; - - let lamports_intermediate_token_price = get_lamports_intermediate_token_price(&self.aggregator, i64::MAX)?; - let fee_in_swim_usd_decimal = lamports_intermediate_token_price - .checked_mul(fee_in_lamports_decimal) - .and_then(|x| x.checked_div(intermediate_token_price_decimal)) +impl<'info> Fees<'info> for PropellerCreateOwnerSwimUsdAta<'info> { + fn calculate_fees_in_lamports(&self) -> Result { + let fees_in_lamports = Rent::get()? + .minimum_balance(TokenAccount::LEN) + .checked_add(self.propeller.init_ata_fee) .ok_or(PropellerError::IntegerOverflow)?; - - let swim_usd_decimals = - get_swim_usd_mint_decimals(&swim_usd_mint_key, &self.marginal_price_pool, &marginal_price_pool_lp_mint)?; - msg!("swim_usd_decimals: {:?}", swim_usd_decimals); - - let ten_pow_decimals = - Decimal::from_u64(10u64.pow(swim_usd_decimals as u32)).ok_or(PropellerError::IntegerOverflow)?; - let fee_in_swim_usd_atomic = fee_in_swim_usd_decimal - .checked_mul(ten_pow_decimals) - .and_then(|v| v.to_u64()) - .ok_or(PropellerError::ConversionError)?; - - msg!( - "fee_in_swim_usd_decimal: {:?} fee_in_swim_usd_atomic: {:?}", - fee_in_swim_usd_decimal, - fee_in_swim_usd_atomic - ); - res = fee_in_swim_usd_atomic; - Ok(res) + Ok(fees_in_lamports) } - pub fn handle_fees(&mut self, fees_in_swim_usd: u64) -> Result<()> { - let fee_tracker = &mut self.fee_tracker; - let updated_fees_owed = - fee_tracker.fees_owed.checked_add(fees_in_swim_usd).ok_or(PropellerError::IntegerOverflow)?; - fee_tracker.fees_owed = updated_fees_owed; - - let cpi_accounts = Transfer { - from: self.redeemer_escrow.to_account_info(), - to: self.fee_vault.to_account_info(), - authority: self.redeemer.to_account_info(), - }; + fn transfer_fees_to_vault(&mut self, fees_in_swim_usd: u64) -> Result<()> { token::transfer( CpiContext::new_with_signer( self.token_program.to_account_info(), - cpi_accounts, - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], + Transfer { + from: self.redeemer_escrow.to_account_info(), + to: self.fee_tracking.fee_vault.to_account_info(), + authority: self.redeemer.to_account_info(), + }, + &[&[(b"redeemer".as_ref()), &[self.propeller.redeemer_bump]]], ), fees_in_swim_usd, ) } - // pub fn validate(&self) -> Result<()> { - // require_keys_eq!(self.user.key(), self.swim_payload_message.owner); - // let expected_user_token_0_ata = get_associated_token_address(&self.user.key(), &self.pool_token_0_mint.key()); - // require_keys_eq!(expected_user_token_0_ata, self.user_pool_token_0_account.key()); - // let expected_user_token_1_ata = get_associated_token_address(&self.user.key(), &self.pool_token_1_mint.key()); - // require_keys_eq!(expected_user_token_1_ata, self.user_pool_token_1_account.key()); - // let expected_user_lp_ata = get_associated_token_address(&self.user.key(), &self.pool_lp_mint.key()); - // require_keys_eq!(expected_user_lp_ata, self.user_lp_token_account.key()); - // Ok(()) - // } } pub fn handle_propeller_create_owner_swim_usd_ata(ctx: Context) -> Result<()> { - let token_id_map = &ctx.accounts.token_id_map; - if let Ok(_) = TokenIdMap::try_deserialize(&mut &**token_id_map.try_borrow_mut_data()?) { - return err!(PropellerError::TokenIdMapExists); - } + let fees_in_lamports = ctx.accounts.calculate_fees_in_lamports()?; + let swim_payload_owner = ctx.accounts.swim_payload_message.owner; + + if swim_payload_owner != ctx.accounts.payer.key() { + // let create_user_swim_usd_ata_fees_in_swim_usd_atomic = + // ctx.accounts.convert_fees_to_swim_usd_atomic(create_user_swim_usd_ata_fees_in_lamports)?; + let propeller = &ctx.accounts.propeller; + let fees_in_swim_usd_atomic = ctx.accounts.fee_tracking.track_fees(fees_in_lamports, propeller)?; + + ctx.accounts.transfer_fees_to_vault(fees_in_swim_usd_atomic)?; - let fees_in_lamports = Rent::get()? - .minimum_balance(TokenAccount::LEN) - .checked_add(ctx.accounts.propeller.init_ata_fee) - .ok_or(PropellerError::IntegerOverflow)?; - // let init_token_bridge_ata_total_fee_in_token_bridge_mint = - // ctx.accounts.convert_fees_to_swim_usd_atomic(fee_in_lamports)?; - let fees_in_swim_usd_atomic = convert_fees_to_swim_usd_atomic( - fees_in_lamports, - &ctx.accounts.propeller, - &ctx.accounts.marginal_price_pool_lp_mint, - ctx.accounts.into_marginal_prices(), - &ctx.accounts.marginal_price_pool, - &ctx.accounts.aggregator, - i64::MAX, - )?; - ctx.accounts.handle_fees(fees_in_swim_usd_atomic)?; - - let transfer_amount = ctx.accounts.swim_payload_message.transfer_amount; - let new_transfer_amount = - transfer_amount.checked_sub(fees_in_swim_usd_atomic).ok_or(error!(PropellerError::InsufficientFunds))?; - - msg!( - "transfer_amount: {} - fees_in_swim_usd_atomic: {} = {}", - transfer_amount, - fees_in_swim_usd_atomic, - new_transfer_amount - ); - ctx.accounts.swim_payload_message.transfer_amount = new_transfer_amount; - - let memo = ctx.accounts.swim_payload_message.memo; - if memo != [0u8; 16] { - let memo_ix = spl_memo::build_memo(std::str::from_utf8(hex::encode(memo).as_bytes()).unwrap().as_ref(), &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; + let transfer_amount = ctx.accounts.swim_payload_message.transfer_amount; + let new_transfer_amount = + transfer_amount.checked_sub(fees_in_swim_usd_atomic).ok_or(error!(PropellerError::InsufficientFunds))?; + + msg!( + "transfer_amount: {} - fees_in_swim_usd: {} = {}", + transfer_amount, + fees_in_swim_usd_atomic, + new_transfer_amount + ); + ctx.accounts.swim_payload_message.transfer_amount = new_transfer_amount; } + ctx.accounts.log_memo()?; Ok(()) } diff --git a/packages/solana-contracts/programs/propeller/src/instructions/fee_tracker.rs b/packages/solana-contracts/programs/propeller/src/instructions/fee_tracker.rs index c448fb51c..e0aa93359 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/fee_tracker.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/fee_tracker.rs @@ -2,10 +2,9 @@ use { crate::{error::*, Propeller}, anchor_lang::prelude::*, anchor_spl::{ - associated_token::{create, AssociatedToken, Create}, + associated_token::{get_associated_token_address}, token::{self, Mint, Token, TokenAccount, Transfer}, }, - two_pool::state::TwoPool, }; //TODO: need to initialize propeller with a "fee_vault" @@ -50,7 +49,7 @@ impl<'info> InitializeFeeTracker<'info> { pub fn handle_initialize_fee_tracker(ctx: Context) -> Result<()> { let fee_tracker = &mut ctx.accounts.fee_tracker; fee_tracker.bump = *ctx.bumps.get("fee_tracker").unwrap(); - fee_tracker.payer = ctx.accounts.payer.key(); + fee_tracker.fees_recipient = ctx.accounts.payer.key(); fee_tracker.fees_owed = 0; fee_tracker.fees_mint = ctx.accounts.swim_usd_mint.key(); Ok(()) @@ -59,7 +58,7 @@ pub fn handle_initialize_fee_tracker(ctx: Context) -> Resu #[account] pub struct FeeTracker { pub bump: u8, - pub payer: Pubkey, + pub fees_recipient: Pubkey, pub fees_owed: u64, pub fees_mint: Pubkey, } @@ -73,13 +72,13 @@ pub struct ClaimFees<'info> { #[account( seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], bump = propeller.bump, - has_one = fee_vault, + has_one = fee_vault @ PropellerError::InvalidFeeVault, )] pub propeller: Box>, #[account( mut, - seeds = [b"propeller".as_ref(), b"fee".as_ref(), fee_tracker.fees_mint.as_ref(), fee_tracker.payer.as_ref()], + seeds = [b"propeller".as_ref(), b"fee".as_ref(), fee_tracker.fees_mint.as_ref(), fee_tracker.fees_recipient.as_ref()], bump = fee_tracker.bump )] pub fee_tracker: Account<'info, FeeTracker>, @@ -89,16 +88,11 @@ pub struct ClaimFees<'info> { #[account( mut, - token::mint = fee_tracker.fees_mint, - token::authority = fee_tracker.payer, + address = get_associated_token_address(&fee_tracker.fees_recipient, &fee_tracker.fees_mint), )] pub fee_account: Account<'info, TokenAccount>, - #[account( - mut, - token::mint = fee_tracker.fees_mint, - token::authority = propeller, - )] + #[account(mut)] pub fee_vault: Account<'info, TokenAccount>, pub token_program: Program<'info, Token>, @@ -130,7 +124,7 @@ pub fn handle_claim_fees(ctx: Context) -> Result<()> { CpiContext::new_with_signer( ctx.accounts.token_program.to_account_info(), cpi_accounts, - &[&[&b"propeller".as_ref(), ctx.accounts.propeller.swim_usd_mint.as_ref(), &[ctx.accounts.propeller.bump]]], + &[&[(b"propeller".as_ref()), ctx.accounts.propeller.swim_usd_mint.as_ref(), &[ctx.accounts.propeller.bump]]], ), fees_owed, )?; diff --git a/packages/solana-contracts/programs/propeller/src/instructions/governance/governance.rs b/packages/solana-contracts/programs/propeller/src/instructions/governance/governance.rs new file mode 100644 index 000000000..02fadd4bc --- /dev/null +++ b/packages/solana-contracts/programs/propeller/src/instructions/governance/governance.rs @@ -0,0 +1,100 @@ +use { + crate::{constants::ENACT_DELAY, error::*, Propeller}, + anchor_lang::prelude::*, + two_pool::DecimalU64Anchor, +}; + +#[derive(Accounts)] +pub struct Governance<'info> { + #[account( + mut, + seeds = [ + b"propeller".as_ref(), + propeller.swim_usd_mint.as_ref(), + ], + bump = propeller.bump, + has_one = governance_key @ PropellerError::InvalidPropellerGovernanceKey, + )] + pub propeller: Account<'info, Propeller>, + pub governance_key: Signer<'info>, +} + +#[derive(Accounts)] +pub struct PrepareGovernanceTransition<'info> { + pub governance: Governance<'info>, + ///CHECK: not specifying type of account since it doesn't matter + pub upcoming_governance_key: UncheckedAccount<'info>, +} +pub fn handle_prepare_governance_transition( + ctx: Context, + upcoming_governance_key: Pubkey, +) -> Result<()> { + let propeller = &mut ctx.accounts.governance.propeller; + require_keys_eq!( + ctx.accounts.upcoming_governance_key.key(), + upcoming_governance_key, + PropellerError::InvalidUpcomingGovernanceKey + ); + require_keys_neq!(upcoming_governance_key, Pubkey::default(), PropellerError::InvalidUpcomingGovernanceKey); + propeller.prepared_governance_key = upcoming_governance_key; + let current_ts = Clock::get()?.unix_timestamp; + require_gt!(current_ts, 0i64); + propeller.governance_transition_ts = current_ts + ENACT_DELAY; + Ok(()) +} + +pub fn handle_enact_governance_transition(ctx: Context) -> Result<()> { + let propeller = &mut ctx.accounts.propeller; + require_neq!(propeller.governance_transition_ts, 0i64, PropellerError::InvalidEnact); + require_keys_neq!(propeller.prepared_governance_key, Pubkey::default(), PropellerError::InvalidEnact); + + let current_ts = Clock::get()?.unix_timestamp; + + require_gt!(current_ts, 0i64); + require_gt!(current_ts, propeller.governance_transition_ts, PropellerError::InsufficientDelay); + + propeller.governance_key = propeller.prepared_governance_key; + propeller.prepared_governance_key = Pubkey::default(); + propeller.governance_transition_ts = 0i64; + + Ok(()) +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct FeeUpdates { + pub secp_verify_init_fee: Option, + pub secp_verify_fee: Option, + pub post_vaa_fee: Option, + pub init_ata_fee: Option, + pub complete_with_payload_fee: Option, + pub process_swim_payload_fee: Option, +} + +/* Update Fees */ + +pub fn handle_update_fees(ctx: Context, fee_updates: FeeUpdates) -> Result<()> { + Ok(()) +} + +pub fn handle_update_gas_kickstart_amount(ctx: Context, new_gas_kickstart_amount: u64) -> Result<()> { + let propeller = &mut ctx.accounts.propeller; + propeller.gas_kickstart_amount = new_gas_kickstart_amount; + Ok(()) +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct GasOracleUpdates { + pub aggregator: Option, + pub max_staleness: Option, + pub max_confidence_interval: Option, + pub fallback_oracle: Option, +} + +// TODO: bundle marginal pool in this update as well. +// if you update the marginal pool, may need to update gas oracle as well for new intermediate token +// e.g. update marginal pool to swimUSD/uxd pool, oracle needs to give sol/uxd price +// pub fn handle_update_gas_oracle(ctx: Context, gas_oracle_updates: GasOracleUpdates) -> Result<()> { +// let propeller = &mut ctx.accounts.propeller; +// propeller.gas_oracle = new_gas_oracle; +// Ok(()) +// } diff --git a/packages/solana-contracts/programs/propeller/src/instructions/governance/mod.rs b/packages/solana-contracts/programs/propeller/src/instructions/governance/mod.rs new file mode 100644 index 000000000..7e179f863 --- /dev/null +++ b/packages/solana-contracts/programs/propeller/src/instructions/governance/mod.rs @@ -0,0 +1,4 @@ +pub use {governance::*, pause::*}; + +mod governance; +mod pause; diff --git a/packages/solana-contracts/programs/propeller/src/instructions/governance/pause.rs b/packages/solana-contracts/programs/propeller/src/instructions/governance/pause.rs new file mode 100644 index 000000000..e1dd82048 --- /dev/null +++ b/packages/solana-contracts/programs/propeller/src/instructions/governance/pause.rs @@ -0,0 +1,41 @@ +use { + crate::{error::*, governance::*, Propeller}, + anchor_lang::prelude::*, +}; + +/// Accounts needed for all pause-related ixs +#[derive(Accounts)] +pub struct SetPaused<'info> { + #[account( + mut, + seeds = [ + b"propeller".as_ref(), + propeller.swim_usd_mint.as_ref(), + ], + bump = propeller.bump, + has_one = pause_key @ PropellerError::InvalidPropellerPauseKey, + )] + pub propeller: Box>, + pub pause_key: Signer<'info>, +} + +pub fn handle_set_paused(ctx: Context, is_paused: bool) -> Result<()> { + let propeller = &mut ctx.accounts.propeller; + propeller.is_paused = is_paused; + Ok(()) +} + +#[derive(Accounts)] +pub struct UpdatePauseKey<'info> { + pub governance: Governance<'info>, + ///CHECK: not specifying type of account since it doesn't matter + pub new_pause_key: UncheckedAccount<'info>, +} + +pub fn handle_change_pause_key(ctx: Context, new_pause_key: Pubkey) -> Result<()> { + require_keys_eq!(new_pause_key, ctx.accounts.new_pause_key.key()); + require_keys_neq!(new_pause_key, Pubkey::default(), PropellerError::InvalidNewPauseKey); + let propeller = &mut ctx.accounts.governance.propeller; + propeller.pause_key = ctx.accounts.new_pause_key.key(); + Ok(()) +} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs b/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs index c4182c5e9..dec7383e1 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs @@ -2,7 +2,7 @@ use { crate::{error::*, Propeller}, anchor_lang::prelude::*, anchor_spl::{ - associated_token::{create, AssociatedToken, Create}, + associated_token::{AssociatedToken}, token::{Mint, Token, TokenAccount}, }, switchboard_v2::{AggregatorAccountData, SWITCHBOARD_PROGRAM_ID}, @@ -41,7 +41,9 @@ pub struct Initialize<'info> { )] pub propeller_fee_vault: Box>, - pub admin: Signer<'info>, + pub governance_key: Signer<'info>, + /// CHECK: pause_key + pub pause_key: Signer<'info>, pub swim_usd_mint: Box>, #[account(mut)] @@ -102,19 +104,21 @@ pub struct InitializeParams { pub marginal_price_pool: Pubkey, pub marginal_price_pool_token_index: u8, pub marginal_price_pool_token_mint: Pubkey, + pub max_staleness: i64, // pub evm_routing_contract_address: [u8; 32], } pub fn handle_initialize(ctx: Context, params: InitializeParams) -> Result<()> { // let pool = &ctx.accounts.pool; // let mint0 = pool.get_token_mint_0().unwrap(); + msg!("in handle_initialize"); let propeller = &mut ctx.accounts.propeller; propeller.bump = *ctx.bumps.get("propeller").unwrap(); - propeller.nonce = 0; - propeller.admin = ctx.accounts.admin.key(); - //TODO: these should be passed in as params or read based on features used when deploying? - propeller.wormhole = propeller.wormhole()?; - propeller.token_bridge = propeller.token_bridge()?; + propeller.is_paused = false; + propeller.governance_key = ctx.accounts.governance_key.key(); + propeller.prepared_governance_key = Pubkey::default(); + propeller.governance_transition_ts = 0; + propeller.pause_key = ctx.accounts.pause_key.key(); propeller.swim_usd_mint = ctx.accounts.swim_usd_mint.key(); propeller.sender_bump = *ctx.bumps.get("propeller_sender").unwrap(); @@ -135,7 +139,6 @@ pub fn handle_initialize(ctx: Context, params: InitializeParams) -> // propeller.evm_routing_contract_address = params.evm_routing_contract_address; propeller.fee_vault = ctx.accounts.propeller_fee_vault.key(); propeller.aggregator = ctx.accounts.aggregator.key(); + propeller.max_staleness = params.max_staleness; Ok(()) } - -// pub fn init_redeemer_escrow diff --git a/packages/solana-contracts/programs/propeller/src/instructions/marginal_price_pool.rs b/packages/solana-contracts/programs/propeller/src/instructions/marginal_price_pool.rs new file mode 100644 index 000000000..4d722e1e1 --- /dev/null +++ b/packages/solana-contracts/programs/propeller/src/instructions/marginal_price_pool.rs @@ -0,0 +1,117 @@ +use { + crate::{error::*, Propeller, TOKEN_COUNT}, + anchor_lang::prelude::*, + anchor_spl::token::{Mint, TokenAccount}, + // primitive_types::U256, + rust_decimal::Decimal, + two_pool::{state::TwoPool, BorshDecimal}, +}; + +#[derive(Accounts)] +pub struct MarginalPricePool<'info> { + #[account( + mut, + seeds = [ + b"two_pool".as_ref(), + pool_token_0_account.mint.as_ref(), + pool_token_1_account.mint.as_ref(), + lp_mint.key().as_ref(), + ], + bump = pool.bump, + seeds::program = two_pool_program.key() + )] + pub pool: Box>, + #[account( + address = pool.token_keys[0], + )] + pub pool_token_0_account: Box>, + #[account( + address = pool.token_keys[1], + )] + pub pool_token_1_account: Box>, + #[account( + address = pool.lp_mint_key, + )] + pub lp_mint: Box>, + pub two_pool_program: Program<'info, two_pool::program::TwoPool>, +} + +impl<'info> MarginalPricePool<'info> { + pub fn validate(&self, propeller: &Propeller) -> Result<()> { + let marginal_price_pool = &self.pool; + require_keys_eq!( + marginal_price_pool.key(), + propeller.marginal_price_pool, + PropellerError::InvalidMarginalPricePool + ); + let pool_token_index = propeller.marginal_price_pool_token_index as usize; + let marginal_price_pool_token_accounts = [&self.pool_token_0_account, &self.pool_token_1_account]; + require_gt!(TOKEN_COUNT, pool_token_index, PropellerError::InvalidMarginalPricePoolAccounts); + let pool_token_account = marginal_price_pool_token_accounts[pool_token_index]; + require_keys_eq!(pool_token_account.mint, propeller.marginal_price_pool_token_mint); + require_keys_eq!( + marginal_price_pool.token_mint_keys[pool_token_index], + propeller.marginal_price_pool_token_mint + ); + require_keys_eq!(pool_token_account.key(), marginal_price_pool_token_accounts[pool_token_index].key()); + msg!("finished marginal price pool validation"); + Ok(()) + } + + fn get_marginal_prices(&self) -> Result<[BorshDecimal; TOKEN_COUNT]> { + let result = two_pool::cpi::marginal_prices(CpiContext::new( + self.two_pool_program.to_account_info(), + two_pool::cpi::accounts::MarginalPrices { + pool: self.pool.to_account_info(), + pool_token_account_0: self.pool_token_0_account.to_account_info(), + pool_token_account_1: self.pool_token_1_account.to_account_info(), + lp_mint: self.lp_mint.to_account_info(), + }, + ))?; + Ok(result.get()) + } + + pub fn get_marginal_price_decimal(&self, propeller: &Propeller) -> Result { + let marginal_prices = self.get_marginal_prices()?; + let marginal_price_pool = &self.pool; + let marginal_price_pool_lp_mint = &self.lp_mint; + let marginal_price_pool_token_index = propeller.marginal_price_pool_token_index; + let swim_usd_mint = propeller.swim_usd_mint; + let mut marginal_price: Decimal = marginal_prices[marginal_price_pool_token_index as usize] + .try_into() + .map_err(|_| error!(PropellerError::ConversionError))?; + if marginal_price_pool_lp_mint.key() != swim_usd_mint { + require_keys_eq!( + marginal_price_pool.token_mint_keys[0], + swim_usd_mint, + PropellerError::InvalidMetapoolTokenMint, + ); + let swim_usd_marginal_price: Decimal = + marginal_prices[0].try_into().map_err(|_| error!(PropellerError::ConversionError))?; + marginal_price = + marginal_price.checked_div(swim_usd_marginal_price).ok_or(error!(PropellerError::IntegerOverflow))?; + } + Ok(marginal_price) + } + + //TODO: fix/test this - maybe just store decimals in propeller state. + pub fn get_swim_usd_mint_decimals(&self, propeller: &Propeller) -> Result { + let swim_usd_mint = propeller.swim_usd_mint; + let marginal_price_pool = &self.pool; + let marginal_price_pool_lp_mint = &self.lp_mint; + if swim_usd_mint == marginal_price_pool_lp_mint.key() { + return Ok(marginal_price_pool_lp_mint.decimals); + } + let marginal_price_pool_lp_mint_decimals = marginal_price_pool_lp_mint.decimals; + if swim_usd_mint == marginal_price_pool.lp_mint_key { + // Ok(marginal_price_pool_lp_mint_decimals) + Ok(marginal_price_pool_lp_mint_decimals + marginal_price_pool.lp_decimal_equalizer) + } else if swim_usd_mint == marginal_price_pool.token_mint_keys[0] { + Ok(marginal_price_pool_lp_mint_decimals + marginal_price_pool.token_decimal_equalizers[0]) + } else if swim_usd_mint == marginal_price_pool.token_mint_keys[1] { + Ok(marginal_price_pool_lp_mint_decimals + marginal_price_pool.token_decimal_equalizers[1]) + } else { + err!(PropellerError::UnableToRetrieveSwimUsdMintDecimals) + } + } +} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/mod.rs b/packages/solana-contracts/programs/propeller/src/instructions/mod.rs index 7918a3a3f..ecd4ce866 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/mod.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/mod.rs @@ -1,6 +1,6 @@ pub use { - create_owner_token_accounts::*, fee_tracker::*, initialize::*, process_swim_payload::*, target_chain_map::*, - token_id_map::*, utils::*, wormhole::*, + create_owner_token_accounts::*, fee_tracker::*, governance::*, initialize::*, marginal_price_pool::*, + process_swim_payload::*, target_chain_map::*, token_number_map::*, utils::*, wormhole::*, }; pub mod process_swim_payload; @@ -9,8 +9,10 @@ pub mod initialize; // pub mod pool; pub mod create_owner_token_accounts; pub mod fee_tracker; +pub mod governance; +pub mod marginal_price_pool; pub mod target_chain_map; -pub mod token_id_map; +pub mod token_number_map; pub mod two_pool_cpi; pub mod utils; pub mod wormhole; diff --git a/packages/solana-contracts/programs/propeller/src/instructions/process_swim_payload.rs b/packages/solana-contracts/programs/propeller/src/instructions/process_swim_payload.rs index 2f04b16bf..c081d2524 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/process_swim_payload.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/process_swim_payload.rs @@ -1,50 +1,30 @@ pub use switchboard_v2::{AggregatorAccountData, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID}; use { crate::{ - constants::LAMPORTS_PER_SOL_DECIMAL, convert_fees_to_swim_usd_atomic, get_marginal_price_decimal, - get_swim_usd_mint_decimals, FeeTracker, - }, - anchor_lang::system_program, - anchor_spl::{associated_token::AssociatedToken, token::Transfer}, - num_traits::{FromPrimitive, ToPrimitive}, - rust_decimal::Decimal, -}; -use { - crate::{ - deserialize_message_payload, - // env::*, error::*, - get_message_data, - get_transfer_with_payload_from_message_account, - hash_vaa, - state::{SwimClaim, SwimPayloadMessage, *}, - token_bridge::TokenBridge, - token_id_map::{PoolInstruction, TokenIdMap}, - ClaimData, - PayloadTransferWithPayload, - PostVAAData, - PostedVAAData, - Propeller, - RawSwimPayload, - TOKEN_COUNT, + fees::*, + get_memo_as_utf8, + state::{SwimClaim, SwimPayloadMessage}, + token_number_map::{ToTokenStep, TokenNumberMap}, + ClaimData, Propeller, TOKEN_COUNT, }, - anchor_lang::{prelude::*, solana_program::program::invoke}, + anchor_lang::{prelude::*, solana_program::program::invoke, system_program}, anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, + associated_token::get_associated_token_address, + token::{self, Mint, Token, TokenAccount, Transfer}, }, - std::convert::TryInto, two_pool::state::TwoPool, }; pub const SWIM_USD_TO_TOKEN_NUMBER: u16 = 0; #[derive(Accounts)] -#[instruction(target_token_id: u16)] +#[instruction(to_token_number: u16)] pub struct ProcessSwimPayload<'info> { #[account( seeds = [ b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], - bump = propeller.bump + bump = propeller.bump, + constraint = !propeller.is_paused @ PropellerError::IsPaused, )] pub propeller: Box>, #[account(mut)] @@ -57,7 +37,7 @@ pub struct ProcessSwimPayload<'info> { swim_payload_message.vaa_sequence.to_be_bytes().as_ref(), ], bump, - seeds::program = propeller.token_bridge().unwrap() + seeds::program = propeller.token_bridge() )] /// CHECK: WH Claim account pub claim: UncheckedAccount<'info>, @@ -83,8 +63,8 @@ pub struct ProcessSwimPayload<'info> { claim.key().as_ref(), ], bump = swim_payload_message.bump, - has_one = swim_payload_message_payer, - has_one = claim, + has_one = swim_payload_message_payer @ PropellerError::InvalidSwimPayloadMessagePayer, + has_one = claim @ PropellerError::InvalidWormholeClaimAccount, )] pub swim_payload_message: Box>, @@ -103,8 +83,7 @@ pub struct ProcessSwimPayload<'info> { pub redeemer: SystemAccount<'info>, #[account( mut, - token::mint = propeller.swim_usd_mint, - token::authority = redeemer, + address = get_associated_token_address(&redeemer.key(), &propeller.swim_usd_mint) )] pub redeemer_escrow: Box>, @@ -113,13 +92,14 @@ pub struct ProcessSwimPayload<'info> { b"propeller".as_ref(), b"token_id".as_ref(), propeller.key().as_ref(), - &target_token_id.to_le_bytes() + &to_token_number.to_le_bytes() ], - bump = token_id_map.bump, + bump = token_number_map.bump, + has_one = pool @ PropellerError::InvalidTokenNumberMapPool, )] - pub token_id_map: Account<'info, TokenIdMap>, + pub token_number_map: Account<'info, TokenNumberMap>, - /* Pool Used for final swap to get token_id_map.pool_token_mint */ + /* Pool Used for final swap to get token_number_map.pool_token_mint */ #[account( mut, seeds = [ @@ -135,40 +115,47 @@ pub struct ProcessSwimPayload<'info> { #[account( mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, + address = get_associated_token_address(&pool.key(), &pool_token_account_0.mint), + constraint = pool.token_keys[0] == pool_token_account_0.key(), )] pub pool_token_account_0: Box>, #[account( mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, + address = get_associated_token_address(&pool.key(), &pool_token_account_1.mint), + constraint = pool.token_keys[1] == pool_token_account_1.key(), )] pub pool_token_account_1: Box>, - #[account(mut)] + #[account(mut, address = pool.lp_mint_key)] pub lp_mint: Box>, #[account( mut, - token::mint = lp_mint + address = pool.governance_fee_key, )] pub governance_fee: Box>, - // needs to be a signer since its a "keypair" account - pub user_transfer_authority: Signer<'info>, - - #[account(mut, token::mint = pool_token_account_0.mint, token::authority = swim_payload_message.owner)] + #[account( + mut, + address = get_associated_token_address(&swim_payload_message.owner, &pool_token_account_0.mint) + )] pub user_token_account_0: Box>, - #[account(mut, token::mint = pool_token_account_1.mint, token::authority = swim_payload_message.owner)] + #[account( + mut, + address = get_associated_token_address(&swim_payload_message.owner, &pool_token_account_1.mint) + )] pub user_token_account_1: Box>, - #[account(mut, token::mint = pool.lp_mint_key, token::authority = swim_payload_message.owner)] + #[account( + mut, + address = get_associated_token_address(&swim_payload_message.owner, &pool.lp_mint_key) + )] pub user_lp_token_account: Box>, pub token_program: Program<'info, Token>, pub two_pool_program: Program<'info, two_pool::program::TwoPool>, + pub system_program: Program<'info, System>, } @@ -179,118 +166,45 @@ impl<'info> ProcessSwimPayload<'info> { require_keys_eq!(ctx.accounts.swim_payload_message.claim.key(), ctx.accounts.claim.key()); require_keys_eq!( ctx.accounts.pool.key(), - ctx.accounts.token_id_map.pool, - PropellerError::InvalidTokenIdMapPool + ctx.accounts.token_number_map.pool, + PropellerError::InvalidTokenNumberMapPool ); - Ok(()) } - pub fn validate(&self) -> Result<()> { - // verify claim - // verify message - require_keys_eq!(self.swim_payload_message.claim.key(), self.claim.key()); - require_keys_eq!(self.pool.key(), self.token_id_map.pool, PropellerError::InvalidTokenIdMapPool); - Ok(()) - } - - pub fn transfer_tokens( - &self, - output_token_index: u16, - transfer_amount: u64, - min_output_amount: u64, - ) -> Result { - let token_id_mapping = &self.token_id_map; - let pool_ix = &token_id_mapping.pool_ix; + pub fn transfer_tokens(&self, to_token_number: u16, transfer_amount: u64, min_output_amount: u64) -> Result { + let token_id_mapping = &self.token_number_map; + let to_token_step = &token_id_mapping.to_token_step; let pool_token_mint = &token_id_mapping.pool_token_mint; let pool_token_index = token_id_mapping.pool_token_index; - //TODO: decide if using user_transfer_auth - // remove user_transfer_authority account if not. - - self.execute_transfer_or_pool_ix( + self.execute_to_token_step( transfer_amount, min_output_amount, - output_token_index, - pool_ix, + to_token_number, + to_token_step, pool_token_index, pool_token_mint, &self.redeemer.to_account_info(), - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], + &[&[(b"redeemer".as_ref()), &[self.propeller.redeemer_bump]]], ) - // self.transfer_with_user_auth( - // transfer_amount, - // min_output_amount, - // output_token_index, - // pool_ix, - // pool_token_index, - // pool_token_mint, - // &self.user_transfer_authority.to_account_info(), - // ) } - fn transfer_with_user_auth( + fn execute_to_token_step( &self, transfer_amount: u64, min_output_amount: u64, output_token_index: u16, - pool_ix: &PoolInstruction, - pool_token_index: u8, - pool_token_mint: &Pubkey, - user_transfer_authority: &AccountInfo<'info>, - ) -> Result { - token::approve( - CpiContext::new_with_signer( - self.token_program.to_account_info(), - token::Approve { - // source - to: self.redeemer_escrow.to_account_info(), - delegate: self.user_transfer_authority.to_account_info(), - authority: self.redeemer.to_account_info(), - }, - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], - ), - transfer_amount, - )?; - require_gt!(TOKEN_COUNT, pool_token_index as usize); - // let user_transfer_authority = &self.user_transfer_authority.to_account_info(); - let output_amount_res = self.execute_transfer_or_pool_ix( - transfer_amount, - min_output_amount, - output_token_index, - pool_ix, - pool_token_index, - pool_token_mint, - user_transfer_authority, - &[], - ); - token::revoke(CpiContext::new_with_signer( - self.token_program.to_account_info(), - token::Revoke { - // source - source: self.redeemer_escrow.to_account_info(), - authority: self.redeemer.to_account_info(), - }, - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], - ))?; - output_amount_res - } - - fn execute_transfer_or_pool_ix( - &self, - transfer_amount: u64, - min_output_amount: u64, - output_token_index: u16, - pool_ix: &PoolInstruction, + to_token_step: &ToTokenStep, pool_token_index: u8, pool_token_mint: &Pubkey, user_transfer_authority: &AccountInfo<'info>, signer_seeds: &[&[&[u8]]], ) -> Result { - let swim_payload_owner = self.swim_payload_message.owner; + let _swim_payload_owner = self.swim_payload_message.owner; require_gt!(TOKEN_COUNT, pool_token_index as usize); - match pool_ix { - PoolInstruction::RemoveExactBurn => { + match to_token_step { + ToTokenStep::RemoveExactBurn => { msg!("Executing RemoveExactBurn"); require_keys_eq!(self.pool.token_mint_keys[pool_token_index as usize], *pool_token_mint); @@ -298,17 +212,28 @@ impl<'info> ProcessSwimPayload<'info> { Ok(two_pool::cpi::remove_exact_burn( CpiContext::new_with_signer( self.two_pool_program.to_account_info(), - two_pool::cpi::accounts::RemoveExactBurn { - pool: self.pool.to_account_info(), - pool_token_account_0: self.pool_token_account_0.to_account_info(), - pool_token_account_1: self.pool_token_account_1.to_account_info(), - lp_mint: self.lp_mint.to_account_info(), - governance_fee: self.governance_fee.to_account_info(), - user_transfer_authority: user_transfer_authority.to_account_info(), - user_token_account_0: self.user_token_account_0.to_account_info(), - user_token_account_1: self.user_token_account_1.to_account_info(), + two_pool::cpi::accounts::AddOrRemove { + swap: two_pool::cpi::accounts::Swap { + pool: self.pool.to_account_info(), + pool_token_account_0: self.pool_token_account_0.to_account_info(), + pool_token_account_1: self.pool_token_account_1.to_account_info(), + lp_mint: self.lp_mint.to_account_info(), + governance_fee: self.governance_fee.to_account_info(), + user_transfer_authority: user_transfer_authority.to_account_info(), + user_token_account_0: self.user_token_account_0.to_account_info(), + user_token_account_1: self.user_token_account_1.to_account_info(), + token_program: self.token_program.to_account_info(), + }, + // pool: self.pool.to_account_info(), + // pool_token_account_0: self.pool_token_account_0.to_account_info(), + // pool_token_account_1: self.pool_token_account_1.to_account_info(), + // lp_mint: self.lp_mint.to_account_info(), + // governance_fee: self.governance_fee.to_account_info(), + // user_transfer_authority: user_transfer_authority.to_account_info(), + // user_token_account_0: self.user_token_account_0.to_account_info(), + // user_token_account_1: self.user_token_account_1.to_account_info(), user_lp_token_account: self.redeemer_escrow.to_account_info(), - token_program: self.token_program.to_account_info(), + // token_program: self.token_program.to_account_info(), }, signer_seeds, ), @@ -318,14 +243,14 @@ impl<'info> ProcessSwimPayload<'info> { )? .get()) } - PoolInstruction::SwapExactInput => { + ToTokenStep::SwapExactInput => { msg!("Executing SwapExactInput"); require_keys_eq!(self.pool.token_mint_keys[pool_token_index as usize], *pool_token_mint); Ok(two_pool::cpi::swap_exact_input( CpiContext::new_with_signer( self.two_pool_program.to_account_info(), - two_pool::cpi::accounts::SwapExactInput { + two_pool::cpi::accounts::Swap { pool: self.pool.to_account_info(), pool_token_account_0: self.pool_token_account_0.to_account_info(), pool_token_account_1: self.pool_token_account_1.to_account_info(), @@ -344,14 +269,14 @@ impl<'info> ProcessSwimPayload<'info> { )? .get()) } - PoolInstruction::Transfer => { + ToTokenStep::Transfer => { require_eq!(output_token_index, SWIM_USD_TO_TOKEN_NUMBER, PropellerError::InvalidOutputTokenIndex); - self.transfer_swim_usd_tokens(transfer_amount, user_transfer_authority, signer_seeds) + self.transfer_swim_usd_to_owner(transfer_amount, user_transfer_authority, signer_seeds) } } } - fn transfer_swim_usd_tokens( + fn transfer_swim_usd_to_owner( &self, transfer_amount: u64, user_transfer_authority: &AccountInfo<'info>, @@ -383,7 +308,7 @@ impl<'info> ProcessSwimPayload<'info> { pub fn handle_process_swim_payload( ctx: Context, - target_token_id: u16, + to_token_number: u16, min_output_amount: u64, ) -> Result { let propeller_message = &ctx.accounts.swim_payload_message; @@ -395,14 +320,15 @@ pub fn handle_process_swim_payload( let transfer_amount = ctx.accounts.swim_payload_message.transfer_amount; - let owner = propeller_message.owner; - let token_program = &ctx.accounts.token_program; + let _owner = propeller_message.owner; + let _token_program = &ctx.accounts.token_program; msg!("transfer_amount: {}", transfer_amount); - let output_amount = ctx.accounts.transfer_tokens(target_token_id, transfer_amount, min_output_amount)?; + let output_amount = ctx.accounts.transfer_tokens(to_token_number, transfer_amount, min_output_amount)?; let swim_claim_bump = *ctx.bumps.get("swim_claim").unwrap(); ctx.accounts.init_swim_claim(swim_claim_bump)?; + msg!("output_amount: {}", output_amount); Ok(output_amount) } @@ -411,68 +337,10 @@ pub fn handle_process_swim_payload( pub struct PropellerProcessSwimPayload<'info> { pub process_swim_payload: ProcessSwimPayload<'info>, - // #[account(mut)] - // pub token_bridge_mint: Box>, - /// Assuming that USD:USDC 1:1 - ///CHECK: account for getting gas -> USD price - #[account( - constraint = - *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount - )] - pub aggregator: AccountLoader<'info, AggregatorAccountData>, - - #[account( - mut, - token::mint = process_swim_payload.propeller.swim_usd_mint, - token::authority = process_swim_payload.propeller, - )] - /// this is "to_fees" - /// recipient of fees for executing complete transfer (e.g. relayer) - pub fee_vault: Box>, - - // Note: anchor 0.25.0 still has some issues auto deriving recursive - // seeds. Seems to be mostly fixed in new version. - // #[account( - // mut, - // seeds = [ - // b"propeller".as_ref(), - // b"fee".as_ref(), - // process_swim_payload.propeller.token_bridge_mint, - // process_swim_payload.payer.key().as_ref() - // ], - // bump = fee_tracker.bump - // )] - #[account( - mut, - seeds = [ - b"propeller".as_ref(), - b"fee".as_ref(), - process_swim_payload.get_swim_usd_mint().as_ref(), - process_swim_payload.payer.key().as_ref() - ], - bump = fee_tracker.bump - )] - pub fee_tracker: Box>, + pub fee_tracking: FeeTracking<'info>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - marginal_price_pool_token_0_account.mint.as_ref(), - marginal_price_pool_token_1_account.mint.as_ref(), - marginal_price_pool_lp_mint.key().as_ref(), - ], - bump = marginal_price_pool.bump, - seeds::program = process_swim_payload.two_pool_program.key() - )] - pub marginal_price_pool: Box>, - #[account(address = marginal_price_pool.token_keys[0])] - pub marginal_price_pool_token_0_account: Box>, - #[account(address = marginal_price_pool.token_keys[1])] - pub marginal_price_pool_token_1_account: Box>, - #[account(address = marginal_price_pool.lp_mint_key)] - pub marginal_price_pool_lp_mint: Box>, /// This is for transferring lamports for kickstart + /// TODO: force this to be system account? #[account(mut, address = process_swim_payload.swim_payload_message.owner)] pub owner: SystemAccount<'info>, #[account(executable, address = spl_memo::id())] @@ -481,72 +349,70 @@ pub struct PropellerProcessSwimPayload<'info> { } impl<'info> PropellerProcessSwimPayload<'info> { - pub fn accounts(ctx: &Context, target_token_id: u16) -> Result<()> { - ctx.accounts.validate()?; + pub fn accounts(ctx: &Context, to_token_number: u16) -> Result<()> { + require_keys_eq!( + ctx.accounts.process_swim_payload.swim_payload_message.claim.key(), + ctx.accounts.process_swim_payload.claim.key() + ); + require_keys_eq!( + ctx.accounts.process_swim_payload.pool.key(), + ctx.accounts.process_swim_payload.token_number_map.pool, + PropellerError::InvalidTokenNumberMapPool + ); require_keys_eq!( ctx.accounts.process_swim_payload.propeller.aggregator, - ctx.accounts.aggregator.key(), + ctx.accounts.fee_tracking.aggregator.key(), PropellerError::InvalidAggregator ); require_eq!( ctx.accounts.process_swim_payload.swim_payload_message.target_token_id, - target_token_id, - // PropellerError::InvalidTargetTokenId + to_token_number, + PropellerError::ToTokenNumberMismatch ); - validate_marginal_prices_pool_accounts( - &ctx.accounts.process_swim_payload.propeller, - &ctx.accounts.marginal_price_pool.key(), - &[ - ctx.accounts.marginal_price_pool_token_0_account.mint, - ctx.accounts.marginal_price_pool_token_1_account.mint, - ], - )?; + let propeller = &ctx.accounts.process_swim_payload.propeller; + let payer = &ctx.accounts.process_swim_payload.payer.key(); + ctx.accounts.fee_tracking.validate(propeller, payer, ctx.program_id)?; + // ctx.accounts.marginal_price_pool.validate(&propeller)?; msg!("Finished PropellerProcessSwimPayload::accounts()"); Ok(()) } - pub fn validate(&self) -> Result<()> { - // verify claim - // verify message - require_keys_eq!( - self.process_swim_payload.swim_payload_message.claim.key(), - self.process_swim_payload.claim.key() - ); - require_keys_eq!( - self.process_swim_payload.pool.key(), - self.process_swim_payload.token_id_map.pool, - PropellerError::InvalidTokenIdMapPool + fn transfer_gas_kickstart(&self) -> Result<()> { + let propeller = &self.process_swim_payload.propeller; + let owner_account = &self.owner.to_account_info(); + let payer = &self.process_swim_payload.payer.to_account_info(); + let owner_starting_lamports = owner_account.lamports(); + let payer_starting_lamports = payer.lamports(); + let system_program = &self.process_swim_payload.system_program; + require_gte!( + payer_starting_lamports, + propeller.gas_kickstart_amount, + PropellerError::PayerInsufficientFundsForGasKickstart ); + system_program::transfer( + CpiContext::new( + system_program.to_account_info(), + system_program::Transfer { from: payer.clone(), to: owner_account.clone() }, + ), + propeller.gas_kickstart_amount, + )?; + msg!("owner_starting_lamports: {}, owner_final_lamports: {}, payer_starting_lamports: {}, payer_final_lamports: {}", + owner_starting_lamports, owner_account.lamports(), payer_starting_lamports, payer.lamports()); Ok(()) } - /// Calculates, transfer and tracks fees - /// returns fees_in_token_bridge_mint - fn handle_fees(&mut self) -> Result { - let fees_in_swim_usd_atomic = self.calculate_fees()?; - let propeller = &self.process_swim_payload.propeller; - let token_program = &self.process_swim_payload.token_program; - msg!("fees_in_swim_usd_atomic: {:?}", fees_in_swim_usd_atomic); - let fee_tracker = &mut self.fee_tracker; - fee_tracker.fees_owed = - fee_tracker.fees_owed.checked_add(fees_in_swim_usd_atomic).ok_or(PropellerError::IntegerOverflow)?; - let cpi_accounts = Transfer { - from: self.process_swim_payload.redeemer_escrow.to_account_info(), - to: self.fee_vault.to_account_info(), - authority: self.process_swim_payload.redeemer.to_account_info(), - }; - token::transfer( - CpiContext::new_with_signer( - token_program.to_account_info(), - cpi_accounts, - &[&[&b"redeemer".as_ref(), &[propeller.redeemer_bump]]], - ), - fees_in_swim_usd_atomic, - )?; - Ok(fees_in_swim_usd_atomic) + fn log_memo(&self) -> Result<()> { + let memo = self.process_swim_payload.swim_payload_message.memo; + if memo != [0u8; 16] { + let memo_ix = spl_memo::build_memo(get_memo_as_utf8(memo)?.as_ref(), &[]); + invoke(&memo_ix, &[self.memo.to_account_info()])?; + } + Ok(()) } +} - fn calculate_fees(&self) -> Result { +impl<'info> Fees<'info> for PropellerProcessSwimPayload<'info> { + fn calculate_fees_in_lamports(&self) -> Result { //TODO: this is in lamports/SOL. need in swimUSD. // for (secp + verify) & postVAA, need to implement a fee tracking mechanism since there's no way to // credit the payer during that step. must be some type of "deferred" fees @@ -556,12 +422,6 @@ impl<'info> PropellerProcessSwimPayload<'info> { let swim_payload_message = &self.process_swim_payload.swim_payload_message; let propeller_process_swim_payload_fees = propeller.process_swim_payload_fee; - let two_pool_program = &self.process_swim_payload.two_pool_program; - let marginal_price_pool = &self.marginal_price_pool; - let marginal_price_pool_token_0_account = &self.marginal_price_pool_token_0_account; - let marginal_price_pool_token_1_account = &self.marginal_price_pool_token_1_account; - let marginal_price_pool_lp_mint = &self.marginal_price_pool_lp_mint; - let swim_claim_rent_exempt_fees = rent.minimum_balance(8 + SwimClaim::LEN); let gas_kickstart_amount = if swim_payload_message.gas_kickstart { propeller.gas_kickstart_amount } else { 0 }; let fee_in_lamports = swim_claim_rent_exempt_fees @@ -581,63 +441,22 @@ impl<'info> PropellerProcessSwimPayload<'info> { gas_kickstart_amount, fee_in_lamports ); - - let cpi_ctx = CpiContext::new( - two_pool_program.to_account_info(), - two_pool::cpi::accounts::MarginalPrices { - pool: marginal_price_pool.to_account_info(), - pool_token_account_0: marginal_price_pool_token_0_account.to_account_info(), - pool_token_account_1: marginal_price_pool_token_1_account.to_account_info(), - lp_mint: marginal_price_pool_lp_mint.to_account_info(), - }, - ); - let fees_in_swim_usd_atomic = convert_fees_to_swim_usd_atomic( - fee_in_lamports, - &propeller, - &marginal_price_pool_lp_mint, - // ctx.accounts.into_marginal_prices(), - cpi_ctx, - &marginal_price_pool, - &self.aggregator, - i64::MAX, - )?; - Ok(fees_in_swim_usd_atomic) + Ok(fee_in_lamports) } - fn transfer_gas_kickstart(&self) -> Result<()> { - let propeller = &self.process_swim_payload.propeller; - let owner_account = &self.owner.to_account_info(); - let payer = &self.process_swim_payload.payer.to_account_info(); - let owner_starting_lamports = owner_account.lamports(); - let payer_starting_lamports = payer.lamports(); - let system_program = &self.process_swim_payload.system_program; - require_gte!( - payer_starting_lamports, - propeller.gas_kickstart_amount, - PropellerError::PayerInsufficientFundsForGasKickstart - ); - system_program::transfer( - CpiContext::new( - system_program.to_account_info(), - system_program::Transfer { from: payer.clone(), to: owner_account.clone() }, + fn transfer_fees_to_vault(&mut self, fees_in_swim_usd: u64) -> Result<()> { + token::transfer( + CpiContext::new_with_signer( + self.process_swim_payload.token_program.to_account_info(), + Transfer { + from: self.process_swim_payload.redeemer_escrow.to_account_info(), + to: self.fee_tracking.fee_vault.to_account_info(), + authority: self.process_swim_payload.redeemer.to_account_info(), + }, + &[&[(b"redeemer".as_ref()), &[self.process_swim_payload.propeller.redeemer_bump]]], ), - propeller.gas_kickstart_amount, - )?; - msg!("owner_starting_lamports: {}, owner_final_lamports: {}, payer_starting_lamports: {}, payer_final_lamports: {}", - owner_starting_lamports, owner_account.lamports(), payer_starting_lamports, payer.lamports()); - Ok(()) - } - - fn log_memo(&self) -> Result<()> { - let memo = self.process_swim_payload.swim_payload_message.memo; - if memo != [0u8; 16] { - let memo_ix = - spl_memo::build_memo(std::str::from_utf8(hex::encode(memo).as_bytes()).unwrap().as_ref(), &[]); - invoke(&memo_ix, &[self.memo.to_account_info()])?; - } else { - msg!("memo is empty"); - } - Ok(()) + fees_in_swim_usd, + ) } } @@ -661,7 +480,7 @@ TODO: */ pub fn handle_propeller_process_swim_payload( ctx: Context, - target_token_id: u16, + _to_token_number: u16, ) -> Result { let swim_payload_message = &ctx.accounts.process_swim_payload.swim_payload_message; let is_gas_kickstart = swim_payload_message.gas_kickstart; @@ -674,22 +493,21 @@ pub fn handle_propeller_process_swim_payload( let mut transfer_amount = swim_payload_message.transfer_amount; let propeller = &ctx.accounts.process_swim_payload.propeller; - let redeemer = &ctx.accounts.process_swim_payload.redeemer; + let _redeemer = &ctx.accounts.process_swim_payload.redeemer; let swim_payload_owner = swim_payload_message.owner; - let token_program = &ctx.accounts.process_swim_payload.token_program; + let _token_program = &ctx.accounts.process_swim_payload.token_program; msg!("original transfer_amount: {:?}", transfer_amount); if swim_payload_owner != ctx.accounts.process_swim_payload.payer.key() { - let fees_in_token_bridge = &ctx.accounts.handle_fees()?; - // let fees_in_token_bridge = calculate_fees2(&ctx)?; - msg!("fees_in_token_bridge: {:?}", fees_in_token_bridge); + let fees_in_lamports = ctx.accounts.calculate_fees_in_lamports()?; + let fees_in_swim_usd_atomic = ctx.accounts.fee_tracking.track_fees(fees_in_lamports, propeller)?; + // let fees_in_swim_usd_atomic = ctx.accounts.convert_fees_to_swim_usd_atomic(fees_in_lamports)?; + ctx.accounts.transfer_fees_to_vault(fees_in_swim_usd_atomic)?; + msg!("fees_in_swim_usd_atomic: {:?}", fees_in_swim_usd_atomic); if is_gas_kickstart { ctx.accounts.transfer_gas_kickstart()?; } transfer_amount = - transfer_amount.checked_sub(*fees_in_token_bridge).ok_or(error!(PropellerError::InsufficientFunds))?; - } else { - //TODO: a user should just call processSwimPayload instead to avoid passing in extra accounts. end result is same. - msg!("swim_payload_owner == ctx.accounts.payer.key(). Owner bypass"); + transfer_amount.checked_sub(fees_in_swim_usd_atomic).ok_or(error!(PropellerError::InsufficientFunds))?; } msg!("transfer_amount - fee = {}", transfer_amount); @@ -710,8 +528,7 @@ pub struct PropellerProcessSwimPayloadFallback<'info> { #[account( seeds = [ b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], bump = propeller.bump, - has_one = marginal_price_pool, - has_one = aggregator @ PropellerError::InvalidAggregator + constraint = !propeller.is_paused @ PropellerError::IsPaused, )] pub propeller: Box>, #[account(mut)] @@ -724,7 +541,7 @@ pub struct PropellerProcessSwimPayloadFallback<'info> { swim_payload_message.vaa_sequence.to_be_bytes().as_ref(), ], bump, - seeds::program = propeller.token_bridge().unwrap() + seeds::program = propeller.token_bridge() )] /// CHECK: WH Claim account pub claim: UncheckedAccount<'info>, @@ -750,9 +567,9 @@ pub struct PropellerProcessSwimPayloadFallback<'info> { claim.key().as_ref(), ], bump = swim_payload_message.bump, - has_one = swim_payload_message_payer, + has_one = swim_payload_message_payer @ PropellerError::InvalidSwimPayloadMessagePayer, + has_one = claim @ PropellerError::InvalidWormholeClaimAccount, has_one = owner, - has_one = claim, )] pub swim_payload_message: Box>, #[account(mut)] @@ -770,73 +587,37 @@ pub struct PropellerProcessSwimPayloadFallback<'info> { pub redeemer: SystemAccount<'info>, #[account( mut, - token::mint = propeller.swim_usd_mint, - token::authority = redeemer, + address = get_associated_token_address(&redeemer.key(), &propeller.swim_usd_mint) )] pub redeemer_escrow: Box>, - /// CHECK: Unchecked b/c if target_token_id is invalid then this account should not exist/be able to be - /// deserialized as a `TokenIdMap`. if it does exist, then engine should have called + #[account( + seeds = [ + b"propeller".as_ref(), + b"token_id".as_ref(), + propeller.key().as_ref(), + &swim_payload_message.target_token_id.to_le_bytes() + ], + bump, + )] + /// CHECK: Unchecked b/c if `to_token_number` is invalid then this account should not exist/be able to be + /// deserialized as a `TokenNumberMap`. if it does exist, then engine should have called /// propeller_create_owner_token_accounts instead - pub token_id_map: UncheckedAccount<'info>, + pub token_number_map: UncheckedAccount<'info>, - // needs to be a signer since its a "keypair" account - pub user_transfer_authority: Signer<'info>, - - #[account(mut, associated_token::mint = propeller.swim_usd_mint, associated_token::authority = owner)] + #[account( + mut, + address = get_associated_token_address(&owner.key(), &propeller.swim_usd_mint) + )] pub user_swim_usd_ata: Box>, pub token_program: Program<'info, Token>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, #[account(executable, address = spl_memo::id())] ///CHECK: memo program pub memo: UncheckedAccount<'info>, pub system_program: Program<'info, System>, - #[account( - constraint = - *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount - )] - pub aggregator: AccountLoader<'info, AggregatorAccountData>, - - #[account( - mut, - associated_token::mint = propeller.swim_usd_mint, - associated_token::authority = propeller, - )] - /// this is "to_fees" - /// recipient of fees for executing complete transfer (e.g. relayer) - pub fee_vault: Box>, - #[account( - mut, - seeds = [ - b"propeller".as_ref(), - b"fee".as_ref(), - propeller.swim_usd_mint.as_ref(), - payer.key().as_ref() - ], - bump = fee_tracker.bump - )] - pub fee_tracker: Box>, - - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - marginal_price_pool_token_0_account.mint.as_ref(), - marginal_price_pool_token_1_account.mint.as_ref(), - marginal_price_pool_lp_mint.key().as_ref(), - ], - bump = marginal_price_pool.bump, - seeds::program = two_pool_program.key() - )] - pub marginal_price_pool: Box>, - #[account(address = marginal_price_pool.token_keys[0])] - pub marginal_price_pool_token_0_account: Box>, - #[account(address = marginal_price_pool.token_keys[1])] - pub marginal_price_pool_token_1_account: Box>, - #[account(address = marginal_price_pool.lp_mint_key)] - pub marginal_price_pool_lp_mint: Box>, + pub fee_tracking: FeeTracking<'info>, /// This is for transferring lamports for kickstart #[account(mut)] pub owner: SystemAccount<'info>, @@ -845,159 +626,13 @@ pub struct PropellerProcessSwimPayloadFallback<'info> { impl<'info> PropellerProcessSwimPayloadFallback<'info> { pub fn accounts(ctx: &Context) -> Result<()> { require_keys_eq!(ctx.accounts.owner.key(), ctx.accounts.swim_payload_message.owner); - let (expected_token_id_map_address, _bump) = Pubkey::find_program_address( - &[ - b"propeller".as_ref(), - b"token_id".as_ref(), - ctx.accounts.propeller.key().as_ref(), - ctx.accounts.swim_payload_message.target_token_id.to_le_bytes().as_ref(), - ], - ctx.program_id, - ); - //Note: the address should at least be valid even though it doesn't exist. - require_keys_eq!(expected_token_id_map_address, ctx.accounts.token_id_map.key()); + let propeller = &ctx.accounts.propeller; + let payer = &ctx.accounts.payer.key(); + ctx.accounts.fee_tracking.validate(propeller, payer, ctx.program_id)?; msg!("Passed PropellerProcessSwimPayloadFallback::accounts() check"); Ok(()) } - /// Calculates, transfer and tracks fees - /// returns fees_in_swim_usd_mint - fn handle_fees(&mut self) -> Result { - let fees_in_token_bridge = self.calculate_fees()?; - let propeller = &self.propeller; - let token_program = &self.token_program; - msg!("fees_in_token_bridge: {:?}", fees_in_token_bridge); - let fee_tracker = &mut self.fee_tracker; - fee_tracker.fees_owed = - fee_tracker.fees_owed.checked_add(fees_in_token_bridge).ok_or(PropellerError::IntegerOverflow)?; - let cpi_accounts = Transfer { - from: self.redeemer_escrow.to_account_info(), - to: self.fee_vault.to_account_info(), - authority: self.redeemer.to_account_info(), - }; - token::transfer( - CpiContext::new_with_signer( - token_program.to_account_info(), - cpi_accounts, - &[&[&b"redeemer".as_ref(), &[propeller.redeemer_bump]]], - ), - fees_in_token_bridge, - )?; - Ok(fees_in_token_bridge) - } - - fn calculate_fees(&self) -> Result { - //TODO: this is in lamports/SOL. need in swimUSD. - // for (secp + verify) & postVAA, need to implement a fee tracking mechanism since there's no way to - // credit the payer during that step. must be some type of "deferred" fees - let rent = Rent::get()?; - - let propeller = &self.propeller; - let swim_payload_message = &self.swim_payload_message; - let propeller_process_swim_payload_fees = propeller.process_swim_payload_fee; - - let two_pool_program = &self.two_pool_program; - let marginal_price_pool = &self.marginal_price_pool; - let marginal_price_pool_token_0_account = &self.marginal_price_pool_token_0_account; - let marginal_price_pool_token_1_account = &self.marginal_price_pool_token_1_account; - let marginal_price_pool_lp_mint = &self.marginal_price_pool_lp_mint; - - let swim_claim_rent_exempt_fees = rent.minimum_balance(8 + SwimClaim::LEN); - let gas_kickstart_amount = if swim_payload_message.gas_kickstart { propeller.gas_kickstart_amount } else { 0 }; - let fee_in_lamports = swim_claim_rent_exempt_fees - .checked_add(propeller_process_swim_payload_fees) - .and_then(|x| x.checked_add(gas_kickstart_amount)) - .ok_or(PropellerError::IntegerOverflow)?; - - msg!( - " - {}(swim_claim_rent_exempt_fees) + - {}(propeller_process_swim_payload_fees) + - {}(gas_kickstart_amount) - = {}(fee_in_lamports) - ", - swim_claim_rent_exempt_fees, - propeller_process_swim_payload_fees, - gas_kickstart_amount, - fee_in_lamports - ); - - let cpi_ctx = CpiContext::new( - two_pool_program.to_account_info(), - two_pool::cpi::accounts::MarginalPrices { - pool: marginal_price_pool.to_account_info(), - pool_token_account_0: marginal_price_pool_token_0_account.to_account_info(), - pool_token_account_1: marginal_price_pool_token_1_account.to_account_info(), - lp_mint: marginal_price_pool_lp_mint.to_account_info(), - }, - ); - let result = two_pool::cpi::marginal_prices(cpi_ctx)?; - // let marginal_prices = result.get().marginal_prices; - let marginal_prices = result.get(); - - msg!("marginal_prices: {:?}", marginal_prices); - let mut res = 0u64; - let feed = &self.aggregator.load()?; - - let sol_usd_price: Decimal = feed.get_result()?.try_into()?; - let name = feed.name; - - let lamports_usd_price = - sol_usd_price.checked_div(LAMPORTS_PER_SOL_DECIMAL).ok_or(PropellerError::IntegerOverflow)?; - msg!("sol_usd_price:{},lamports_usd_price: {}", sol_usd_price, lamports_usd_price); - // check whether the feed has been updated in the last 300 seconds - feed.check_staleness( - Clock::get().unwrap().unix_timestamp, - // 300 - i64::MAX, - ) - .map_err(|_| error!(PropellerError::StaleFeed))?; - // check feed does not exceed max_confidence_interval - // if let Some(max_confidence_interval) = params.max_confidence_interval { - // feed.check_confidence_interval(SwitchboardDecimal::from_f64(max_confidence_interval)) - // .map_err(|_| error!(PropellerError::ConfidenceIntervalExceeded))?; - // } - let lp_mint_key = marginal_price_pool_lp_mint.key(); - - let swim_usd_mint_key = self.propeller.swim_usd_mint; - let marginal_price: Decimal = get_marginal_price_decimal( - &marginal_price_pool, - &marginal_prices, - &propeller, - // propeller.marginal_price_pool_token_index as usize, - &marginal_price_pool_lp_mint.key(), - // &token_bridge_mint_key, - )?; - - msg!("marginal_price: {}", marginal_price); - let fee_in_lamports_decimal = Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::ConversionError)?; - msg!("fee_in_lamports(u64): {:?} fee_in_lamports_decimal: {:?}", fee_in_lamports, fee_in_lamports_decimal); - let fee_in_swim_usd_mint_decimal = marginal_price - .checked_mul(lamports_usd_price) - .and_then(|v| v.checked_mul(fee_in_lamports_decimal)) - .ok_or(PropellerError::IntegerOverflow)?; - - let swim_usd_mint_decimals = - get_swim_usd_mint_decimals(&swim_usd_mint_key, &marginal_price_pool, &marginal_price_pool_lp_mint)?; - - msg!("swim_usd_mint_mint_decimals: {:?} ", swim_usd_mint_decimals); - - let ten_pow_decimals = - Decimal::from_u64(10u64.pow(swim_usd_mint_decimals as u32)).ok_or(PropellerError::IntegerOverflow)?; - let fee_in_swim_usd_atomic = fee_in_swim_usd_mint_decimal - .checked_mul(ten_pow_decimals) - .and_then(|v| v.to_u64()) - .ok_or(PropellerError::ConversionError)?; - - msg!( - "fee_in_swim_usd_decimal: {:?} fee_in_swim_usd_atomic: {:?}", - fee_in_swim_usd_mint_decimal, - fee_in_swim_usd_atomic - ); - res = fee_in_swim_usd_atomic; - Ok(res) - } - fn transfer_gas_kickstart(&self) -> Result<()> { let propeller = &self.propeller; let owner_account = &self.owner.to_account_info(); @@ -1022,82 +657,6 @@ impl<'info> PropellerProcessSwimPayloadFallback<'info> { Ok(()) } - pub fn transfer_tokens(&self, transfer_amount: u64) -> Result { - //TODO: decide if using user_transfer_auth - // remove user_transfer_authority account if not. - self.transfer_swim_usd_tokens( - transfer_amount, - &self.redeemer.to_account_info(), - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], - ) - // self.execute_transfer_or_pool_ix( - // transfer_amount, - // min_output_amount, - // output_token_index, - // pool_ix, - // pool_token_index, - // pool_token_mint, - // &self.redeemer.to_account_info(), - // &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], - // ) - // self.transfer_with_user_auth( - // transfer_amount, - // min_output_amount, - // output_token_index, - // pool_ix, - // pool_token_index, - // pool_token_mint, - // &self.user_transfer_authority.to_account_info(), - // ) - } - - // fn transfer_with_user_auth( - // &self, - // transfer_amount: u64, - // min_output_amount: u64, - // output_token_index: u16, - // pool_ix: &PoolInstruction, - // pool_token_index: u8, - // pool_token_mint: &Pubkey, - // user_transfer_authority: &AccountInfo<'info>, - // ) -> Result { - // token::approve( - // CpiContext::new_with_signer( - // self.token_program.to_account_info(), - // token::Approve { - // // source - // to: self.redeemer_escrow.to_account_info(), - // delegate: self.user_transfer_authority.to_account_info(), - // authority: self.redeemer.to_account_info(), - // }, - // &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], - // ), - // transfer_amount, - // )?; - // require_gt!(TOKEN_COUNT, pool_token_index as usize); - // // let user_transfer_authority = &self.user_transfer_authority.to_account_info(); - // let output_amount_res = self.execute_transfer_or_pool_ix( - // transfer_amount, - // min_output_amount, - // output_token_index, - // pool_ix, - // pool_token_index, - // pool_token_mint, - // user_transfer_authority, - // &[], - // ); - // token::revoke(CpiContext::new_with_signer( - // self.token_program.to_account_info(), - // token::Revoke { - // // source - // source: self.redeemer_escrow.to_account_info(), - // authority: self.redeemer.to_account_info(), - // }, - // &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], - // ))?; - // output_amount_res - // } - fn transfer_swim_usd_tokens( &self, transfer_amount: u64, @@ -1126,20 +685,65 @@ impl<'info> PropellerProcessSwimPayloadFallback<'info> { fn log_memo(&self) -> Result<()> { let memo = self.swim_payload_message.memo; if memo != [0u8; 16] { - let memo_ix = - spl_memo::build_memo(std::str::from_utf8(hex::encode(memo).as_bytes()).unwrap().as_ref(), &[]); + let memo_ix = spl_memo::build_memo(get_memo_as_utf8(memo)?.as_ref(), &[]); invoke(&memo_ix, &[self.memo.to_account_info()])?; } Ok(()) } } +impl<'info> Fees<'info> for PropellerProcessSwimPayloadFallback<'info> { + fn calculate_fees_in_lamports(&self) -> Result { + let rent = Rent::get()?; + + let propeller = &self.propeller; + let swim_payload_message = &self.swim_payload_message; + let propeller_process_swim_payload_fees = propeller.process_swim_payload_fee; + + let swim_claim_rent_exempt_fees = rent.minimum_balance(8 + SwimClaim::LEN); + let gas_kickstart_amount = if swim_payload_message.gas_kickstart { propeller.gas_kickstart_amount } else { 0 }; + let fee_in_lamports = swim_claim_rent_exempt_fees + .checked_add(propeller_process_swim_payload_fees) + .and_then(|x| x.checked_add(gas_kickstart_amount)) + .ok_or(PropellerError::IntegerOverflow)?; + + msg!( + " + {}(swim_claim_rent_exempt_fees) + + {}(propeller_process_swim_payload_fees) + + {}(gas_kickstart_amount) + = {}(fee_in_lamports) + ", + swim_claim_rent_exempt_fees, + propeller_process_swim_payload_fees, + gas_kickstart_amount, + fee_in_lamports + ); + Ok(fee_in_lamports) + } + + fn transfer_fees_to_vault(&mut self, fees_in_swim_usd: u64) -> Result<()> { + token::transfer( + CpiContext::new_with_signer( + self.token_program.to_account_info(), + Transfer { + from: self.redeemer_escrow.to_account_info(), + to: self.fee_tracking.fee_vault.to_account_info(), + authority: self.redeemer.to_account_info(), + }, + &[&[(b"redeemer".as_ref()), &[self.propeller.redeemer_bump]]], + ), + fees_in_swim_usd, + ) + } +} + pub fn handle_propeller_process_swim_payload_fallback( ctx: Context, ) -> Result { let swim_payload_message = &ctx.accounts.swim_payload_message; let is_gas_kickstart = swim_payload_message.gas_kickstart; - let target_token_id = swim_payload_message.target_token_id; + let _target_token_id = swim_payload_message.target_token_id; let claim_data = ClaimData::try_from_slice(&mut ctx.accounts.claim.data.borrow()) .map_err(|_| error!(PropellerError::InvalidClaimData))?; @@ -1149,29 +753,28 @@ pub fn handle_propeller_process_swim_payload_fallback( let mut transfer_amount = swim_payload_message.transfer_amount; let propeller = &ctx.accounts.propeller; let propeller_redeemer_bump = ctx.accounts.propeller.redeemer_bump; - let redeemer = &ctx.accounts.redeemer; + let _redeemer = &ctx.accounts.redeemer; let swim_payload_owner = swim_payload_message.owner; - let token_program = &ctx.accounts.token_program; + let _token_program = &ctx.accounts.token_program; msg!("original transfer_amount: {:?}", transfer_amount); if swim_payload_owner != ctx.accounts.payer.key() { - let fees_in_token_bridge = &ctx.accounts.handle_fees()?; - // let fees_in_token_bridge = calculate_fees2(&ctx)?; - msg!("fees_in_token_bridge: {:?}", fees_in_token_bridge); + let fees_in_lamports = ctx.accounts.calculate_fees_in_lamports()?; + // let fees_in_swim_usd_atomic = ctx.accounts.convert_fees_to_swim_usd_atomic(fees_in_lamports)?; + let fees_in_swim_usd_atomic = ctx.accounts.fee_tracking.track_fees(fees_in_lamports, propeller)?; + ctx.accounts.transfer_fees_to_vault(fees_in_swim_usd_atomic)?; + msg!("fees_in_swim_usd_atomic: {:?}", fees_in_swim_usd_atomic); if is_gas_kickstart { ctx.accounts.transfer_gas_kickstart()?; } transfer_amount = - transfer_amount.checked_sub(*fees_in_token_bridge).ok_or(error!(PropellerError::InsufficientFunds))?; - } else { - //TODO: a user should just call processSwimPayload instead to avoid passing in extra accounts. end result is same. - msg!("swim_payload_owner == ctx.accounts.payer.key(). Owner bypass"); + transfer_amount.checked_sub(fees_in_swim_usd_atomic).ok_or(error!(PropellerError::InsufficientFunds))?; } msg!("transfer_amount - fee = {}", transfer_amount); let output_amount = ctx.accounts.transfer_swim_usd_tokens( transfer_amount, &ctx.accounts.redeemer.to_account_info(), - &[&[&b"redeemer".as_ref(), &[propeller_redeemer_bump]]], + &[&[(b"redeemer".as_ref()), &[propeller_redeemer_bump]]], )?; let swim_claim_bump = *ctx.bumps.get("swim_claim").unwrap(); diff --git a/packages/solana-contracts/programs/propeller/src/instructions/target_chain_map.rs b/packages/solana-contracts/programs/propeller/src/instructions/target_chain_map.rs index e59621982..dd82147fe 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/target_chain_map.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/target_chain_map.rs @@ -1,11 +1,6 @@ use { - crate::{error::PropellerError, Propeller, TOKEN_COUNT}, + crate::{error::PropellerError, Propeller}, anchor_lang::prelude::*, - anchor_spl::{ - associated_token::{create, AssociatedToken, Create}, - token::{Mint, Token, TokenAccount}, - }, - two_pool::state::TwoPool, }; #[derive(Accounts)] @@ -14,11 +9,11 @@ pub struct CreateTargetChainMap<'info> { #[account( seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], bump = propeller.bump, - has_one = admin, + has_one = governance_key @ PropellerError::InvalidPropellerGovernanceKey, )] pub propeller: Account<'info, Propeller>, - pub admin: Signer<'info>, + pub governance_key: Signer<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -42,10 +37,11 @@ pub struct TargetChainMap { pub bump: u8, pub target_chain: u16, pub target_address: [u8; 32], + pub is_paused: bool, } impl TargetChainMap { - pub const LEN: usize = 1 + 2 + 32; + pub const LEN: usize = 1 + 2 + 32 + 1; } pub fn handle_create_target_chain_map( @@ -58,19 +54,21 @@ pub fn handle_create_target_chain_map( target_chain_map.bump = *bump; target_chain_map.target_chain = target_chain; target_chain_map.target_address = target_address; + target_chain_map.is_paused = false; Ok(()) } #[derive(Accounts)] +#[instruction(target_chain: u16)] pub struct UpdateTargetChainMap<'info> { #[account( seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], bump = propeller.bump, - has_one = admin, + has_one = governance_key @ PropellerError::InvalidPropellerGovernanceKey, )] - pub propeller: Account<'info, Propeller>, + pub propeller: Box>, - pub admin: Signer<'info>, + pub governance_key: Signer<'info>, #[account(mut)] pub payer: Signer<'info>, @@ -79,16 +77,20 @@ pub struct UpdateTargetChainMap<'info> { seeds = [ b"propeller".as_ref(), propeller.key().as_ref(), - &target_chain_map.target_chain.to_le_bytes() + &target_chain.to_le_bytes() ], bump = target_chain_map.bump, )] pub target_chain_map: Account<'info, TargetChainMap>, - pub system_program: Program<'info, System>, } -pub fn handle_update_target_chain_map(ctx: Context, routing_contract: [u8; 32]) -> Result<()> { +pub fn handle_update_target_chain_map( + ctx: Context, + target_chain: u16, + routing_contract: [u8; 32], +) -> Result<()> { let target_chain_map = &mut ctx.accounts.target_chain_map; + require_eq!(target_chain, target_chain_map.target_chain, PropellerError::InvalidTargetChainMap); target_chain_map.target_address = routing_contract; Ok(()) } @@ -96,3 +98,41 @@ pub fn handle_update_target_chain_map(ctx: Context, routin pub fn handle_close_target_chain_map() -> Result<()> { todo!() } + +#[derive(Accounts)] +#[instruction(target_chain: u16)] +pub struct TargetChainMapSetPaused<'info> { + #[account( + seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], + bump = propeller.bump, + has_one = pause_key @ PropellerError::InvalidPropellerPauseKey, + )] + pub propeller: Account<'info, Propeller>, + + pub pause_key: Signer<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + #[account( + mut, + seeds = [ + b"propeller".as_ref(), + propeller.key().as_ref(), + &target_chain.to_le_bytes() + ], + bump = target_chain_map.bump, + )] + pub target_chain_map: Account<'info, TargetChainMap>, +} + +pub fn handle_target_chain_map_set_paused( + ctx: Context, + target_chain: u16, + is_paused: bool, +) -> Result<()> { + let target_chain_map = &mut ctx.accounts.target_chain_map; + require_eq!(target_chain, target_chain_map.target_chain, PropellerError::InvalidTargetChainMap); + target_chain_map.is_paused = is_paused; + Ok(()) +} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/token_id_map.rs b/packages/solana-contracts/programs/propeller/src/instructions/token_id_map.rs deleted file mode 100644 index 616419581..000000000 --- a/packages/solana-contracts/programs/propeller/src/instructions/token_id_map.rs +++ /dev/null @@ -1,132 +0,0 @@ -use { - crate::{error::PropellerError, Propeller, TOKEN_COUNT}, - anchor_lang::prelude::*, - anchor_spl::{ - associated_token::{create, AssociatedToken, Create}, - token::{Mint, Token, TokenAccount}, - }, - two_pool::state::TwoPool, -}; - -#[derive(Accounts)] -#[instruction(target_token_index: u16, pool: Pubkey, pool_token_index: u8, pool_token_mint: Pubkey)] -pub struct CreateTokenIdMap<'info> { - #[account( - seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], - bump = propeller.bump, - has_one = admin @ PropellerError::InvalidPropellerAdmin, - )] - pub propeller: Account<'info, Propeller>, - - pub admin: Signer<'info>, - - #[account(mut)] - pub payer: Signer<'info>, - - #[account( - seeds = [ - b"two_pool".as_ref(), - pool.get_token_mint_0().unwrap().as_ref(), - pool.get_token_mint_1().unwrap().as_ref(), - pool.lp_mint_key.as_ref(), - ], - bump = pool.bump, - seeds::program = two_pool_program.key(), - )] - pub pool: Account<'info, TwoPool>, - - #[account( - init, - payer = payer, - seeds = [ - b"propeller".as_ref(), - b"token_id".as_ref(), - propeller.key().as_ref(), - &target_token_index.to_le_bytes() - ], - bump, - space = 8 + TokenIdMap::LEN, - )] - pub token_id_map: Account<'info, TokenIdMap>, - pub system_program: Program<'info, System>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, -} - -#[account] -pub struct TokenIdMap { - pub output_token_index: u16, - pub pool: Pubkey, - pub pool_token_index: u8, - pub pool_token_mint: Pubkey, - pub pool_ix: PoolInstruction, - pub bump: u8, -} - -impl TokenIdMap { - pub const LEN: usize = 2 + 32 + 1 + 32 + 1 + 1 + 1; -} - -#[derive(AnchorSerialize, AnchorDeserialize, Copy, Clone, Debug)] -pub enum PoolInstruction { - Transfer, - RemoveExactBurn, - SwapExactInput, -} - -impl<'info> CreateTokenIdMap<'info> { - pub fn accounts( - ctx: &Context, - target_token_index: u16, - pool: Pubkey, - pool_token_index: u8, - pool_token_mint: Pubkey, - pool_ix: PoolInstruction, - ) -> Result<()> { - //TODO: add error codes - require_keys_eq!(ctx.accounts.propeller.admin, ctx.accounts.admin.key(), PropellerError::InvalidPropellerAdmin); - require_keys_eq!(ctx.accounts.pool.key(), pool, PropellerError::InvalidTokenIdMapPool); - if let PoolInstruction::Transfer = pool_ix { - require_keys_eq!( - ctx.accounts.propeller.swim_usd_mint, - pool_token_mint, - PropellerError::InvalidTokenIdMapPoolTokenMint - ); - return Ok(()); - } - - let pool_token_index = pool_token_index as usize; - require_gt!(TOKEN_COUNT, pool_token_index, PropellerError::InvalidTokenIdMapPoolTokenIndex); - require_keys_eq!( - ctx.accounts.pool.token_mint_keys[pool_token_index], - pool_token_mint, - PropellerError::InvalidTokenIdMapPoolTokenMint - ); - Ok(()) - } -} - -pub fn handle_create_token_id_map( - ctx: Context, - target_token_index: u16, - pool: Pubkey, - pool_token_index: u8, - pool_token_mint: Pubkey, - pool_ix: PoolInstruction, -) -> Result<()> { - let mut token_id_map = &mut ctx.accounts.token_id_map; - token_id_map.output_token_index = target_token_index; - token_id_map.pool = pool; - token_id_map.pool_token_index = pool_token_index; - token_id_map.pool_token_mint = pool_token_mint; - token_id_map.bump = *ctx.bumps.get("token_id_map").unwrap(); - token_id_map.pool_ix = pool_ix; - Ok(()) -} - -pub fn handle_update_token_id_map() -> Result<()> { - todo!() -} - -pub fn handle_close_token_id_map() -> Result<()> { - todo!() -} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/token_number_map.rs b/packages/solana-contracts/programs/propeller/src/instructions/token_number_map.rs new file mode 100644 index 000000000..12e06d4e3 --- /dev/null +++ b/packages/solana-contracts/programs/propeller/src/instructions/token_number_map.rs @@ -0,0 +1,272 @@ +use { + crate::{error::PropellerError, Propeller, TOKEN_COUNT}, + anchor_lang::prelude::*, + two_pool::state::TwoPool, +}; + +#[derive(Accounts)] +#[instruction(to_token_number: u16, pool: Pubkey, pool_token_index: u8, pool_token_mint: Pubkey)] +pub struct CreateTokenNumberMap<'info> { + #[account( + seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], + bump = propeller.bump, + has_one = governance_key @ PropellerError::InvalidPropellerGovernanceKey, + )] + pub propeller: Box>, + + pub governance_key: Signer<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + #[account( + seeds = [ + b"two_pool".as_ref(), + pool.token_mint_keys[0].as_ref(), + pool.token_mint_keys[1].as_ref(), + pool.lp_mint_key.as_ref(), + ], + bump = pool.bump, + seeds::program = two_pool_program.key(), + )] + pub pool: Box>, + + #[account( + init, + payer = payer, + seeds = [ + b"propeller".as_ref(), + b"token_id".as_ref(), + propeller.key().as_ref(), + &to_token_number.to_le_bytes() + ], + bump, + space = 8 + TokenNumberMap::LEN, + )] + pub token_number_map: Account<'info, TokenNumberMap>, + pub system_program: Program<'info, System>, + pub two_pool_program: Program<'info, two_pool::program::TwoPool>, +} + +#[account] +pub struct TokenNumberMap { + pub bump: u8, + pub to_token_number: u16, + pub pool: Pubkey, + pub pool_token_index: u8, + pub pool_token_mint: Pubkey, + pub to_token_step: ToTokenStep, +} + +impl TokenNumberMap { + pub const LEN: usize = 2 + 32 + 1 + 32 + 1 + 1 + 1; + + pub fn assert_is_invalid(token_id_map: &AccountInfo) -> Result<()> { + if let Ok(_) = TokenNumberMap::try_deserialize(&mut &**token_id_map.try_borrow_mut_data()?) { + return err!(PropellerError::TokenNumberMapExists); + } + Ok(()) + } +} + +#[derive(AnchorSerialize, AnchorDeserialize, Copy, Clone, Debug)] +pub enum ToTokenStep { + Transfer, + RemoveExactBurn, + SwapExactInput, +} + +impl<'info> CreateTokenNumberMap<'info> { + pub fn accounts( + ctx: &Context, + _to_token_number: u16, + pool: Pubkey, + pool_token_index: u8, + pool_token_mint: Pubkey, + to_token_step: ToTokenStep, + ) -> Result<()> { + require_keys_eq!(ctx.accounts.pool.key(), pool, PropellerError::InvalidTokenNumberMapPool); + if let ToTokenStep::Transfer = to_token_step { + require_keys_eq!( + ctx.accounts.propeller.swim_usd_mint, + pool_token_mint, + PropellerError::InvalidTokenNumberMapPoolTokenMint + ); + return Ok(()); + } + + let pool_token_index = pool_token_index as usize; + require_gt!(TOKEN_COUNT, pool_token_index, PropellerError::InvalidTokenNumberMapPoolTokenIndex); + require_keys_eq!( + ctx.accounts.pool.token_mint_keys[pool_token_index], + pool_token_mint, + PropellerError::InvalidTokenNumberMapPoolTokenMint + ); + Ok(()) + } +} + +pub fn handle_create_token_number_map( + ctx: Context, + to_token_number: u16, + pool: Pubkey, + pool_token_index: u8, + pool_token_mint: Pubkey, + to_token_step: ToTokenStep, +) -> Result<()> { + let token_number_map = &mut ctx.accounts.token_number_map; + token_number_map.to_token_number = to_token_number; + token_number_map.pool = pool; + token_number_map.pool_token_index = pool_token_index; + token_number_map.pool_token_mint = pool_token_mint; + token_number_map.bump = *ctx.bumps.get("token_number_map").unwrap(); + token_number_map.to_token_step = to_token_step; + Ok(()) +} + +#[derive(Accounts)] +#[instruction(to_token_number: u16)] +pub struct UpdateTokenNumberMap<'info> { + #[account( + seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], + bump = propeller.bump, + has_one = governance_key @ PropellerError::InvalidPropellerGovernanceKey, + )] + pub propeller: Box>, + + pub governance_key: Signer<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + // #[account( + // seeds = [ + // b"two_pool".as_ref(), + // pool.token_mint_keys[0].as_ref(), + // pool.token_mint_keys[1].as_ref(), + // pool.lp_mint_key.as_ref(), + // ], + // bump = pool.bump, + // seeds::program = two_pool_program.key(), + // )] + // Note: anchor is unable to compile when i use the `#[account]` macro here even though it's the + // exact same as above. manually validating the address in `accounts` fn + pub pool: Box>, + + #[account( + mut, + seeds = [ + b"propeller".as_ref(), + b"token_id".as_ref(), + propeller.key().as_ref(), + &to_token_number.to_le_bytes() + ], + bump = token_number_map.bump, + constraint = token_number_map.to_token_number == to_token_number, + )] + pub token_number_map: Account<'info, TokenNumberMap>, + pub two_pool_program: Program<'info, two_pool::program::TwoPool>, +} + +impl<'info> UpdateTokenNumberMap<'info> { + pub fn accounts( + ctx: &Context, + pool_token_index: &u8, + pool_token_mint: &Pubkey, + to_token_step: &ToTokenStep, + ) -> Result<()> { + let propeller = &ctx.accounts.propeller; + let pool = &ctx.accounts.pool; + let pool_token_mint = *pool_token_mint; + let expected_pool_key = Pubkey::create_program_address( + &[ + b"two_pool".as_ref(), + pool.token_mint_keys[0].as_ref(), + (pool.token_mint_keys[1].as_ref()), + (pool.lp_mint_key.as_ref()), + &[pool.bump], + ], + &ctx.accounts.two_pool_program.key(), + ) + .map_err(|_| PropellerError::InvalidTokenNumberMapPool)?; + require_keys_eq!( + *pool.to_account_info().owner, + ctx.accounts.two_pool_program.key(), + PropellerError::InvalidTokenNumberMapPool + ); + require_keys_eq!(ctx.accounts.pool.key(), expected_pool_key, PropellerError::InvalidTokenNumberMapPool); + let pool_token_index = *pool_token_index as usize; + require_gt!(TOKEN_COUNT, pool_token_index as usize, PropellerError::InvalidTokenNumberMapPoolTokenIndex); + match *to_token_step { + // metapools must have swimUSD as token_mint_keys[0] + ToTokenStep::SwapExactInput => { + require_keys_eq!(pool.token_mint_keys[0], propeller.swim_usd_mint); + require_keys_eq!(pool.token_mint_keys[1], pool_token_mint); + require_eq!(pool_token_index, 1_usize); + } + ToTokenStep::RemoveExactBurn => { + require_keys_eq!(pool.lp_mint_key, pool_token_mint); + require_keys_eq!(pool.lp_mint_key, propeller.swim_usd_mint); + require_keys_eq!(pool.token_mint_keys[pool_token_index], pool_token_mint); + } + ToTokenStep::Transfer => { + require_keys_eq!( + ctx.accounts.propeller.swim_usd_mint, + pool_token_mint, + PropellerError::InvalidTokenNumberMapPoolTokenMint + ); + } + } + Ok(()) + } +} + +pub fn handle_update_token_number_map( + ctx: Context, + _to_token_number: u16, + pool_token_index: u8, + pool_token_mint: Pubkey, + to_token_step: ToTokenStep, +) -> Result<()> { + let token_number_map = &mut ctx.accounts.token_number_map; + token_number_map.pool = ctx.accounts.pool.key(); + token_number_map.pool_token_index = pool_token_index; + token_number_map.pool_token_mint = pool_token_mint; + token_number_map.to_token_step = to_token_step; + Ok(()) +} + +#[derive(Accounts)] +#[instruction(to_token_number: u16)] +pub struct CloseTokenNumberMap<'info> { + #[account( + seeds = [b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], + bump = propeller.bump, + has_one = governance_key @ PropellerError::InvalidPropellerGovernanceKey, + )] + pub propeller: Box>, + + pub governance_key: Signer<'info>, + + #[account(mut)] + pub payer: Signer<'info>, + + #[account( + mut, + seeds = [ + b"propeller".as_ref(), + b"token_id".as_ref(), + propeller.key().as_ref(), + &to_token_number.to_le_bytes() + ], + bump = token_number_map.bump, + close = payer, + )] + pub token_number_map: Account<'info, TokenNumberMap>, +} + +pub fn handle_close_token_number_map(ctx: Context, to_token_number: u16) -> Result<()> { + //TODO emit event? + msg!("Closed TokenNumberMap {} for to_token_number {}", ctx.accounts.token_number_map.key(), to_token_number); + Ok(()) +} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/add.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/add.rs deleted file mode 100644 index 2dc13ea37..000000000 --- a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/add.rs +++ /dev/null @@ -1,175 +0,0 @@ -use { - crate::{constants::PROPELLER_MINIMUM_OUTPUT_AMOUNT, error::*, Propeller}, - anchor_lang::{prelude::*, solana_program::program::invoke}, - anchor_spl::{ - associated_token::get_associated_token_address, - token::{Mint, Token, TokenAccount}, - }, - two_pool::{gen_pool_signer_seeds, program::TwoPool as TwoPoolProgram, state::TwoPool, TOKEN_COUNT}, -}; - -#[derive(Accounts)] -pub struct Add<'info> { - #[account( - seeds = [ b"propeller".as_ref(), lp_mint.key().as_ref()], - bump = propeller.bump, - - )] - pub propeller: Box>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump, - seeds::program = two_pool_program.key() - )] - pub pool: Account<'info, TwoPool>, - // /// TODO: could be removed if initialized with pool_v2 - // /// CHECK: checked in CPI - // pub pool_auth: UncheckedAccount<'info>, - #[account( - mut, - address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[0]), - constraint = pool_token_account_0.key() == pool.token_keys[0], - )] - pub pool_token_account_0: Box>, - #[account( - mut, - address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[1]), - constraint = pool_token_account_1.key() == pool.token_keys[1], - )] - pub pool_token_account_1: Box>, - #[account( - mut, - address = pool.lp_mint_key, - constraint = propeller.swim_usd_mint == lp_mint.key(), - )] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint, - address = pool.governance_fee_key, - )] - pub governance_fee: Box>, - ///CHECK: checked in CPI - pub user_transfer_authority: Signer<'info>, - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - - pub token_program: Program<'info, Token>, - - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, -} - -impl<'info> Add<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - require_keys_eq!(ctx.accounts.lp_mint.key(), ctx.accounts.propeller.swim_usd_mint); - Ok(()) - } -} - -// pub fn handle_add( -// ctx: Context, -// input_amounts: [u64; TOKEN_COUNT], -// minimum_mint_amount: u64, -// memo: &[u8], -// propeller_enabled: bool, -// target_chain: u16, -// ) -> Result { -// let cpi_ctx = CpiContext::new( -// ctx.accounts.two_pool_program.to_account_info(), -// two_pool::cpi::accounts::Add { -// pool: ctx.accounts.pool.to_account_info(), -// pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), -// pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), -// lp_mint: ctx.accounts.lp_mint.to_account_info(), -// governance_fee: ctx.accounts.governance_fee.to_account_info(), -// user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), -// user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), -// user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), -// user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), -// token_program: ctx.accounts.token_program.to_account_info(), -// }, -// ); -// -// let result = two_pool::cpi::add(cpi_ctx, input_amounts, minimum_mint_amount)?; -// let return_val = result.get(); -// let memo_ix = spl_memo::build_memo(memo, &[]); -// invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; -// anchor_lang::prelude::msg!("add return_val: {:?}", return_val); -// is_transfer_amount_sufficient( -// &ctx.accounts.propeller, -// &ctx.accounts.lp_mint, -// propeller_enabled, -// target_chain, -// return_val, -// )?; -// Ok(return_val) -// } - -pub fn handle_cross_chain_add( - ctx: Context, - input_amounts: [u64; TOKEN_COUNT], - minimum_mint_amount: u64, -) -> Result { - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::Add { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - let result = two_pool::cpi::add(cpi_ctx, input_amounts, minimum_mint_amount)?; - let return_val = result.get(); - anchor_lang::prelude::msg!("cross_chain_add return_val: {:?}", return_val); - Ok(return_val) -} - -pub fn handle_propeller_add(ctx: Context, input_amounts: [u64; TOKEN_COUNT], max_fee: u64) -> Result { - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::Add { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - let result = two_pool::cpi::add(cpi_ctx, input_amounts, PROPELLER_MINIMUM_OUTPUT_AMOUNT)?; - let output_amount = result.get(); - anchor_lang::prelude::msg!("propeller_add output_amount: {:?}", output_amount); - require_gt!(output_amount, max_fee, PropellerError::InsufficientAmount); - Ok(output_amount) -} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/init_to_swim_usd.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/init_to_swim_usd.rs new file mode 100644 index 000000000..1dcc64602 --- /dev/null +++ b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/init_to_swim_usd.rs @@ -0,0 +1,191 @@ +use { + crate::{ + constants::{METAPOOL_SWIM_USD_INDEX, PROPELLER_MINIMUM_OUTPUT_AMOUNT}, + error::*, + Propeller, TOKEN_COUNT, + }, + anchor_lang::prelude::*, + anchor_spl::{ + associated_token::get_associated_token_address, + token::{Mint, Token, TokenAccount}, + }, + two_pool::state::TwoPool, +}; + +#[derive(Accounts)] +pub struct InitToSwimUsd<'info> { + #[account( + seeds = [ b"propeller".as_ref(), propeller.swim_usd_mint.as_ref()], + bump = propeller.bump, + constraint = !propeller.is_paused @ PropellerError::IsPaused, + )] + pub propeller: Box>, + #[account( + mut, + seeds = [ + b"two_pool".as_ref(), + pool_token_account_0.mint.as_ref(), + pool_token_account_1.mint.as_ref(), + lp_mint.key().as_ref(), + ], + bump = pool.bump, + seeds::program = two_pool_program.key() + )] + pub pool: Box>, + + #[account( + mut, + address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[0]), + constraint = pool_token_account_0.key() == pool.token_keys[0], + )] + pub pool_token_account_0: Box>, + #[account( + mut, + address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[1]), + constraint = pool_token_account_1.key() == pool.token_keys[1], + )] + pub pool_token_account_1: Box>, + #[account( + mut, + address = pool.lp_mint_key, + )] + pub lp_mint: Box>, + #[account( + mut, + token::mint = lp_mint, + address = pool.governance_fee_key, + )] + pub governance_fee: Box>, + ///CHECK: checked in CPI + pub user_transfer_authority: Signer<'info>, + #[account( + mut, + token::mint = pool_token_account_0.mint, + )] + pub user_token_account_0: Box>, + #[account( + mut, + token::mint = pool_token_account_1.mint, + )] + pub user_token_account_1: Box>, + + #[account(mut)] + /// CHECK: leaving as unchecked account since it's not needed if invoking swap_exact_input + /// validation will be checked in the pool CPI call anyways + pub user_lp_token_account: UncheckedAccount<'info>, + + pub token_program: Program<'info, Token>, + + pub two_pool_program: Program<'info, two_pool::program::TwoPool>, +} + +pub enum ToSwimUsdStep { + Add, + SwapExactInput, +} + +impl<'info> InitToSwimUsd<'info> { + fn determine_to_swim_usd_step(&self) -> Result { + let pool = &self.pool; + let swim_usd_mint = self.propeller.swim_usd_mint; + if pool.lp_mint_key == swim_usd_mint { + Ok(ToSwimUsdStep::Add) + } else if pool.token_mint_keys[0] == swim_usd_mint { + Ok(ToSwimUsdStep::SwapExactInput) + } else { + err!(PropellerError::InvalidPoolForInitToSwimUsd) + } + } + + fn invoke_pool_ix_for_swim_usd( + &self, + input_amounts: [u64; TOKEN_COUNT], + minimum_output_amount: u64, + ) -> Result { + let to_swim_usd_step = self.determine_to_swim_usd_step()?; + let output_amount = match to_swim_usd_step { + ToSwimUsdStep::Add => self.invoke_add(input_amounts, minimum_output_amount)?, + ToSwimUsdStep::SwapExactInput => self.invoke_swap_exact_input(input_amounts, minimum_output_amount)?, + }; + Ok(output_amount) + } + + fn invoke_add(&self, input_amounts: [u64; TOKEN_COUNT], minimum_mint_amount: u64) -> Result { + let cpi_ctx = CpiContext::new( + self.two_pool_program.to_account_info(), + two_pool::cpi::accounts::AddOrRemove { + swap: two_pool::cpi::accounts::Swap { + pool: self.pool.to_account_info(), + pool_token_account_0: self.pool_token_account_0.to_account_info(), + pool_token_account_1: self.pool_token_account_1.to_account_info(), + lp_mint: self.lp_mint.to_account_info(), + governance_fee: self.governance_fee.to_account_info(), + user_transfer_authority: self.user_transfer_authority.to_account_info(), + user_token_account_0: self.user_token_account_0.to_account_info(), + user_token_account_1: self.user_token_account_1.to_account_info(), + token_program: self.token_program.to_account_info(), + }, + user_lp_token_account: self.user_lp_token_account.to_account_info(), + }, + ); + let result = two_pool::cpi::add(cpi_ctx, input_amounts, minimum_mint_amount)?; + let output_amount = result.get(); + msg!("ToSwimUsd(add) - output_amount: {}", output_amount); + Ok(output_amount) + } + + fn invoke_swap_exact_input( + &self, + exact_input_amounts: [u64; TOKEN_COUNT], + minimum_output_amount: u64, + ) -> Result { + require_eq!( + exact_input_amounts[METAPOOL_SWIM_USD_INDEX as usize], + 0u64, + PropellerError::InvalidSwapExactInputInputAmount + ); + let cpi_ctx = CpiContext::new( + self.two_pool_program.to_account_info(), + two_pool::cpi::accounts::Swap { + pool: self.pool.to_account_info(), + pool_token_account_0: self.pool_token_account_0.to_account_info(), + pool_token_account_1: self.pool_token_account_1.to_account_info(), + lp_mint: self.lp_mint.to_account_info(), + governance_fee: self.governance_fee.to_account_info(), + user_transfer_authority: self.user_transfer_authority.to_account_info(), + user_token_account_0: self.user_token_account_0.to_account_info(), + user_token_account_1: self.user_token_account_1.to_account_info(), + token_program: self.token_program.to_account_info(), + }, + ); + + let result = two_pool::cpi::swap_exact_input( + cpi_ctx, + exact_input_amounts, + METAPOOL_SWIM_USD_INDEX, + minimum_output_amount, + )?; + let output_amount = result.get(); + msg!("ToSwimUsd[swap_exact_input] - output_amount: {}", output_amount); + Ok(output_amount) + } +} + +pub fn handle_cross_chain_init_to_swim_usd( + ctx: Context, + input_amounts: [u64; TOKEN_COUNT], + minimum_output_amount: u64, +) -> Result { + let output_amount = ctx.accounts.invoke_pool_ix_for_swim_usd(input_amounts, minimum_output_amount)?; + Ok(output_amount) +} + +pub fn handle_propeller_init_to_swim_usd( + ctx: Context, + input_amounts: [u64; TOKEN_COUNT], + max_fee: u64, +) -> Result { + let output_amount = handle_cross_chain_init_to_swim_usd(ctx, input_amounts, PROPELLER_MINIMUM_OUTPUT_AMOUNT)?; + require_gt!(output_amount, max_fee, PropellerError::InsufficientAmount); + Ok(output_amount) +} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/mod.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/mod.rs index f918d23e5..0559195b4 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/mod.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/mod.rs @@ -1,8 +1 @@ -pub mod add; -pub mod remove_exact_burn; -pub mod remove_exact_output; -pub mod remove_uniform; -pub mod swap_exact_input; -pub mod swap_exact_output; - -pub const TOKEN_COUNT: usize = 2; +pub mod init_to_swim_usd; diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_exact_burn.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_exact_burn.rs deleted file mode 100644 index 98f735b77..000000000 --- a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_exact_burn.rs +++ /dev/null @@ -1,198 +0,0 @@ -use { - crate::{ - constants::{PROPELLER_MINIMUM_OUTPUT_AMOUNT, REMOVE_EXACT_BURN_OUTPUT_TOKEN_INDEX}, - error::*, - Propeller, - }, - anchor_lang::{prelude::*, solana_program::program::invoke}, - anchor_spl::token::{Mint, Token, TokenAccount}, - two_pool::{gen_pool_signer_seeds, program::TwoPool as TwoPoolProgram, state::TwoPool, TOKEN_COUNT}, -}; - -#[derive(Accounts)] -pub struct RemoveExactBurn<'info> { - #[account( - seeds = [ - b"propeller".as_ref(), - pool_token_account_0.mint.as_ref(), - ], - bump = propeller.bump, - has_one = swim_usd_mint @ PropellerError::InvalidSwimUsdMint, - )] - pub propeller: Account<'info, Propeller>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump, - seeds::program = two_pool_program.key() - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, - #[account(executable, address = spl_memo::id())] - ///CHECK: memo program - pub memo: UncheckedAccount<'info>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, - pub swim_usd_mint: Account<'info, Mint>, -} - -// pub fn handle_remove_exact_burn( -// ctx: Context, -// exact_burn_amount: u64, -// // output_token_index: u8, -// minimum_output_amount: u64, -// memo: &[u8], -// propeller_enabled: bool, -// target_chain: u16, -// ) -> Result { -// let cpi_ctx = CpiContext::new( -// ctx.accounts.two_pool_program.to_account_info(), -// two_pool::cpi::accounts::RemoveExactBurn { -// pool: ctx.accounts.pool.to_account_info(), -// pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), -// pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), -// lp_mint: ctx.accounts.lp_mint.to_account_info(), -// governance_fee: ctx.accounts.governance_fee.to_account_info(), -// user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), -// user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), -// user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), -// user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), -// token_program: ctx.accounts.token_program.to_account_info(), -// }, -// ); -// let result = two_pool::cpi::remove_exact_burn( -// cpi_ctx, -// exact_burn_amount, -// REMOVE_EXACT_BURN_OUTPUT_TOKEN_INDEX, -// minimum_output_amount, -// )?; -// let return_val = result.get(); -// let memo_ix = spl_memo::build_memo(memo, &[]); -// invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; -// anchor_lang::prelude::msg!("remove_exact_burn return_val: {:?}", return_val); -// is_transfer_amount_sufficient( -// &ctx.accounts.propeller, -// &ctx.accounts.token_bridge_mint, -// propeller_enabled, -// target_chain, -// return_val, -// )?; -// Ok(return_val) -// } - -pub fn handle_cross_chain_remove_exact_burn( - ctx: Context, - exact_burn_amount: u64, - minimum_output_amount: u64, - memo: &[u8], -) -> Result { - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::RemoveExactBurn { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - let result = two_pool::cpi::remove_exact_burn( - cpi_ctx, - exact_burn_amount, - REMOVE_EXACT_BURN_OUTPUT_TOKEN_INDEX, - minimum_output_amount, - )?; - let output_amount = result.get(); - let memo_ix = spl_memo::build_memo(memo, &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - anchor_lang::prelude::msg!("remove_exact_burn return_val: {:?}", output_amount); - Ok(output_amount) -} - -pub fn handle_propeller_remove_exact_burn( - ctx: Context, - exact_burn_amount: u64, - memo: &[u8], - max_fee: u64, -) -> Result { - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::RemoveExactBurn { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - let result = two_pool::cpi::remove_exact_burn( - cpi_ctx, - exact_burn_amount, - REMOVE_EXACT_BURN_OUTPUT_TOKEN_INDEX, - PROPELLER_MINIMUM_OUTPUT_AMOUNT, - )?; - let output_amount = result.get(); - let memo_ix = spl_memo::build_memo(memo, &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - anchor_lang::prelude::msg!("remove_exact_burn return_val: {:?}", output_amount); - require_gt!(output_amount, max_fee, PropellerError::InsufficientAmount); - Ok(output_amount) -} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_exact_output.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_exact_output.rs deleted file mode 100644 index 2d75ec414..000000000 --- a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_exact_output.rs +++ /dev/null @@ -1,121 +0,0 @@ -use { - crate::{error::*, Propeller}, - anchor_lang::{prelude::*, solana_program::program::invoke}, - anchor_spl::token::{Mint, Token, TokenAccount}, - two_pool::{gen_pool_signer_seeds, program::TwoPool as TwoPoolProgram, state::TwoPool, TOKEN_COUNT}, -}; - -#[derive(Accounts)] -pub struct RemoveExactOutput<'info> { - #[account( - seeds = [ - b"propeller".as_ref(), - pool_token_account_0.mint.as_ref(), - ], - bump = propeller.bump, - has_one = swim_usd_mint @ PropellerError::InvalidSwimUsdMint, - )] - pub propeller: Account<'info, Propeller>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump, - seeds::program = two_pool_program.key() - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, - #[account(executable, address = spl_memo::id())] - ///CHECK: memo program - pub memo: UncheckedAccount<'info>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, - pub swim_usd_mint: Account<'info, Mint>, -} - -pub fn handle_remove_exact_output( - ctx: Context, - maximum_burn_amount: u64, - exact_output_amount: u64, - memo: &[u8], - propeller_enabled: bool, - target_chain: u16, -) -> Result> { - // is_transfer_amount_sufficient( - // &ctx.accounts.propeller, - // &ctx.accounts.token_bridge_mint, - // propeller_enabled, - // target_chain, - // exact_output_amount, - // )?; - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::RemoveExactOutput { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - - let exact_output_amounts = [exact_output_amount, 0]; - let result = two_pool::cpi::remove_exact_output(cpi_ctx, maximum_burn_amount, exact_output_amounts)?; - let return_val = result.get(); - let memo_ix = spl_memo::build_memo(memo, &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - anchor_lang::prelude::msg!("remove_exact_output return_val: {:?}", return_val); - Ok(return_val) -} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_uniform.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_uniform.rs deleted file mode 100644 index 4e1685655..000000000 --- a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/remove_uniform.rs +++ /dev/null @@ -1,102 +0,0 @@ -use { - crate::Propeller, - anchor_lang::{prelude::*, solana_program::program::invoke}, - anchor_spl::token::{Mint, Token, TokenAccount}, - two_pool::{gen_pool_signer_seeds, program::TwoPool as TwoPoolProgram, state::TwoPool, TOKEN_COUNT}, -}; - -#[derive(Accounts)] -pub struct RemoveUniform<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump, - seeds::program = two_pool_program.key() - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, - #[account(executable, address = spl_memo::id())] - ///CHECK: memo program - pub memo: UncheckedAccount<'info>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, -} - -pub fn handle_remove_uniform( - ctx: Context, - exact_burn_amount: u64, - minimum_output_amounts: [u64; TOKEN_COUNT], - memo: &[u8], -) -> Result> { - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::RemoveUniform { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - user_lp_token_account: ctx.accounts.user_lp_token_account.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - - let result = two_pool::cpi::remove_uniform(cpi_ctx, exact_burn_amount, minimum_output_amounts)?; - let return_val = result.get(); - let memo_ix = spl_memo::build_memo(memo, &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - anchor_lang::prelude::msg!("remove_uniform return_val: {:?}", return_val); - Ok(return_val) -} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/swap_exact_input.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/swap_exact_input.rs deleted file mode 100644 index 36186376f..000000000 --- a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/swap_exact_input.rs +++ /dev/null @@ -1,190 +0,0 @@ -use { - crate::{ - constants::{PROPELLER_MINIMUM_OUTPUT_AMOUNT, SWAP_EXACT_INPUT_OUTPUT_TOKEN_INDEX}, - Propeller, PropellerError, - }, - anchor_lang::{prelude::*, solana_program::program::invoke}, - anchor_spl::token::{Mint, Token, TokenAccount}, - two_pool::{gen_pool_signer_seeds, program::TwoPool as TwoPoolProgram, state::TwoPool, TOKEN_COUNT}, -}; - -#[derive(Accounts)] -pub struct SwapExactInput<'info> { - #[account( - seeds = [ - b"propeller".as_ref(), - pool_token_account_0.mint.as_ref(), - ], - bump = propeller.bump, - has_one = swim_usd_mint @ PropellerError::InvalidSwimUsdMint, - )] - pub propeller: Account<'info, Propeller>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump, - seeds::program = two_pool_program.key() - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, - pub swim_usd_mint: Account<'info, Mint>, -} - -// pub fn handle_swap_exact_input( -// ctx: Context, -// exact_input_amount: u64, -// // exact_input_amounts: [u64; TOKEN_COUNT], -// // output_token_index: u8, -// minimum_output_amount: u64, -// memo: &[u8], -// propeller_enabled: bool, -// target_chain: u16, -// ) -> Result { -// require_gt!(exact_input_amount, 0, PropellerError::InvalidSwapExactInputInputAmount); -// let exact_input_amounts = [0, exact_input_amount]; -// let cpi_ctx = CpiContext::new( -// ctx.accounts.two_pool_program.to_account_info(), -// two_pool::cpi::accounts::SwapExactInput { -// pool: ctx.accounts.pool.to_account_info(), -// pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), -// pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), -// lp_mint: ctx.accounts.lp_mint.to_account_info(), -// governance_fee: ctx.accounts.governance_fee.to_account_info(), -// user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), -// user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), -// user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), -// token_program: ctx.accounts.token_program.to_account_info(), -// }, -// ); -// -// let result = two_pool::cpi::swap_exact_input( -// cpi_ctx, -// exact_input_amounts, -// SWAP_EXACT_INPUT_OUTPUT_TOKEN_INDEX, -// minimum_output_amount, -// )?; -// let return_val = result.get(); -// let memo_ix = spl_memo::build_memo(memo, &[]); -// invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; -// anchor_lang::prelude::msg!("swap_exact_input return_val: {:?}", return_val); -// is_transfer_amount_sufficient( -// &ctx.accounts.propeller, -// &ctx.accounts.token_bridge_mint, -// propeller_enabled, -// target_chain, -// return_val, -// )?; -// Ok(return_val) -// } - -pub fn handle_cross_chain_swap_exact_input( - ctx: Context, - exact_input_amount: u64, - minimum_output_amount: u64, -) -> Result { - require_gt!(exact_input_amount, 0, PropellerError::InvalidSwapExactInputInputAmount); - let exact_input_amounts = [0, exact_input_amount]; - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::SwapExactInput { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - - let result = two_pool::cpi::swap_exact_input( - cpi_ctx, - exact_input_amounts, - SWAP_EXACT_INPUT_OUTPUT_TOKEN_INDEX, - minimum_output_amount, - )?; - let return_val = result.get(); - anchor_lang::prelude::msg!("swap_exact_input return_val: {:?}", return_val); - Ok(return_val) -} - -pub fn handle_propeller_swap_exact_input( - ctx: Context, - exact_input_amount: u64, - max_fee: u64, -) -> Result { - require_gt!(exact_input_amount, 0, PropellerError::InvalidSwapExactInputInputAmount); - let exact_input_amounts = [0, exact_input_amount]; - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::SwapExactInput { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - - let result = two_pool::cpi::swap_exact_input( - cpi_ctx, - exact_input_amounts, - SWAP_EXACT_INPUT_OUTPUT_TOKEN_INDEX, - PROPELLER_MINIMUM_OUTPUT_AMOUNT, - )?; - let output_amount = result.get(); - anchor_lang::prelude::msg!("swap_exact_input return_val: {:?}", output_amount); - require_gt!(output_amount, max_fee, PropellerError::InsufficientAmount); - Ok(output_amount) -} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/swap_exact_output.rs b/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/swap_exact_output.rs deleted file mode 100644 index aae4ba2e0..000000000 --- a/packages/solana-contracts/programs/propeller/src/instructions/two_pool_cpi/swap_exact_output.rs +++ /dev/null @@ -1,190 +0,0 @@ -use { - crate::{ - constants::{SWAP_EXACT_OUTPUT_INPUT_TOKEN_INDEX, TOKEN_BRIDGE_MINT_OUTPUT_TOKEN_INDEX}, - error::*, - Propeller, - }, - anchor_lang::{prelude::*, solana_program::program::invoke}, - anchor_spl::token::{Mint, Token, TokenAccount}, - two_pool::{gen_pool_signer_seeds, program::TwoPool as TwoPoolProgram, state::TwoPool, TOKEN_COUNT}, -}; - -#[derive(Accounts)] -pub struct SwapExactOutput<'info> { - #[account( - seeds = [ - b"propeller".as_ref(), - pool_token_account_0.mint.as_ref(), - ], - bump = propeller.bump, - has_one = swim_usd_mint @ PropellerError::InvalidSwimUsdMint, - )] - pub propeller: Account<'info, Propeller>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump, - seeds::program = two_pool_program.key() - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, - #[account(executable, address = spl_memo::id())] - ///CHECK: memo program - pub memo: UncheckedAccount<'info>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, - pub swim_usd_mint: Account<'info, Mint>, -} - -pub fn handle_swap_exact_output( - ctx: Context, - maximum_input_amount: u64, - exact_output_amount: u64, - // exact_output_amounts: [u64; TOKEN_COUNT], // params: SwapExactOutputParams, - memo: &[u8], - propeller_enabled: bool, - target_chain: u16, -) -> Result> { - // is_transfer_amount_sufficient( - // &ctx.accounts.propeller, - // &ctx.accounts.token_bridge_mint, - // propeller_enabled, - // target_chain, - // exact_output_amount, - // )?; - let input_token_index = SWAP_EXACT_OUTPUT_INPUT_TOKEN_INDEX; - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::SwapExactOutput { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - let exact_output_amounts = [exact_output_amount, 0]; - let result = - two_pool::cpi::swap_exact_output(cpi_ctx, maximum_input_amount, input_token_index, exact_output_amounts)?; - let return_val: Vec = result.get(); - let memo_ix = spl_memo::build_memo(memo, &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - anchor_lang::prelude::msg!("swap_exact_output return_val: {:?}", return_val); - - Ok(return_val) -} - -pub fn handle_cross_chain_swap_exact_output( - ctx: Context, - maximum_input_amount: u64, - exact_output_amount: u64, - memo: &[u8], -) -> Result { - let input_token_index = SWAP_EXACT_OUTPUT_INPUT_TOKEN_INDEX; - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::SwapExactOutput { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - let exact_output_amounts = [exact_output_amount, 0]; - let result = - two_pool::cpi::swap_exact_output(cpi_ctx, maximum_input_amount, input_token_index, exact_output_amounts)?; - let return_val: Vec = result.get(); - let memo_ix = spl_memo::build_memo(memo, &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - anchor_lang::prelude::msg!("cross_chain_swap_exact_output return_val[0]: {:?}", return_val.get(0)); - //TODO: adding this check just for testing purposes. remove later. - require_eq!(return_val.get(0).unwrap(), &exact_output_amount); - Ok(exact_output_amount) -} - -pub fn handle_propeller_swap_exact_output( - ctx: Context, - maximum_input_amount: u64, - exact_output_amount: u64, - // exact_output_amounts: [u64; TOKEN_COUNT], // params: SwapExactOutputParams, - memo: &[u8], - max_fee: u64, -) -> Result> { - require_gt!(exact_output_amount, max_fee, PropellerError::InsufficientAmount); - let input_token_index = SWAP_EXACT_OUTPUT_INPUT_TOKEN_INDEX; - let cpi_ctx = CpiContext::new( - ctx.accounts.two_pool_program.to_account_info(), - two_pool::cpi::accounts::SwapExactOutput { - pool: ctx.accounts.pool.to_account_info(), - pool_token_account_0: ctx.accounts.pool_token_account_0.to_account_info(), - pool_token_account_1: ctx.accounts.pool_token_account_1.to_account_info(), - lp_mint: ctx.accounts.lp_mint.to_account_info(), - governance_fee: ctx.accounts.governance_fee.to_account_info(), - user_transfer_authority: ctx.accounts.user_transfer_authority.to_account_info(), - user_token_account_0: ctx.accounts.user_token_account_0.to_account_info(), - user_token_account_1: ctx.accounts.user_token_account_1.to_account_info(), - token_program: ctx.accounts.token_program.to_account_info(), - }, - ); - let exact_output_amounts = [exact_output_amount, 0]; - let result = - two_pool::cpi::swap_exact_output(cpi_ctx, maximum_input_amount, input_token_index, exact_output_amounts)?; - let return_val: Vec = result.get(); - let memo_ix = spl_memo::build_memo(memo, &[]); - invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - anchor_lang::prelude::msg!("swap_exact_output return_val: {:?}", return_val); - - Ok(return_val) -} diff --git a/packages/solana-contracts/programs/propeller/src/instructions/utils.rs b/packages/solana-contracts/programs/propeller/src/instructions/utils.rs index 8650ba6da..9268cc80b 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/utils.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/utils.rs @@ -1,139 +1,5 @@ -use { - crate::{constants::LAMPORTS_PER_SOL_DECIMAL, error::*, Propeller, TOKEN_COUNT}, - anchor_lang::prelude::*, - anchor_spl::token::{Mint, TokenAccount}, - num_traits::{FromPrimitive, ToPrimitive}, - rust_decimal::Decimal, - switchboard_v2::AggregatorAccountData, - two_pool::{state::TwoPool, BorshDecimal}, -}; +use {crate::error::*, anchor_lang::prelude::*}; -pub fn get_marginal_prices<'info>( - cpi_ctx: CpiContext<'_, '_, '_, 'info, two_pool::cpi::accounts::MarginalPrices<'info>>, -) -> Result<[BorshDecimal; TOKEN_COUNT]> { - let result = two_pool::cpi::marginal_prices(cpi_ctx)?; - Ok(result.get()) -} - -pub fn convert_fees_to_swim_usd_atomic<'info>( - fee_in_lamports: u64, - propeller: &Propeller, - marginal_price_pool_lp_mint: &Account<'info, Mint>, - cpi_ctx: CpiContext<'_, '_, '_, 'info, two_pool::cpi::accounts::MarginalPrices<'info>>, - marginal_price_pool: &TwoPool, - aggregator: &AccountLoader, - max_staleness: i64, -) -> Result { - // let propeller = &self.propeller; - - msg!("fee_in_lamports: {:?}", fee_in_lamports); - let marginal_price_pool_lp_mint = &marginal_price_pool_lp_mint; - - let swim_usd_mint_key = propeller.swim_usd_mint; - let marginal_prices = get_marginal_prices(cpi_ctx)?; - - let intermediate_token_price_decimal: Decimal = get_marginal_price_decimal( - &marginal_price_pool, - &marginal_prices, - &propeller, - &marginal_price_pool_lp_mint.key(), - )?; - - msg!("intermediate_token_price_decimal: {:?}", intermediate_token_price_decimal); - - let fee_in_lamports_decimal = Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::ConversionError)?; - msg!("fee_in_lamports(u64): {:?} fee_in_lamports_decimal: {:?}", fee_in_lamports, fee_in_lamports_decimal); - - let mut res = 0u64; - - let lamports_intermediate_token_price = get_lamports_intermediate_token_price(&aggregator, max_staleness)?; - let fee_in_swim_usd_decimal = lamports_intermediate_token_price - .checked_mul(fee_in_lamports_decimal) - .and_then(|x| x.checked_div(intermediate_token_price_decimal)) - .ok_or(PropellerError::IntegerOverflow)?; - - let swim_usd_decimals = - get_swim_usd_mint_decimals(&swim_usd_mint_key, &marginal_price_pool, &marginal_price_pool_lp_mint)?; - msg!("swim_usd_decimals: {:?}", swim_usd_decimals); - - let ten_pow_decimals = - Decimal::from_u64(10u64.pow(swim_usd_decimals as u32)).ok_or(PropellerError::IntegerOverflow)?; - let fee_in_swim_usd_atomic = fee_in_swim_usd_decimal - .checked_mul(ten_pow_decimals) - .and_then(|v| v.to_u64()) - .ok_or(PropellerError::ConversionError)?; - - msg!("fee_in_swim_usd_decimal: {:?} fee_in_swim_usd_atomic: {:?}", fee_in_swim_usd_decimal, fee_in_swim_usd_atomic); - res = fee_in_swim_usd_atomic; - Ok(res) -} - -pub fn get_swim_usd_mint_decimals( - swim_usd_mint: &Pubkey, - marginal_price_pool: &TwoPool, - marginal_price_pool_lp_mint: &Mint, -) -> Result { - let marginal_price_pool_lp_mint_decimals = marginal_price_pool_lp_mint.decimals; - if *swim_usd_mint == marginal_price_pool.lp_mint_key { - Ok(marginal_price_pool_lp_mint_decimals) - } else if *swim_usd_mint == marginal_price_pool.token_mint_keys[0] { - Ok(marginal_price_pool_lp_mint_decimals + marginal_price_pool.token_decimal_equalizers[0]) - } else if *swim_usd_mint == marginal_price_pool.token_mint_keys[1] { - Ok(marginal_price_pool_lp_mint_decimals + marginal_price_pool.token_decimal_equalizers[1]) - } else { - return err!(PropellerError::UnableToRetrieveSwimUsdMintDecimals); - } -} - -pub fn get_marginal_price_decimal( - marginal_price_pool: &TwoPool, - marginal_prices: &[BorshDecimal; TOKEN_COUNT], - propeller: &Propeller, - marginal_price_pool_lp_mint: &Pubkey, -) -> Result { - let marginal_price_pool_token_index = propeller.marginal_price_pool_token_index; - let swim_usd_mint = &propeller.swim_usd_mint; - let mut marginal_price: Decimal = marginal_prices[marginal_price_pool_token_index as usize] - .try_into() - .map_err(|_| error!(PropellerError::ConversionError))?; - if *marginal_price_pool_lp_mint != *swim_usd_mint { - require_keys_eq!( - marginal_price_pool.token_mint_keys[0], - *swim_usd_mint, - PropellerError::InvalidMetapoolTokenMint, - ); - let swim_usd_marginal_price: Decimal = - marginal_prices[0].try_into().map_err(|_| error!(PropellerError::ConversionError))?; - marginal_price = - marginal_price.checked_div(swim_usd_marginal_price).ok_or(error!(PropellerError::IntegerOverflow))?; - } - Ok(marginal_price) -} - -pub fn get_lamports_intermediate_token_price( - aggregator: &AccountLoader, - max_staleness: i64, -) -> Result { - let feed = aggregator.load()?; - feed.check_staleness( - Clock::get().unwrap().unix_timestamp, - // 300 - i64::MAX, - ) - .map_err(|_| error!(PropellerError::StaleFeed))?; - - // check feed does not exceed max_confidence_interval - // if let Some(max_confidence_interval) = params.max_confidence_interval { - // feed.check_confidence_interval(SwitchboardDecimal::from_f64(max_confidence_interval)) - // .map_err(|_| error!(PropellerError::ConfidenceIntervalExceeded))?; - // } - - let sol_usd_price: Decimal = feed.get_result()?.try_into()?; - let name = feed.name; - - let lamports_usd_price = - sol_usd_price.checked_div(LAMPORTS_PER_SOL_DECIMAL).ok_or(PropellerError::IntegerOverflow)?; - msg!("sol_usd_price:{},lamports_usd_price: {}", sol_usd_price, lamports_usd_price); - Ok(lamports_usd_price) - // check whether the feed has been updated in the last 300 seconds +pub fn get_memo_as_utf8(memo: [u8; 16]) -> Result { + String::from_utf8(hex::encode(memo).into_bytes()).map_err(|_| error!(PropellerError::InvalidMemo)) } diff --git a/packages/solana-contracts/programs/propeller/src/instructions/wormhole/complete_native_with_payload.rs b/packages/solana-contracts/programs/propeller/src/instructions/wormhole/complete_native_with_payload.rs index 746e1a13c..c6b90beeb 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/wormhole/complete_native_with_payload.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/wormhole/complete_native_with_payload.rs @@ -1,28 +1,18 @@ use { crate::{ - constants::LAMPORTS_PER_SOL_DECIMAL, deserialize_message_payload, error::*, - get_lamports_intermediate_token_price, get_marginal_price_decimal, get_marginal_prices, get_message_data, - get_swim_usd_mint_decimals, get_transfer_with_payload_from_message_account, hash_vaa, - instructions::fee_tracker::FeeTracker, state::SwimPayloadMessage, validate_marginal_prices_pool_accounts, - Address, ChainID, ClaimData, MessageData, PayloadTransferWithPayload, PostVAAData, PostedMessageData, - PostedVAAData, Propeller, RawSwimPayload, TokenBridge, Wormhole, COMPLETE_NATIVE_WITH_PAYLOAD_INSTRUCTION, - TOKEN_COUNT, + error::*, fees::*, get_memo_as_utf8, get_message_data, state::SwimPayloadMessage, wormhole::SwimPayload, + ClaimData, Fees, MessageData, PayloadTransferWithPayload, Propeller, TokenBridge, Wormhole, + COMPLETE_NATIVE_WITH_PAYLOAD_INSTRUCTION, }, anchor_lang::{ prelude::*, solana_program::{instruction::Instruction, program::invoke_signed, system_program, sysvar::SysvarId}, }, anchor_spl::{ - token, - token::{Mint, Token, TokenAccount, Transfer}, + associated_token::get_associated_token_address, + token::{self, Mint, Token, TokenAccount, Transfer}, }, - byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}, - num_traits::{FromPrimitive, ToPrimitive}, - primitive_types::U256, - rust_decimal::Decimal, solana_program::program::invoke, - switchboard_v2::{AggregatorAccountData, SwitchboardDecimal, SWITCHBOARD_PROGRAM_ID}, - two_pool::{state::TwoPool, BorshDecimal}, }; #[derive(Accounts)] @@ -32,6 +22,8 @@ pub struct CompleteNativeWithPayload<'info> { seeds = [ b"propeller".as_ref(), propeller.swim_usd_mint.as_ref() ], bump = propeller.bump, has_one = swim_usd_mint @ PropellerError::InvalidSwimUsdMint, + has_one = fee_vault @ PropellerError::InvalidFeeVault, + constraint = !propeller.is_paused @ PropellerError::IsPaused, )] pub propeller: Box>, @@ -42,7 +34,7 @@ pub struct CompleteNativeWithPayload<'info> { mut, seeds = [ b"config".as_ref() ], bump, - seeds::program = propeller.token_bridge().unwrap() + seeds::program = token_bridge.key(), )] /// CHECK: Token Bridge Config pub token_bridge_config: UncheckedAccount<'info>, @@ -72,16 +64,22 @@ pub struct CompleteNativeWithPayload<'info> { // hash_vaa(&vaa).as_ref() // ], // bump, - // seeds::program = propeller.wormhole()? + // seeds::program = propeller.wormhole() // )] + // pub message: Box>, #[account( mut, - owner = propeller.wormhole()?, + owner = wormhole.key(), )] /// CHECK: wormhole message account. seeds = [ "PostedVAA", hash(vaa) ], seeds::program = token_bridge pub message: UncheckedAccount<'info>, - // pub message: Account<'info, PostedMessageData>, - // pub message: Account<'info, PostedVAAData>, + // Note: this works only without the Box wrapper + // error[E0277]: the trait bound `Box>: AsRef>` is not satisfied + // + // #[derive(Accounts)] + // ^^^^^^^^ the trait `AsRef>` is not implemented for `Box>` + + // pub message: Box>, /// seeds = [ /// vaa.emitter_address, vaa.emitter_chain, vaa.sequence ///], @@ -94,21 +92,22 @@ pub struct CompleteNativeWithPayload<'info> { // vaa.sequence.to_be_bytes().as_ref(), // ], // bump, - // seeds::program = propeller.wormhole()? + // seeds::program = propeller.wormhole() // )] #[account(mut)] /// CHECK: wormhole claim account to prevent double spending + /// checked in CPI pub claim: UncheckedAccount<'info>, /// CHECK: wormhole endpoint account. seeds = [ vaa.emitter_chain, vaa.emitter_address ] + /// checked in CPI pub endpoint: UncheckedAccount<'info>, - /// owned by redeemer. "redeemerEscrow" + /// `to` account in `CompleteNativeWithPayload` #[account( mut, - token::mint = swim_usd_mint.key(), - token::authority = redeemer, + address = get_associated_token_address(&redeemer.key(), &swim_usd_mint.key()) )] - pub to: Box>, + pub redeemer_escrow: Box>, #[account( seeds = [ b"redeemer".as_ref()], @@ -123,53 +122,52 @@ pub struct CompleteNativeWithPayload<'info> { /// and that the `redeemer` account will be the PDA derived from ["redeemer"], seeds::program = propeller::id() pub redeemer: SystemAccount<'info>, - //TODO: should this just always be fee_vault? - // not actually being used unless this is a propellerEnabled ix. - // wormhole complete_native_with_payload doesn't do anything with this - // the only thing it does is check that the mint is correct. - // note: we only care that this is actually the fee_vault if it's called from propellerCompleteNativeWithPayload - // so the checks are done there. + #[account(mut)] + /// this is "to_fees" + /// recipient of fees for executing complete transfer (e.g. relayer) + /// this is only used in `propellerCompleteNativeWithPayload`. + pub fee_vault: Box>, - // TODO: rename to `fee_vault` and add has_one in propeller constraints? #[account( - mut, - token::mint = propeller.swim_usd_mint, + init, + payer = payer, + seeds = [ + b"propeller".as_ref(), + b"swim_payload".as_ref(), + claim.key().as_ref(), + ], + bump, + space = 8 + SwimPayloadMessage::LEN, )] - /// this is "to_fees" - /// recipient of fees for executing complete transfer (e.g. relayer) - pub fee_recipient: Box>, + pub swim_payload_message: Box>, // #[account(mut)] // /// this is "to_fees" // /// TODO: type as TokenAccount? // /// CHECK: recipient of fees for executing complete transfer (e.g. relayer) // pub fee_recipient: AccountInfo<'info>, - #[account(mut)] + #[account( + mut, + seeds = [swim_usd_mint.key().as_ref()], + seeds::program = token_bridge.key(), + bump, + )] /// CHECK: wormhole_custody_account: seeds = [mint], seeds::program = token_bridge pub custody: UncheckedAccount<'info>, // #[account(address = propeller.token_bridge_mint)] pub swim_usd_mint: Box>, + #[account( + seeds = [b"custody_signer".as_ref()], + seeds::program = token_bridge.key(), + bump, + )] /// CHECK: custody_signer_account: seeds = [b"custody_signer"], seeds::program = token_bridge pub custody_signer: UncheckedAccount<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub wormhole: Program<'info, Wormhole>, pub token_program: Program<'info, Token>, pub token_bridge: Program<'info, TokenBridge>, - - #[account( - init, - payer = payer, - seeds = [ - b"propeller".as_ref(), - b"swim_payload".as_ref(), - claim.key().as_ref(), - ], - bump, - space = 8 + SwimPayloadMessage::LEN, - )] - pub swim_payload_message: Account<'info, SwimPayloadMessage>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, } impl<'info> CompleteNativeWithPayload<'info> { @@ -181,7 +179,7 @@ impl<'info> CompleteNativeWithPayload<'info> { //TODO: allow for "user" to call CompleteNativeWithPayload through this program? // dependent on if `vaa.to` is this programId for user initiated transfers - fn redeemer_check(ctx: &Context) -> bool { + fn redeemer_check(_ctx: &Context) -> bool { // if ctx.accounts.redeemer.address == ctx.accounts.message.payload.to_address() { // return ctx.accounts.redeemer.to_account_info().is_signer; // } @@ -195,9 +193,9 @@ impl<'info> CompleteNativeWithPayload<'info> { self.message.to_account_info().clone(), self.claim.to_account_info().clone(), self.endpoint.to_account_info().clone(), - self.to.to_account_info().clone(), + self.redeemer_escrow.to_account_info().clone(), self.redeemer.to_account_info().clone(), - self.fee_recipient.to_account_info().clone(), + self.fee_vault.to_account_info().clone(), self.custody.to_account_info().clone(), self.swim_usd_mint.to_account_info().clone(), self.custody_signer.to_account_info().clone(), @@ -215,9 +213,9 @@ impl<'info> CompleteNativeWithPayload<'info> { AccountMeta::new_readonly(self.message.key(), false), AccountMeta::new(self.claim.key(), false), AccountMeta::new_readonly(self.endpoint.key(), false), - AccountMeta::new(self.to.key(), false), + AccountMeta::new(self.redeemer_escrow.key(), false), AccountMeta::new_readonly(self.redeemer.key(), true), - AccountMeta::new(self.fee_recipient.key(), false), + AccountMeta::new(self.fee_vault.key(), false), AccountMeta::new(self.custody.key(), false), AccountMeta::new_readonly(self.swim_usd_mint.key(), false), AccountMeta::new_readonly(self.custody_signer.key(), false), @@ -234,7 +232,7 @@ impl<'info> CompleteNativeWithPayload<'info> { &complete_transfer_with_payload_ix, &wh_complete_native_with_payload_acct_infos, // &self.to_account_infos(), - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], + &[&[(b"redeemer".as_ref()), &[self.propeller.redeemer_bump]]], )?; msg!("successfully invoked self.complete_native_with_payload()"); Ok(()) @@ -248,10 +246,8 @@ impl<'info> CompleteNativeWithPayload<'info> { } pub fn get_transfer_with_payload(&self, message_data: &MessageData) -> Result { - // let message_account_info = &self.message.to_account_info(); - // let message_data = get_message_data(message_account_info)?; - // msg!("message_data: {:?}", message_data); - let transfer_with_payload = deserialize_message_payload(&mut message_data.payload.as_slice())?; + // let transfer_with_payload = deserialize_message_payload(&mut message_data.payload.as_slice())?; + let transfer_with_payload = PayloadTransferWithPayload::deserialize(&mut message_data.payload.as_slice())?; msg!("transfer_with_payload: {:?}", transfer_with_payload); Ok(transfer_with_payload) } @@ -261,7 +257,7 @@ impl<'info> CompleteNativeWithPayload<'info> { bump: u8, message_data: &MessageData, transfer_amount: u64, - swim_payload: &RawSwimPayload, + swim_payload: &SwimPayload, ) -> Result<()> { let swim_payload_message = &mut self.swim_payload_message; swim_payload_message.bump = bump; @@ -274,11 +270,12 @@ impl<'info> CompleteNativeWithPayload<'info> { swim_payload_message.vaa_sequence = message_data.sequence; swim_payload_message.transfer_amount = transfer_amount; swim_payload_message.swim_payload_version = swim_payload.swim_payload_version; - swim_payload_message.target_token_id = swim_payload.target_token_id; swim_payload_message.owner = Pubkey::new_from_array(swim_payload.owner); - swim_payload_message.memo = swim_payload.memo; - swim_payload_message.propeller_enabled = swim_payload.propeller_enabled; - swim_payload_message.gas_kickstart = swim_payload.gas_kickstart; + swim_payload_message.propeller_enabled = swim_payload.propeller_enabled.unwrap_or_default(); + swim_payload_message.gas_kickstart = swim_payload.gas_kickstart.unwrap_or_default(); + swim_payload_message.max_fee = swim_payload.max_fee.unwrap_or_default(); + swim_payload_message.target_token_id = swim_payload.target_token_id.unwrap_or_default(); + swim_payload_message.memo = swim_payload.memo.unwrap_or_default(); Ok(()) } } @@ -306,8 +303,6 @@ pub fn handle_complete_native_with_payload(ctx: Context 8 { transfer_amount *= 10u64.pow(ctx.accounts.swim_usd_mint.decimals as u32); @@ -316,12 +311,6 @@ pub fn handle_complete_native_with_payload(ctx: Context (pool, pool_token_index) - // // need to know when to do remove_exact_burn & when to do swap_exact_input - // let memo_ix = spl_memo::build_memo(memo.as_slice(), &[]); - // invoke(&memo_ix, &[ctx.accounts.memo.to_account_info()])?; - Ok(()) } @@ -329,41 +318,9 @@ pub fn handle_complete_native_with_payload(ctx: Context { pub complete_native_with_payload: CompleteNativeWithPayload<'info>, - // pub marginal_price_pool: MarginalPricePool<'info>, - #[account( - mut, - seeds = [ - b"propeller".as_ref(), - b"fee".as_ref(), - complete_native_with_payload.swim_usd_mint.key().as_ref(), - complete_native_with_payload.payer.key().as_ref() - ], - bump = fee_tracker.bump - )] - pub fee_tracker: Account<'info, FeeTracker>, + // note - fee_vault is repeated in here but txn size-wise it should only take an additional byte not 32. + pub fee_tracking: FeeTracking<'info>, - #[account( - constraint = - *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount - )] - pub aggregator: AccountLoader<'info, AggregatorAccountData>, - // pub two_pool_program: Program<'info, two_pool::program::TwoPool>, - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - marginal_price_pool_token_0_account.mint.as_ref(), - marginal_price_pool_token_1_account.mint.as_ref(), - marginal_price_pool_lp_mint.key().as_ref(), - ], - bump = marginal_price_pool.bump, - seeds::program = two_pool_program.key() - )] - pub marginal_price_pool: Box>, - pub marginal_price_pool_token_0_account: Box>, - pub marginal_price_pool_token_1_account: Box>, - pub marginal_price_pool_lp_mint: Box>, - pub two_pool_program: Program<'info, two_pool::program::TwoPool>, #[account(executable, address = spl_memo::id())] ///CHECK: memo program pub memo: UncheckedAccount<'info>, @@ -372,21 +329,21 @@ pub struct PropellerCompleteNativeWithPayload<'info> { impl<'info> PropellerCompleteNativeWithPayload<'info> { pub fn accounts(ctx: &Context) -> Result<()> { let propeller = &ctx.accounts.complete_native_with_payload.propeller; - validate_marginal_prices_pool_accounts( - &propeller, - &ctx.accounts.marginal_price_pool.key(), - &[ - ctx.accounts.marginal_price_pool_token_0_account.mint, - ctx.accounts.marginal_price_pool_token_1_account.mint, - ], - )?; - require_keys_eq!(ctx.accounts.complete_native_with_payload.fee_recipient.key(), propeller.fee_vault); - require_keys_eq!(ctx.accounts.complete_native_with_payload.fee_recipient.owner, propeller.key()); - require_keys_eq!(ctx.accounts.aggregator.key(), propeller.aggregator, PropellerError::InvalidAggregator); + ctx.accounts.fee_tracking.marginal_price_pool.validate(propeller)?; + Ok(()) + } + + fn log_memo(&self, memo: [u8; 16]) -> Result<()> { + if memo != [0u8; 16] { + let memo_ix = spl_memo::build_memo(get_memo_as_utf8(memo)?.as_ref(), &[]); + invoke(&memo_ix, &[self.memo.to_account_info()])?; + } Ok(()) } +} - fn calculate_fees(&self) -> Result { +impl<'info> Fees<'info> for PropellerCompleteNativeWithPayload<'info> { + fn calculate_fees_in_lamports(&self) -> Result { let rent = Rent::get()?; let wormhole_message_rent_exempt_fees = rent.minimum_balance(self.complete_native_with_payload.message.to_account_info().data_len()); @@ -423,85 +380,23 @@ impl<'info> PropellerCompleteNativeWithPayload<'info> { complete_with_payload_fee, fee_in_lamports ); - - let marginal_prices = get_marginal_prices(self.into_marginal_prices())?; - - let intermediate_token_price_decimal: Decimal = get_marginal_price_decimal( - &self.marginal_price_pool, - &marginal_prices, - &propeller, - // propeller.marginal_price_pool_token_index as usize, - &self.marginal_price_pool_lp_mint.key(), - // &token_bridge_mint_key, - )?; - - msg!("intermediate_token_price_decimal: {:?}", intermediate_token_price_decimal); - - //swimUSD is lp token of marginal price pool - let mut res = 0u64; - - let fee_in_lamports_decimal = Decimal::from_u64(fee_in_lamports).ok_or(PropellerError::ConversionError)?; - msg!("fee_in_lamports(u64): {:?} fee_in_lamports_decimal: {:?}", fee_in_lamports, fee_in_lamports_decimal); - let lamports_intermediate_token_price = get_lamports_intermediate_token_price(&self.aggregator, i64::MAX)?; - let fee_in_swim_usd_decimal = lamports_intermediate_token_price - .checked_mul(fee_in_lamports_decimal) - .and_then(|x| x.checked_div(intermediate_token_price_decimal)) - .ok_or(PropellerError::IntegerOverflow)?; - - let swim_usd_mint_key = self.complete_native_with_payload.propeller.swim_usd_mint; - - let swim_usd_mint_decimals = get_swim_usd_mint_decimals( - &swim_usd_mint_key, - &self.marginal_price_pool, - &self.marginal_price_pool_lp_mint, - )?; - //TODO: good lord forgive me for this terrible naming. i will fix later. - let ten_pow_decimals = - Decimal::from_u64(10u64.pow(swim_usd_mint_decimals as u32)).ok_or(PropellerError::IntegerOverflow)?; - let fee_in_swim_usd_atomic = fee_in_swim_usd_decimal - .checked_mul(ten_pow_decimals) - .and_then(|v| v.to_u64()) - .ok_or(PropellerError::ConversionError)?; - msg!( - "fee_in_swim_usd_decimal: {:?} fee_in_swim_usd_atomic: {:?}", - fee_in_swim_usd_decimal, - fee_in_swim_usd_atomic - ); - res = fee_in_swim_usd_atomic; - Ok(res) + Ok(fee_in_lamports) } - fn handle_fees(&mut self, fees_in_token_bridge_mint: u64) -> Result<()> { - let fee_tracker = &mut self.fee_tracker; - let updated_fees_owed = - fee_tracker.fees_owed.checked_add(fees_in_token_bridge_mint).ok_or(PropellerError::IntegerOverflow)?; - fee_tracker.fees_owed = updated_fees_owed; - - let cpi_accounts = Transfer { - from: self.complete_native_with_payload.to.to_account_info(), - to: self.complete_native_with_payload.fee_recipient.to_account_info(), - authority: self.complete_native_with_payload.redeemer.to_account_info(), - }; + fn transfer_fees_to_vault(&mut self, fees_in_swim_usd: u64) -> Result<()> { token::transfer( CpiContext::new_with_signer( self.complete_native_with_payload.token_program.to_account_info(), - cpi_accounts, - &[&[&b"redeemer".as_ref(), &[self.complete_native_with_payload.propeller.redeemer_bump]]], + Transfer { + from: self.complete_native_with_payload.redeemer_escrow.to_account_info(), + to: self.complete_native_with_payload.fee_vault.to_account_info(), + authority: self.complete_native_with_payload.redeemer.to_account_info(), + }, + &[&[(b"redeemer".as_ref()), &[self.complete_native_with_payload.propeller.redeemer_bump]]], ), - fees_in_token_bridge_mint, + fees_in_swim_usd, ) } - - fn into_marginal_prices(&self) -> CpiContext<'_, '_, '_, 'info, two_pool::cpi::accounts::MarginalPrices<'info>> { - let program = self.two_pool_program.to_account_info(); - let accounts = two_pool::cpi::accounts::MarginalPrices { - pool: self.marginal_price_pool.to_account_info(), - pool_token_account_0: self.marginal_price_pool_token_0_account.to_account_info(), - pool_token_account_1: self.marginal_price_pool_token_1_account.to_account_info(), - lp_mint: self.marginal_price_pool_lp_mint.to_account_info(), - }; - CpiContext::new(program, accounts) - } } pub fn handle_propeller_complete_native_with_payload(ctx: Context) -> Result<()> { @@ -513,7 +408,7 @@ pub fn handle_propeller_complete_native_with_payload(ctx: Context swimUSD if the payer isn't the actual logical owner. // This is for if the propeller engine is unavailable and the user is manually calling // this ix. They should use the `CompleteNativeWithPayload` ix instead but adding this just in case.\ - // TODO: if swim payload owner calling though they will need a fee tracker account already. + // if swim payload owner calling though they will need a fee tracker account already. let swim_payload_owner = Pubkey::new_from_array(swim_payload.owner); - let token_program = &ctx.accounts.complete_native_with_payload.token_program; if swim_payload_owner != ctx.accounts.complete_native_with_payload.payer.key() { - let fees_in_token_bridge_mint = ctx.accounts.calculate_fees()?; - ctx.accounts.handle_fees(fees_in_token_bridge_mint)?; - - msg!("propeller_complete_native_with_payload fees(swimUSD): {:?}", fees_in_token_bridge_mint); + let fees_in_lamports = ctx.accounts.calculate_fees_in_lamports()?; + // let fees_in_swim_usd_atomic = ctx.accounts.convert_fees_to_swim_usd_atomic(fees_in_lamports)?; + let fees_in_swim_usd_atomic = ctx.accounts.fee_tracking.track_fees(fees_in_lamports, propeller)?; + ctx.accounts.transfer_fees_to_vault(fees_in_swim_usd_atomic)?; + msg!("fees_in_swim_usd_atomic: {:?}", fees_in_swim_usd_atomic); transfer_amount = - transfer_amount.checked_sub(fees_in_token_bridge_mint).ok_or(PropellerError::IntegerOverflow)?; + transfer_amount.checked_sub(fees_in_swim_usd_atomic).ok_or(error!(PropellerError::InsufficientFunds))?; } msg!("transfer_amount(swimUSD) after fees: {:?}", transfer_amount); @@ -549,14 +441,11 @@ pub fn handle_propeller_complete_native_with_payload(ctx: Context { } impl<'info> Secp256k1AndVerify<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { + pub fn accounts(_ctx: &Context) -> Result<()> { Ok(()) } } @@ -54,10 +54,10 @@ pub struct VerifySignaturesData { pub fn handle_secp256k1_and_verify( ctx: Context, secp_payload: Vec, - guardian_set_index: u32, + _guardian_set_index: u32, verify_signatures_data: VerifySignaturesData, ) -> Result<()> { - let test = secp256k1_program::id(); + let _test = secp256k1_program::id(); let secp_ix = Instruction { program_id: ctx.accounts.secp256k1_program.key(), accounts: vec![], data: secp_payload }; invoke(&secp_ix, &[])?; diff --git a/packages/solana-contracts/programs/propeller/src/instructions/wormhole/transfer_native_with_payload.rs b/packages/solana-contracts/programs/propeller/src/instructions/wormhole/transfer_native_with_payload.rs index 17f425fe4..51182ce95 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/wormhole/transfer_native_with_payload.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/wormhole/transfer_native_with_payload.rs @@ -1,55 +1,46 @@ use { crate::{ - constants::CURRENT_SWIM_PAYLOAD_VERSION, error::*, target_chain_map::TargetChainMap, Propeller, RawSwimPayload, - TokenBridge, Wormhole, TOKEN_COUNT, TRANSFER_NATIVE_WITH_PAYLOAD_INSTRUCTION, + constants::CURRENT_SWIM_PAYLOAD_VERSION, error::*, target_chain_map::TargetChainMap, wormhole::SwimPayload, + Propeller, TokenBridge, Wormhole, TRANSFER_NATIVE_WITH_PAYLOAD_INSTRUCTION, }, anchor_lang::{ prelude::*, - solana_program::{ - borsh::try_from_slice_unchecked, - instruction::Instruction, - program::{get_return_data, invoke, invoke_signed}, - program_option::COption, - system_instruction::transfer, - sysvar::SysvarId, - }, + solana_program::{instruction::Instruction, program::invoke_signed, sysvar::SysvarId}, }, anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, + associated_token::get_associated_token_address, + token::{self, Mint, Token, TokenAccount}, }, - byteorder::{BigEndian, WriteBytesExt}, - primitive_types::U256, - std::{io::Write, str}, }; #[derive(Accounts)] -#[instruction(amount: u64, target_chain: u16)] +#[instruction(nonce: u32, amount: u64, target_chain: u16)] pub struct TransferNativeWithPayload<'info> { #[account( mut, seeds = [b"propeller".as_ref(), swim_usd_mint.key().as_ref()], bump = propeller.bump, has_one = swim_usd_mint @ PropellerError::InvalidSwimUsdMint, + constraint = !propeller.is_paused @ PropellerError::IsPaused, )] pub propeller: Box>, #[account(mut)] pub payer: Signer<'info>, + #[account( - mut, - seeds = [ b"config".as_ref() ], - bump, - seeds::program = propeller.token_bridge().unwrap() - )] + mut, + seeds = [ b"config".as_ref() ], + bump, + seeds::program = token_bridge.key() + )] /// CHECK: Token Bridge Config - pub token_bridge_config: AccountInfo<'info>, + pub token_bridge_config: UncheckedAccount<'info>, #[account( - mut, - associated_token::mint = swim_usd_mint, - associated_token::authority = payer - )] + mut, + address = get_associated_token_address(&payer.key(), &swim_usd_mint.key()), + )] pub user_swim_usd_ata: Box>, #[account(mut)] @@ -65,33 +56,32 @@ pub struct TransferNativeWithPayload<'info> { /// CHECK: Will either be token bridge custody account or wrapped meta account pub custody: UncheckedAccount<'info>, - #[account(executable, address = propeller.token_bridge()?)] pub token_bridge: Program<'info, TokenBridge>, #[account( - seeds=[b"custody_signer".as_ref()], - bump, - seeds::program = token_bridge.key() - )] + seeds=[b"custody_signer".as_ref()], + bump, + seeds::program = token_bridge.key() + )] /// CHECK: Only used for bridging assets native to Solana. pub custody_signer: UncheckedAccount<'info>, #[account( - seeds=[b"authority_signer".as_ref()], - bump, - seeds::program = token_bridge.key() - )] + seeds=[b"authority_signer".as_ref()], + bump, + seeds::program = token_bridge.key() + )] /// CHECK: Token Bridge Authority Signer, delegated approval for transfer - pub authority_signer: AccountInfo<'info>, + pub authority_signer: UncheckedAccount<'info>, #[account( - mut, - seeds = [b"Bridge".as_ref()], - bump, - seeds::program = propeller.wormhole().unwrap() - )] + mut, + seeds = [b"Bridge".as_ref()], + bump, + seeds::program = wormhole.key() + )] /// CHECK: Wormhole Config - pub wormhole_config: AccountInfo<'info>, + pub wormhole_config: UncheckedAccount<'info>, #[account(mut)] // Note: @@ -108,34 +98,34 @@ pub struct TransferNativeWithPayload<'info> { pub wormhole_message: Signer<'info>, #[account( - mut, - seeds = [b"emitter".as_ref()], - bump, - seeds::program = propeller.token_bridge().unwrap() - )] + mut, + seeds = [b"emitter".as_ref()], + bump, + seeds::program = token_bridge.key() + )] /// CHECK: Wormhole Emitter is PDA representing the Token Bridge Program - pub wormhole_emitter: AccountInfo<'info>, + pub wormhole_emitter: UncheckedAccount<'info>, #[account( - mut, - seeds = [ - b"Sequence".as_ref(), - wormhole_emitter.key().as_ref() - ], - bump, - seeds::program = propeller.wormhole().unwrap() - )] + mut, + seeds = [ + b"Sequence".as_ref(), + wormhole_emitter.key().as_ref() + ], + bump, + seeds::program = wormhole.key() + )] /// CHECK: Wormhole Sequence Number - pub wormhole_sequence: AccountInfo<'info>, + pub wormhole_sequence: UncheckedAccount<'info>, #[account( - mut, - seeds = [b"fee_collector".as_ref()], - bump, - seeds::program = propeller.wormhole().unwrap() - )] + mut, + seeds = [b"fee_collector".as_ref()], + bump, + seeds::program = wormhole.key() + )] /// CHECK: Wormhole Fee Collector. leaving as UncheckedAccount since it could be uninitialized for the first transfer. - pub wormhole_fee_collector: AccountInfo<'info>, + pub wormhole_fee_collector: UncheckedAccount<'info>, /// Transfers with payload also include the address of the account or contract /// that sent the transfer. Semantically this is identical to "msg.sender" on @@ -170,9 +160,9 @@ pub struct TransferNativeWithPayload<'info> { /// that case the PDA's address will directly be encoded into the payload /// instead of the sender program's id. #[account( - seeds = [ b"sender".as_ref()], - bump = propeller.sender_bump, - )] + seeds = [ b"sender".as_ref() ], + bump = propeller.sender_bump, + )] /// CHECK: Sender Account pub sender: SystemAccount<'info>, @@ -189,7 +179,8 @@ pub struct TransferNativeWithPayload<'info> { propeller.key().as_ref(), &target_chain.to_le_bytes() ], - bump = target_chain_map.bump + bump = target_chain_map.bump, + constraint = !target_chain_map.is_paused @ PropellerError::TargetChainIsPaused, )] pub target_chain_map: Account<'info, TargetChainMap>, pub system_program: Program<'info, System>, @@ -199,42 +190,26 @@ pub struct TransferNativeWithPayload<'info> { } impl<'info> TransferNativeWithPayload<'info> { - //Note: some of the checks are excessive (checked in CPI etc) and add.rs to compute budget but since we now have access to requesting - // up to 1.4M compute budget per transaction, better safe than sorry to perform them. - pub fn accounts(ctx: &Context) -> Result<()> { - require_keys_eq!( - ctx.accounts.swim_usd_mint.key(), - ctx.accounts.propeller.swim_usd_mint, - PropellerError::InvalidSwimUsdMint - ); - // let pool_state_acct = &ctx.accounts.pool_state; - // let pool: two_pool::state::PoolState<{two_pool::TOKEN_COUNT}> = two_pool::state::PoolState::try_from_slice(&pool_state_acct.data.borrow())?; - // constraint = lp_mint.key() == propeller.token_bridge_mint @ PropellerError::InvalidMint - msg!("finished accounts context check"); - Ok(()) - } - fn invoke_transfer_native_with_payload(&self, transfer_with_payload_data: TransferWithPayloadData) -> Result<()> { let wh_token_transfer_acct_infos = vec![ - self.payer.to_account_info().clone(), - self.token_bridge_config.to_account_info().clone(), - // ctx.accounts.token_bridge.to_account_info().clone(), - self.user_swim_usd_ata.to_account_info().clone(), - self.swim_usd_mint.to_account_info().clone(), - self.custody.to_account_info().clone(), - self.authority_signer.to_account_info().clone(), - self.custody_signer.to_account_info().clone(), - self.wormhole_config.to_account_info().clone(), - self.wormhole_message.to_account_info().clone(), - self.wormhole_emitter.to_account_info().clone(), - self.wormhole_sequence.to_account_info().clone(), - self.wormhole_fee_collector.to_account_info().clone(), - self.clock.to_account_info().clone(), - self.sender.to_account_info().clone(), - self.rent.to_account_info().clone(), - self.system_program.to_account_info().clone(), - self.wormhole.to_account_info().clone(), - self.token_program.to_account_info().clone(), + self.payer.to_account_info(), + self.token_bridge_config.to_account_info(), + self.user_swim_usd_ata.to_account_info(), + self.swim_usd_mint.to_account_info(), + self.custody.to_account_info(), + self.authority_signer.to_account_info(), + self.custody_signer.to_account_info(), + self.wormhole_config.to_account_info(), + self.wormhole_message.to_account_info(), + self.wormhole_emitter.to_account_info(), + self.wormhole_sequence.to_account_info(), + self.wormhole_fee_collector.to_account_info(), + self.clock.to_account_info(), + self.sender.to_account_info(), + self.rent.to_account_info(), + self.system_program.to_account_info(), + self.wormhole.to_account_info(), + self.token_program.to_account_info(), ]; invoke_signed( @@ -266,16 +241,10 @@ impl<'info> TransferNativeWithPayload<'info> { }, // &self.to_account_infos(), &wh_token_transfer_acct_infos, - &[&[&b"sender".as_ref(), &[self.propeller.sender_bump]]], + &[&[(b"sender".as_ref()), &[self.propeller.sender_bump]]], )?; Ok(()) } - - pub fn increment_nonce(&mut self) -> Result<()> { - let propeller = &mut self.propeller; - propeller.nonce = propeller.nonce.wrapping_add(1); - Ok(()) - } } #[derive(AnchorDeserialize, AnchorSerialize, Default)] @@ -291,6 +260,7 @@ pub struct TransferWithPayloadData { pub fn handle_cross_chain_transfer_native_with_payload( ctx: Context, + nonce: u32, amount: u64, target_chain: u16, owner: Vec, @@ -316,43 +286,11 @@ pub fn handle_cross_chain_transfer_native_with_payload( let swim_payload = SwimPayload { swim_payload_version: CURRENT_SWIM_PAYLOAD_VERSION, owner: owner_addr, ..Default::default() }; - // let swim_payload = RawSwimPayload { - // //TODO: this should come from the propeller or global constant? - // swim_payload_version: CURRENT_SWIM_PAYLOAD_VERSION, - // owner: owner_addr, - // propeller_enabled, - // gas_kickstart, - // max_fee, - // target_token_id, - // // min_output_amount: U256::from(0u64), - // memo: memo.clone().try_into().unwrap(), - // }; msg!("transfer_native_with_payload swim_payload: {:?}", swim_payload); - // let mut swim_payload_bytes = [0u8; 32]; - // let swim_payload_bytes = swim_payload.try_to_vec()?; - // anchor_lang::prelude::msg!("swim_payload_bytes {:?}", swim_payload_bytes); - // - // Note: - // 1. nonce is created randomly client side using this - // export function createNonce() { - // const nonceConst = Math.random() * 100000; - // const nonceBuffer = Buffer.alloc(4); - // nonceBuffer.writeUInt32LE(nonceConst, 0); - // return nonceBuffer; - // } - // 2. fee is relayerFee - // a. removed in payload3 - // 3. targetAddress is Uint8Array (on wasm.rs its Vec - // a. WH client has special handling/formatting for this - // see - wh-sdk/src/utils/array.ts tryNativeToUint8Array(address: string, chain: ChainId | ChainName) - // 4. targetChain is number/u16 - // 5. payload is Vec - // ok - let target_address = ctx.accounts.target_chain_map.target_address.clone(); + let target_address = ctx.accounts.target_chain_map.target_address; let transfer_with_payload_data = TransferWithPayloadData { - //TODO: update this. - nonce: ctx.accounts.propeller.nonce, + nonce, amount, target_address, target_chain, @@ -371,12 +309,12 @@ pub fn handle_cross_chain_transfer_native_with_payload( }, ))?; msg!("Revoked authority_signer approval"); - ctx.accounts.increment_nonce()?; Ok(()) } pub fn handle_propeller_transfer_native_with_payload( ctx: Context, + nonce: u32, amount: u64, target_chain: u16, owner: Vec, @@ -400,8 +338,6 @@ pub fn handle_propeller_transfer_native_with_payload( amount, )?; msg!("finished approve for authority_signer"); - // let mut target_token_addr = [0u8; 32]; - // target_token_addr.copy_from_slice(target_token.as_slice()); let mut owner_addr = [0u8; 32]; owner_addr.copy_from_slice(owner.as_slice()); @@ -416,10 +352,9 @@ pub fn handle_propeller_transfer_native_with_payload( }; msg!("transfer_native_with_payload swim_payload: {:?}", swim_payload); - let target_address = ctx.accounts.target_chain_map.target_address.clone(); + let target_address = ctx.accounts.target_chain_map.target_address; let transfer_with_payload_data = TransferWithPayloadData { - //TODO: update this. - nonce: ctx.accounts.propeller.nonce, + nonce, amount, target_address, target_chain, @@ -439,41 +374,5 @@ pub fn handle_propeller_transfer_native_with_payload( }, ))?; msg!("Revoked authority_signer approval"); - ctx.accounts.increment_nonce()?; Ok(()) } - -#[derive(PartialEq, Debug, Clone, AnchorDeserialize, Default)] -pub struct SwimPayload { - //TOOD: should this come from propeller? - //required - pub swim_payload_version: u8, - pub owner: [u8; 32], - // required for all propellerEngines - pub propeller_enabled: Option, - pub gas_kickstart: Option, - pub max_fee: Option, - pub target_token_id: Option, - // required for SWIM propellerEngine - pub memo: Option<[u8; 16]>, -} - -impl AnchorSerialize for SwimPayload { - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - // Payload ID - // writer.write_u8(self.swim_payload_version)?; - writer.write_u8(self.swim_payload_version)?; - writer.write_all(&self.owner)?; - - if self.propeller_enabled.is_some() { - writer.write_u8(1)?; - writer.write_u8(self.gas_kickstart.unwrap() as u8)?; - writer.write_u64::(self.max_fee.unwrap())?; - writer.write_u16::(self.target_token_id.unwrap())?; - if self.memo.is_some() { - writer.write_all(&self.memo.unwrap())?; - } - } - Ok(()) - } -} diff --git a/packages/solana-contracts/programs/propeller/src/lib.rs b/packages/solana-contracts/programs/propeller/src/lib.rs index a30a11dab..1eb224d08 100644 --- a/packages/solana-contracts/programs/propeller/src/lib.rs +++ b/packages/solana-contracts/programs/propeller/src/lib.rs @@ -1,41 +1,22 @@ -use anchor_lang::solana_program::{ - borsh::try_from_slice_unchecked, - instruction::Instruction, - program::{get_return_data, invoke, invoke_signed}, - program_option::COption, - system_instruction::transfer, - sysvar::SysvarId, // sysvar::Sysvar::to_account_info, -}; use { - crate::two_pool_cpi::{ - add::*, remove_exact_burn::*, remove_exact_output::*, remove_uniform::*, swap_exact_input::*, - swap_exact_output::*, - }, - anchor_lang::{prelude::*, solana_program}, + crate::two_pool_cpi::init_to_swim_usd::*, + anchor_lang::prelude::*, // crate::two_pool_cpi::*, - anchor_spl::{ - associated_token::{get_associated_token_address, AssociatedToken}, - token::{Mint, Token, TokenAccount}, - *, - }, constants::TOKEN_COUNT, - // error::PropellerError, - // instructions::*, - solana_program::clock::Epoch, + fees::*, state::*, token_bridge::*, - two_pool::instructions::AddParams, wormhole::*, }; mod constants; mod error; +mod fees; mod instructions; mod state; mod token_bridge; mod wormhole; -use two_pool::state::TwoPool; pub use {error::*, instructions::*}; declare_id!("9z6G41AyXk73r1E4nTv81drQPtEqupCSAnsLdGV5WGfK"); @@ -50,27 +31,67 @@ pub mod propeller { handle_initialize(ctx, params) } + pub fn set_paused(ctx: Context, paused: bool) -> Result<()> { + handle_set_paused(ctx, paused) + } + + pub fn change_pause_key(ctx: Context, new_pause_key: Pubkey) -> Result<()> { + handle_change_pause_key(ctx, new_pause_key) + } + + pub fn prepare_governance_transition( + ctx: Context, + upcoming_governance_key: Pubkey, + ) -> Result<()> { + handle_prepare_governance_transition(ctx, upcoming_governance_key) + } + + pub fn enact_governance_transition(ctx: Context) -> Result<()> { + handle_enact_governance_transition(ctx) + } + + pub fn update_fees(ctx: Context, fee_updates: FeeUpdates) -> Result<()> { + handle_update_fees(ctx, fee_updates) + } + #[inline(never)] #[access_control( - CreateTokenIdMap::accounts( + CreateTokenNumberMap::accounts( &ctx, - target_token_index, + to_token_number, pool, pool_token_index, pool_token_mint, - pool_ix, + to_token_step, ))] - pub fn create_token_id_map( - ctx: Context, - target_token_index: u16, + pub fn create_token_number_map( + ctx: Context, + to_token_number: u16, pool: Pubkey, pool_token_index: u8, pool_token_mint: Pubkey, - pool_ix: PoolInstruction, + to_token_step: ToTokenStep, + ) -> Result<()> { + handle_create_token_number_map(ctx, to_token_number, pool, pool_token_index, pool_token_mint, to_token_step) + } + + #[inline(never)] + #[access_control(UpdateTokenNumberMap::accounts(&ctx, &pool_token_index, &pool_token_mint, &to_token_step))] + pub fn update_token_number_map( + ctx: Context, + to_token_number: u16, + pool_token_index: u8, + pool_token_mint: Pubkey, + to_token_step: ToTokenStep, ) -> Result<()> { - handle_create_token_id_map(ctx, target_token_index, pool, pool_token_index, pool_token_mint, pool_ix) + handle_update_token_number_map(ctx, to_token_number, pool_token_index, pool_token_mint, to_token_step) + } + + pub fn close_token_number_map(ctx: Context, to_token_number: u16) -> Result<()> { + handle_close_token_number_map(ctx, to_token_number) } + /** Target Chain Map **/ pub fn create_target_chain_map( ctx: Context, target_chain: u16, @@ -79,8 +100,21 @@ pub mod propeller { handle_create_target_chain_map(ctx, target_chain, target_address) } - pub fn update_target_chain_map(ctx: Context, routing_contract: [u8; 32]) -> Result<()> { - handle_update_target_chain_map(ctx, routing_contract) + //TODO: pass in `target_chain` as input parameter for these? + pub fn update_target_chain_map( + ctx: Context, + target_chain: u16, + routing_contract: [u8; 32], + ) -> Result<()> { + handle_update_target_chain_map(ctx, target_chain, routing_contract) + } + + pub fn target_chain_map_set_paused( + ctx: Context, + target_chain: u16, + is_paused: bool, + ) -> Result<()> { + handle_target_chain_map_set_paused(ctx, target_chain, is_paused) } #[inline(never)] @@ -94,205 +128,41 @@ pub mod propeller { handle_claim_fees(ctx) } - // #[access_control(Add::accounts(&ctx))] - // pub fn add( - // ctx: Context, - // input_amounts: [u64; TOKEN_COUNT], - // minimum_mint_amount: u64, - // memo: Vec, - // propeller_enabled: bool, - // target_chain: u16, - // ) -> Result { - // handle_add(ctx, input_amounts, minimum_mint_amount, memo.as_slice(), propeller_enabled, target_chain) - // } - - #[access_control(Add::accounts(&ctx))] - pub fn cross_chain_add( - ctx: Context, + /** Sending */ + pub fn cross_chain_init_to_swim_usd( + ctx: Context, input_amounts: [u64; TOKEN_COUNT], minimum_mint_amount: u64, ) -> Result { - handle_cross_chain_add(ctx, input_amounts, minimum_mint_amount) - } - - #[access_control(Add::accounts(&ctx))] - pub fn propeller_add(ctx: Context, input_amounts: [u64; TOKEN_COUNT], max_fee: u64) -> Result { - handle_propeller_add(ctx, input_amounts, max_fee) - } - - // pub fn swap_exact_input( - // ctx: Context, - // exact_input_amount: u64, - // minimum_output_amount: u64, - // memo: Vec, - // propeller_enabled: bool, - // target_chain: u16, - // ) -> Result { - // handle_swap_exact_input( - // ctx, - // exact_input_amount, - // minimum_output_amount, - // memo.as_slice(), - // propeller_enabled, - // target_chain, - // ) - // } - - /* For metapools */ - - pub fn cross_chain_swap_exact_input( - ctx: Context, - exact_input_amount: u64, - minimum_output_amount: u64, - ) -> Result { - handle_cross_chain_swap_exact_input(ctx, exact_input_amount, minimum_output_amount) + handle_cross_chain_init_to_swim_usd(ctx, input_amounts, minimum_mint_amount) } - pub fn propeller_swap_exact_input( - ctx: Context, - exact_input_amount: u64, + pub fn propeller_init_to_swim_usd( + ctx: Context, + input_amounts: [u64; TOKEN_COUNT], max_fee: u64, ) -> Result { - handle_propeller_swap_exact_input(ctx, exact_input_amount, max_fee) + handle_propeller_init_to_swim_usd(ctx, input_amounts, max_fee) } - /* - // pub fn swap_exact_output( - // ctx: Context, - // maximum_input_amount: u64, - // exact_output_amount: u64, // params: SwapExactOutputParams, - // memo: Vec, - // propeller_enabled: bool, - // target_chain: u16, - // ) -> Result> { - // handle_swap_exact_output( - // ctx, - // maximum_input_amount, - // exact_output_amount, - // memo.as_slice(), - // propeller_enabled, - // target_chain, - // ) - // } - - // pub fn cross_chain_swap_exact_output( - // ctx: Context, - // maximum_input_amount: u64, - // exact_output_amount: u64, // params: SwapExactOutputParams, - // memo: Vec, - // ) -> Result> { - // handle_cross_chain_swap_exact_output( - // ctx, - // maximum_input_amount, - // exact_output_amount, - // memo.as_slice(), - // propeller_enabled, - // target_chain, - // ) - // } - // - // pub fn propeller_swap_exact_output( - // ctx: Context, - // maximum_input_amount: u64, - // memo: Vec, - // max_fee: u64, - // ) -> Result> { - // handle_propeller_swap_exact_output( - // ctx, - // maximum_input_amount, - // exact_output_amount, - // memo.as_slice(), - // propeller_enabled, - // target_chain, - // ) - // } - - // pub fn remove_uniform( - // ctx: Context, - // exact_burn_amount: u64, - // minimum_output_amounts: [u64; TOKEN_COUNT], - // memo: Vec, - // ) -> Result> { - // handle_remove_uniform(ctx, exact_burn_amount, minimum_output_amounts, memo.as_slice()) - // } - - // pub fn remove_exact_burn( - // ctx: Context, - // exact_burn_amount: u64, - // minimum_output_amount: u64, - // memo: Vec, - // propeller_enabled: bool, - // target_chain: u16, - // ) -> Result { - // handle_remove_exact_burn( - // ctx, - // exact_burn_amount, - // minimum_output_amount, - // memo.as_slice(), - // propeller_enabled, - // target_chain, - // ) - // } - */ - - //TODO: does remove_exact_burn make sense for metapools? - // burn metapool lp token to get token_bridge_mint - - // pub fn cross_chain_remove_exact_burn( - // ctx: Context, - // exact_burn_amount: u64, - // minimum_output_amount: u64, - // memo: Vec, - // ) -> Result { - // handle_cross_chain_remove_exact_burn(ctx, exact_burn_amount, minimum_output_amount, memo.as_slice()) - // } - // - // pub fn propeller_remove_exact_burn( - // ctx: Context, - // exact_burn_amount: u64, - // memo: Vec, - // max_fee: u64, - // ) -> Result { - // handle_propeller_remove_exact_burn(ctx, exact_burn_amount, memo.as_slice(), max_fee) - // } - - // pub fn remove_exact_output( - // ctx: Context, - // maximum_burn_amount: u64, - // exact_output_amount: u64, - // memo: Vec, - // propeller_enabled: bool, - // target_chain: u16, - // ) -> Result> { - // handle_remove_exact_output( - // ctx, - // maximum_burn_amount, - // exact_output_amount, - // memo.as_slice(), - // propeller_enabled, - // target_chain, - // ) - // } - #[inline(never)] - #[access_control(TransferNativeWithPayload::accounts(&ctx))] pub fn cross_chain_transfer_native_with_payload( ctx: Context, + nonce: u32, amount: u64, target_chain: u16, owner: Vec, ) -> Result<()> { - handle_cross_chain_transfer_native_with_payload(ctx, amount, target_chain, owner) + handle_cross_chain_transfer_native_with_payload(ctx, nonce, amount, target_chain, owner) } #[inline(never)] - #[access_control(TransferNativeWithPayload::accounts(&ctx))] pub fn propeller_transfer_native_with_payload( ctx: Context, + nonce: u32, amount: u64, target_chain: u16, owner: Vec, - // propeller_enabled: bool, gas_kickstart: bool, max_fee: u64, target_token_id: u16, @@ -300,6 +170,7 @@ pub mod propeller { ) -> Result<()> { handle_propeller_transfer_native_with_payload( ctx, + nonce, amount, target_chain, owner, @@ -310,6 +181,8 @@ pub mod propeller { ) } + /** Receiving */ + #[inline(never)] #[access_control(CompleteNativeWithPayload::accounts(&ctx))] pub fn complete_native_with_payload(ctx: Context) -> Result<()> { @@ -320,10 +193,10 @@ pub mod propeller { #[access_control(ProcessSwimPayload::accounts(&ctx))] pub fn process_swim_payload( ctx: Context, - target_token_id: u16, + to_token_number: u16, min_output_amount: u64, ) -> Result { - handle_process_swim_payload(ctx, target_token_id, min_output_amount) + handle_process_swim_payload(ctx, to_token_number, min_output_amount) } #[inline(never)] @@ -342,12 +215,12 @@ pub mod propeller { /// Note: passing in target_token_id here due to PDA seed derivation. /// for propeller_process_swim_payload, require_eq!(target_token_id, propeller_message.target_token_id); #[inline(never)] - #[access_control(PropellerProcessSwimPayload::accounts(&ctx, target_token_id))] + #[access_control(PropellerProcessSwimPayload::accounts(&ctx, to_token_number))] pub fn propeller_process_swim_payload( ctx: Context, - target_token_id: u16, + to_token_number: u16, ) -> Result { - handle_propeller_process_swim_payload(ctx, target_token_id) + handle_propeller_process_swim_payload(ctx, to_token_number) } /// This ix is used if a propeller engine detects (off-chain) that the target_token_id is not valid @@ -499,7 +372,7 @@ pub mod propeller { // mut, // seeds = [ b"config".as_ref() ], // bump, -// seeds::program = propeller.token_bridge().unwrap() +// seeds::program = propeller.token_bridge() // )] // /// CHECK: Token Bridge Config // pub token_bridge_config: AccountInfo<'info>, @@ -547,7 +420,7 @@ pub mod propeller { // mut, // seeds = [b"Bridge".as_ref()], // bump, -// seeds::program = propeller.wormhole().unwrap() +// seeds::program = propeller.wormhole() // )] // /// CHECK: Wormhole Config // pub wormhole_config: AccountInfo<'info>, @@ -570,7 +443,7 @@ pub mod propeller { // mut, // seeds = [b"emitter".as_ref()], // bump, -// seeds::program = propeller.token_bridge().unwrap() +// seeds::program = propeller.token_bridge() // )] // /// CHECK: Wormhole Emitter is PDA representing the Token Bridge Program // pub wormhole_emitter: AccountInfo<'info>, @@ -582,7 +455,7 @@ pub mod propeller { // wormhole_emitter.key().as_ref() // ], // bump, -// seeds::program = propeller.wormhole().unwrap() +// seeds::program = propeller.wormhole() // )] // /// CHECK: Wormhole Sequence Number // pub wormhole_sequence: AccountInfo<'info>, @@ -591,7 +464,7 @@ pub mod propeller { // mut, // seeds = [b"fee_collector".as_ref()], // bump, -// seeds::program = propeller.wormhole().unwrap() +// seeds::program = propeller.wormhole() // )] // /// CHECK: Wormhole Fee Collector. leaving as UncheckedAccount since it could be uninitialized for the first transfer. // pub wormhole_fee_collector: AccountInfo<'info>, diff --git a/packages/solana-contracts/programs/propeller/src/state/propeller.rs b/packages/solana-contracts/programs/propeller/src/state/propeller.rs index ec6a50c4d..6b93438e0 100644 --- a/packages/solana-contracts/programs/propeller/src/state/propeller.rs +++ b/packages/solana-contracts/programs/propeller/src/state/propeller.rs @@ -1,12 +1,9 @@ use { - crate::{constants::CURRENT_SWIM_PAYLOAD_VERSION, error::PropellerError, Address, TOKEN_COUNT}, + crate::{error::PropellerError, TOKEN_COUNT}, anchor_lang::prelude::*, anchor_spl::token::TokenAccount, - byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}, - std::{ - io::{Cursor, ErrorKind, Read, Write}, - str::FromStr, - }, + std::io::Write, + two_pool::state::TwoPool, }; // Do i need this to hold configs & state? @@ -15,11 +12,12 @@ use { #[account] pub struct Propeller { pub bump: u8, - pub nonce: u32, - pub admin: Pubkey, //32 - pub wormhole: Pubkey, //32 - pub token_bridge: Pubkey, //32 - pub swim_usd_mint: Pubkey, //32 + pub is_paused: bool, // 1 + pub governance_key: Pubkey, //32 + pub prepared_governance_key: Pubkey, // 32 + pub governance_transition_ts: i64, // 8 + pub pause_key: Pubkey, //32 + pub swim_usd_mint: Pubkey, //32 pub sender_bump: u8, pub redeemer_bump: u8, @@ -34,20 +32,12 @@ pub struct Propeller { pub init_ata_fee: u64, pub complete_with_payload_fee: u64, pub process_swim_payload_fee: u64, - // minimum amount of tokens that must be transferred in token bridge transfer - // if propeller enabled transfer. - // Note: No longer using min transfer amounts - // client will set `max_fee` for propellerEnabled transfers and engine will - // be responsible for checking if the fee is sufficient for it to relay the transfer - // pub propeller_min_transfer_amount: u64, - // pub propeller_eth_min_transfer_amount: u64, // gas kickstart parameters // 1. marginal_price_pool will be pool used to calculate token_bridge_mint -> stablecoin // 2. marginal_price_pool_token_index will be index of token in pool used to calculate token_bridge_mint -> stablecoin - - // pool used to get marginal price - // e.g. usdc-usdt pool + /// pool used to get marginal price of swimUSD -> stablecoin for gas conversion + /// e.g. usdc-usdt pool pub marginal_price_pool: Pubkey, pub marginal_price_pool_token_mint: Pubkey, // index of token used for calculating gas price @@ -56,33 +46,29 @@ pub struct Propeller { // pub evm_routing_contract_address: [u8; 32], pub fee_vault: Pubkey, //32 + // switchboard + // + // from discord: + // default sol/usd feed has 10K samples => ~0.7 days of data + // https://switchboard.xyz/explorer/3/GvDMxPzN1sCj7L26YDK2HnMRXEQmQ2aemov8YBtPS7vR + // should be safe to use unless we need custom sources or larger history buffer size + // example feed eefn: + // 1. switchboard feed w/ SOL/USD prices from variety of exchanges weighed by 7D volume along w/ history buffer + // 2. switchboard feed w/ oracleTask to fetch Pyth SOL/USD prices every 10 seconds + history buffer + // 3. switchboard feed w/ oracleTask to fetch ChainLink SOL/USD prices every 10 seconds + history buffer + // 4. switchboard feed calculating 1 min TWAP of (1) (2) and (3) and returns median of results pub aggregator: Pubkey, //32 + pub max_staleness: i64, //8 + // pub max_confidence_interval: i64, //8 + //TODO: add this? // pub fallback_oracle: Pubkey, //32 - // pub custody_signer_key: Pubkey, // 32 - // pub custody_signer_bump: u8, // 1 - - // pub mint_signer_key: Pubkey, // 32 - // pub mint_signer_bump: u8, // 1 - - // pub authority_signer_key: Pubkey, // 32 - // pub authority_signer_bump: u8, // 1 - // - // pub bridge_config_key: Pubkey, // 32 - // pub bridge_config_bump: u8, // 1 + // pyth // - // pub wormhole_config_key: Pubkey, // 32 - // pub wormhole_config_bump: u8, // 1 - // - // pub fee_collector_key: Pubkey, // 32 - // pub fee_collector_bump: u8, // 1 - // - // pub wormhole_emitter_key: Pubkey, // 32 - // pub wormhole_emitter_bump: u8, // 1 - // - // pub wormhole_sequence_key: Pubkey, // 32 - // pub wormhole_sequence_bump: u8, // 1 + // mango + // conf = price.agg.conf / price.agg.value + // => if conf > 0.10 => conf > 10% of price => filter out } // better to save pda keys on chain and always calculate/derive client side? // - if save pubkeys and don't use #[account(seeds=[...])] then need to manually call or save @@ -90,16 +76,17 @@ pub struct Propeller { // or save pda bumps impl Propeller { pub const LEN: usize = 1 + //bump - 4 + //nonce - 32 + //admin - 32 + //wormhole - 32 + //token_bridge + 1 + //is_paused + 32 + //governance_key + 32 + //prepared_governance_key + 8 + //governance_transition_ts + 32 + //pause_key 32 + //swim_usd_mint + 1 + //sender_bump + 1 + //redeemer_bump 32 + //marginal_price_pool 32 + //marginal_price_token_mint 1 + //marginal_price_pool_token_index - 1 + //sender_bump - 1 + //redeemer_bump 8 + //gas_kickstart_amount 8 + //propeller_fee 8 + // secp_verify_init_fee @@ -109,25 +96,17 @@ impl Propeller { 8 + //process_swim_payload_fee 8 + // complete_with_payload_cost 8 + // process_swim_payload_cost - // 8 + //propeller_min_transfer_amount - // 8 + //propeller_eth_min_transfer_amount 32 + //fee_vault - 32; //aggregator - // 32; // evm_routing_contract_address + 32 + //aggregator + 8; //max_staleness + // 32; // evm_routing_contract_address - pub fn wormhole(&self) -> Result { - // let pubkey = Pubkey::from_str(CORE_BRIDGE_ADDRESS) - // .map_err(|_| PropellerError::InvalidWormholeAddress)?; - // let pubkey = CORE_BRIDGE_ID; - let pubkey = crate::Wormhole::id(); - Ok(pubkey) + pub fn wormhole(&self) -> Pubkey { + crate::Wormhole::id() } - pub fn token_bridge(&self) -> Result { - // let pubkey = Pubkey::from_str(TOKEN_BRIDGE_ADDRESS) - // .map_err(|_| PropellerError::InvalidWormholeAddress)?; - let pubkey = crate::TokenBridge::id(); - Ok(pubkey) + pub fn token_bridge(&self) -> Pubkey { + crate::TokenBridge::id() } pub fn get_complete_native_with_payload_fee(&self) -> u64 { @@ -179,140 +158,27 @@ impl SwimPayloadMessage { 8 + // vaa_sequence 8 + // transfer_amount // swim_payload - RawSwimPayload::LEN; // swim_payload - // 1 + //version - // 32 + //owner - // 1 + // propeller_enabled - // 1 + // gas_kickstart - // 8 + // max_fee - // 2 + // target_token_id - // 16; // memo - // SwimPayload::LEN; // swim_payload -} - -//TODO: look into options for versioning. -// ex - metaplex metadata versioning - (probably not. its messy). -#[derive(PartialEq, Debug, Clone, Default)] -pub struct RawSwimPayload { - /* always required fields */ - pub swim_payload_version: u8, - pub owner: Address, - /* required for all propellerEnabled */ - pub propeller_enabled: bool, - pub gas_kickstart: bool, - pub max_fee: u64, - pub target_token_id: u16, - /* required for swim propeller */ - pub memo: [u8; 16], -} - -impl RawSwimPayload { - pub const LEN: usize = 1 + //version + 1 + //version 32 + //owner 1 + // propeller_enabled 1 + // gas_kickstart 8 + // max_fee 2 + // target_token_id 16; // memo -} - -#[repr(u8)] -#[derive(PartialEq, Debug, Clone)] -pub enum SwimPayloadVersion { - V0 = 0, - V1 = 1, -} - -impl AnchorDeserialize for RawSwimPayload { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - let mut v = Cursor::new(buf); - - //TODO: add some error handling/checking here if payload version is incorrect. - // https://stackoverflow.com/questions/28028854/how-do-i-match-enum-values-with-an-integer - let swim_payload_version = v.read_u8()?; - // if swim_payload_version == 1 { - // deseraialize_swim_payload_v1() - // } - if swim_payload_version != CURRENT_SWIM_PAYLOAD_VERSION { - return Err(std::io::Error::new(ErrorKind::InvalidInput, "Wrong Swim Payload Version".to_string())); - } - - let mut owner: [u8; 32] = Address::default(); - v.read_exact(&mut owner)?; - - /* optional fields */ - match v.read_u8() { - Ok(propeller_enabled_val) => { - let propeller_enabled = !(propeller_enabled_val == 0); - let gas_kickstart = !(v.read_u8()? == 0); - let max_fee = v.read_u64::()?; - let target_token_id = v.read_u16::()?; - // optional memo field - let mut memo: [u8; 16] = [0; 16]; - if let Ok(_) = v.read_exact(&mut memo) { - Ok(RawSwimPayload { - swim_payload_version, - owner, - propeller_enabled, - gas_kickstart, - max_fee, - target_token_id, - memo, - }) - } else { - Ok(RawSwimPayload { - swim_payload_version, - owner, - propeller_enabled, - gas_kickstart, - max_fee, - target_token_id, - memo: [0; 16], - }) - } - } - Err(error) => match error.kind() { - ErrorKind::UnexpectedEof => Ok(RawSwimPayload { swim_payload_version, owner, ..Default::default() }), - _ => return Err(error), - }, - } - } -} - -impl AnchorSerialize for RawSwimPayload { - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - // Payload ID - // writer.write_u8(self.swim_payload_version)?; - writer.write_u8(CURRENT_SWIM_PAYLOAD_VERSION)?; - - writer.write_all(&self.owner)?; - writer.write_u8(if self.propeller_enabled { 1 } else { 0 })?; - writer.write_u8(if self.gas_kickstart { 1 } else { 0 })?; - writer.write_u64::(self.max_fee)?; - writer.write_u16::(self.target_token_id)?; - writer.write_all(&self.memo)?; - Ok(()) - } -} -pub fn validate_marginal_prices_pool_accounts( - propeller: &Propeller, - marginal_price_pool: &Pubkey, - marginal_price_pool_token_account_mints: &[Pubkey; TOKEN_COUNT], -) -> Result<()> { - require_keys_eq!(*marginal_price_pool, propeller.marginal_price_pool); - let pool_token_index = propeller.marginal_price_pool_token_index as usize; - require_gt!(TOKEN_COUNT, pool_token_index, PropellerError::InvalidMarginalPricePoolAccounts); - require_keys_eq!( - marginal_price_pool_token_account_mints[pool_token_index], - propeller.marginal_price_pool_token_mint, - ); - Ok(()) + // RawSwimPayload::LEN; // swim_payload + // 1 + //version + // 32 + //owner + // 1 + // propeller_enabled + // 1 + // gas_kickstart + // 8 + // max_fee + // 2 + // target_token_id + // 16; // memo + // SwimPayload::LEN; // swim_payload } #[cfg(test)] mod tests { - use super::*; #[test] fn test_non_propeller_swim_payload() {} diff --git a/packages/solana-contracts/programs/propeller/src/token_bridge.rs b/packages/solana-contracts/programs/propeller/src/token_bridge.rs index 0cb7a79fc..c58f92fb1 100644 --- a/packages/solana-contracts/programs/propeller/src/token_bridge.rs +++ b/packages/solana-contracts/programs/propeller/src/token_bridge.rs @@ -1,8 +1,21 @@ use { - anchor_lang::{prelude::*, solana_program::pubkey}, - borsh::{BorshDeserialize, BorshSerialize}, + anchor_lang::{ + prelude::*, + solana_program::{pubkey}, + }, }; - +// mod token_bridge { +// use super::*; +// #[cfg(all(feature = "localnet", not(feature = "devnet"), not(feature = "mainnet")))] +// declare_id!("B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE"); +// +// // #[cfg(feature = "devnet")] +// #[cfg(all(feature = "devnet", not(feature = "localnet"), not(feature = "mainnet")))] +// declare_id!("DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe"); +// +// #[cfg(all(feature = "mainnet", not(feature = "localnet"), not(feature = "devnet")))] +// declare_id!("wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"); +// } #[derive(Debug, Clone)] pub struct TokenBridge; @@ -11,6 +24,7 @@ impl anchor_lang::Id for TokenBridge { // #[cfg(feature = "localnet")] #[cfg(all(feature = "localnet", not(feature = "devnet"), not(feature = "mainnet")))] fn id() -> Pubkey { + // declare_id!("B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE") pubkey!("B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE") } @@ -18,11 +32,13 @@ impl anchor_lang::Id for TokenBridge { #[cfg(all(feature = "devnet", not(feature = "localnet"), not(feature = "mainnet")))] fn id() -> Pubkey { + // declare_id!("DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe") pubkey!("DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe") } #[cfg(all(feature = "mainnet", not(feature = "localnet"), not(feature = "devnet")))] fn id() -> Pubkey { + // declare_id!("wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb") pubkey!("wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb") } } @@ -40,8 +56,6 @@ pub const TRANSFER_NATIVE_WITH_PAYLOAD_INSTRUCTION: u8 = 12; mod test { use { super::*, - // crate::TOKEN_BRIDGE_ID, - std::str::FromStr, }; #[test] diff --git a/packages/solana-contracts/programs/propeller/src/wormhole.rs b/packages/solana-contracts/programs/propeller/src/wormhole.rs index 3d17a44b7..3a0d13a8c 100644 --- a/packages/solana-contracts/programs/propeller/src/wormhole.rs +++ b/packages/solana-contracts/programs/propeller/src/wormhole.rs @@ -1,5 +1,5 @@ use { - crate::{Propeller, PropellerError, RawSwimPayload}, + crate::{constants::CURRENT_SWIM_PAYLOAD_VERSION}, anchor_lang::{prelude::*, solana_program::pubkey}, borsh::{BorshDeserialize, BorshSerialize}, byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}, @@ -8,7 +8,6 @@ use { std::{ io::{Cursor, ErrorKind, Read, Write}, ops::{Deref, DerefMut}, - str::FromStr, }, }; @@ -94,13 +93,48 @@ pub struct BridgeConfig { /// [From womrhole repo] #[repr(transparent)] -#[derive(Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct PostedMessageData { pub message: MessageData, } +impl AnchorSerialize for PostedMessageData { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + writer.write(b"msg")?; + BorshSerialize::serialize(&self.message, writer) + } +} + +impl AnchorDeserialize for PostedMessageData { + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + *buf = &buf[3..]; + Ok(PostedMessageData { message: ::deserialize(buf)? }) + } +} +impl anchor_lang::Owner for PostedMessageData { + fn owner() -> Pubkey { + Wormhole::id() + } +} + +impl anchor_lang::AccountDeserialize for PostedMessageData { + fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result { + *buf = &buf[3..]; + Ok(::deserialize(buf)?) + } +} +impl anchor_lang::AccountSerialize for PostedMessageData {} + +impl Deref for PostedMessageData { + type Target = PostedMessageData; + + fn deref(&self) -> &Self { + self + } +} + // #[derive(Debug, Default, BorshDeserialize, BorshSerialize)] -#[derive(Debug, AnchorDeserialize, AnchorSerialize, Clone)] +#[derive(Debug, AnchorDeserialize, AnchorSerialize, Clone, PartialEq, Eq)] pub struct MessageData { /// Header of the posted VAA pub vaa_version: u8, @@ -155,7 +189,102 @@ impl anchor_lang::Owner for MessageData { impl anchor_lang::AccountSerialize for MessageData {} -#[derive(PartialEq, Debug, Clone)] +// #[derive(PartialEq, Debug, Clone)] +// pub struct PayloadTransferWithPayload { +// pub message_type: u8, +// /// Amount being transferred (big-endian uint256) +// pub amount: U256, +// +// //TODO: safe to assume pubkey for these since this should only be used for +// // completeTransferWithPayload(e.g. solana side)? +// /// Address of the token. Left-zero-padded if shorter than 32 bytes +// pub token_address: Address, +// /// Chain ID of the token +// pub token_chain: ChainID, +// /// Address of the recipient. Left-zero-padded if shorter than 32 bytes +// pub to: Address, +// /// Chain ID of the recipient +// pub to_chain: ChainID, +// +// /// TODO: only this one needs to be `Address` since it should come from the evm contract/user +// /// Sender of the transaction +// pub from_address: Address, +// /// Arbitrary payload +// // pub payload: Vec, +// // pub payload: RawSwimPayload, +// } +// +// impl AnchorDeserialize for PayloadTransferWithPayload { +// fn deserialize(buf: &mut &[u8]) -> std::io::Result { +// let mut v = Cursor::new(buf); +// let message_type = v.read_u8()?; +// if message_type != 3 { +// // return Err(error!(PropellerError::InvalidPayloadTypeInVaa)).into() +// // return Err(ProgramError::BorshIoError("Wrong Payload Type".to_string()).into()); +// return Err(std::io::Error::new(ErrorKind::InvalidInput, "Wrong Payload Type".to_string())); +// // return Err(PropellerError::InvalidPayloadTypeInVaa); +// }; +// +// let mut am_data: [u8; 32] = [0; 32]; +// v.read_exact(&mut am_data)?; +// let amount = U256::from_big_endian(&am_data); +// +// let mut token_address = Address::default(); +// v.read_exact(&mut token_address)?; +// +// let token_chain = v.read_u16::()?; +// +// let mut to = Address::default(); +// v.read_exact(&mut to)?; +// +// let to_chain = v.read_u16::()?; +// +// let mut from_address = Address::default(); +// v.read_exact(&mut from_address)?; +// +// let mut payload = vec![]; +// v.read_to_end(&mut payload)?; +// let swim_payload = RawSwimPayload::deserialize(&mut payload.as_slice())?; +// +// Ok(PayloadTransferWithPayload { +// message_type, +// amount, +// token_address, +// token_chain, +// to, +// to_chain, +// from_address, +// payload: swim_payload, +// }) +// } +// } +// +// //TODO: probably not needed since we shouldn't be serializing any payload directly. +// // this would be handled in CPI +// impl AnchorSerialize for PayloadTransferWithPayload { +// fn serialize(&self, writer: &mut W) -> std::io::Result<()> { +// // Payload ID +// writer.write_u8(3)?; +// +// let mut am_data: [u8; 32] = [0; 32]; +// self.amount.to_big_endian(&mut am_data); +// writer.write_all(&am_data)?; +// +// writer.write_all(&self.token_address)?; +// writer.write_u16::(self.token_chain)?; +// writer.write_all(&self.to)?; +// writer.write_u16::(self.to_chain)?; +// +// writer.write_all(&self.from_address)?; +// +// AnchorSerialize::serialize(&self.payload, writer)?; +// // writer.write_all(self.payload.as_slice())?; +// +// Ok(()) +// } +// } + +#[derive(PartialEq, Eq, Debug, Clone)] pub struct PayloadTransferWithPayload { pub message_type: u8, /// Amount being transferred (big-endian uint256) @@ -177,7 +306,8 @@ pub struct PayloadTransferWithPayload { pub from_address: Address, /// Arbitrary payload // pub payload: Vec, - pub payload: RawSwimPayload, + // pub payload: RawSwimPayload, + pub payload: SwimPayload, } impl AnchorDeserialize for PayloadTransferWithPayload { @@ -210,7 +340,7 @@ impl AnchorDeserialize for PayloadTransferWithPayload { let mut payload = vec![]; v.read_to_end(&mut payload)?; - let swim_payload = RawSwimPayload::deserialize(&mut payload.as_slice())?; + let swim_payload = SwimPayload::deserialize(&mut payload.as_slice())?; Ok(PayloadTransferWithPayload { message_type, @@ -243,33 +373,20 @@ impl AnchorSerialize for PayloadTransferWithPayload { writer.write_all(&self.from_address)?; - AnchorSerialize::serialize(&self.payload, writer)?; + self.payload.serialize(writer)?; + // AnchorSerialize::serialize(&self.payload, writer)?; // writer.write_all(self.payload.as_slice())?; Ok(()) } } -impl AnchorSerialize for PostedMessageData { - fn serialize(&self, writer: &mut W) -> std::io::Result<()> { - writer.write(b"msg")?; - BorshSerialize::serialize(&self.message, writer) - } -} - -impl AnchorDeserialize for PostedMessageData { - fn deserialize(buf: &mut &[u8]) -> std::io::Result { - *buf = &buf[3..]; - Ok(PostedMessageData { message: ::deserialize(buf)? }) - } -} - pub fn get_message_data(vaa_account: &AccountInfo) -> Result { Ok(PostedMessageData::try_from_slice(&vaa_account.data.borrow())?.message) } pub fn get_transfer_with_payload_from_message_account(vaa_account: &AccountInfo) -> Result { - let message_data = get_message_data(&vaa_account)?; + let message_data = get_message_data(vaa_account)?; let payload_transfer_with_payload = deserialize_message_payload(&mut message_data.payload.as_slice())?; Ok(payload_transfer_with_payload) } @@ -278,9 +395,6 @@ pub fn deserialize_message_payload(buf: &mut &[u8]) -> Res Ok(T::deserialize(buf)?) } -pub fn deserialize_swim_payload(buf: &mut &[u8]) -> Result { - Ok(RawSwimPayload::deserialize(buf)?) -} /** Adding PostedVAA version here for parity */ #[repr(transparent)] @@ -411,3 +525,91 @@ pub fn hash_vaa(vaa: &PostVAAData) -> [u8; 32] { h.write_all(body.as_slice()).unwrap(); h.finalize().into() } + +#[derive(PartialEq, Eq, Debug, Clone, Default)] +pub struct SwimPayload { + //TOOD: should this come from propeller? + //required + pub swim_payload_version: u8, + pub owner: [u8; 32], + // required for all propellerEngines + pub propeller_enabled: Option, + pub gas_kickstart: Option, + pub max_fee: Option, + pub target_token_id: Option, + // required for SWIM propellerEngine + pub memo: Option<[u8; 16]>, +} + +impl AnchorSerialize for SwimPayload { + fn serialize(&self, writer: &mut W) -> std::io::Result<()> { + // Payload ID + // writer.write_u8(self.swim_payload_version)?; + writer.write_u8(self.swim_payload_version)?; + writer.write_all(&self.owner)?; + + if self.propeller_enabled.is_some() { + writer.write_u8(1)?; + writer.write_u8(self.gas_kickstart.unwrap() as u8)?; + writer.write_u64::(self.max_fee.unwrap())?; + writer.write_u16::(self.target_token_id.unwrap())?; + if self.memo.is_some() { + writer.write_all(&self.memo.unwrap())?; + } + } + Ok(()) + } +} + +impl AnchorDeserialize for SwimPayload { + fn deserialize(buf: &mut &[u8]) -> std::io::Result { + let mut v = Cursor::new(buf); + + //TODO: add some error handling/checking here if payload version is incorrect. + // https://stackoverflow.com/questions/28028854/how-do-i-match-enum-values-with-an-integer + let swim_payload_version = v.read_u8()?; + if swim_payload_version != CURRENT_SWIM_PAYLOAD_VERSION { + return Err(std::io::Error::new(ErrorKind::InvalidInput, "Wrong Swim Payload Version".to_string())); + } + + let mut owner: [u8; 32] = Address::default(); + v.read_exact(&mut owner)?; + + /* optional fields */ + match v.read_u8() { + Ok(propeller_enabled_val) => { + let propeller_enabled = propeller_enabled_val != 0; + let gas_kickstart = v.read_u8()? != 0; + let max_fee = v.read_u64::()?; + let target_token_id = v.read_u16::()?; + // optional memo field + let mut memo: [u8; 16] = [0; 16]; + if let Ok(_) = v.read_exact(&mut memo) { + Ok(SwimPayload { + swim_payload_version, + owner, + propeller_enabled: Some(propeller_enabled), + gas_kickstart: Some(gas_kickstart), + max_fee: Some(max_fee), + target_token_id: Some(target_token_id), + memo: Some(memo), + }) + } else { + Ok(SwimPayload { + swim_payload_version, + owner, + propeller_enabled: Some(propeller_enabled), + gas_kickstart: Some(gas_kickstart), + max_fee: Some(max_fee), + target_token_id: Some(target_token_id), + memo: None, + }) + } + } + Err(error) => match error.kind() { + ErrorKind::UnexpectedEof => Ok(SwimPayload { swim_payload_version, owner, ..Default::default() }), + _ => Err(error), + }, + } + } +} diff --git a/packages/solana-contracts/programs/two-pool/src/amp_factor.rs b/packages/solana-contracts/programs/two-pool/src/amp_factor.rs index ba2cc6222..d8fa85776 100644 --- a/packages/solana-contracts/programs/two-pool/src/amp_factor.rs +++ b/packages/solana-contracts/programs/two-pool/src/amp_factor.rs @@ -1,8 +1,6 @@ use { crate::{decimal::DecimalU64, error::PoolError, DecimalU64Anchor}, anchor_lang::{prelude::*, solana_program::clock::UnixTimestamp}, - rust_decimal::prelude::*, - rust_decimal_macros::dec, std::ops::{Add, Sub}, }; diff --git a/packages/solana-contracts/programs/two-pool/src/common.rs b/packages/solana-contracts/programs/two-pool/src/common.rs index a77692b66..c43d1bb82 100644 --- a/packages/solana-contracts/programs/two-pool/src/common.rs +++ b/packages/solana-contracts/programs/two-pool/src/common.rs @@ -1,4 +1,8 @@ -use {arrayvec::ArrayVec, std::fmt::Debug}; +use { + crate::{decimal::U128, TOKEN_COUNT}, + arrayvec::ArrayVec, + std::fmt::Debug, +}; //final unwraps are safe because we know that there is enough capacity @@ -11,3 +15,55 @@ pub fn create_result_array( ) -> Result<[T; SIZE], E> { Ok((0..SIZE).into_iter().map(closure).collect::, _>>()?.into_inner().unwrap()) } + +pub fn to_equalized(value: u64, equalizer: u8) -> U128 { + if equalizer > 0 { + U128::from(value) * U128::ten_to_the(equalizer) + } else { + U128::from(value) + } +} + +pub fn from_equalized(value: U128, equalizer: u8) -> u64 { + if equalizer > 0 { + ((value + U128::ten_to_the(equalizer - 1) * 5u64) / U128::ten_to_the(equalizer)).as_u64() + } else { + value.as_u64() + } +} + +pub fn array_equalize(amounts: [u64; TOKEN_COUNT], equalizers: [u8; TOKEN_COUNT]) -> [U128; TOKEN_COUNT] { + amounts + .iter() + .zip(equalizers.iter()) + .map(|(&amount, &equalizer)| to_equalized(amount, equalizer)) + .collect::>() + .as_slice() + .try_into() + .unwrap() +} + +/// `result_from_equalized` takes in a user's amount, the user's equalizer, the governance mint amount, +/// the lp decimal equalizer, and the latest depth, and returns the user's amount, the governance mint +/// amount, and the latest depth +/// +/// Arguments: +/// +/// * `user_amount`: The amount of tokens the user is staking +/// * `user_equalizer`: The equalizer of the user's token. +/// * `governance_mint_amount`: The amount of governance tokens that will be minted to the user. +/// * `lp_decimal_equalizer`: The equalizer for the LP token. should always be pool_state.lp_decimal_equalizer +/// * `latest_depth`: The amount of liquidity in the pool. +pub fn result_from_equalized( + user_amount: U128, + user_equalizer: u8, + governance_mint_amount: U128, + lp_decimal_equalizer: u8, + latest_depth: U128, +) -> (u64, u64, u128) { + ( + from_equalized(user_amount, user_equalizer), + from_equalized(governance_mint_amount, lp_decimal_equalizer), + latest_depth.as_u128(), + ) +} diff --git a/packages/solana-contracts/programs/two-pool/src/decimal.rs b/packages/solana-contracts/programs/two-pool/src/decimal.rs index 8f2942096..02b94825b 100644 --- a/packages/solana-contracts/programs/two-pool/src/decimal.rs +++ b/packages/solana-contracts/programs/two-pool/src/decimal.rs @@ -13,7 +13,7 @@ use { crate::PoolError, - borsh::{BorshDeserialize, BorshSchema, BorshSerialize}, + borsh::BorshSchema, std::{ cmp, cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}, @@ -63,7 +63,7 @@ const U128_MAX_DECIMALS: usize = 39; const TEN_TO_THE: [u128; U128_MAX_DECIMALS] = create_ten_to_the(); const fn create_ten_to_the() -> [u128; U128_MAX_DECIMALS] { - let mut ttt = [1 as u128; U128_MAX_DECIMALS]; + let mut ttt = [1_u128; U128_MAX_DECIMALS]; let mut i = 1; loop { //const functions can't use for loops @@ -118,7 +118,7 @@ pub const BIT_TO_DEC_ARRAY: [u8; BIT_TO_DEC_SIZE] = create_bit_to_dec_array(); const fn create_bit_to_dec_array() -> [u8; BIT_TO_DEC_SIZE] { let mut btd = [0; BIT_TO_DEC_SIZE]; let mut pot: u128 = 10; - let mut i = 1 as usize; //we start with the second iteration + let mut i = 1_usize; //we start with the second iteration loop { //const functions can't use for loops let jump = ((1 << i as u128) / pot) as u8; diff --git a/packages/solana-contracts/programs/two-pool/src/error.rs b/packages/solana-contracts/programs/two-pool/src/error.rs index 7fcd09775..6ef147de6 100644 --- a/packages/solana-contracts/programs/two-pool/src/error.rs +++ b/packages/solana-contracts/programs/two-pool/src/error.rs @@ -74,6 +74,8 @@ pub enum PoolError { InvalidTokenIndex, #[msg("Invalid Pause Key")] InvalidPauseKey, + #[msg("Invalid New Pause Key")] + InvalidNewPauseKey, #[msg("Not a valid Switchboard account")] InvalidSwitchboardAccount, #[msg("Switchboard feed has not been updated in 5 minutes")] @@ -84,6 +86,10 @@ pub enum PoolError { MaxDecimalsExceeded, #[msg("Conversion error")] ConversionError, + #[msg("Burn amount exceeds lp total supply")] + BurnAmountExceedsTotalSupply, + #[msg("Invalid Upcoming Governance Key")] + InvalidUpcomingGovernanceKey, } // impl From for ProgramError { diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/add.rs b/packages/solana-contracts/programs/two-pool/src/instructions/add.rs deleted file mode 100644 index 0d6562ae6..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/add.rs +++ /dev/null @@ -1,405 +0,0 @@ -use { - crate::{decimal::U128, error::*, gen_pool_signer_seeds, invariant::Invariant, TwoPool, TOKEN_COUNT}, - anchor_lang::{ - prelude::*, - solana_program::{ - borsh::try_from_slice_unchecked, - instruction::Instruction, - program::{get_return_data, invoke, invoke_signed}, - program_option::COption, - system_instruction::transfer, - sysvar::SysvarId, - }, - }, - anchor_spl::{ - associated_token::get_associated_token_address, - token::{self, Mint, Token, TokenAccount}, - }, - std::iter::zip, -}; - -#[derive(Accounts)] -pub struct Add<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump - )] - pub pool: Box>, - // /// TODO: could be removed if initialized with pool_v2 - // /// CHECK: checked in CPI - // pub pool_auth: UncheckedAccount<'info>, - #[account( - mut, - address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[0]), - constraint = pool_token_account_0.key() == pool.token_keys[0], - )] - pub pool_token_account_0: Box>, - #[account( - mut, - address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[1]), - constraint = pool_token_account_1.key() == pool.token_keys[1], - )] - pub pool_token_account_1: Box>, - #[account( - mut, - address = pool.lp_mint_key, - )] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint, - address = pool.governance_fee_key, - )] - pub governance_fee: Box>, - ///CHECK: checked in CPI - pub user_transfer_authority: Signer<'info>, - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - - //TODO: vanilla solana we didn't pass/ask for this account - // w/ user_transfer_authority it's not explicitly needed. - // is there any type of checks that we HAVE to do related to payer? - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct AddParams { - pub input_amounts: [u64; TOKEN_COUNT], - pub minimum_mint_amount: u64, -} - -impl<'info> Add<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - let pool_state = &ctx.accounts.pool; - require!(!pool_state.is_paused, PoolError::PoolIsPaused); - require_keys_eq!( - ctx.accounts.pool_token_account_0.key(), - pool_state.token_keys[0], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!( - ctx.accounts.pool_token_account_1.key(), - pool_state.token_keys[1], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!(ctx.accounts.lp_mint.key(), pool_state.lp_mint_key, PoolError::InvalidMintAccount); - require_keys_eq!( - ctx.accounts.governance_fee.key(), - pool_state.governance_fee_key, - PoolError::InvalidGovernanceFeeAccount - ); - - // let pool_state_acct = &ctx.accounts.pool_state; - // let pool: two_pool::state::PoolState<{two_pool::TOKEN_COUNT}> = two_pool::state::PoolState::try_from_slice(&pool_state_acct.data.borrow())?; - // constraint = lp_mint.key() == propeller.token_bridge_mint @ PropellerError::InvalidMint - msg!("finished accounts context check"); - // let - Ok(()) - } -} - -// -pub fn handle_add( - ctx: Context, - params: AddParams, - // input_amounts: [u64; TOKEN_COUNT], - // minimum_mint_amount: u64, -) -> Result { - let input_amounts = params.input_amounts; - let minimum_mint_amount = params.minimum_mint_amount; - require!(input_amounts.iter().any(|&x| x > 0), PoolError::AddRequiresAtLeastOneToken); - let lp_total_supply = ctx.accounts.lp_mint.supply; - //initial add to pool must add all tokens - if lp_total_supply == 0 { - for i in 0..TOKEN_COUNT { - require_gt!(input_amounts[i], 0u64, PoolError::InitialAddRequiresAllTokens); - } - } - - let pool = &ctx.accounts.pool; - let user_token_accounts = [&ctx.accounts.user_token_account_0, &ctx.accounts.user_token_account_1]; - let pool_token_accounts = [&ctx.accounts.pool_token_account_0, &ctx.accounts.pool_token_account_1]; - // let pool_balances = pool_token_accounts - // .iter() - // .map(|account| account.amount) - // .collect::>() - // .as_slice() - // .try_into().unwrap(); - let pool_balances = [ctx.accounts.pool_token_account_0.amount, ctx.accounts.pool_token_account_1.amount]; - let current_ts = Clock::get()?.unix_timestamp; - require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); - let (user_amount, governance_mint_amount, latest_depth) = Invariant::::add( - &array_equalize(input_amounts, pool.token_decimal_equalizers), - &array_equalize(pool_balances, pool.token_decimal_equalizers), - pool.amp_factor.get(current_ts), - pool.lp_fee.get(), - pool.governance_fee.get(), - to_equalized(lp_total_supply, pool.lp_decimal_equalizer), - pool.previous_depth.into(), - )?; - let (mint_amount, governance_mint_amount, latest_depth) = result_from_equalized( - user_amount, - pool.lp_decimal_equalizer, - governance_mint_amount, - pool.lp_decimal_equalizer, - latest_depth, - ); - require_gte!(mint_amount, minimum_mint_amount, PoolError::OutsideSpecifiedLimits); - let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); - for i in 0..TOKEN_COUNT { - let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); - if input_amounts[i] > 0 { - token::transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: user_token_account.to_account_info(), - to: pool_token_account.to_account_info(), - authority: ctx.accounts.user_transfer_authority.to_account_info(), - }, - ), - input_amounts[i], - )?; - } - } - - token::mint_to( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::MintTo { - // source - mint: ctx.accounts.lp_mint.to_account_info(), - to: ctx.accounts.user_lp_token_account.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - mint_amount, - )?; - - if governance_mint_amount > 0 { - token::mint_to( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::MintTo { - // source - mint: ctx.accounts.lp_mint.to_account_info(), - to: ctx.accounts.governance_fee.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - governance_mint_amount, - )?; - } - - let pool_state = &mut ctx.accounts.pool; - pool_state.previous_depth = latest_depth; - Ok(mint_amount) - // msg!("add_and_wormhole_transfer"); - // handle_approve(&ctx, &pool_add_params)?; - // - // let mint_amount = handle_add_liquidity(&ctx, pool_add_params)?; - // msg!("finished add_liquidity. mint_amount: {}", mint_amount); - // - // revoke(&ctx) - // - // // Ok(mint_amount) -} - -pub fn to_equalized(value: u64, equalizer: u8) -> U128 { - if equalizer > 0 { - U128::from(value) * U128::ten_to_the(equalizer) - } else { - U128::from(value) - } -} - -pub fn from_equalized(value: U128, equalizer: u8) -> u64 { - if equalizer > 0 { - ((value + U128::ten_to_the(equalizer - 1) * 5u64) / U128::ten_to_the(equalizer)).as_u64() - } else { - value.as_u64() - } -} - -pub fn array_equalize(amounts: [u64; TOKEN_COUNT], equalizers: [u8; TOKEN_COUNT]) -> [U128; TOKEN_COUNT] { - amounts - .iter() - .zip(equalizers.iter()) - .map(|(&amount, &equalizer)| to_equalized(amount, equalizer)) - .collect::>() - .as_slice() - .try_into() - .unwrap() -} - -/// `result_from_equalized` takes in a user's amount, the user's equalizer, the governance mint amount, -/// the lp decimal equalizer, and the latest depth, and returns the user's amount, the governance mint -/// amount, and the latest depth -/// -/// Arguments: -/// -/// * `user_amount`: The amount of tokens the user is staking -/// * `user_equalizer`: The equalizer of the user's token. -/// * `governance_mint_amount`: The amount of governance tokens that will be minted to the user. -/// * `lp_decimal_equalizer`: The equalizer for the LP token. should always be pool_state.lp_decimal_equalizer -/// * `latest_depth`: The amount of liquidity in the pool. -pub fn result_from_equalized( - user_amount: U128, - user_equalizer: u8, - governance_mint_amount: U128, - lp_decimal_equalizer: u8, - latest_depth: U128, -) -> (u64, u64, u128) { - ( - from_equalized(user_amount, user_equalizer), - from_equalized(governance_mint_amount, lp_decimal_equalizer), - latest_depth.as_u128(), - ) -} -// -// pub fn handle_approve( -// ctx: &Context, -// pool_add_params: &AddParams, -// ) -> Result<()> { -// msg!("[handle_approve]: approve"); -// token::approve( -// CpiContext::new( -// ctx.accounts.token_program.to_account_info(), -// token::Approve { -// // source -// to: ctx.accounts.user_token_account_0.to_account_info(), -// delegate: ctx.accounts.pool_auth.to_account_info(), -// authority: ctx.accounts.payer.to_account_info(), -// }, -// ), -// pool_add_params.input_amounts[0], -// )?; -// -// token::approve( -// CpiContext::new( -// ctx.accounts.token_program.to_account_info(), -// token::Approve { -// // source -// to: ctx.accounts.user_token_account_1.to_account_info(), -// delegate: ctx.accounts.pool_auth.to_account_info(), -// authority: ctx.accounts.payer.to_account_info(), -// }, -// ), -// pool_add_params.input_amounts[1], -// )?; -// msg!("[handle_approve]: finished approves"); -// Ok(()) -// } -// -// pub fn handle_add_liquidity( -// ctx: &Context, -// pool_add_params: AddParams -// ) -> Result { -// let pool_program = &ctx.accounts.pool_program; -// let pool = &ctx.accounts.pool_state; -// let pool_auth = &ctx.accounts.pool_auth; -// let pool_token_account_0 = &ctx.accounts.pool_token_account_0; -// let pool_token_account_1 = &ctx.accounts.pool_token_account_1; -// let lp_mint = &ctx.accounts.lp_mint; -// let governance_fee = &ctx.accounts.governance_fee; -// // let user_transfer_auth = &ctx.accounts.user_transfer_authority; -// let user_token_account_0 = &ctx.accounts.user_token_account_0; -// let user_token_account_1 = &ctx.accounts.user_token_account_1; -// let user_lp_token_account = &ctx.accounts.user_lp_token_account; -// // let token_program = &self.token_program; -// -// let add_defi_ix = two_pool::instruction::DeFiInstruction::Add{ -// input_amounts: pool_add_params.input_amounts, -// minimum_mint_amount: pool_add_params.minimum_mint_amount -// }; -// let add_ix = two_pool::instruction::create_defi_ix( -// add_defi_ix, -// &pool_program.key(), -// &pool.key(), -// &pool_auth.key(), -// &[ -// pool_token_account_0.key(), -// pool_token_account_1.key() -// ], -// &lp_mint.key(), -// &governance_fee.key(), -// &pool_auth.key(), -// // &user_transfer_auth.key(), -// &[ -// user_token_account_0.key(), -// user_token_account_1.key() -// ], -// Some(&user_lp_token_account.key()), -// )?; -// invoke( -// &add_ix, -// &ctx.accounts.to_account_infos(), -// )?; -// let (program_id, data) = get_return_data().unwrap(); -// //.unwrap_or_else(PropellerError::InvalidCpiReturnValue); -// require_keys_eq!(program_id, ctx.accounts.pool_program.key(), PropellerError::InvalidCpiReturnProgramId); -// Ok(u64::try_from_slice(&data).map_err(|_| PropellerError::InvalidCpiReturnValue)?) -// } -// -// pub fn revoke(ctx: &Context) -> Result<()> { -// msg!("[revoke]: revoke"); -// token::revoke( -// CpiContext::new( -// ctx.accounts.token_program.to_account_info(), -// token::Revoke { -// // source -// source: ctx.accounts.user_token_account_0.to_account_info(), -// authority: ctx.accounts.payer.to_account_info(), -// }, -// ) -// )?; -// token::revoke( -// CpiContext::new( -// ctx.accounts.token_program.to_account_info(), -// token::Revoke { -// // source -// source: ctx.accounts.user_token_account_1.to_account_info(), -// authority: ctx.accounts.payer.to_account_info(), -// }, -// ) -// )?; -// msg!("Revoked delegate authority for user_token_accounts"); -// token::revoke( -// CpiContext::new( -// ctx.accounts.token_program.to_account_info(), -// token::Revoke { -// // source -// source: ctx.accounts.user_lp_token_account.to_account_info(), -// authority: ctx.accounts.payer.to_account_info(), -// }, -// ) -// )?; -// msg!("Revoked delegate authority for user_lp_token_account"); -// msg!("[revoke]: finished revoke"); -// Ok(()) -// } diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/defi/add_or_remove.rs b/packages/solana-contracts/programs/two-pool/src/instructions/defi/add_or_remove.rs new file mode 100644 index 000000000..978cc2e2f --- /dev/null +++ b/packages/solana-contracts/programs/two-pool/src/instructions/defi/add_or_remove.rs @@ -0,0 +1,293 @@ +use { + crate::{ + common::{self, array_equalize, result_from_equalized, to_equalized}, + decimal::DecimalU64, + defi::swap::*, + error::*, + gen_pool_signer_seeds, get_current_ts, + invariant::Invariant, + TwoPool, TOKEN_COUNT, + }, + anchor_lang::prelude::*, + anchor_spl::{ + associated_token::get_associated_token_address, + token::{self, Mint, Token, TokenAccount}, + }, + std::iter::zip, +}; + +#[derive(Accounts)] +pub struct AddOrRemove<'info> { + pub swap: Swap<'info>, + + #[account( + mut, + token::mint = swap.lp_mint, + )] + pub user_lp_token_account: Box>, +} + +impl<'info> AddOrRemove<'info> { + fn burn_user_lp_tokens(&self, burn_amount: u64) -> Result<()> { + token::burn( + CpiContext::new( + self.swap.token_program.to_account_info(), + token::Burn { + mint: self.swap.lp_mint.to_account_info(), + from: self.user_lp_token_account.to_account_info(), + authority: self.swap.user_transfer_authority.to_account_info(), + }, + ), + burn_amount, + ) + } +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct AddParams { + pub input_amounts: [u64; TOKEN_COUNT], + pub minimum_mint_amount: u64, +} +pub fn handle_add( + ctx: Context, + params: AddParams, + // input_amounts: [u64; TOKEN_COUNT], + // minimum_mint_amount: u64, +) -> Result { + let pool_state = &ctx.accounts.swap.pool; + require!(!pool_state.is_paused, PoolError::PoolIsPaused); + + let input_amounts = params.input_amounts; + let minimum_mint_amount = params.minimum_mint_amount; + require!(input_amounts.iter().any(|&x| x > 0), PoolError::AddRequiresAtLeastOneToken); + let lp_total_supply = ctx.accounts.swap.lp_mint.supply; + //initial add to pool must add all tokens + if lp_total_supply == 0 { + require!(input_amounts.iter().all(|&x| x > 0), PoolError::InitialAddRequiresAllTokens); + } + + let pool = &ctx.accounts.swap.pool; + let user_token_accounts = &[&ctx.accounts.swap.user_token_account_0, &ctx.accounts.swap.user_token_account_1]; + let pool_token_accounts = &[&ctx.accounts.swap.pool_token_account_0, &ctx.accounts.swap.pool_token_account_1]; + let pool_balances = [ctx.accounts.swap.pool_token_account_0.amount, ctx.accounts.swap.pool_token_account_1.amount]; + let current_ts = Clock::get()?.unix_timestamp; + require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); + let (user_amount, governance_mint_amount, latest_depth) = Invariant::::add( + &common::array_equalize(input_amounts, pool.token_decimal_equalizers), + &common::array_equalize(pool_balances, pool.token_decimal_equalizers), + pool.amp_factor.get(current_ts), + pool.lp_fee.get(), + pool.governance_fee.get(), + common::to_equalized(lp_total_supply, pool.lp_decimal_equalizer), + pool.previous_depth.into(), + )?; + let (mint_amount, governance_mint_amount, latest_depth) = common::result_from_equalized( + user_amount, + pool.lp_decimal_equalizer, + governance_mint_amount, + pool.lp_decimal_equalizer, + latest_depth, + ); + require_gte!(mint_amount, minimum_mint_amount, PoolError::OutsideSpecifiedLimits); + let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); + let user_transfer_authority = &ctx.accounts.swap.user_transfer_authority; + for input_amount in input_amounts.iter().take(TOKEN_COUNT) { + let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); + ctx.accounts.swap.transfer_from_user_to_pool( + user_token_account, + user_transfer_authority, + pool_token_account, + *input_amount, + )?; + } + + // mint user lp tokens + ctx.accounts.swap.mint_lp_tokens(&ctx.accounts.user_lp_token_account, mint_amount)?; + + // mint governance fee + ctx.accounts.swap.mint_lp_tokens(&ctx.accounts.swap.governance_fee, governance_mint_amount)?; + + ctx.accounts.swap.update_previous_depth(latest_depth)?; + Ok(mint_amount) +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct RemoveExactBurnParams { + pub exact_burn_amount: u64, + pub output_token_index: u8, + pub minimum_output_amount: u64, +} + +pub fn handle_remove_exact_burn( + ctx: Context, + remove_exact_burn_params: RemoveExactBurnParams, +) -> Result { + let output_token_index = remove_exact_burn_params.output_token_index as usize; + let exact_burn_amount = remove_exact_burn_params.exact_burn_amount; + let lp_total_supply = ctx.accounts.swap.lp_mint.supply; + require!(output_token_index < TOKEN_COUNT, PoolError::InvalidRemoveExactBurnParameters); + require_gt!(exact_burn_amount, 0u64, PoolError::InvalidRemoveExactBurnParameters); + require_gt!(lp_total_supply, exact_burn_amount, PoolError::InvalidRemoveExactBurnParameters); + + let pool = &ctx.accounts.swap.pool; + require!(!pool.is_paused, PoolError::PoolIsPaused); + let user_token_accounts = [&ctx.accounts.swap.user_token_account_0, &ctx.accounts.swap.user_token_account_1]; + let pool_token_accounts = [&ctx.accounts.swap.pool_token_account_0, &ctx.accounts.swap.pool_token_account_1]; + let pool_balances = [ctx.accounts.swap.pool_token_account_0.amount, ctx.accounts.swap.pool_token_account_1.amount]; + + let current_ts = Clock::get()?.unix_timestamp; + require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); + let (user_amount, governance_mint_amount, latest_depth) = Invariant::::remove_exact_burn( + to_equalized(exact_burn_amount, pool.lp_decimal_equalizer), + output_token_index, + &array_equalize(pool_balances, pool.token_decimal_equalizers), + pool.amp_factor.get(current_ts), + pool.lp_fee.get(), + pool.governance_fee.get(), + to_equalized(lp_total_supply, pool.lp_decimal_equalizer), + pool.previous_depth.into(), + )?; + let (output_amount, governance_mint_amount, latest_depth) = result_from_equalized( + user_amount, + pool.token_decimal_equalizers[output_token_index], + governance_mint_amount, + pool.lp_decimal_equalizer, + latest_depth, + ); + let minimum_output_amount = remove_exact_burn_params.minimum_output_amount; + require_gte!(output_amount, minimum_output_amount, PoolError::OutsideSpecifiedLimits); + + // let _token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); + ctx.accounts.burn_user_lp_tokens(exact_burn_amount)?; + + let user_output_token_account = user_token_accounts[output_token_index]; + let pool_output_token_account = pool_token_accounts[output_token_index]; + ctx.accounts.swap.transfer_from_pool_to_user( + pool_output_token_account, + user_output_token_account, + output_amount, + )?; + + ctx.accounts.swap.mint_lp_tokens(&ctx.accounts.swap.governance_fee, governance_mint_amount)?; + + ctx.accounts.swap.update_previous_depth(latest_depth)?; + Ok(output_amount) +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct RemoveExactOutputParams { + pub maximum_burn_amount: u64, + pub exact_output_amounts: [u64; TOKEN_COUNT], +} + +pub fn handle_remove_exact_output( + ctx: Context, + remove_exact_output_params: RemoveExactOutputParams, +) -> Result> { + let maximum_burn_amount = remove_exact_output_params.maximum_burn_amount; + let exact_output_amounts = remove_exact_output_params.exact_output_amounts; + let pool = &ctx.accounts.swap.pool; + require!(!pool.is_paused, PoolError::PoolIsPaused); + let user_token_accounts = &[&ctx.accounts.swap.user_token_account_0, &ctx.accounts.swap.user_token_account_1]; + let pool_token_accounts = &[&ctx.accounts.swap.pool_token_account_0, &ctx.accounts.swap.pool_token_account_1]; + let pool_balances = [ctx.accounts.swap.pool_token_account_0.amount, ctx.accounts.swap.pool_token_account_1.amount]; + + let lp_total_supply = ctx.accounts.swap.lp_mint.supply; + + require!(exact_output_amounts.iter().any(|amount| *amount > 0), PoolError::InvalidRemoveExactOutputParameters); + require_gt!(maximum_burn_amount, 0u64, PoolError::InvalidRemoveExactOutputParameters); + let are_output_amounts_valid = exact_output_amounts + .iter() + .zip(pool_balances.iter()) + .all(|(output_amount, pool_balance)| *output_amount < *pool_balance); + require!(are_output_amounts_valid, PoolError::InvalidRemoveExactOutputParameters); + + let current_ts = get_current_ts()?; + + let (burn_amount, governance_mint_amount, latest_depth) = Invariant::::remove_exact_output( + &array_equalize(exact_output_amounts, pool.token_decimal_equalizers), + &array_equalize(pool_balances, pool.token_decimal_equalizers), + pool.amp_factor.get(current_ts), + pool.lp_fee.get(), + pool.governance_fee.get(), + to_equalized(lp_total_supply, pool.lp_decimal_equalizer), + pool.previous_depth.into(), + )?; + let (burn_amount, governance_mint_amount, latest_depth) = result_from_equalized( + burn_amount, + pool.lp_decimal_equalizer, + governance_mint_amount, + pool.lp_decimal_equalizer, + latest_depth, + ); + + let maximum_burn_amount = remove_exact_output_params.maximum_burn_amount; + require_gte!(maximum_burn_amount, burn_amount, PoolError::OutsideSpecifiedLimits); + require_gte!(lp_total_supply, burn_amount, PoolError::BurnAmountExceedsTotalSupply); + + let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); + ctx.accounts.burn_user_lp_tokens(burn_amount)?; + + for i in 0..TOKEN_COUNT { + let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); + ctx.accounts.swap.transfer_from_pool_to_user( + pool_token_account, + user_token_account, + exact_output_amounts[i], + )?; + } + ctx.accounts.swap.mint_lp_tokens(&ctx.accounts.swap.governance_fee, governance_mint_amount)?; + + ctx.accounts.swap.update_previous_depth(latest_depth)?; + Ok(exact_output_amounts.into()) +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct RemoveUniformParams { + pub exact_burn_amount: u64, + pub minimum_output_amounts: [u64; TOKEN_COUNT], +} + +pub fn handle_remove_uniform( + ctx: Context, + remove_uniform_params: RemoveUniformParams, +) -> Result> { + let exact_burn_amount = remove_uniform_params.exact_burn_amount; + let minimum_output_amounts = remove_uniform_params.minimum_output_amounts; + let lp_total_supply = ctx.accounts.swap.lp_mint.supply; + require_gt!(exact_burn_amount, 0u64, PoolError::InvalidRemoveUniformParameters); + require_gte!(lp_total_supply, exact_burn_amount, PoolError::InvalidRemoveUniformParameters); + + //Note: this is the only ix that can be called even if the pool is paused. + let pool = &ctx.accounts.swap.pool; + let user_token_accounts = &[&ctx.accounts.swap.user_token_account_0, &ctx.accounts.swap.user_token_account_1]; + let pool_token_accounts = &[&ctx.accounts.swap.pool_token_account_0, &ctx.accounts.swap.pool_token_account_1]; + let pool_balances = [ctx.accounts.swap.pool_token_account_0.amount, ctx.accounts.swap.pool_token_account_1.amount]; + + let user_share = DecimalU64::from(exact_burn_amount) / lp_total_supply; + //u64 can store 19 decimals, previous_depth can theoretically go up to TOKEN_COUNT * u64::MAX + //hence, just to be safe, we allow for previous depth to have up to 20 decimals + //therefore we can only multiply with a number with at most 18 decimals to stay within + //the 38 max decimals range of u128 + const DECIMAL_UPSHIFT: u32 = 18; + let user_depth = (pool.previous_depth * ((user_share * 10u64.pow(DECIMAL_UPSHIFT)).trunc() as u128)) + / 10u128.pow(DECIMAL_UPSHIFT); + let latest_depth = pool.previous_depth - user_depth; + + let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); + + let mut output_amounts = vec![]; + for i in 0..TOKEN_COUNT { + let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); + let output_amount = (pool_balances[i] * user_share).trunc(); + output_amounts.push(output_amount); + require_gte!(output_amount, minimum_output_amounts[i], PoolError::OutsideSpecifiedLimits); + ctx.accounts.swap.transfer_from_pool_to_user(&pool_token_account, &user_token_account, output_amount)?; + } + + ctx.accounts.burn_user_lp_tokens(exact_burn_amount)?; + + ctx.accounts.swap.update_previous_depth(latest_depth)?; + + Ok(output_amounts) +} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/defi/mod.rs b/packages/solana-contracts/programs/two-pool/src/instructions/defi/mod.rs new file mode 100644 index 000000000..c82e6e422 --- /dev/null +++ b/packages/solana-contracts/programs/two-pool/src/instructions/defi/mod.rs @@ -0,0 +1,3 @@ +pub use {add_or_remove::*, swap::*}; +pub mod add_or_remove; +pub mod swap; diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/defi/swap.rs b/packages/solana-contracts/programs/two-pool/src/instructions/defi/swap.rs new file mode 100644 index 000000000..4ee4b8fc6 --- /dev/null +++ b/packages/solana-contracts/programs/two-pool/src/instructions/defi/swap.rs @@ -0,0 +1,310 @@ +use { + crate::{ + common::{self, array_equalize, result_from_equalized, to_equalized}, + error::*, + gen_pool_signer_seeds, get_current_ts, + invariant::Invariant, + AddParams, TwoPool, TOKEN_COUNT, + }, + anchor_lang::prelude::*, + anchor_spl::{ + associated_token::get_associated_token_address, + token::{self, Mint, Token, TokenAccount}, + }, + std::iter::zip, +}; + +#[derive(Accounts)] +pub struct Swap<'info> { + #[account( + mut, + seeds = [ + b"two_pool".as_ref(), + pool_token_account_0.mint.as_ref(), + pool_token_account_1.mint.as_ref(), + lp_mint.key().as_ref(), + ], + bump = pool.bump + )] + pub pool: Box>, + #[account( + mut, + address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[0]) @ PoolError::PoolTokenAccountExpected, + constraint = pool_token_account_0.key() == pool.token_keys[0] @ PoolError::PoolTokenAccountExpected, + )] + pub pool_token_account_0: Box>, + #[account( + mut, + address = get_associated_token_address(&pool.key(), &pool.token_mint_keys[1]) @ PoolError::PoolTokenAccountExpected, + constraint = pool_token_account_1.key() == pool.token_keys[1] @ PoolError::PoolTokenAccountExpected, + )] + pub pool_token_account_1: Box>, + + #[account(mut, address = pool.lp_mint_key @ PoolError::InvalidMintAccount)] + pub lp_mint: Box>, + #[account( + mut, + token::mint = lp_mint, + address = pool.governance_fee_key @ PoolError::InvalidGovernanceFeeAccount, + )] + pub governance_fee: Box>, + + pub user_transfer_authority: Signer<'info>, + + #[account( + mut, + token::mint = pool_token_account_0.mint, + )] + pub user_token_account_0: Box>, + + #[account( + mut, + token::mint = pool_token_account_1.mint, + )] + pub user_token_account_1: Box>, + + // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. + // // payer could be the same as user_auth if user manually completing the txn but still need + // // to have a separate field to account for it + // #[account(mut)] + // pub payer: Signer<'info>, + pub token_program: Program<'info, Token>, +} + +impl<'info> Swap<'info> { + pub fn update_previous_depth(&mut self, latest_depth: u128) -> Result<()> { + let pool_state = &mut self.pool; + pool_state.previous_depth = latest_depth; + Ok(()) + } + + pub fn transfer_from_user_to_pool( + &self, + user_token_account: &Account<'info, TokenAccount>, + user_transfer_authority: &Signer<'info>, + pool_token_account: &Account<'info, TokenAccount>, + amount: u64, + ) -> Result<()> { + if amount > 0u64 { + token::transfer( + CpiContext::new( + self.token_program.to_account_info(), + token::Transfer { + from: user_token_account.to_account_info(), + to: pool_token_account.to_account_info(), + authority: user_transfer_authority.to_account_info(), + }, + ), + amount, + )?; + } + Ok(()) + } + + pub fn transfer_from_pool_to_user( + &self, + pool_token_account: &Account<'info, TokenAccount>, + user_token_account: &Account<'info, TokenAccount>, + amount: u64, + ) -> Result<()> { + if amount > 0u64 { + token::transfer( + CpiContext::new_with_signer( + self.token_program.to_account_info(), + token::Transfer { + // source + from: pool_token_account.to_account_info(), + to: user_token_account.to_account_info(), + authority: self.pool.to_account_info(), + }, + &[&gen_pool_signer_seeds!(self.pool)[..]], + ), + amount, + )?; + } + Ok(()) + } + + pub fn mint_lp_tokens(&self, recipient: &Account<'info, TokenAccount>, mint_amount: u64) -> Result<()> { + if mint_amount > 0 { + token::mint_to( + CpiContext::new_with_signer( + self.token_program.to_account_info(), + token::MintTo { + // source + mint: self.lp_mint.to_account_info(), + to: recipient.to_account_info(), + authority: self.pool.to_account_info(), + }, + &[&gen_pool_signer_seeds!(self.pool)[..]], + ), + mint_amount, + )?; + } + Ok(()) + } +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct SwapExactInputParams { + pub exact_input_amounts: [u64; TOKEN_COUNT], + pub output_token_index: u8, + pub minimum_output_amount: u64, +} + +pub fn handle_swap_exact_input( + ctx: Context, + swap_exact_input_params: SwapExactInputParams, + // exact_input_amounts: [u64; TOKEN_COUNT], + // output_token_index: u8, + // minimum_output_amount: u64, +) -> Result { + // let output_token_index = output_token_index as usize; + let output_token_index = swap_exact_input_params.output_token_index as usize; + let exact_input_amounts = swap_exact_input_params.exact_input_amounts; + let minimum_output_amount = swap_exact_input_params.minimum_output_amount; + if exact_input_amounts.iter().all(|amount| *amount == 0) + || output_token_index >= TOKEN_COUNT + || exact_input_amounts[output_token_index] != 0 + { + return err!(PoolError::InvalidSwapExactInputParameters); + } + + let pool = &ctx.accounts.pool; + require!(!pool.is_paused, PoolError::PoolIsPaused); + + let user_token_accounts = &[&ctx.accounts.user_token_account_0, &ctx.accounts.user_token_account_1]; + let pool_token_accounts = &[&ctx.accounts.pool_token_account_0, &ctx.accounts.pool_token_account_1]; + let pool_balances = pool_token_accounts.map(|account| account.amount); + + let lp_total_supply = ctx.accounts.lp_mint.supply; + let current_ts = Clock::get()?.unix_timestamp; + require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); + let (user_amount, governance_mint_amount, latest_depth) = Invariant::::swap_exact_input( + &array_equalize(exact_input_amounts, pool.token_decimal_equalizers), + output_token_index, + &array_equalize(pool_balances, pool.token_decimal_equalizers), + pool.amp_factor.get(current_ts), + pool.lp_fee.get(), + pool.governance_fee.get(), + to_equalized(lp_total_supply, pool.lp_decimal_equalizer), + pool.previous_depth.into(), + )?; + let (output_amount, governance_mint_amount, latest_depth) = result_from_equalized( + user_amount, + pool.token_decimal_equalizers[output_token_index], + governance_mint_amount, + pool.lp_decimal_equalizer, + latest_depth, + ); + + require_gte!(output_amount, minimum_output_amount, PoolError::OutsideSpecifiedLimits); + + let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); + let user_transfer_auth = &ctx.accounts.user_transfer_authority; + for i in 0..TOKEN_COUNT { + let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); + ctx.accounts.transfer_from_user_to_pool( + user_token_account, + user_transfer_auth, + pool_token_account, + exact_input_amounts[i], + )?; + } + let user_output_token_account = user_token_accounts[output_token_index]; + let pool_output_token_account = pool_token_accounts[output_token_index]; + + ctx.accounts.transfer_from_pool_to_user(&pool_output_token_account, &user_output_token_account, output_amount)?; + + ctx.accounts.mint_lp_tokens(&ctx.accounts.governance_fee, governance_mint_amount)?; + + ctx.accounts.update_previous_depth(latest_depth)?; + Ok(output_amount) +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct SwapExactOutputParams { + pub maximum_input_amount: u64, + pub input_token_index: u8, + pub exact_output_amounts: [u64; TOKEN_COUNT], +} + +pub fn handle_swap_exact_output( + ctx: Context, + swap_exact_output_params: SwapExactOutputParams, +) -> Result> { + let input_token_index = swap_exact_output_params.input_token_index as usize; + let exact_output_amounts = swap_exact_output_params.exact_output_amounts; + + require!(input_token_index < TOKEN_COUNT, PoolError::InvalidSwapExactOutputParameters); + require!(exact_output_amounts[input_token_index] == 0, PoolError::InvalidSwapExactOutputParameters); + + let user_token_accounts = &[&ctx.accounts.user_token_account_0, &ctx.accounts.user_token_account_1]; + let pool_token_accounts = &[&ctx.accounts.pool_token_account_0, &ctx.accounts.pool_token_account_1]; + let pool_balances = pool_token_accounts.map(|account| account.amount); + let are_output_amounts_valid = exact_output_amounts.iter().any(|amount| *amount > 0); + // at least one of the output amounts should be greater than 0 + require!(are_output_amounts_valid, PoolError::InvalidSwapExactOutputParameters); + // || exact_output_amounts[input_token_index] != 0 + // if exact_output_amounts.iter().all(|amount| *amount == 0) + // || input_token_index >= TOKEN_COUNT + // || exact_output_amounts[input_token_index] != 0 + // { + // return err!(PoolError::InvalidSwapExactOutputParameters); + // } + let are_pool_balances_sufficient = exact_output_amounts + .iter() + .zip(pool_balances.iter()) + .all(|(output_amount, pool_balance)| *output_amount < *pool_balance); + require!(are_pool_balances_sufficient, PoolError::InsufficientPoolTokenAccountBalance); + + let pool = &ctx.accounts.pool; + require!(!pool.is_paused, PoolError::PoolIsPaused); + + let user_transfer_auth = &ctx.accounts.user_transfer_authority; + + let lp_total_supply = ctx.accounts.lp_mint.supply; + let current_ts = Clock::get()?.unix_timestamp; + require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); + let (user_amount, governance_mint_amount, latest_depth) = Invariant::::swap_exact_output( + input_token_index, + &array_equalize(exact_output_amounts, pool.token_decimal_equalizers), + &array_equalize(pool_balances, pool.token_decimal_equalizers), + pool.amp_factor.get(current_ts), + pool.lp_fee.get(), + pool.governance_fee.get(), + to_equalized(lp_total_supply, pool.lp_decimal_equalizer), + pool.previous_depth.into(), + )?; + let (input_amount, governance_mint_amount, latest_depth) = result_from_equalized( + user_amount, + pool.token_decimal_equalizers[input_token_index], + governance_mint_amount, + pool.lp_decimal_equalizer, + latest_depth, + ); + + let maximum_input_amount = swap_exact_output_params.maximum_input_amount; + require_gte!(maximum_input_amount, input_amount, PoolError::OutsideSpecifiedLimits); + + let user_input_token_account = user_token_accounts[input_token_index]; + let pool_input_token_account = pool_token_accounts[input_token_index]; + ctx.accounts.transfer_from_user_to_pool( + user_input_token_account, + user_transfer_auth, + pool_input_token_account, + input_amount, + )?; + + let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); + + for i in 0..TOKEN_COUNT { + let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); + ctx.accounts.transfer_from_pool_to_user(pool_token_account, user_token_account, exact_output_amounts[i])?; + } + + ctx.accounts.mint_lp_tokens(&ctx.accounts.governance_fee, governance_mint_amount)?; + + ctx.accounts.update_previous_depth(latest_depth)?; + Ok(exact_output_amounts.into()) +} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/adjust_amp_factor.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/adjust_amp_factor.rs deleted file mode 100644 index e8f60bb5f..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/adjust_amp_factor.rs +++ /dev/null @@ -1,32 +0,0 @@ -use { - crate::{ - common_governance::*, error::*, get_current_ts, governance::ENACT_DELAY, DecimalU64, DecimalU64Anchor, - UnixTimestamp, - }, - anchor_lang::prelude::*, -}; - -#[derive(Accounts)] -pub struct AdjustAmpFactor<'info> { - pub common_governance: CommonGovernance<'info>, -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct AdjustAmpFactorParams { - pub target_ts: i64, - pub target_value: DecimalU64Anchor, -} - -impl<'info> AdjustAmpFactor<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -pub fn handle_adjust_amp_factor(ctx: Context, params: AdjustAmpFactorParams) -> Result<()> { - let pool = &mut ctx.accounts.common_governance.pool; - let current_ts = get_current_ts()?; - pool.amp_factor.set_target(current_ts, params.target_value.into(), params.target_ts)?; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/change_governance_fee_account.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/change_governance_fee_account.rs deleted file mode 100644 index 901283f97..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/change_governance_fee_account.rs +++ /dev/null @@ -1,38 +0,0 @@ -use { - crate::{ - common_governance::*, error::*, get_current_ts, state::TwoPool, DecimalU64, DecimalU64Anchor, PoolFee, - UnixTimestamp, - }, - anchor_lang::prelude::*, - anchor_spl::token::TokenAccount, -}; - -#[derive(Accounts)] -pub struct ChangeGovernanceFeeAccount<'info> { - pub common_governance: CommonGovernance<'info>, - #[account( - token::mint = common_governance.pool.lp_mint_key - )] - pub new_governance_fee: Account<'info, TokenAccount>, -} - -impl<'info> ChangeGovernanceFeeAccount<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -pub fn handle_change_governance_fee_account( - ctx: Context, - new_governance_fee_key: Pubkey, -) -> Result<()> { - require_keys_eq!( - ctx.accounts.new_governance_fee.key(), - new_governance_fee_key, - PoolError::InvalidGovernanceFeeAccount - ); - let pool = &mut ctx.accounts.common_governance.pool; - pool.governance_fee_key = new_governance_fee_key; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/change_pause_key.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/change_pause_key.rs deleted file mode 100644 index 06dd4e94c..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/change_pause_key.rs +++ /dev/null @@ -1,19 +0,0 @@ -use {crate::common_governance::*, anchor_lang::prelude::*}; - -#[derive(Accounts)] -pub struct ChangePauseKey<'info> { - pub common_governance: CommonGovernance<'info>, -} - -impl<'info> ChangePauseKey<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -pub fn handle_change_pause_key(ctx: Context, new_pause_key: Pubkey) -> Result<()> { - let pool = &mut ctx.accounts.common_governance.pool; - pool.pause_key = new_pause_key; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/common_governance.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/common_governance.rs deleted file mode 100644 index 7fba51476..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/common_governance.rs +++ /dev/null @@ -1,32 +0,0 @@ -use { - crate::{error::*, TwoPool}, - anchor_lang::prelude::*, -}; - -#[derive(Accounts)] -pub struct CommonGovernance<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool.get_token_mint_0().unwrap().as_ref(), - pool.get_token_mint_1().unwrap().as_ref(), - pool.lp_mint_key.as_ref(), - ], - bump = pool.bump - )] - pub pool: Account<'info, TwoPool>, - - pub governance: Signer<'info>, -} - -impl<'info> CommonGovernance<'info> { - pub fn accounts(common_governance: &CommonGovernance) -> Result<()> { - require_keys_eq!( - common_governance.governance.key(), - common_governance.pool.governance_key, - PoolError::InvalidGovernanceAccount - ); - Ok(()) - } -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/create_lp_metadata.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/create_lp_metadata.rs deleted file mode 100644 index 08c4ba604..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/create_lp_metadata.rs +++ /dev/null @@ -1,129 +0,0 @@ -use { - crate::{common_governance::*, error::*, gen_pool_signer_seeds, AnchorDataV2, TwoPool}, - anchor_lang::prelude::*, - anchor_spl::{ - metadata::{create_metadata_accounts_v2, CreateMetadataAccountsV2, Metadata}, - token::Mint, - }, - mpl_token_metadata::state::{DataV2, PREFIX}, -}; - -#[derive(Accounts)] -pub struct CreateLpMetadata<'info> { - pub common_governance: CommonGovernance<'info>, - /// this didn't work for some reason. - pub create_metadata_accounts: CreateMetadataAccounts<'info>, - // /// CHECK: Checked in CPI - // pub metadata: AccountInfo<'info>, - // pub mint: Account<'info, Mint>, - // // pub mint_authority: AccountInfo<'info>, - // #[account(mut)] - // pub payer: Signer<'info>, - // // ///CHECK: Checked in CPI - // // pub update_authority: AccountInfo<'info>, - // pub system_program: Program<'info, System>, - // pub rent: Sysvar<'info, Rent>, - #[account( - executable, - address = Metadata::id() - )] - ///CHECK: mpl_token_metadata program - pub mpl_token_metadata: Program<'info, Metadata>, -} - -//pub fn find_metadata_account(mint: &Pubkey) -> (Pubkey, u8) { -// Pubkey::find_program_address( -// &[PREFIX.as_bytes(), crate::id().as_ref(), mint.as_ref()], -// &crate::id(), -// ) -// } - -#[derive(Accounts)] -pub struct CreateMetadataAccounts<'info> { - #[account(mut)] - /// CHECK: Checked in CPI - pub metadata: UncheckedAccount<'info>, - pub mint: Account<'info, Mint>, - /// CHECK: Checked in CPI - pub mint_authority: UncheckedAccount<'info>, - #[account(mut)] - pub payer: Signer<'info>, - ///CHECK: Checked in CPI - pub update_authority: UncheckedAccount<'info>, - pub system_program: Program<'info, System>, - pub rent: Sysvar<'info, Rent>, -} - -impl<'info> CreateMetadataAccounts<'info> { - pub fn to_create_metadata_accounts_v2(&self) -> CreateMetadataAccountsV2<'info> { - CreateMetadataAccountsV2 { - metadata: self.metadata.to_account_info(), - mint: self.mint.to_account_info(), - mint_authority: self.mint_authority.to_account_info(), - payer: self.payer.to_account_info(), - update_authority: self.update_authority.to_account_info(), - system_program: self.system_program.to_account_info(), - rent: self.rent.to_account_info(), - } - } -} - -// impl<'info> From for CreateMetadataAccountsV2<'info> { -// fn from(ctx: Context) -> Self<'info> { -// CreateMetadataAccountsV2 { -// metadata: ctx.accounts.metadata.to_account_info(), -// mint: ctx.accounts.mint.to_account_info(), -// mint_authority: ctx.accounts.mint_authority.to_account_info(), -// payer: ctx.accounts.payer.to_account_info(), -// update_authority: ctx.accounts.update_authority.to_account_info(), -// system_program: ctx.accounts.system_program.to_account_info(), -// rent: ctx.accounts.rent.to_account_info(), -// } -// } -// -// fn from(accounts: CreateMetadataAccounts) -> Self { -// CreateMetadataAccountsV2 { -// metadata: accounts.metadata.to_account_info(), -// mint: accounts.mint.to_account_info(), -// mint_authority: accounts.mint_authority.to_account_info(), -// payer: accounts.payer.to_account_info(), -// update_authority: accounts.update_authority.to_account_info(), -// system_program: accounts.system_program.to_account_info(), -// rent: accounts.rent.to_account_info(), -// } -// } -// } - -impl<'info> CreateLpMetadata<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - require_keys_eq!( - ctx.accounts.common_governance.pool.lp_mint_key.key(), - ctx.accounts.create_metadata_accounts.mint.key(), - PoolError::InvalidMintAccount - ); - Ok(()) - } -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct CreateLpMetadataParams { - pub data: AnchorDataV2, - pub is_mutable: bool, - //default to true? update auth is pool state itself and should be a signer - pub update_authority_is_signer: bool, -} - -pub fn handle_create_lp_metadata(ctx: Context, params: CreateLpMetadataParams) -> Result<()> { - create_metadata_accounts_v2( - CpiContext::new_with_signer( - ctx.accounts.mpl_token_metadata.to_account_info(), - ctx.accounts.create_metadata_accounts.to_create_metadata_accounts_v2(), - &[&gen_pool_signer_seeds!(ctx.accounts.common_governance.pool)[..]], - ), - params.data.into(), - params.is_mutable, - params.update_authority_is_signer, - )?; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/enact_fee_change.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/enact_fee_change.rs deleted file mode 100644 index 7edca329e..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/enact_fee_change.rs +++ /dev/null @@ -1,36 +0,0 @@ -use { - crate::{ - common_governance::*, error::*, get_current_ts, governance::ENACT_DELAY, DecimalU64, DecimalU64Anchor, PoolFee, - UnixTimestamp, - }, - anchor_lang::prelude::*, -}; - -#[derive(Accounts)] -pub struct EnactFeeChange<'info> { - pub common_governance: CommonGovernance<'info>, -} - -impl<'info> EnactFeeChange<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -pub fn handle_enact_fee_change(ctx: Context) -> Result<()> { - let pool = &mut ctx.accounts.common_governance.pool; - require_neq!(pool.fee_transition_ts, 0i64, PoolError::InvalidEnact); - let current_ts = get_current_ts()?; - require_gte!(current_ts, pool.fee_transition_ts, PoolError::InsufficientDelay); - // Note: no longer allowing pubkey::default() as governance_fee_account even if gov_fee == 0 - // if pool.prepared_governance_fee.get() > DecimalU64::from(0) { - // require_keys_neq!(pool.governance_fee_key, Pubkey::default(), PoolError::InvalidGovernanceFeeAccount); - // } - pool.lp_fee = pool.prepared_lp_fee.clone(); - pool.governance_fee = pool.prepared_governance_fee.clone(); - pool.prepared_lp_fee = PoolFee::default(); - pool.prepared_governance_fee = PoolFee::default(); - pool.fee_transition_ts = 0i64; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/enact_governance_transition.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/enact_governance_transition.rs deleted file mode 100644 index 718f12410..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/enact_governance_transition.rs +++ /dev/null @@ -1,33 +0,0 @@ -use { - crate::{ - common_governance::*, error::*, get_current_ts, governance::ENACT_DELAY, DecimalU64, DecimalU64Anchor, PoolFee, - UnixTimestamp, - }, - anchor_lang::prelude::*, -}; - -#[derive(Accounts)] -pub struct EnactGovernanceTransition<'info> { - pub common_governance: CommonGovernance<'info>, -} - -impl<'info> EnactGovernanceTransition<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -pub fn handle_enact_governance_transition(ctx: Context) -> Result<()> { - let pool = &mut ctx.accounts.common_governance.pool; - require_neq!(pool.governance_transition_ts, 0i64, PoolError::InvalidEnact); - - let current_ts = get_current_ts()?; - - require_gte!(current_ts, pool.governance_transition_ts, PoolError::InsufficientDelay); - - pool.governance_key = pool.prepared_governance_key; - pool.prepared_governance_key = Pubkey::default(); - pool.governance_transition_ts = 0i64; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/governance.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/governance.rs new file mode 100644 index 000000000..5cbfd7c55 --- /dev/null +++ b/packages/solana-contracts/programs/two-pool/src/instructions/governance/governance.rs @@ -0,0 +1,193 @@ +use { + crate::{ + decimal::DecimalU64, + error::*, + instructions::{get_current_ts, ENACT_DELAY}, + pool_fee::PoolFee, + DecimalU64Anchor, TwoPool, + }, + anchor_lang::prelude::*, + anchor_spl::token::TokenAccount, +}; + +#[derive(Accounts)] +pub struct Governance<'info> { + #[account( + mut, + seeds = [ + b"two_pool".as_ref(), + pool.get_token_mint_0().as_ref(), + pool.get_token_mint_1().as_ref(), + pool.lp_mint_key.as_ref(), + ], + bump = pool.bump, + has_one = governance_key @ PoolError::InvalidGovernanceAccount + )] + pub pool: Account<'info, TwoPool>, + + pub governance_key: Signer<'info>, +} + +/* Update Amp Factor */ + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct AdjustAmpFactorParams { + pub target_ts: i64, + pub target_value: DecimalU64Anchor, +} + +pub fn handle_adjust_amp_factor(ctx: Context, params: AdjustAmpFactorParams) -> Result<()> { + let pool = &mut ctx.accounts.pool; + let current_ts = get_current_ts()?; + pool.amp_factor.set_target(current_ts, params.target_value.into(), params.target_ts)?; + Ok(()) +} + +/* Update Fees */ + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct PrepareFeeChangeParams { + pub lp_fee: DecimalU64Anchor, + pub governance_fee: DecimalU64Anchor, +} + +pub fn handle_prepare_fee_change( + ctx: Context, + // params: PrepareFeeChangeParams, + lp_fee: DecimalU64Anchor, + governance_fee: DecimalU64Anchor, +) -> Result<()> { + let lp_fee: DecimalU64 = lp_fee.into(); + let governance_fee: DecimalU64 = governance_fee.into(); + require_gt!(DecimalU64::from(1), lp_fee + governance_fee, PoolError::InvalidFeeInput); + let pool = &mut ctx.accounts.pool; + pool.prepared_lp_fee = PoolFee::new(lp_fee)?; + pool.prepared_governance_fee = PoolFee::new(governance_fee)?; + + let current_ts = get_current_ts()?; + + pool.fee_transition_ts = current_ts + ENACT_DELAY; + Ok(()) +} + +pub fn handle_enact_fee_change(ctx: Context) -> Result<()> { + let pool = &mut ctx.accounts.pool; + require_neq!(pool.fee_transition_ts, 0i64, PoolError::InvalidEnact); + let current_ts = get_current_ts()?; + require_gte!(current_ts, pool.fee_transition_ts, PoolError::InsufficientDelay); + // Note: no longer allowing pubkey::default() as governance_fee_account even if gov_fee == 0 + // if pool.prepared_governance_fee.get() > DecimalU64::from(0) { + // require_keys_neq!(pool.governance_fee_key, Pubkey::default(), PoolError::InvalidGovernanceFeeAccount); + // } + pool.lp_fee = pool.prepared_lp_fee.clone(); + pool.governance_fee = pool.prepared_governance_fee.clone(); + pool.prepared_lp_fee = PoolFee::default(); + pool.prepared_governance_fee = PoolFee::default(); + pool.fee_transition_ts = 0i64; + Ok(()) +} + +/* Update Governance Fee Account */ + +#[derive(Accounts)] +pub struct ChangeGovernanceFeeAccount<'info> { + pub governance: Governance<'info>, + #[account(token::mint = governance.pool.lp_mint_key)] + pub new_governance_fee: Account<'info, TokenAccount>, +} + +pub fn handle_change_governance_fee_account( + ctx: Context, + new_governance_fee_key: Pubkey, +) -> Result<()> { + require_keys_eq!( + ctx.accounts.new_governance_fee.key(), + new_governance_fee_key, + PoolError::InvalidGovernanceFeeAccount + ); + let pool = &mut ctx.accounts.governance.pool; + pool.governance_fee_key = new_governance_fee_key; + Ok(()) +} + +/* Update Governance Account */ + +#[derive(Accounts)] +pub struct PrepareGovernanceTransition<'info> { + pub governance: Governance<'info>, + ///CHECK: not specifying type of account since it doesn't matter + pub upcoming_governance_key: UncheckedAccount<'info>, +} + +pub fn handle_prepare_governance_transition( + ctx: Context, + upcoming_governance_key: Pubkey, +) -> Result<()> { + require_keys_eq!( + upcoming_governance_key, + ctx.accounts.upcoming_governance_key.key(), + PoolError::InvalidUpcomingGovernanceKey + ); + let pool = &mut ctx.accounts.governance.pool; + pool.prepared_governance_key = upcoming_governance_key; + + let current_ts = get_current_ts()?; + pool.governance_transition_ts = current_ts + ENACT_DELAY; + + Ok(()) +} + +pub fn handle_enact_governance_transition(ctx: Context) -> Result<()> { + let pool = &mut ctx.accounts.pool; + require_neq!(pool.governance_transition_ts, 0i64, PoolError::InvalidEnact); + require_keys_neq!(pool.prepared_governance_key, Pubkey::default(), PoolError::InvalidEnact); + + let current_ts = get_current_ts()?; + + require_gte!(current_ts, pool.governance_transition_ts, PoolError::InsufficientDelay); + + pool.governance_key = pool.prepared_governance_key; + pool.prepared_governance_key = Pubkey::default(); + pool.governance_transition_ts = 0i64; + Ok(()) +} + +/* Pause */ + +#[derive(Accounts)] +pub struct ChangePauseKey<'info> { + pub governance: Governance<'info>, + ///CHECK: not specifying type of account since it doesn't matter + pub new_pause_key: UncheckedAccount<'info>, +} + +pub fn handle_change_pause_key(ctx: Context, new_pause_key: Pubkey) -> Result<()> { + require_keys_eq!(ctx.accounts.new_pause_key.key(), new_pause_key, PoolError::InvalidNewPauseKey); + require_keys_neq!(ctx.accounts.new_pause_key.key(), Pubkey::default(), PoolError::InvalidNewPauseKey); + let pool = &mut ctx.accounts.governance.pool; + pool.pause_key = new_pause_key; + Ok(()) +} + +#[derive(Accounts)] +pub struct SetPaused<'info> { + #[account( + mut, + seeds = [ + b"two_pool".as_ref(), + pool.get_token_mint_0().as_ref(), + pool.get_token_mint_1().as_ref(), + pool.lp_mint_key.as_ref(), + ], + bump = pool.bump, + has_one = pause_key @ PoolError::InvalidPauseKey + )] + pub pool: Account<'info, TwoPool>, + pub pause_key: Signer<'info>, +} + +pub fn handle_set_paused(ctx: Context, paused: bool) -> Result<()> { + let pool = &mut ctx.accounts.pool; + pool.is_paused = paused; + Ok(()) +} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/lp_metadata.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/lp_metadata.rs new file mode 100644 index 000000000..8cfd1e4db --- /dev/null +++ b/packages/solana-contracts/programs/two-pool/src/instructions/governance/lp_metadata.rs @@ -0,0 +1,264 @@ +use { + crate::{error::*, gen_pool_signer_seeds, governance::*}, + anchor_lang::prelude::*, + anchor_spl::{ + metadata::{ + create_metadata_accounts_v2, update_metadata_accounts_v2, CreateMetadataAccountsV2, Metadata, + UpdateMetadataAccountsV2, + }, + token::Mint, + }, + mpl_token_metadata::state::{Collection, Creator, DataV2, UseMethod, Uses}, +}; + +#[derive(Accounts)] +pub struct CreateLpMetadata<'info> { + pub governance: Governance<'info>, + /// this didn't work for some reason. + pub create_metadata_accounts: CreateMetadataAccounts<'info>, + pub mpl_token_metadata: Program<'info, Metadata>, +} + +//pub fn find_metadata_account(mint: &Pubkey) -> (Pubkey, u8) { +// Pubkey::find_program_address( +// &[PREFIX.as_bytes(), crate::id().as_ref(), mint.as_ref()], +// &crate::id(), +// ) +// } + +#[derive(Accounts)] +pub struct CreateMetadataAccounts<'info> { + #[account(mut)] + /// CHECK: Checked in CPI + pub metadata: UncheckedAccount<'info>, + pub mint: Account<'info, Mint>, + /// CHECK: Checked in CPI + pub mint_authority: UncheckedAccount<'info>, + #[account(mut)] + pub payer: Signer<'info>, + ///CHECK: Checked in CPI + pub update_authority: UncheckedAccount<'info>, + pub system_program: Program<'info, System>, + pub rent: Sysvar<'info, Rent>, +} + +impl<'info> CreateMetadataAccounts<'info> { + pub fn to_create_metadata_accounts_v2(&self) -> CreateMetadataAccountsV2<'info> { + CreateMetadataAccountsV2 { + metadata: self.metadata.to_account_info(), + mint: self.mint.to_account_info(), + mint_authority: self.mint_authority.to_account_info(), + payer: self.payer.to_account_info(), + update_authority: self.update_authority.to_account_info(), + system_program: self.system_program.to_account_info(), + rent: self.rent.to_account_info(), + } + } +} + +// impl<'info> From for CreateMetadataAccountsV2<'info> { +// fn from(ctx: Context) -> Self<'info> { +// CreateMetadataAccountsV2 { +// metadata: ctx.accounts.metadata.to_account_info(), +// mint: ctx.accounts.mint.to_account_info(), +// mint_authority: ctx.accounts.mint_authority.to_account_info(), +// payer: ctx.accounts.payer.to_account_info(), +// update_authority: ctx.accounts.update_authority.to_account_info(), +// system_program: ctx.accounts.system_program.to_account_info(), +// rent: ctx.accounts.rent.to_account_info(), +// } +// } +// +// fn from(accounts: CreateMetadataAccounts) -> Self { +// CreateMetadataAccountsV2 { +// metadata: accounts.metadata.to_account_info(), +// mint: accounts.mint.to_account_info(), +// mint_authority: accounts.mint_authority.to_account_info(), +// payer: accounts.payer.to_account_info(), +// update_authority: accounts.update_authority.to_account_info(), +// system_program: accounts.system_program.to_account_info(), +// rent: accounts.rent.to_account_info(), +// } +// } +// } + +impl<'info> CreateLpMetadata<'info> { + pub fn accounts(ctx: &Context) -> Result<()> { + require_keys_eq!( + ctx.accounts.governance.pool.lp_mint_key.key(), + ctx.accounts.create_metadata_accounts.mint.key(), + PoolError::InvalidMintAccount + ); + Ok(()) + } +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct CreateLpMetadataParams { + pub data: AnchorDataV2, + pub is_mutable: bool, + //default to true? update auth is pool state itself and should be a signer + pub update_authority_is_signer: bool, +} + +pub fn handle_create_lp_metadata(ctx: Context, params: CreateLpMetadataParams) -> Result<()> { + create_metadata_accounts_v2( + CpiContext::new_with_signer( + ctx.accounts.mpl_token_metadata.to_account_info(), + ctx.accounts.create_metadata_accounts.to_create_metadata_accounts_v2(), + &[&gen_pool_signer_seeds!(ctx.accounts.governance.pool)[..]], + ), + params.data.into(), + params.is_mutable, + params.update_authority_is_signer, + )?; + Ok(()) +} + +#[derive(Accounts)] +pub struct UpdateLpMetadata<'info> { + pub governance: Governance<'info>, + pub update_metadata_accounts: UpdateMetadataAccounts<'info>, + #[account( + executable, + address = Metadata::id() + )] + ///CHECK: mpl_token_metadata program + pub mpl_token_metadata: Program<'info, Metadata>, +} + +impl<'info> UpdateLpMetadata<'info> { + pub fn accounts(ctx: &Context) -> Result<()> { + Ok(()) + } +} + +#[derive(Accounts)] +pub struct UpdateMetadataAccounts<'info> { + #[account(mut)] + ///CHECK: Checked in CPI + pub metadata: UncheckedAccount<'info>, + /// CHECK: Checked in CPI + pub update_authority: UncheckedAccount<'info>, +} + +impl<'info> UpdateMetadataAccounts<'info> { + pub fn to_update_metadata_accounts_v2(&self) -> UpdateMetadataAccountsV2<'info> { + UpdateMetadataAccountsV2 { + metadata: self.metadata.to_account_info(), + update_authority: self.update_authority.to_account_info(), + } + } +} + +#[derive(AnchorSerialize, AnchorDeserialize)] +pub struct UpdateLpMetadataParams { + pub new_update_authority: Option, + pub data: Option, + pub primary_sale_happened: Option, + pub is_mutable: Option, +} + +pub fn handle_update_lp_metadata(ctx: Context, params: UpdateLpMetadataParams) -> Result<()> { + update_metadata_accounts_v2( + CpiContext::new_with_signer( + ctx.accounts.mpl_token_metadata.to_account_info(), + ctx.accounts.update_metadata_accounts.to_update_metadata_accounts_v2(), + &[&gen_pool_signer_seeds!(ctx.accounts.governance.pool)[..]], + ), + params.new_update_authority, + params.data.map(|data| data.into()), + params.primary_sale_happened, + params.is_mutable, + ) +} + +// copied from https://github.com/CalebEverett/nftfactory/blob/master/programs/nftfactory/src/lib.rs + +#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Eq, Debug, Clone)] +pub struct AnchorDataV2 { + /// The name of the asset + pub name: String, + /// The symbol for the asset + pub symbol: String, + /// URI pointing to JSON representing the asset + pub uri: String, + /// Royalty basis points that goes to creators in secondary sales (0-10000) + pub seller_fee_basis_points: u16, + /// Array of creators, optional + pub creators: Option>, + /// Collection + pub collection: Option, + /// Uses + pub uses: Option, +} + +impl From for DataV2 { + fn from(item: AnchorDataV2) -> Self { + DataV2 { + name: item.name, + symbol: item.symbol, + uri: item.uri, + seller_fee_basis_points: item.seller_fee_basis_points, + creators: item.creators.map(|a| a.into_iter().map(|v| v.into()).collect()), + collection: item.collection.map(|v| v.into()), + uses: item.uses.map(|v| v.into()), + } + } +} + +#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Eq, Debug, Clone, Copy)] +pub struct AnchorCreator { + pub address: Pubkey, + pub verified: bool, + // In percentages, NOT basis points ;) Watch out! + pub share: u8, +} + +impl From for Creator { + fn from(item: AnchorCreator) -> Self { + Creator { address: item.address, verified: item.verified, share: item.share } + } +} + +#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Eq, Debug, Clone, Copy)] +pub enum AnchorUseMethod { + Burn, + Multiple, + Single, +} + +#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Eq, Debug, Clone, Copy)] +pub struct AnchorUses { + pub use_method: AnchorUseMethod, + pub remaining: u64, + pub total: u64, +} + +impl From for Uses { + fn from(item: AnchorUses) -> Self { + Uses { use_method: item.use_method.into(), remaining: item.remaining, total: item.total } + } +} + +impl From for UseMethod { + fn from(item: AnchorUseMethod) -> Self { + match item { + AnchorUseMethod::Burn => UseMethod::Burn, + AnchorUseMethod::Multiple => UseMethod::Burn, + AnchorUseMethod::Single => UseMethod::Burn, + } + } +} + +#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Eq, Debug, Clone, Copy)] +pub struct AnchorCollection { + pub verified: bool, + pub key: Pubkey, +} + +impl From for Collection { + fn from(item: AnchorCollection) -> Self { + Collection { verified: item.verified, key: item.key } + } +} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/mod.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/mod.rs index d9505bb09..65491d0e1 100644 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/mod.rs +++ b/packages/solana-contracts/programs/two-pool/src/instructions/governance/mod.rs @@ -1,20 +1,6 @@ -pub use { - adjust_amp_factor::*, change_governance_fee_account::*, change_pause_key::*, common_governance::*, - create_lp_metadata::*, enact_fee_change::*, enact_governance_transition::*, mpl::*, prepare_fee_change::*, - prepare_governance_transition::*, set_paused::*, update_lp_metadata::*, -}; +pub use {governance::*, lp_metadata::*}; -pub mod adjust_amp_factor; -pub mod change_governance_fee_account; -pub mod change_pause_key; -pub mod common_governance; -pub mod create_lp_metadata; -pub mod enact_fee_change; -pub mod enact_governance_transition; -pub mod mpl; -pub mod prepare_fee_change; -pub mod prepare_governance_transition; -pub mod set_paused; -pub mod update_lp_metadata; +pub mod governance; +pub mod lp_metadata; pub const ENACT_DELAY: i64 = 3 * 86400; diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/mpl.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/mpl.rs deleted file mode 100644 index 26f906676..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/mpl.rs +++ /dev/null @@ -1,94 +0,0 @@ -use { - anchor_lang::prelude::*, - mpl_token_metadata::state::{Collection, Creator, DataV2, UseMethod, Uses}, -}; - -// copied from https://github.com/CalebEverett/nftfactory/blob/master/programs/nftfactory/src/lib.rs - -#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Debug, Clone)] -pub struct AnchorDataV2 { - /// The name of the asset - pub name: String, - /// The symbol for the asset - pub symbol: String, - /// URI pointing to JSON representing the asset - pub uri: String, - /// Royalty basis points that goes to creators in secondary sales (0-10000) - pub seller_fee_basis_points: u16, - /// Array of creators, optional - pub creators: Option>, - /// Collection - pub collection: Option, - /// Uses - pub uses: Option, -} - -impl From for DataV2 { - fn from(item: AnchorDataV2) -> Self { - DataV2 { - name: item.name, - symbol: item.symbol, - uri: item.uri, - seller_fee_basis_points: item.seller_fee_basis_points, - creators: item.creators.map(|a| a.into_iter().map(|v| v.into()).collect()), - collection: item.collection.map(|v| v.into()), - uses: item.uses.map(|v| v.into()), - } - } -} - -#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Debug, Clone, Copy)] -pub struct AnchorCreator { - pub address: Pubkey, - pub verified: bool, - // In percentages, NOT basis points ;) Watch out! - pub share: u8, -} - -impl From for Creator { - fn from(item: AnchorCreator) -> Self { - Creator { address: item.address, verified: item.verified, share: item.share } - } -} - -#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Debug, Clone, Copy)] -pub enum AnchorUseMethod { - Burn, - Multiple, - Single, -} - -#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Debug, Clone, Copy)] -pub struct AnchorUses { - pub use_method: AnchorUseMethod, - pub remaining: u64, - pub total: u64, -} - -impl From for Uses { - fn from(item: AnchorUses) -> Self { - Uses { use_method: item.use_method.into(), remaining: item.remaining, total: item.total } - } -} - -impl From for UseMethod { - fn from(item: AnchorUseMethod) -> Self { - match item { - AnchorUseMethod::Burn => UseMethod::Burn, - AnchorUseMethod::Multiple => UseMethod::Burn, - AnchorUseMethod::Single => UseMethod::Burn, - } - } -} - -#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Debug, Clone, Copy)] -pub struct AnchorCollection { - pub verified: bool, - pub key: Pubkey, -} - -impl From for Collection { - fn from(item: AnchorCollection) -> Self { - Collection { verified: item.verified, key: item.key } - } -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/prepare_fee_change.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/prepare_fee_change.rs deleted file mode 100644 index 4d9da91a6..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/prepare_fee_change.rs +++ /dev/null @@ -1,43 +0,0 @@ -use { - crate::{ - common_governance::*, error::*, get_current_ts, governance::ENACT_DELAY, DecimalU64, DecimalU64Anchor, PoolFee, - }, - anchor_lang::prelude::*, -}; - -#[derive(Accounts)] -pub struct PrepareFeeChange<'info> { - pub common_governance: CommonGovernance<'info>, -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct PrepareFeeChangeParams { - pub lp_fee: DecimalU64Anchor, - pub governance_fee: DecimalU64Anchor, -} - -impl<'info> PrepareFeeChange<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -pub fn handle_prepare_fee_change( - ctx: Context, - // params: PrepareFeeChangeParams, - lp_fee: DecimalU64Anchor, - governance_fee: DecimalU64Anchor, -) -> Result<()> { - let lp_fee: DecimalU64 = lp_fee.into(); - let governance_fee: DecimalU64 = governance_fee.into(); - require_gt!(DecimalU64::from(1), lp_fee + governance_fee, PoolError::InvalidFeeInput); - let pool = &mut ctx.accounts.common_governance.pool; - pool.prepared_lp_fee = PoolFee::new(lp_fee)?; - pool.prepared_governance_fee = PoolFee::new(governance_fee)?; - - let current_ts = get_current_ts()?; - - pool.fee_transition_ts = current_ts + ENACT_DELAY; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/prepare_governance_transition.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/prepare_governance_transition.rs deleted file mode 100644 index b2e32982d..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/prepare_governance_transition.rs +++ /dev/null @@ -1,32 +0,0 @@ -use { - crate::{ - common_governance::*, get_current_ts, governance::ENACT_DELAY, DecimalU64, DecimalU64Anchor, PoolFee, - UnixTimestamp, - }, - anchor_lang::prelude::*, -}; - -#[derive(Accounts)] -pub struct PrepareGovernanceTransition<'info> { - pub common_governance: CommonGovernance<'info>, -} - -impl<'info> PrepareGovernanceTransition<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -pub fn handle_prepare_governance_transition( - ctx: Context, - upcoming_governance_key: Pubkey, -) -> Result<()> { - let pool = &mut ctx.accounts.common_governance.pool; - pool.prepared_governance_key = upcoming_governance_key; - - let current_ts = get_current_ts()?; - pool.governance_transition_ts = current_ts + ENACT_DELAY; - - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/set_paused.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/set_paused.rs deleted file mode 100644 index e8c13ec66..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/set_paused.rs +++ /dev/null @@ -1,36 +0,0 @@ -use { - crate::{ - common_governance::*, error::*, get_current_ts, governance::ENACT_DELAY, DecimalU64, DecimalU64Anchor, PoolFee, - TwoPool, UnixTimestamp, - }, - anchor_lang::prelude::*, -}; - -#[derive(Accounts)] -pub struct SetPaused<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool.get_token_mint_0().unwrap().as_ref(), - pool.get_token_mint_1().unwrap().as_ref(), - pool.lp_mint_key.as_ref(), - ], - bump = pool.bump - )] - pub pool: Account<'info, TwoPool>, - pub pause_key: Signer<'info>, -} - -impl<'info> SetPaused<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - require_keys_eq!(ctx.accounts.pause_key.key(), ctx.accounts.pool.pause_key, PoolError::InvalidPauseKey); - Ok(()) - } -} - -pub fn handle_set_paused(ctx: Context, paused: bool) -> Result<()> { - let pool = &mut ctx.accounts.pool; - pool.is_paused = paused; - Ok(()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/governance/update_lp_metadata.rs b/packages/solana-contracts/programs/two-pool/src/instructions/governance/update_lp_metadata.rs deleted file mode 100644 index ac93b57bc..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/governance/update_lp_metadata.rs +++ /dev/null @@ -1,71 +0,0 @@ -use { - crate::{common_governance::*, error::*, gen_pool_signer_seeds, AnchorDataV2, TwoPool}, - anchor_lang::prelude::*, - anchor_spl::{ - metadata::{ - create_metadata_accounts_v2, update_metadata_accounts_v2, CreateMetadataAccountsV2, Metadata, - UpdateMetadataAccountsV2, - }, - token::Mint, - }, - mpl_token_metadata::state::{DataV2, PREFIX}, -}; - -#[derive(Accounts)] -pub struct UpdateLpMetadata<'info> { - pub common_governance: CommonGovernance<'info>, - pub update_metadata_accounts: UpdateMetadataAccounts<'info>, - #[account( - executable, - address = Metadata::id() - )] - ///CHECK: mpl_token_metadata program - pub mpl_token_metadata: Program<'info, Metadata>, -} - -impl<'info> UpdateLpMetadata<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - CommonGovernance::accounts(&ctx.accounts.common_governance)?; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct UpdateMetadataAccounts<'info> { - #[account(mut)] - ///CHECK: Checked in CPI - pub metadata: UncheckedAccount<'info>, - /// CHECK: Checked in CPI - pub update_authority: UncheckedAccount<'info>, -} - -impl<'info> UpdateMetadataAccounts<'info> { - pub fn to_update_metadata_accounts_v2(&self) -> UpdateMetadataAccountsV2<'info> { - UpdateMetadataAccountsV2 { - metadata: self.metadata.to_account_info(), - update_authority: self.update_authority.to_account_info(), - } - } -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct UpdateLpMetadataParams { - pub new_update_authority: Option, - pub data: Option, - pub primary_sale_happened: Option, - pub is_mutable: Option, -} - -pub fn handle_update_lp_metadata(ctx: Context, params: UpdateLpMetadataParams) -> Result<()> { - update_metadata_accounts_v2( - CpiContext::new_with_signer( - ctx.accounts.mpl_token_metadata.to_account_info(), - ctx.accounts.update_metadata_accounts.to_update_metadata_accounts_v2(), - &[&gen_pool_signer_seeds!(ctx.accounts.common_governance.pool)[..]], - ), - params.new_update_authority, - params.data.map(|data| data.into()), - params.primary_sale_happened, - params.is_mutable, - ) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/initialize.rs b/packages/solana-contracts/programs/two-pool/src/instructions/initialize.rs index 3f72464ae..a3070b220 100644 --- a/packages/solana-contracts/programs/two-pool/src/instructions/initialize.rs +++ b/packages/solana-contracts/programs/two-pool/src/instructions/initialize.rs @@ -1,5 +1,5 @@ use { - crate::{decimal::DecimalU64, error::PoolError::*, AmpFactor, DecimalU64Anchor, PoolError, PoolFee, TwoPool}, + crate::{decimal::DecimalU64, AmpFactor, DecimalU64Anchor, PoolError, PoolFee, TwoPool}, anchor_lang::prelude::*, anchor_spl::{ associated_token::{get_associated_token_address, AssociatedToken}, @@ -67,6 +67,7 @@ pub struct Initialize<'info> { payer = payer, associated_token::mint = lp_mint, associated_token::authority = governance_account, + address = get_associated_token_address(&governance_account.key(), &lp_mint.key()), )] pub governance_fee_account: Box>, @@ -77,6 +78,7 @@ pub struct Initialize<'info> { pub system_program: Program<'info, System>, //explicitly needed for initializing associated token accounts + // setting it last so it can be removed once version after 0.25.0 is released pub rent: Sysvar<'info, Rent>, } @@ -148,28 +150,5 @@ pub fn handle_initialize( two_pool.prepared_governance_fee = PoolFee::default(); two_pool.fee_transition_ts = 0; two_pool.previous_depth = 0; - - /** - &PoolState { - nonce, - is_paused: false, - amp_factor: AmpFactor::new(amp_factor)?, - lp_fee: PoolFee::new(lp_fee)?, - governance_fee: PoolFee::new(governance_fee)?, - lp_mint_key: lp_mint_account.key.clone(), - lp_decimal_equalizer: decimal_range_max - lp_mint_state.decimals, - token_mint_keys: create_array(|i| token_mint_accounts[i].key.clone()), - token_decimal_equalizers: create_array(|i| decimal_range_max - token_decimals[i]), - token_keys: create_array(|i| token_accounts[i].key.clone()), - governance_key: governance_account.key.clone(), - governance_fee_key: governance_fee_account.key.clone(), - prepared_governance_key: Pubkey::default(), - governance_transition_ts: 0, - prepared_lp_fee: PoolFee::default(), - prepared_governance_fee: PoolFee::default(), - fee_transition_ts: 0, - previous_depth: 0, - }, - */ Ok(()) } diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/marginal_prices.rs b/packages/solana-contracts/programs/two-pool/src/instructions/marginal_prices.rs index f87977783..528ce7086 100644 --- a/packages/solana-contracts/programs/two-pool/src/instructions/marginal_prices.rs +++ b/packages/solana-contracts/programs/two-pool/src/instructions/marginal_prices.rs @@ -1,15 +1,12 @@ use { crate::{ - array_equalize, common::create_array, decimal::U128, error::*, invariant::Invariant, to_equalized, AmpFactor, - BorshDecimal, DecimalU64Anchor, TwoPool, TOKEN_COUNT, + common::{array_equalize, create_array, to_equalized}, + error::*, + invariant::Invariant, + BorshDecimal, TwoPool, TOKEN_COUNT, }, anchor_lang::prelude::*, - anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, - }, - rust_decimal::Decimal, - std::iter::zip, + anchor_spl::token::{Mint, TokenAccount}, }; #[derive(Accounts)] diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/mod.rs b/packages/solana-contracts/programs/two-pool/src/instructions/mod.rs index 19dc73588..d7253ab28 100644 --- a/packages/solana-contracts/programs/two-pool/src/instructions/mod.rs +++ b/packages/solana-contracts/programs/two-pool/src/instructions/mod.rs @@ -2,20 +2,12 @@ use { crate::error::*, anchor_lang::{prelude::*, require_gt, solana_program::clock::UnixTimestamp}, }; -pub use { - add::*, governance::*, initialize::*, marginal_prices::*, remove_exact_burn::*, remove_exact_output::*, - remove_uniform::*, swap_exact_input::*, swap_exact_output::*, -}; +pub use {defi::*, governance::*, initialize::*, marginal_prices::*}; -pub mod add; pub mod initialize; pub mod marginal_prices; -pub mod remove_exact_burn; -pub mod remove_exact_output; -pub mod remove_uniform; -pub mod swap_exact_input; -pub mod swap_exact_output; +pub mod defi; pub mod governance; pub fn get_current_ts() -> Result { diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/remove_exact_burn.rs b/packages/solana-contracts/programs/two-pool/src/instructions/remove_exact_burn.rs deleted file mode 100644 index c5c51c8d9..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/remove_exact_burn.rs +++ /dev/null @@ -1,204 +0,0 @@ -use { - crate::{ - array_equalize, error::*, gen_pool_signer_seeds, invariant::Invariant, result_from_equalized, to_equalized, - TwoPool, TOKEN_COUNT, - }, - anchor_lang::prelude::*, - anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, - }, - std::iter::zip, -}; - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct RemoveExactBurnParams { - pub exact_burn_amount: u64, - pub output_token_index: u8, - pub minimum_output_amount: u64, -} - -#[derive(Accounts)] -pub struct RemoveExactBurn<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, -} - -impl<'info> RemoveExactBurn<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - let pool_state = &ctx.accounts.pool; - require!(!pool_state.is_paused, PoolError::PoolIsPaused); - require_keys_eq!( - ctx.accounts.pool_token_account_0.key(), - pool_state.token_keys[0], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!( - ctx.accounts.pool_token_account_1.key(), - pool_state.token_keys[1], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!(ctx.accounts.lp_mint.key(), pool_state.lp_mint_key, PoolError::InvalidMintAccount); - require_keys_eq!( - ctx.accounts.governance_fee.key(), - pool_state.governance_fee_key, - PoolError::InvalidGovernanceFeeAccount - ); - - // let pool_state_acct = &ctx.accounts.pool_state; - // let pool: two_pool::state::PoolState<{two_pool::TOKEN_COUNT}> = two_pool::state::PoolState::try_from_slice(&pool_state_acct.data.borrow())?; - // constraint = lp_mint.key() == propeller.token_bridge_mint @ PropellerError::InvalidMint - msg!("finished accounts context check"); - // let - Ok(()) - } -} - -pub fn handle_remove_exact_burn( - ctx: Context, - remove_exact_burn_params: RemoveExactBurnParams, -) -> Result { - let output_token_index = remove_exact_burn_params.output_token_index as usize; - let exact_burn_amount = remove_exact_burn_params.exact_burn_amount; - let lp_total_supply = ctx.accounts.lp_mint.supply; - require!(output_token_index < TOKEN_COUNT, PoolError::InvalidRemoveExactBurnParameters); - require_gt!(exact_burn_amount, 0, PoolError::InvalidRemoveExactBurnParameters); - require_gt!(lp_total_supply, exact_burn_amount, PoolError::InvalidRemoveExactBurnParameters); - - let pool = &ctx.accounts.pool; - let pool_token_account_0 = &ctx.accounts.pool_token_account_0; - let pool_token_account_1 = &ctx.accounts.pool_token_account_1; - let pool_balances = [ctx.accounts.pool_token_account_0.amount, ctx.accounts.pool_token_account_1.amount]; - let lp_mint = &ctx.accounts.lp_mint; - let governance_fee = &ctx.accounts.governance_fee; - // let user_transfer_auth = &ctx.accounts.user_transfer_authority; - let user_token_account_0 = &ctx.accounts.user_token_account_0; - let user_token_account_1 = &ctx.accounts.user_token_account_1; - let user_token_accounts = [user_token_account_0, user_token_account_1]; - - let pool_token_accounts = [pool_token_account_0, pool_token_account_1]; - - let current_ts = Clock::get()?.unix_timestamp; - require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); - let (user_amount, governance_mint_amount, latest_depth) = Invariant::::remove_exact_burn( - to_equalized(exact_burn_amount, pool.lp_decimal_equalizer), - output_token_index, - &array_equalize(pool_balances, pool.token_decimal_equalizers), - pool.amp_factor.get(current_ts), - pool.lp_fee.get(), - pool.governance_fee.get(), - to_equalized(lp_total_supply, pool.lp_decimal_equalizer), - pool.previous_depth.into(), - )?; - let (output_amount, governance_mint_amount, latest_depth) = result_from_equalized( - user_amount, - pool.token_decimal_equalizers[output_token_index], - governance_mint_amount, - pool.lp_decimal_equalizer, - latest_depth, - ); - let minimum_output_amount = remove_exact_burn_params.minimum_output_amount; - require_gte!(output_amount, minimum_output_amount, PoolError::OutsideSpecifiedLimits); - - let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); - token::burn( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Burn { - mint: ctx.accounts.lp_mint.to_account_info(), - from: ctx.accounts.user_lp_token_account.to_account_info(), - authority: ctx.accounts.user_transfer_authority.to_account_info(), - }, - ), - exact_burn_amount, - )?; - - let user_output_token_account = user_token_accounts[output_token_index]; - let pool_output_token_account = pool_token_accounts[output_token_index]; - token::transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: pool_output_token_account.to_account_info(), - to: user_output_token_account.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - output_amount, - )?; - - if governance_mint_amount > 0 { - token::mint_to( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::MintTo { - // source - mint: ctx.accounts.lp_mint.to_account_info(), - to: ctx.accounts.governance_fee.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - governance_mint_amount, - )?; - } - let pool = &mut ctx.accounts.pool; - pool.previous_depth = latest_depth; - Ok(output_amount) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/remove_exact_output.rs b/packages/solana-contracts/programs/two-pool/src/instructions/remove_exact_output.rs deleted file mode 100644 index b3f1dd1ab..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/remove_exact_output.rs +++ /dev/null @@ -1,211 +0,0 @@ -use { - crate::{ - array_equalize, error::*, gen_pool_signer_seeds, get_current_ts, invariant::Invariant, result_from_equalized, - to_equalized, TwoPool, TOKEN_COUNT, - }, - anchor_lang::prelude::*, - anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, - }, - std::iter::zip, -}; - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct RemoveExactOutputParams { - pub maximum_burn_amount: u64, - pub exact_output_amounts: [u64; TOKEN_COUNT], -} - -#[derive(Accounts)] -pub struct RemoveExactOutput<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, -} - -impl<'info> RemoveExactOutput<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - let pool_state = &ctx.accounts.pool; - require!(!pool_state.is_paused, PoolError::PoolIsPaused); - require_keys_eq!( - ctx.accounts.pool_token_account_0.key(), - pool_state.token_keys[0], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!( - ctx.accounts.pool_token_account_1.key(), - pool_state.token_keys[1], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!(ctx.accounts.lp_mint.key(), pool_state.lp_mint_key, PoolError::InvalidMintAccount); - require_keys_eq!( - ctx.accounts.governance_fee.key(), - pool_state.governance_fee_key, - PoolError::InvalidGovernanceFeeAccount - ); - - // let pool_state_acct = &ctx.accounts.pool_state; - // let pool: two_pool::state::PoolState<{two_pool::TOKEN_COUNT}> = two_pool::state::PoolState::try_from_slice(&pool_state_acct.data.borrow())?; - // constraint = lp_mint.key() == propeller.token_bridge_mint @ PropellerError::InvalidMint - msg!("finished accounts context check"); - // let - Ok(()) - } -} - -pub fn handle_remove_exact_output( - ctx: Context, - remove_exact_output_params: RemoveExactOutputParams, -) -> Result> { - let maximum_burn_amount = remove_exact_output_params.maximum_burn_amount; - let exact_output_amounts = remove_exact_output_params.exact_output_amounts; - let lp_total_supply = ctx.accounts.lp_mint.supply; - let pool_balances = [ctx.accounts.pool_token_account_0.amount, ctx.accounts.pool_token_account_1.amount]; - - require!(exact_output_amounts.iter().any(|amount| *amount > 0), PoolError::InvalidRemoveExactOutputParameters); - require_gt!(maximum_burn_amount, 0u64, PoolError::InvalidRemoveExactOutputParameters); - let are_output_amounts_valid = exact_output_amounts - .iter() - .zip(pool_balances.iter()) - .all(|(output_amount, pool_balance)| *output_amount < *pool_balance); - require!(are_output_amounts_valid, PoolError::InvalidRemoveExactOutputParameters); - - let pool = &ctx.accounts.pool; - let pool_token_account_0 = &ctx.accounts.pool_token_account_0; - let pool_token_account_1 = &ctx.accounts.pool_token_account_1; - let lp_mint = &ctx.accounts.lp_mint; - let governance_fee = &ctx.accounts.governance_fee; - // let user_transfer_auth = &ctx.accounts.user_transfer_authority; - let user_token_account_0 = &ctx.accounts.user_token_account_0; - let user_token_account_1 = &ctx.accounts.user_token_account_1; - let user_token_accounts = [user_token_account_0, user_token_account_1]; - - let pool_token_accounts = [pool_token_account_0, pool_token_account_1]; - - let current_ts = get_current_ts()?; - - let (burn_amount, governance_mint_amount, latest_depth) = Invariant::::remove_exact_output( - &array_equalize(exact_output_amounts, pool.token_decimal_equalizers), - &array_equalize(pool_balances, pool.token_decimal_equalizers), - pool.amp_factor.get(current_ts), - pool.lp_fee.get(), - pool.governance_fee.get(), - to_equalized(lp_total_supply, pool.lp_decimal_equalizer), - pool.previous_depth.into(), - )?; - let (burn_amount, governance_mint_amount, latest_depth) = result_from_equalized( - burn_amount, - pool.lp_decimal_equalizer, - governance_mint_amount, - pool.lp_decimal_equalizer, - latest_depth, - ); - - let maximum_burn_amount = remove_exact_output_params.maximum_burn_amount; - require_gte!(maximum_burn_amount, burn_amount, PoolError::OutsideSpecifiedLimits); - - let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); - token::burn( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Burn { - mint: ctx.accounts.lp_mint.to_account_info(), - from: ctx.accounts.user_lp_token_account.to_account_info(), - authority: ctx.accounts.user_transfer_authority.to_account_info(), - }, - ), - burn_amount, - )?; - - for i in 0..TOKEN_COUNT { - let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); - if exact_output_amounts[i] > 0 { - token::transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: pool_token_account.to_account_info(), - to: user_token_account.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - exact_output_amounts[i], - )?; - } - } - - if governance_mint_amount > 0 { - token::mint_to( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::MintTo { - // source - mint: ctx.accounts.lp_mint.to_account_info(), - to: ctx.accounts.governance_fee.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - governance_mint_amount, - )?; - } - let pool = &mut ctx.accounts.pool; - pool.previous_depth = latest_depth; - Ok(exact_output_amounts.into()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/remove_uniform.rs b/packages/solana-contracts/programs/two-pool/src/instructions/remove_uniform.rs deleted file mode 100644 index a92a612fa..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/remove_uniform.rs +++ /dev/null @@ -1,181 +0,0 @@ -use { - crate::{ - array_equalize, error::*, gen_pool_signer_seeds, invariant::Invariant, result_from_equalized, to_equalized, - DecimalU64, TwoPool, TOKEN_COUNT, - }, - anchor_lang::prelude::*, - anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, - }, - std::iter::zip, -}; - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct RemoveUniformParams { - pub exact_burn_amount: u64, - pub minimum_output_amounts: [u64; TOKEN_COUNT], -} - -#[derive(Accounts)] -pub struct RemoveUniform<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - #[account( - mut, - token::mint = lp_mint, - )] - pub user_lp_token_account: Box>, - - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, -} - -impl<'info> RemoveUniform<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - let pool_state = &ctx.accounts.pool; - require!(!pool_state.is_paused, PoolError::PoolIsPaused); - require_keys_eq!( - ctx.accounts.pool_token_account_0.key(), - pool_state.token_keys[0], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!( - ctx.accounts.pool_token_account_1.key(), - pool_state.token_keys[1], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!(ctx.accounts.lp_mint.key(), pool_state.lp_mint_key, PoolError::InvalidMintAccount); - require_keys_eq!( - ctx.accounts.governance_fee.key(), - pool_state.governance_fee_key, - PoolError::InvalidGovernanceFeeAccount - ); - - // let pool_state_acct = &ctx.accounts.pool_state; - // let pool: two_pool::state::PoolState<{two_pool::TOKEN_COUNT}> = two_pool::state::PoolState::try_from_slice(&pool_state_acct.data.borrow())?; - // constraint = lp_mint.key() == propeller.token_bridge_mint @ PropellerError::InvalidMint - msg!("finished accounts context check"); - // let - Ok(()) - } -} - -pub fn handle_remove_uniform( - ctx: Context, - remove_uniform_params: RemoveUniformParams, -) -> Result> { - let exact_burn_amount = remove_uniform_params.exact_burn_amount; - let minimum_output_amounts = remove_uniform_params.minimum_output_amounts; - let lp_total_supply = ctx.accounts.lp_mint.supply; - require_gt!(exact_burn_amount, 0u64, PoolError::InvalidRemoveUniformParameters); - require_gte!(lp_total_supply, exact_burn_amount, PoolError::InvalidRemoveUniformParameters); - - let pool = &ctx.accounts.pool; - let pool_token_account_0 = &ctx.accounts.pool_token_account_0; - let pool_token_account_1 = &ctx.accounts.pool_token_account_1; - let pool_balances = [ctx.accounts.pool_token_account_0.amount, ctx.accounts.pool_token_account_1.amount]; - - let user_share = DecimalU64::from(exact_burn_amount) / lp_total_supply; - //u64 can store 19 decimals, previous_depth can theoretically go up to TOKEN_COUNT * u64::MAX - //hence, just to be safe, we allow for previous depth to have up to 20 decimals - //therefore we can only multiply with a number with at most 18 decimals to stay within - //the 38 max decimals range of u128 - const DECIMAL_UPSHIFT: u32 = 18; - let user_depth = (pool.previous_depth * ((user_share * 10u64.pow(DECIMAL_UPSHIFT)).trunc() as u128)) - / 10u128.pow(DECIMAL_UPSHIFT); - let latest_depth = pool.previous_depth - user_depth; - - let user_token_account_0 = &ctx.accounts.user_token_account_0; - let user_token_account_1 = &ctx.accounts.user_token_account_1; - let user_token_accounts = [user_token_account_0, user_token_account_1]; - - let pool_token_accounts = [pool_token_account_0, pool_token_account_1]; - - let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); - - let mut output_amounts = vec![]; - for i in 0..TOKEN_COUNT { - let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); - let output_amount = (pool_balances[i] * user_share).trunc(); - output_amounts.push(output_amount); - require_gte!(output_amount, minimum_output_amounts[i], PoolError::OutsideSpecifiedLimits); - token::transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: pool_token_account.to_account_info(), - to: user_token_account.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - output_amount, - )?; - } - - token::burn( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Burn { - mint: ctx.accounts.lp_mint.to_account_info(), - from: ctx.accounts.user_lp_token_account.to_account_info(), - authority: ctx.accounts.user_transfer_authority.to_account_info(), - }, - ), - exact_burn_amount, - )?; - - let pool = &mut ctx.accounts.pool; - pool.previous_depth = latest_depth; - - Ok(output_amounts) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/swap_exact_input.rs b/packages/solana-contracts/programs/two-pool/src/instructions/swap_exact_input.rs deleted file mode 100644 index 48aa48b49..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/swap_exact_input.rs +++ /dev/null @@ -1,224 +0,0 @@ -use { - crate::{ - array_equalize, error::*, gen_pool_signer_seeds, invariant::Invariant, result_from_equalized, to_equalized, - TwoPool, TOKEN_COUNT, - }, - anchor_lang::prelude::*, - anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, - }, - std::iter::zip, -}; - -/// Swaps in the exact specified amounts for -/// at least `minimum_out_amount` of the output_token specified -/// by output_token_index -/// -/// Accounts expected by this instruction: -/// 0. `[w]` The pool state account -/// 1. `[]` pool authority -/// 2. ..2 + TOKEN_COUNT `[w]` pool's token accounts -/// 3. ..3 + TOKEN_COUNT `[w]` LP Token Mint -/// 4. ..4 + TOKEN_COUNT `[w]` governance_fee_account -/// 5. ..5 + TOKEN_COUNT `[s]` user transfer authority account -/// 6. ..6 + TOKEN_COUNT `[w]` user token accounts -#[derive(Accounts)] -pub struct SwapExactInput<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, -} - -impl<'info> SwapExactInput<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - let pool_state = &ctx.accounts.pool; - require!(!pool_state.is_paused, PoolError::PoolIsPaused); - require_keys_eq!( - ctx.accounts.pool_token_account_0.key(), - pool_state.token_keys[0], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!( - ctx.accounts.pool_token_account_1.key(), - pool_state.token_keys[1], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!(ctx.accounts.lp_mint.key(), pool_state.lp_mint_key, PoolError::InvalidMintAccount); - require_keys_eq!( - ctx.accounts.governance_fee.key(), - pool_state.governance_fee_key, - PoolError::InvalidGovernanceFeeAccount - ); - - // let pool_state_acct = &ctx.accounts.pool_state; - // let pool: two_pool::state::PoolState<{two_pool::TOKEN_COUNT}> = two_pool::state::PoolState::try_from_slice(&pool_state_acct.data.borrow())?; - // constraint = lp_mint.key() == propeller.token_bridge_mint @ PropellerError::InvalidMint - msg!("finished accounts context check"); - // let - Ok(()) - } -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct SwapExactInputParams { - pub exact_input_amounts: [u64; TOKEN_COUNT], - pub output_token_index: u8, - pub minimum_output_amount: u64, -} - -pub fn handle_swap_exact_input( - ctx: Context, - swap_exact_input_params: SwapExactInputParams, - // exact_input_amounts: [u64; TOKEN_COUNT], - // output_token_index: u8, - // minimum_output_amount: u64, -) -> Result { - // let output_token_index = output_token_index as usize; - let output_token_index = swap_exact_input_params.output_token_index as usize; - let exact_input_amounts = swap_exact_input_params.exact_input_amounts; - let minimum_output_amount = swap_exact_input_params.minimum_output_amount; - if exact_input_amounts.iter().all(|amount| *amount == 0) - || output_token_index >= TOKEN_COUNT - || exact_input_amounts[output_token_index] != 0 - { - return err!(PoolError::InvalidSwapExactInputParameters); - } - - let pool = &ctx.accounts.pool; - let pool_token_account_0 = &ctx.accounts.pool_token_account_0; - let pool_token_account_1 = &ctx.accounts.pool_token_account_1; - let pool_balances = [ctx.accounts.pool_token_account_0.amount, ctx.accounts.pool_token_account_1.amount]; - let lp_mint = &ctx.accounts.lp_mint; - let governance_fee = &ctx.accounts.governance_fee; - // let user_transfer_auth = &ctx.accounts.user_transfer_authority; - let user_token_account_0 = &ctx.accounts.user_token_account_0; - let user_token_account_1 = &ctx.accounts.user_token_account_1; - let user_token_accounts = [user_token_account_0, user_token_account_1]; - - let pool_token_accounts = [pool_token_account_0, pool_token_account_1]; - - let lp_total_supply = ctx.accounts.lp_mint.supply; - let current_ts = Clock::get()?.unix_timestamp; - require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); - let (user_amount, governance_mint_amount, latest_depth) = Invariant::::swap_exact_input( - &array_equalize(exact_input_amounts, pool.token_decimal_equalizers), - output_token_index, - &array_equalize(pool_balances, pool.token_decimal_equalizers), - pool.amp_factor.get(current_ts), - pool.lp_fee.get(), - pool.governance_fee.get(), - to_equalized(lp_total_supply, pool.lp_decimal_equalizer), - pool.previous_depth.into(), - )?; - let (output_amount, governance_mint_amount, latest_depth) = result_from_equalized( - user_amount, - pool.token_decimal_equalizers[output_token_index], - governance_mint_amount, - pool.lp_decimal_equalizer, - latest_depth, - ); - - require_gte!(output_amount, minimum_output_amount, PoolError::OutsideSpecifiedLimits); - - let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); - for i in 0..TOKEN_COUNT { - let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); - if exact_input_amounts[i] > 0 { - token::transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: user_token_account.to_account_info(), - to: pool_token_account.to_account_info(), - authority: ctx.accounts.user_transfer_authority.to_account_info(), - }, - ), - exact_input_amounts[i], - )?; - } - } - let user_output_token_account = user_token_accounts[output_token_index]; - let pool_output_token_account = pool_token_accounts[output_token_index]; - token::transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: pool_output_token_account.to_account_info(), - to: user_output_token_account.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - output_amount, - )?; - - if governance_mint_amount > 0 { - token::mint_to( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::MintTo { - // source - mint: ctx.accounts.lp_mint.to_account_info(), - to: ctx.accounts.governance_fee.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - governance_mint_amount, - )?; - } - let pool = &mut ctx.accounts.pool; - pool.previous_depth = latest_depth; - Ok(output_amount) -} diff --git a/packages/solana-contracts/programs/two-pool/src/instructions/swap_exact_output.rs b/packages/solana-contracts/programs/two-pool/src/instructions/swap_exact_output.rs deleted file mode 100644 index 549c6152b..000000000 --- a/packages/solana-contracts/programs/two-pool/src/instructions/swap_exact_output.rs +++ /dev/null @@ -1,238 +0,0 @@ -use { - crate::{ - array_equalize, error::*, gen_pool_signer_seeds, invariant::Invariant, result_from_equalized, to_equalized, - TwoPool, TOKEN_COUNT, - }, - anchor_lang::{ - prelude::*, - solana_program::{ - borsh::try_from_slice_unchecked, - instruction::Instruction, - program::{get_return_data, invoke, invoke_signed}, - program_option::COption, - system_instruction::transfer, - sysvar::SysvarId, - }, - }, - anchor_spl::{ - token, - token::{Mint, Token, TokenAccount}, - }, - std::iter::zip, -}; - -/// Swaps in the exact specified amounts for -/// at least `minimum_out_amount` of the output_token specified -/// by output_token_index -/// -/// Accounts expected by this instruction: -/// 0. `[w]` The pool state account -/// 1. `[]` pool authority -/// 2. ..2 + TOKEN_COUNT `[w]` pool's token accounts -/// 3. ..3 + TOKEN_COUNT `[w]` LP Token Mint -/// 4. ..4 + TOKEN_COUNT `[w]` governance_fee_account -/// 5. ..5 + TOKEN_COUNT `[s]` user transfer authority account -/// 6. ..6 + TOKEN_COUNT `[w]` user token accounts -#[derive(Accounts)] -pub struct SwapExactOutput<'info> { - #[account( - mut, - seeds = [ - b"two_pool".as_ref(), - pool_token_account_0.mint.as_ref(), - pool_token_account_1.mint.as_ref(), - lp_mint.key().as_ref(), - ], - bump = pool.bump - )] - pub pool: Account<'info, TwoPool>, - #[account( - mut, - token::mint = pool.token_mint_keys[0], - token::authority = pool, - )] - pub pool_token_account_0: Box>, - #[account( - mut, - token::mint = pool.token_mint_keys[1], - token::authority = pool, - )] - pub pool_token_account_1: Box>, - #[account(mut)] - pub lp_mint: Box>, - #[account( - mut, - token::mint = lp_mint - )] - pub governance_fee: Box>, - - pub user_transfer_authority: Signer<'info>, - - #[account( - mut, - token::mint = pool_token_account_0.mint, - )] - pub user_token_account_0: Box>, - - #[account( - mut, - token::mint = pool_token_account_1.mint, - )] - pub user_token_account_1: Box>, - - // //TODO: probably need a user_transfer_auth account since either the user or propeller could be payer for txn. - // // payer could be the same as user_auth if user manually completing the txn but still need - // // to have a separate field to account for it - // #[account(mut)] - // pub payer: Signer<'info>, - pub token_program: Program<'info, Token>, -} - -impl<'info> SwapExactOutput<'info> { - pub fn accounts(ctx: &Context) -> Result<()> { - let pool_state = &ctx.accounts.pool; - require!(!pool_state.is_paused, PoolError::PoolIsPaused); - require_keys_eq!( - ctx.accounts.pool_token_account_0.key(), - pool_state.token_keys[0], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!( - ctx.accounts.pool_token_account_1.key(), - pool_state.token_keys[1], - PoolError::PoolTokenAccountExpected - ); - require_keys_eq!(ctx.accounts.lp_mint.key(), pool_state.lp_mint_key, PoolError::InvalidMintAccount); - require_keys_eq!( - ctx.accounts.governance_fee.key(), - pool_state.governance_fee_key, - PoolError::InvalidGovernanceFeeAccount - ); - msg!("finished accounts context check"); - Ok(()) - } -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct SwapExactOutputParams { - pub maximum_input_amount: u64, - pub input_token_index: u8, - pub exact_output_amounts: [u64; TOKEN_COUNT], -} - -pub fn handle_swap_exact_output( - ctx: Context, - swap_exact_output_params: SwapExactOutputParams, -) -> Result> { - let input_token_index = swap_exact_output_params.input_token_index as usize; - let exact_output_amounts = swap_exact_output_params.exact_output_amounts; - let pool_balances = [ctx.accounts.pool_token_account_0.amount, ctx.accounts.pool_token_account_1.amount]; - let are_output_amounts_valid = exact_output_amounts.iter().any(|amount| *amount == 0); - require!(are_output_amounts_valid, PoolError::InvalidSwapExactOutputParameters); - require!(input_token_index < TOKEN_COUNT, PoolError::InvalidSwapExactOutputParameters); - require!(exact_output_amounts[input_token_index] == 0, PoolError::InvalidSwapExactOutputParameters); - // || exact_output_amounts[input_token_index] != 0 - // if exact_output_amounts.iter().all(|amount| *amount == 0) - // || input_token_index >= TOKEN_COUNT - // || exact_output_amounts[input_token_index] != 0 - // { - // return err!(PoolError::InvalidSwapExactOutputParameters); - // } - let are_pool_balances_sufficient = exact_output_amounts - .iter() - .zip(pool_balances.iter()) - .all(|(output_amount, pool_balance)| *output_amount < *pool_balance); - require!(are_pool_balances_sufficient, PoolError::InsufficientPoolTokenAccountBalance); - - let pool = &ctx.accounts.pool; - let pool_token_account_0 = &ctx.accounts.pool_token_account_0; - let pool_token_account_1 = &ctx.accounts.pool_token_account_1; - let pool_balances = [ctx.accounts.pool_token_account_0.amount, ctx.accounts.pool_token_account_1.amount]; - let lp_mint = &ctx.accounts.lp_mint; - let governance_fee = &ctx.accounts.governance_fee; - // let user_transfer_auth = &ctx.accounts.user_transfer_authority; - let user_token_account_0 = &ctx.accounts.user_token_account_0; - let user_token_account_1 = &ctx.accounts.user_token_account_1; - let user_token_accounts = [user_token_account_0, user_token_account_1]; - - let pool_token_accounts = [pool_token_account_0, pool_token_account_1]; - - let lp_total_supply = ctx.accounts.lp_mint.supply; - let current_ts = Clock::get()?.unix_timestamp; - require_gt!(current_ts, 0i64, PoolError::InvalidTimestamp); - let (user_amount, governance_mint_amount, latest_depth) = Invariant::::swap_exact_output( - input_token_index, - &array_equalize(exact_output_amounts, pool.token_decimal_equalizers), - &array_equalize(pool_balances, pool.token_decimal_equalizers), - pool.amp_factor.get(current_ts), - pool.lp_fee.get(), - pool.governance_fee.get(), - to_equalized(lp_total_supply, pool.lp_decimal_equalizer), - pool.previous_depth.into(), - )?; - let (input_amount, governance_mint_amount, latest_depth) = result_from_equalized( - user_amount, - pool.token_decimal_equalizers[input_token_index], - governance_mint_amount, - pool.lp_decimal_equalizer, - latest_depth, - ); - - let maximum_input_amount = swap_exact_output_params.maximum_input_amount; - require_gte!(maximum_input_amount, input_amount, PoolError::OutsideSpecifiedLimits); - - let user_input_token_account = user_token_accounts[input_token_index]; - let pool_input_token_account = pool_token_accounts[input_token_index]; - token::transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: user_input_token_account.to_account_info(), - to: pool_input_token_account.to_account_info(), - authority: ctx.accounts.user_transfer_authority.to_account_info(), - }, - ), - input_amount, - )?; - - let mut token_accounts = zip(user_token_accounts.into_iter(), pool_token_accounts.into_iter()); - - for i in 0..TOKEN_COUNT { - let (user_token_account, pool_token_account) = token_accounts.next().unwrap(); - if exact_output_amounts[i] > 0 { - token::transfer( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::Transfer { - // source - from: pool_token_account.to_account_info(), - to: user_token_account.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - exact_output_amounts[i], - )?; - } - } - - if governance_mint_amount > 0 { - token::mint_to( - CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::MintTo { - // source - mint: ctx.accounts.lp_mint.to_account_info(), - to: ctx.accounts.governance_fee.to_account_info(), - authority: ctx.accounts.pool.to_account_info(), - }, - &[&gen_pool_signer_seeds!(ctx.accounts.pool)[..]], - ), - governance_mint_amount, - )?; - } - let pool = &mut ctx.accounts.pool; - pool.previous_depth = latest_depth; - Ok(exact_output_amounts.into()) -} diff --git a/packages/solana-contracts/programs/two-pool/src/invariant.rs b/packages/solana-contracts/programs/two-pool/src/invariant.rs index 6aabc1e50..59909699e 100644 --- a/packages/solana-contracts/programs/two-pool/src/invariant.rs +++ b/packages/solana-contracts/programs/two-pool/src/invariant.rs @@ -63,7 +63,7 @@ impl From for DecT { let d = min(value.scale() as u8, unused_decimals_lower_bound); (value * Decimal::from(decimal::ten_to_the(d)), d) }; - Self::new(v.to_u64().unwrap(), d.into()).unwrap() + Self::new(v.to_u64().unwrap(), d).unwrap() } } @@ -159,7 +159,7 @@ impl Invariant { ) -> InvariantResult<(AmountT, AmountT, AmountT)> { let amp_factor: Decimal = amp_factor.into(); if lp_total_supply.is_zero() { - let depth = fast_round(Self::calculate_depth(&input_amounts, amp_factor, previous_depth.into())?); + let depth = fast_round(Self::calculate_depth(input_amounts, amp_factor, previous_depth.into())?); Ok((depth, 0.into(), depth)) } else { let lp_fee: FeeT = lp_fee.into(); @@ -167,8 +167,8 @@ impl Invariant { let total_fee = lp_fee + governance_fee; Self::add_remove( true, - &input_amounts, - &pool_balances, + input_amounts, + pool_balances, amp_factor, total_fee, governance_fee, @@ -194,9 +194,9 @@ impl Invariant { let total_fee = lp_fee + governance_fee; Self::swap( true, - &input_amounts, + input_amounts, output_index, - &pool_balances, + pool_balances, amp_factor, total_fee, governance_fee, @@ -221,9 +221,9 @@ impl Invariant { let total_fee = lp_fee + governance_fee; Self::swap( false, - &output_amounts, + output_amounts, input_index, - &pool_balances, + pool_balances, amp_factor, total_fee, governance_fee, @@ -249,7 +249,7 @@ impl Invariant { Self::remove_exact_burn_impl( burn_amount, output_index, - &pool_balances, + pool_balances, amp_factor, total_fee, governance_fee, @@ -273,8 +273,8 @@ impl Invariant { let total_fee = lp_fee + governance_fee; Self::add_remove( false, - &output_amounts, - &pool_balances, + output_amounts, + pool_balances, amp_factor, total_fee, governance_fee, @@ -323,7 +323,7 @@ impl Invariant { let initial_depth = Self::calculate_depth(pool_balances, amp_factor, previous_depth.into())?; // println!("SWAP initial_depth: {}", initial_depth); let mut updated_balances = - binary_op_balances(if is_exact_input { AmountT::add } else { AmountT::sub }, &pool_balances, &amounts); + binary_op_balances(if is_exact_input { AmountT::add } else { AmountT::sub }, pool_balances, amounts); // println!("SWAP updated_balances: {:?}", updated_balances); let swap_base_balances = &(if is_exact_input && !total_fee.is_zero() { let input_fee_amounts = unary_op_balances(|v| fast_round(total_fee * Decimal::from(v)), amounts); @@ -387,7 +387,7 @@ impl Invariant { ) -> InvariantResult<(AmountT, AmountT, AmountT)> { let initial_depth = Self::calculate_depth(pool_balances, amp_factor, previous_depth.into())?; let updated_balances = - binary_op_balances(if is_add { AmountT::add } else { AmountT::sub }, &pool_balances, &amounts); + binary_op_balances(if is_add { AmountT::add } else { AmountT::sub }, pool_balances, amounts); let sum_updated_balances = sum_balances(&updated_balances); let sum_pool_balances = sum_balances(pool_balances); let updated_depth = Self::calculate_depth( @@ -481,17 +481,17 @@ impl Invariant { previous_depth: AmountT, ) -> InvariantResult<(AmountT, AmountT, AmountT)> { debug_assert!(burn_amount > AmountT::zero()); - let initial_depth = Self::calculate_depth(&pool_balances, amp_factor, previous_depth.into())?; + let initial_depth = Self::calculate_depth(pool_balances, amp_factor, previous_depth.into())?; let updated_depth = initial_depth * (Decimal::from(lp_total_supply - burn_amount) / Decimal::from(lp_total_supply)); debug_assert!(initial_depth > updated_depth); - let known_balances = exclude_index(output_index, &pool_balances); + let known_balances = exclude_index(output_index, pool_balances); //we can pass the original pool balance as an initial guess because we know that the unknown balance has to be smaller let unknown_balance = Self::calculate_unknown_balance(&known_balances, updated_depth, amp_factor, pool_balances[output_index])?; let base_amount = pool_balances[output_index] - unknown_balance; let (output_amount, governance_mint_amount) = if !total_fee.is_zero() { - let sum_pool_balances = sum_balances(&pool_balances); + let sum_pool_balances = sum_balances(pool_balances); let taxable_percentage = Decimal::from(sum_pool_balances - pool_balances[output_index]) / Decimal::from(sum_pool_balances); let fee = Decimal::one() / (Decimal::one() - total_fee) - Decimal::one(); @@ -529,8 +529,7 @@ impl Invariant { return Ok(((pool_balances[0] * pool_balances[1]).integer_sqrt() * U128::from(2i32)).into()); } - let pool_balances_times_n: [_; TOKEN_COUNT] = - create_array(|i| U128::from(pool_balances[i]) * AmountT::from(TOKEN_COUNT)); + let pool_balances_times_n: [_; TOKEN_COUNT] = create_array(|i| pool_balances[i] * AmountT::from(TOKEN_COUNT)); let pool_balances_sum = sum_balances(pool_balances); // use f64 to calculate either the exact result (if there's sufficient precision) or an updated initial guess @@ -624,7 +623,7 @@ impl Invariant { fn calculate_unknown_balance( // this should have type &[AmountT; TOKEN_COUNT-1] but Rust currently does not support const operations // on const generics and hence TOKEN_COUNT-1 is illegal and so it has to be a Vec instead... - known_balances: &Vec, + known_balances: &[AmountT], depth: Decimal, amp_factor: AmpT, initial_guess: AmountT, @@ -701,7 +700,10 @@ impl Invariant { mod tests { use { super::*, - crate::{array_equalize, decimal::DecimalU128, to_equalized}, + crate::{ + common::{array_equalize, to_equalized}, + decimal::DecimalU128, + }, }; const BASE: AmountT = ten_to_the(10); @@ -970,8 +972,8 @@ mod tests { amp_factor, lp_fee, governance_fee, - lp_total_supply.into(), - lp_total_supply.into(), + lp_total_supply, + lp_total_supply, ) .unwrap(); println!( diff --git a/packages/solana-contracts/programs/two-pool/src/lib.rs b/packages/solana-contracts/programs/two-pool/src/lib.rs index 08e49942d..211df3608 100644 --- a/packages/solana-contracts/programs/two-pool/src/lib.rs +++ b/packages/solana-contracts/programs/two-pool/src/lib.rs @@ -1,4 +1,3 @@ -use rust_decimal::Decimal; use { crate::{ amp_factor::AmpFactor, @@ -9,8 +8,8 @@ use { pool_fee::PoolFee, state::TwoPool, }, - anchor_lang::{prelude::*, solana_program::clock::UnixTimestamp}, - anchor_spl::token::*, + anchor_lang::prelude::*, + rust_decimal::Decimal, }; pub mod amp_factor; @@ -25,14 +24,6 @@ pub mod state; // #[macro_use] mod macros; -//Note - using this b/c of not all bytes read error. found from using this - https://brson.github.io/2021/06/08/rust-on-solana -// use solana_program::borsh::try_from_slice_unchecked; -// const ENACT_DELAY: UnixTimestamp = 3 * 86400; -// const MAX_DECIMAL_DIFFERENCE: u8 = 8; -// -// type AtomicT = u64; -// type DecT = DecimalU64; - pub const TOKEN_COUNT: usize = 2; //TODO: option to have separate programIds depending on cluster. probably not needed and should keep the same @@ -73,21 +64,16 @@ pub mod two_pool { handle_initialize(ctx, amp_factor, lp_fee, governance_fee) } - #[access_control(Add::accounts(&ctx))] - pub fn add( - ctx: Context, - input_amounts: [u64; TOKEN_COUNT], - minimum_mint_amount: u64, - // params: AddParams - ) -> Result { + // #[access_control(AddOrRemove::accounts(&ctx))] + pub fn add(ctx: Context, input_amounts: [u64; TOKEN_COUNT], minimum_mint_amount: u64) -> Result { let params = AddParams { input_amounts, minimum_mint_amount }; handle_add(ctx, params) // handle_add(ctx, input_amounts, minimum_mint_amount) } - #[access_control(SwapExactInput::accounts(&ctx))] + // #[access_control(ctx.accounts.validate())] pub fn swap_exact_input( - ctx: Context, + ctx: Context, exact_input_amounts: [u64; TOKEN_COUNT], output_token_index: u8, minimum_output_amount: u64, @@ -100,19 +86,10 @@ pub mod two_pool { // let minimum_output_amount = params.minimum_output_amount; // handle_swap_exact_input(ctx, params) } - // pub fn swap_exact_input( - // ctx: Context, - // params: SwapExactInputParams, - // ) -> Result { - // handle_swap_exact_input(ctx, params) - // } - - //returning cpi data from this is a little redundant since it's already passed in the params - //but keeping for parity with other ixs - // note using Vec instead of [u64; TOKEN_COUNT] since anchor can't handle it properly. - #[access_control(SwapExactOutput::accounts(&ctx))] + + // #[access_control(ctx.accounts.validate())] pub fn swap_exact_output( - ctx: Context, + ctx: Context, maximum_input_amount: u64, input_token_index: u8, exact_output_amounts: [u64; TOKEN_COUNT], // params: SwapExactOutputParams, @@ -121,20 +98,17 @@ pub mod two_pool { handle_swap_exact_output(ctx, params) } - #[access_control(RemoveUniform::accounts(&ctx))] pub fn remove_uniform( - ctx: Context, + ctx: Context, exact_burn_amount: u64, minimum_output_amounts: [u64; TOKEN_COUNT], - // params: RemoveUniformParams, ) -> Result> { let params = RemoveUniformParams { exact_burn_amount, minimum_output_amounts }; handle_remove_uniform(ctx, params) } - #[access_control(RemoveExactBurn::accounts(&ctx))] pub fn remove_exact_burn( - ctx: Context, + ctx: Context, exact_burn_amount: u64, output_token_index: u8, minimum_output_amount: u64, @@ -144,55 +118,46 @@ pub mod two_pool { handle_remove_exact_burn(ctx, params) } - #[access_control(RemoveExactOutput::accounts(&ctx))] pub fn remove_exact_output( - ctx: Context, + ctx: Context, maximum_burn_amount: u64, exact_output_amounts: [u64; TOKEN_COUNT], - // params: RemoveExactOutputParams, ) -> Result> { let params = RemoveExactOutputParams { maximum_burn_amount, exact_output_amounts }; handle_remove_exact_output(ctx, params) } - //TODO: using 2 instead of TOKEN_COUNT const since anchor can't handle it properly. + //Note: using 2 instead of TOKEN_COUNT const since anchor can't handle it properly. #[access_control(MarginalPrices::accounts(&ctx))] - // pub fn marginal_prices(ctx: Context) -> Result { pub fn marginal_prices(ctx: Context) -> Result<[BorshDecimal; 2]> { handle_marginal_prices(ctx) } /** Governance Ixs **/ - #[access_control(PrepareGovernanceTransition::accounts(&ctx))] - pub fn prepare_governance_transition( - ctx: Context, - upcoming_governance_key: Pubkey, - ) -> Result<()> { - handle_prepare_governance_transition(ctx, upcoming_governance_key) - } - #[access_control(EnactGovernanceTransition::accounts(&ctx))] - pub fn enact_governance_transition(ctx: Context) -> Result<()> { - handle_enact_governance_transition(ctx) + pub fn adjust_amp_factor( + // ctx: Context, + ctx: Context, + target_ts: i64, + target_value: DecimalU64Anchor, + // params: AdjustAmpFactorParams, + ) -> Result<()> { + let params = AdjustAmpFactorParams { target_ts, target_value }; + handle_adjust_amp_factor(ctx, params) } - #[access_control(PrepareFeeChange::accounts(&ctx))] pub fn prepare_fee_change( - ctx: Context, + ctx: Context, lp_fee: DecimalU64Anchor, governance_fee: DecimalU64Anchor, - // params: PrepareFeeChangeParams, ) -> Result<()> { - // handle_prepare_fee_change(ctx, params) handle_prepare_fee_change(ctx, lp_fee, governance_fee) } - #[access_control(EnactFeeChange::accounts(&ctx))] - pub fn enact_fee_change(ctx: Context) -> Result<()> { + pub fn enact_fee_change(ctx: Context) -> Result<()> { handle_enact_fee_change(ctx) } - #[access_control(ChangeGovernanceFeeAccount::accounts(&ctx))] pub fn change_governance_fee_account( ctx: Context, new_governance_fee_key: Pubkey, @@ -200,27 +165,25 @@ pub mod two_pool { handle_change_governance_fee_account(ctx, new_governance_fee_key) } - #[access_control(AdjustAmpFactor::accounts(&ctx))] - pub fn adjust_amp_factor( - ctx: Context, - target_ts: i64, - target_value: DecimalU64Anchor, - // params: AdjustAmpFactorParams, + pub fn prepare_governance_transition( + ctx: Context, + upcoming_governance_key: Pubkey, ) -> Result<()> { - let params = AdjustAmpFactorParams { target_ts, target_value }; - handle_adjust_amp_factor(ctx, params) + handle_prepare_governance_transition(ctx, upcoming_governance_key) } - #[access_control(SetPaused::accounts(&ctx))] - pub fn set_paused(ctx: Context, paused: bool) -> Result<()> { - handle_set_paused(ctx, paused) + pub fn enact_governance_transition(ctx: Context) -> Result<()> { + handle_enact_governance_transition(ctx) } - #[access_control(ChangePauseKey::accounts(&ctx))] pub fn change_pause_key(ctx: Context, new_pause_key: Pubkey) -> Result<()> { handle_change_pause_key(ctx, new_pause_key) } + pub fn set_paused(ctx: Context, paused: bool) -> Result<()> { + handle_set_paused(ctx, paused) + } + #[access_control(CreateLpMetadata::accounts(&ctx))] pub fn create_lp_metadata( ctx: Context, diff --git a/packages/solana-contracts/programs/two-pool/src/pool_fee.rs b/packages/solana-contracts/programs/two-pool/src/pool_fee.rs index c93794ac1..e8524c773 100644 --- a/packages/solana-contracts/programs/two-pool/src/pool_fee.rs +++ b/packages/solana-contracts/programs/two-pool/src/pool_fee.rs @@ -76,7 +76,7 @@ mod tests { fn new_pool_fee2() { let swimlake_fee_value = new_u64(0, 0); let floored_fee = swimlake_fee_value.floor(DECIMALS); - let swimlake_u32 = (floored_fee.get_raw() * 10u64.pow((DECIMALS - floored_fee.get_decimals()) as u32)) as u32; + let _swimlake_u32 = (floored_fee.get_raw() * 10u64.pow((DECIMALS - floored_fee.get_decimals()) as u32)) as u32; // println!("swimlake_u32: {}", swimlake_u32); let swimlake_fee = PoolFee::new(swimlake_fee_value).unwrap(); assert_eq!(swimlake_fee_value, swimlake_fee_value.floor(DECIMALS)); @@ -85,8 +85,8 @@ mod tests { let metapool_fee_value = new_u64(300, 6); let metapool_fee_value2 = new_u64(3, 4); - let metapool_u32 = get_fee_u32(metapool_fee_value); - let metapool2_u32 = get_fee_u32(metapool_fee_value2); + let _metapool_u32 = get_fee_u32(metapool_fee_value); + let _metapool2_u32 = get_fee_u32(metapool_fee_value2); // println!("metapool_u32: {}", metapool_u32); // println!("metapool2_u32: {}", metapool2_u32); diff --git a/packages/solana-contracts/programs/two-pool/src/state.rs b/packages/solana-contracts/programs/two-pool/src/state.rs index 6f05e8d2a..ac07efb97 100644 --- a/packages/solana-contracts/programs/two-pool/src/state.rs +++ b/packages/solana-contracts/programs/two-pool/src/state.rs @@ -1,6 +1,6 @@ use { - crate::{amp_factor::AmpFactor, error::*, pool_fee::PoolFee, TOKEN_COUNT}, - anchor_lang::{prelude::*, solana_program::clock::UnixTimestamp}, + crate::{amp_factor::AmpFactor, pool_fee::PoolFee, TOKEN_COUNT}, + anchor_lang::prelude::*, }; // use pool_lib::amp_factor::AmpFactor; @@ -45,54 +45,42 @@ impl TwoPool { // self.lp_mint_key != Pubkey::default() // } - pub const LEN: usize = - // nonce - 1 + - // is_paused - 1 + - // amp_factor - AmpFactor::LEN + - // lp_fee - PoolFee::LEN + - // governance_fee - PoolFee::LEN + - // lp_mint_key - 32 + - // lp_decimal_equalizer - 1 + - // token_mint_keys - 32 * TOKEN_COUNT + - // token_decimal_equalizers - 1 * TOKEN_COUNT + - // token_keys - 32 * TOKEN_COUNT + - // pause_key - 32 + - // governance_key - 32 + - // governance_fee_key - 32 + - // prepared_governance_key - 32 + - // governance_transition_ts - 8 + - // prepared_lp_fee - PoolFee::LEN + - // prepared_governance_fee - PoolFee::LEN + - // fee_transition_ts - 8 + - // previous_depth - 16; + pub const LEN: usize = 1 + // nonce + 1 + // is_paused + AmpFactor::LEN + // amp_factor + PoolFee::LEN + // lp_fee + PoolFee::LEN + // governance_fee + 32 + // lp_mint_key + 1 + // lp_decimal_equalizer + (32 * TOKEN_COUNT) + // token_mint_keys + TOKEN_COUNT + // token_decimal_equalizers + (32 * TOKEN_COUNT) + // token_keys + 32 + // pause_key + 32 + // governance_key + 32 + // governance_fee_key + 32 + // prepared_governance_key + 8 + // governance_transition_ts + PoolFee::LEN + // prepared_lp_fee + PoolFee::LEN + // prepared_governance_fee + 8 + // fee_transition_ts + 16; // previous_depth // Note: this is a workaround for to be able to declare the seeds in the // governance ix. anchor does not handle using `pool.token_mint_keys[0]` directly // in the #[account] macro - pub fn get_token_mint_0(&self) -> Result { - Ok(self.token_mint_keys[0]) + pub fn get_token_mint_0(&self) -> Pubkey { + self.token_mint_keys[0] } - pub fn get_token_mint_1(&self) -> Result { - Ok(self.token_mint_keys[1]) + pub fn get_token_mint_1(&self) -> Pubkey { + self.token_mint_keys[1] + } + + pub fn new_get_token_mint_0(&self) -> Pubkey { + self.token_mint_keys[0] + } + + pub fn new_get_token_mint_1(&self) -> Pubkey { + self.token_mint_keys[1] } } diff --git a/packages/solana-contracts/programs/two-pool/tests/governance_test.rs b/packages/solana-contracts/programs/two-pool/tests/governance_test.rs index 5546f3bdd..0b0f44e8b 100644 --- a/packages/solana-contracts/programs/two-pool/tests/governance_test.rs +++ b/packages/solana-contracts/programs/two-pool/tests/governance_test.rs @@ -62,10 +62,11 @@ async fn test_governance_transition() { let prepare_gov_transition_ix = program .request() .accounts(two_pool::accounts::PrepareGovernanceTransition { - common_governance: two_pool::accounts::CommonGovernance { + governance: two_pool::accounts::Governance { pool: pt_ctxt.pool_key, - governance: pt_ctxt.get_governance().pubkey(), + governance_key: pt_ctxt.get_governance().pubkey(), }, + upcoming_governance_key: new_governance_key.pubkey(), }) .args(two_pool::instruction::PrepareGovernanceTransition { upcoming_governance_key: new_governance_key.pubkey(), @@ -95,11 +96,9 @@ async fn test_governance_transition() { let enact_gov_transition_ix = program .request() - .accounts(two_pool::accounts::EnactGovernanceTransition { - common_governance: two_pool::accounts::CommonGovernance { - pool: pt_ctxt.pool_key, - governance: pt_ctxt.get_governance().pubkey(), - }, + .accounts(two_pool::accounts::Governance { + pool: pt_ctxt.pool_key, + governance_key: pt_ctxt.get_governance().pubkey(), }) .args(two_pool::instruction::EnactGovernanceTransition {}) .instructions() @@ -144,6 +143,76 @@ async fn test_governance_transition() { assert_eq!(pool_state.governance_key, new_governance_key.pubkey()); assert_eq!(pool_state.prepared_governance_key, Pubkey::default()); assert_eq!(pool_state.governance_transition_ts, 0i64); + + let lp_fee = DecimalU64Anchor { value: 400u64, decimals: 6u8 }; + let governance_fee = DecimalU64Anchor { value: 200u64, decimals: 6u8 }; + + // governance ixs with old key should fail. + + let prepare_fee_change_ix_fail = program + .request() + .accounts(two_pool::accounts::Governance { + pool: pt_ctxt.pool_key, + governance_key: pt_ctxt.get_governance().pubkey(), + }) + .args(two_pool::instruction::PrepareFeeChange { + lp_fee, + governance_fee, + // params: PrepareFeeChangeParams { + // lp_fee, + // governance_fee, + // }, + }) + .instructions() + .unwrap() + .pop() + .unwrap(); + + let recent_blockhash = pt_ctxt.get_latest_blockhash().await; + + let prepare_fee_change_txn_fail = Transaction::new_signed_with_payer( + &[prepare_fee_change_ix_fail], + Some(&pt_ctxt.get_payer().pubkey()), + &[pt_ctxt.get_payer(), pt_ctxt.get_governance()], + recent_blockhash, + ); + + pt_ctxt + .process_transaction(prepare_fee_change_txn_fail) + .await + .expect_err("prepare fee change should fail with old governance key"); + + // should be able to execute governance ix with new governance key now. + + let prepare_fee_change_ix = program + .request() + .accounts(two_pool::accounts::Governance { + pool: pt_ctxt.pool_key, + governance_key: new_governance_key.pubkey(), + }) + .args(two_pool::instruction::PrepareFeeChange { + lp_fee, + governance_fee, + // params: PrepareFeeChangeParams { + // lp_fee, + // governance_fee, + // }, + }) + .instructions() + .unwrap() + .pop() + .unwrap(); + + let recent_blockhash = pt_ctxt.get_latest_blockhash().await; + + let prepare_fee_change_txn = Transaction::new_signed_with_payer( + &[prepare_fee_change_ix], + Some(&pt_ctxt.get_payer().pubkey()), + &[pt_ctxt.get_payer(), &new_governance_key], + recent_blockhash, + ); + + pt_ctxt.process_transaction(prepare_fee_change_txn).await.unwrap(); } #[tokio::test] @@ -169,22 +238,13 @@ async fn test_fee_change() { let lp_fee = DecimalU64Anchor { value: 400u64, decimals: 6u8 }; let governance_fee = DecimalU64Anchor { value: 200u64, decimals: 6u8 }; - let prepare_gov_transition_ix = program + let prepare_fee_change_ix = program .request() - .accounts(two_pool::accounts::PrepareFeeChange { - common_governance: two_pool::accounts::CommonGovernance { - pool: pt_ctxt.pool_key, - governance: pt_ctxt.get_governance().pubkey(), - }, - }) - .args(two_pool::instruction::PrepareFeeChange { - lp_fee, - governance_fee, - // params: PrepareFeeChangeParams { - // lp_fee, - // governance_fee, - // }, + .accounts(two_pool::accounts::Governance { + pool: pt_ctxt.pool_key, + governance_key: pt_ctxt.get_governance().pubkey(), }) + .args(two_pool::instruction::PrepareFeeChange { lp_fee, governance_fee }) .instructions() .unwrap() .pop() @@ -192,14 +252,14 @@ async fn test_fee_change() { let recent_blockhash = pt_ctxt.get_latest_blockhash().await; - let prepare_gov_transition_txn = Transaction::new_signed_with_payer( - &[prepare_gov_transition_ix], + let prepare_fee_change_txn = Transaction::new_signed_with_payer( + &[prepare_fee_change_ix], Some(&pt_ctxt.get_payer().pubkey()), &[pt_ctxt.get_payer(), pt_ctxt.get_governance()], recent_blockhash, ); - pt_ctxt.process_transaction(prepare_gov_transition_txn).await.unwrap(); + pt_ctxt.process_transaction(prepare_fee_change_txn).await.unwrap(); let pool_state_account = pt_ctxt.get_pool_state_data(pt_ctxt.pool_key).await; let pool_state: TwoPool = TwoPool::try_deserialize(&mut pool_state_account.data.as_slice()).unwrap(); @@ -211,11 +271,9 @@ async fn test_fee_change() { let enact_fee_change_ix = program .request() - .accounts(two_pool::accounts::EnactFeeChange { - common_governance: two_pool::accounts::CommonGovernance { - pool: pt_ctxt.pool_key, - governance: pt_ctxt.get_governance().pubkey(), - }, + .accounts(two_pool::accounts::Governance { + pool: pt_ctxt.pool_key, + governance_key: pt_ctxt.get_governance().pubkey(), }) .args(two_pool::instruction::EnactFeeChange {}) .instructions() @@ -393,16 +451,7 @@ impl DeployedPoolProgramTestContext { system_program: anchor_lang::prelude::System::id(), rent: anchor_lang::prelude::Rent::id(), }) - .args(two_pool::instruction::Initialize { - // params: InitializeParams { - // amp_factor, - // lp_fee, - // governance_fee, - // }, - amp_factor, - lp_fee, - governance_fee, - }) + .args(two_pool::instruction::Initialize { amp_factor, lp_fee, governance_fee }) .instructions() .unwrap() .pop() diff --git a/packages/solana-contracts/scripts/initializePropeller.ts b/packages/solana-contracts/scripts/initializePropeller.ts index 8cc5eff4b..dad22b9ab 100644 --- a/packages/solana-contracts/scripts/initializePropeller.ts +++ b/packages/solana-contracts/scripts/initializePropeller.ts @@ -1,9 +1,11 @@ +import crypto from "crypto"; import * as fs from "fs"; import * as path from "path"; import type { ChainId } from "@certusone/wormhole-sdk"; import { CHAIN_ID_ETH, + createNonce, tryHexToNativeString, tryNativeToHexString, } from "@certusone/wormhole-sdk"; @@ -33,13 +35,14 @@ import { TWO_POOL_PID, USDC_TO_TOKEN_NUMBER, USDT_TO_TOKEN_NUMBER, + maxStaleness, setComputeUnitLimitIx, } from "../src/__tests__/consts"; import { getPropellerPda, getPropellerRedeemerPda, - getTargetChainIdMapAddr, - getTargetTokenIdMapAddr, + getTargetChainMapAddr, + getToTokenNumberMapAddr, } from "../src/__tests__/propeller/propellerUtils"; // import type { Propeller } from "../src/artifacts/propeller"; @@ -61,7 +64,8 @@ const propellerProgram = new Program( const splToken = Spl.token(provider); const payer = (provider.wallet as NodeWallet).payer; -const propellerAdmin = payer; +const propellerGovernanceKey = payer; +const propellerPauseKey = payer; type InitParameters = { readonly gasKickstartAmount: BN; @@ -160,7 +164,7 @@ async function setupPropeller() { console.info(`swimUsdPoolInfo: ${JSON.stringify(swimUsdPoolInfo)}`); propellerInfo = await initializePropellerState(); console.info(`propellerInfo: ${JSON.stringify(propellerInfo, null, 2)}`); - targetTokenIdMaps = await createTargetTokenIdMaps(); + targetTokenIdMaps = await createTokenNumberMaps(); console.info( `targetTokenIdMaps: ${JSON.stringify( Object.fromEntries(targetTokenIdMaps), @@ -168,7 +172,7 @@ async function setupPropeller() { ); await fetchAndPrintIdMap( targetTokenIdMaps, - async (addr) => await propellerProgram.account.tokenIdMap.fetch(addr), + async (addr) => await propellerProgram.account.tokenNumberMap.fetch(addr), // propellerProgram.account.tokenIdMap.fetch, "targetTokenId", ); @@ -278,15 +282,16 @@ async function initializePropellerState(): Promise { ...DEFAULT_INIT_PROPELLER_PARAMS, marginalPricePool: swimUsdPoolInfo.address, marginalPricePoolTokenMint: swimUsdPoolInfo.tokenMints[0], + maxStaleness: maxStaleness, }; console.info(` propellerAddr: ${propellerAddr.toString()} propellerFeeVault: ${propellerFeeVault.toString()} propellerRedeemer: ${propellerRedeemer.toString()} propellerRedeemerEscrow: ${propellerRedeemerEscrow.toString()} - propellerAdmin: ${propellerAdmin.publicKey.toString()} + propellerGovernanceKey: ${propellerGovernanceKey.publicKey.toString()} + propellerPauseKey: ${propellerPauseKey.publicKey.toString()} payer: ${payer.publicKey.toString()} - `); let propellerData = await propellerProgram.account.propeller.fetchNullable( @@ -300,7 +305,8 @@ async function initializePropellerState(): Promise { propeller: propellerAddr, propellerRedeemerEscrow, propellerFeeVault, - admin: propellerAdmin.publicKey, + governanceKey: propellerGovernanceKey.publicKey, + pauseKey: propellerGovernanceKey.publicKey, swimUsdMint, payer: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, @@ -314,7 +320,7 @@ async function initializePropellerState(): Promise { twoPoolProgram: twoPoolProgram.programId, aggregator, }) - .signers([propellerAdmin, payer]) + .signers([propellerGovernanceKey, payer]) .rpc(); } propellerData = await propellerProgram.account.propeller.fetch(propellerAddr); @@ -329,83 +335,89 @@ async function initializePropellerState(): Promise { }; } -async function createTargetTokenIdMaps(): Promise< +async function createTokenNumberMaps(): Promise< ReadonlyMap > { - const swimUsdTokenIdMap = { + const swimUsdTokenNumberMap = { pool: swimUsdPoolInfo.address, poolTokenIndex: 0, poolTokenMint: swimUsdPoolInfo.lpMint, - poolIx: { transfer: {} }, + toTokenStep: { transfer: {} }, }; - const usdcTokenIdMap = { + const usdcTokenNumberMap = { pool: swimUsdPoolInfo.address, poolTokenIndex: 0, poolTokenMint: swimUsdPoolInfo.tokenMints[0], - poolIx: { removeExactBurn: {} }, + toTokenStep: { removeExactBurn: {} }, }; - const usdtTokenIdMap = { + const usdtTokenNumberMap = { pool: swimUsdPoolInfo.address, poolTokenIndex: 1, poolTokenMint: swimUsdPoolInfo.tokenMints[1], - poolIx: { removeExactBurn: {} }, + toTokenStep: { removeExactBurn: {} }, }; - const outputTokenIdMapAddrEntries = await Promise.all( + const tokenNumberMapAddrEntries = await Promise.all( [ { - targetTokenId: SWIM_USD_TO_TOKEN_NUMBER, - tokenIdMap: swimUsdTokenIdMap, + toTokenNumber: SWIM_USD_TO_TOKEN_NUMBER, + tokenNumberMap: swimUsdTokenNumberMap, + }, + { + toTokenNumber: USDC_TO_TOKEN_NUMBER, + tokenNumberMap: usdcTokenNumberMap, + }, + { + toTokenNumber: USDT_TO_TOKEN_NUMBER, + tokenNumberMap: usdtTokenNumberMap, }, - { targetTokenId: USDC_TO_TOKEN_NUMBER, tokenIdMap: usdcTokenIdMap }, - { targetTokenId: USDT_TO_TOKEN_NUMBER, tokenIdMap: usdtTokenIdMap }, - ].map(async ({ targetTokenId, tokenIdMap }) => { - const [tokenIdMapAddr] = await getTargetTokenIdMapAddr( + ].map(async ({ toTokenNumber, tokenNumberMap }) => { + const [toTokenNumberMapAddr] = await getToTokenNumberMapAddr( propellerInfo.address, - targetTokenId, + toTokenNumber, propellerProgram.programId, ); if ( - !(await propellerProgram.account.tokenIdMap.fetchNullable( - tokenIdMapAddr, + !(await propellerProgram.account.tokenNumberMap.fetchNullable( + toTokenNumberMapAddr, )) ) { const createTokenIdMapTxn = propellerProgram.methods - .createTokenIdMap( - targetTokenId, - tokenIdMap.pool, - tokenIdMap.poolTokenIndex, - tokenIdMap.poolTokenMint, - tokenIdMap.poolIx, + .createTokenNumberMap( + toTokenNumber, + tokenNumberMap.pool, + tokenNumberMap.poolTokenIndex, + tokenNumberMap.poolTokenMint, + tokenNumberMap.toTokenStep, ) .accounts({ propeller: propellerInfo.address, - admin: propellerAdmin.publicKey, + governanceKey: propellerGovernanceKey.publicKey, payer: payer.publicKey, systemProgram: web3.SystemProgram.programId, // rent: web3.SYSVAR_RENT_PUBKEY, - pool: tokenIdMap.pool, + pool: tokenNumberMap.pool, twoPoolProgram: twoPoolProgram.programId, }) - .signers([propellerAdmin]); + .signers([propellerGovernanceKey]); const pubkeys = await createTokenIdMapTxn.pubkeys(); await createTokenIdMapTxn.rpc(); - const derivedTokenIdMapAddr = pubkeys.tokenIdMap; - if (!derivedTokenIdMapAddr) { + const derivedTokenNumberMapAddr = pubkeys.tokenNumberMap; + if (!derivedTokenNumberMapAddr) { throw new Error("Failed to derive tokenIdMapAddr"); } console.info(` - derivedTokenIdMapAddr: ${derivedTokenIdMapAddr.toString()} - tokenIdMapAddr: ${tokenIdMapAddr.toString()} + derivedTokenIdMapAddr: ${derivedTokenNumberMapAddr.toString()} + toTokenNumberMapAddr: ${toTokenNumberMapAddr.toString()} `); } - return { targetTokenId, tokenIdMapAddr }; + return { toTokenNumber, toTokenNumberMapAddr }; }), ); return new Map( - outputTokenIdMapAddrEntries.map(({ targetTokenId, tokenIdMapAddr }) => { - return [targetTokenId, tokenIdMapAddr]; + tokenNumberMapAddrEntries.map(({ toTokenNumber, toTokenNumberMapAddr }) => { + return [toTokenNumber, toTokenNumberMapAddr]; }), ); } @@ -449,7 +461,7 @@ async function createTargetChainMaps() { tryNativeToHexString(targetAddress, wormholeChainId as ChainId), "hex", ); - const [targetChainMapAddr] = await getTargetChainIdMapAddr( + const [targetChainMapAddr] = await getTargetChainMapAddr( propellerInfo.address, wormholeChainId, propellerProgram.programId, @@ -465,13 +477,13 @@ async function createTargetChainMaps() { .createTargetChainMap(wormholeChainId, targetAddrWormholeFormat) .accounts({ propeller: propellerInfo.address, - admin: propellerAdmin.publicKey, + governanceKey: propellerGovernanceKey.publicKey, payer: payer.publicKey, targetChainMap: targetChainMapAddr, systemProgram: web3.SystemProgram.programId, // rent: web3.SYSVAR_RENT_PUBKEY, }) - .signers([propellerAdmin]) + .signers([propellerGovernanceKey]) .rpc(); } @@ -545,7 +557,6 @@ async function transferTokens() { payer.publicKey, ); - let memo = 0; // const evmTargetTokenIds = [0, 1]; const evmTargetTokenIds = [0]; const targetChain = CHAIN_ID_ETH; @@ -602,10 +613,7 @@ async function transferTokens() { // crossChainTransferNativeWithPayloadTxnSig: ${crossChainTransferNativeWithPayloadTxnSig}`, // ); - const memoStr = (++memo).toString().padStart(16, "0"); - const memoBuffer2 = Buffer.alloc(16); - memoBuffer2.write(memoStr); - + const memoBuffer = createMemoId(); const propellerEnabledTransferAmount = transferAmount.div(new BN(2)); const propellerEnabledWormholeMessage = web3.Keypair.generate(); @@ -618,21 +626,23 @@ async function transferTokens() { targetChain: ${targetChain}, targetOwner(native): ${evmOwnerNative}, targetOwner(Hex): ${evmOwnerEthHexStr}, - memo(str): ${memoStr}, - memo(buffer): ${memoBuffer2.toString("hex")}, + memo("hex"): ${memoBuffer.toString("hex")}, + ) `); + const nonce = createNonce().readUInt32LE(0); const propellerTransferNativeWithPayloadTxnSig: string = await propellerProgram.methods .propellerTransferNativeWithPayload( + nonce, propellerEnabledTransferAmount, targetChain, evmOwner, gasKickstart, maxFee, evmTargetTokenId, - memoBuffer2, + memoBuffer, ) .accounts({ propeller: propellerInfo.address, @@ -659,7 +669,7 @@ async function transferTokens() { tokenProgram: splToken.programId, }) .preInstructions([setComputeUnitLimitIx]) - .postInstructions([createMemoInstruction(memoStr)]) + .postInstructions([createMemoInstruction(memoBuffer.toString("hex"))]) .signers([payer, propellerEnabledWormholeMessage]) .rpc(); @@ -670,5 +680,12 @@ async function transferTokens() { ); } +function createMemoId() { + const SWIM_MEMO_LENGTH = 16; + // NOTE: Please always use random bytes to avoid conflicts with other users + return crypto.randomBytes(SWIM_MEMO_LENGTH); + // return (++memoId).toString().padStart(16, "0"); +} + void setupPropeller(); // voidloadConfig(); diff --git a/packages/solana-contracts/scripts/parseAccount.ts b/packages/solana-contracts/scripts/parseAccount.ts new file mode 100644 index 000000000..acc96678e --- /dev/null +++ b/packages/solana-contracts/scripts/parseAccount.ts @@ -0,0 +1,44 @@ +import { AnchorProvider, web3 } from "@project-serum/anchor"; + +import { + formatParsedTokenTransferWithSwimPayloadPostedMessage, + parseTokenTransferWithSwimPayloadPostedMessage, +} from "../src/__tests__/propeller/propellerUtils"; + +const envProvider = AnchorProvider.env(); + +async function parseWormholeMessageAccount(address: web3.PublicKey) { + const wormholeMessageAccountInfo = + await envProvider.connection.getAccountInfo(address); + if (!wormholeMessageAccountInfo) { + console.error(`No account found at ${address.toBase58()}`); + return; + } + const parsedTokenTransferWithSwimPayloadPostedMessage = + await parseTokenTransferWithSwimPayloadPostedMessage( + wormholeMessageAccountInfo.data, + ); + console.info(`finished parsing`); + const formattedMessage = + formatParsedTokenTransferWithSwimPayloadPostedMessage( + parsedTokenTransferWithSwimPayloadPostedMessage, + ); + console.info( + `formattedMessage: ${JSON.stringify(formattedMessage, null, 2)}`, + ); +} + +const main = async (): Promise => { + const [address] = process.argv.slice(2); + if (!address) { + console.error("Please provide a wormhole message account address"); + process.exit(1); + } + console.info(`address: ${address}`); + await parseWormholeMessageAccount(new web3.PublicKey(address)); +}; + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/packages/solana-contracts/scripts/scratch.ts b/packages/solana-contracts/scripts/scratch.ts index 9c28a0ff0..6cdcf9b4c 100644 --- a/packages/solana-contracts/scripts/scratch.ts +++ b/packages/solana-contracts/scripts/scratch.ts @@ -30,8 +30,8 @@ import { import { getPropellerPda, getPropellerRedeemerPda, - getTargetChainIdMapAddr, - getTargetTokenIdMapAddr, + getTargetChainMapAddr, + getToTokenNumberMapAddr, } from "../src/__tests__/propeller/propellerUtils"; import type { Propeller } from "../src/artifacts/propeller"; import type { TwoPool } from "../src/artifacts/two_pool"; @@ -143,8 +143,8 @@ async function setupPropeller() { // ); // await fetchAndPrintIdMap( // targetTokenIdMaps, - // async (addr) => await propellerProgram.account.tokenIdMap.fetch(addr), - // // propellerProgram.account.tokenIdMap.fetch, + // async (addr) => await propellerProgram.account.tokenNumberMap.fetch(addr), + // // propellerProgram.account.tokenNumberMap.fetch, // "targetTokenId", // ); // @@ -258,7 +258,7 @@ async function initializePropellerState(): Promise { propeller: propellerAddr, propellerRedeemerEscrow, propellerFeeVault, - admin: propellerAdmin.publicKey, + governanceKey: propellerAdmin.publicKey, swimUsdMint, payer: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, diff --git a/packages/solana-contracts/src/__tests__/consts.ts b/packages/solana-contracts/src/__tests__/consts.ts index 4a0bcad4f..af5c9be5a 100644 --- a/packages/solana-contracts/src/__tests__/consts.ts +++ b/packages/solana-contracts/src/__tests__/consts.ts @@ -14,10 +14,11 @@ export const rpcCommitmentConfig = { skipPreflight: true, }; -const ethTokenBridgeStr = "0x0290FB167208Af455bB137780163b7B7a9a10C16"; +export const ethTokenBridgeNativeStr = + "0x0290FB167208Af455bB137780163b7B7a9a10C16"; //0000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16 const ethTokenBridgeEthHexStr = tryNativeToHexString( - ethTokenBridgeStr, + ethTokenBridgeNativeStr, CHAIN_ID_ETH, ); //ethTokenBridge.toString() = gibberish @@ -28,10 +29,12 @@ const bscTokenBridgeBscHexStr = tryNativeToHexString( bscTokenBridgeStr, CHAIN_ID_BSC, ); + export const bscTokenBridge = Buffer.from(bscTokenBridgeBscHexStr, "hex"); -const ethRoutingContractStr = "0x0290FB167208Af455bB137780163b7B7a9a10C17"; +const evmRoutingContractNativeStr = + "0x280999aB9aBfDe9DC5CE7aFB25497d6BB3e8bDD4"; // const ethRoutingContractEthUint8Arr = tryNativeToUint8Array( -// ethRoutingContractStr, +// evmRoutingContractNativeStr, // CHAIN_ID_ETH, // ); // console.info(` @@ -40,21 +43,21 @@ const ethRoutingContractStr = "0x0290FB167208Af455bB137780163b7B7a9a10C17"; // ethRoutingContractEthUint8Arr, // )} // `); -export const ethRoutingContractEthHexStr = tryNativeToHexString( - ethRoutingContractStr, +export const evmRoutingContractHexStr = tryNativeToHexString( + evmRoutingContractNativeStr, CHAIN_ID_ETH, ); -export const ethRoutingContract = Buffer.from( - ethRoutingContractEthHexStr, +export const evmRoutingContractBuffer = Buffer.from( + evmRoutingContractHexStr, "hex", ); export const routingContracts = [ - { targetChainId: CHAIN_ID_ETH, address: ethRoutingContract }, - { targetChainId: CHAIN_ID_BSC, address: ethRoutingContract }, + { targetChainId: CHAIN_ID_ETH, address: evmRoutingContractBuffer }, + { targetChainId: CHAIN_ID_BSC, address: evmRoutingContractBuffer }, ]; +//TODO: figure out actual value for compute budget export const setComputeUnitLimitIx: web3.TransactionInstruction = web3.ComputeBudgetProgram.setComputeUnitLimit({ - // units: 420690, units: 900000, }); export const SWIM_USD_TO_TOKEN_NUMBER = 0; @@ -64,16 +67,17 @@ export const marginalPricePoolTokenIndex = 0; export const swimPayloadVersion = 1; export const usdcPoolTokenIndex = 0; export const usdtPoolTokenIndex = 1; -export const metapoolMint1OutputTokenIndex = 3; +export const metapoolMint1ToTokenNumber = 3; export const metapoolMint1PoolTokenIndex = 1; // const evmOwner = Buffer.from(evmOwnerEthHexStr, "hex"); -export const gasKickstartAmount: BN = new BN(0.75 * LAMPORTS_PER_SOL); -export const initAtaFee: BN = new BN(0.25 * LAMPORTS_PER_SOL); +export const gasKickstartAmount: BN = new BN(0.25 * LAMPORTS_PER_SOL); +export const initAtaFee: BN = new BN(0.0025 * LAMPORTS_PER_SOL); export const secpVerifyInitFee: BN = new BN(0.000045 * LAMPORTS_PER_SOL); export const secpVerifyFee: BN = new BN(0.00004 * LAMPORTS_PER_SOL); export const postVaaFee: BN = new BN(0.00005 * LAMPORTS_PER_SOL); export const completeWithPayloadFee: BN = new BN(0.0000055 * LAMPORTS_PER_SOL); export const processSwimPayloadFee: BN = new BN(0.00001 * LAMPORTS_PER_SOL); // const confirmedCommitment = { commitment: "confirmed" as web3.Finality }; +export const maxStaleness = new BN("9223372036854775807"); export const ampFactor = { value: new BN(300), decimals: 0 }; export const lpFee = { value: new BN(300), decimals: 6 }; //lp fee = .000300 = 0.0300% 3bps export const governanceFee = { value: new BN(100), decimals: 6 }; //gov fee = .000100 = (0.0100%) 1bps @@ -96,3 +100,5 @@ export const TWO_POOL_PID = new web3.PublicKey( export const PROPELLER_PID = new web3.PublicKey( "9z6G41AyXk73r1E4nTv81drQPtEqupCSAnsLdGV5WGfK", ); + +export const SWIM_MEMO_LENGTH = 16; diff --git a/packages/solana-contracts/src/__tests__/propeller/engine.test.ts b/packages/solana-contracts/src/__tests__/propeller/engine.test.ts index 51f62b3a5..2a0aec056 100644 --- a/packages/solana-contracts/src/__tests__/propeller/engine.test.ts +++ b/packages/solana-contracts/src/__tests__/propeller/engine.test.ts @@ -52,16 +52,17 @@ import { bscTokenBridge, commitment, completeWithPayloadFee, - ethRoutingContract, ethTokenBridge, evmOwner, + evmRoutingContractBuffer, gasKickstartAmount, governanceFee, initAtaFee, lpFee, marginalPricePoolTokenIndex, - metapoolMint1OutputTokenIndex, + maxStaleness, metapoolMint1PoolTokenIndex, + metapoolMint1ToTokenNumber, postVaaFee, processSwimPayloadFee, routingContracts, @@ -74,22 +75,25 @@ import { usdtPoolTokenIndex, } from "../consts"; import { + getAddOrRemoveAccounts, + getOwnerAtaAddrsForPool, getPoolUserBalances, printPoolUserBalances, setupPoolPrereqs, setupUserAssociatedTokenAccts, } from "../twoPool/poolTestUtils"; -import type { WormholeAddresses } from "./propellerUtils"; +import type { FeeTrackingAccounts, WormholeAddresses } from "./propellerUtils"; import { encodeSwimPayload, generatePropellerEngineTxns, - getOwnerTokenAccountsForPool, + getFeeTrackerPda, + getFeeTrackingAccounts, getPropellerPda, getPropellerRedeemerPda, getSwimClaimPda, getSwimPayloadMessagePda, - getTargetTokenIdMapAddr, + getToTokenNumberMapAddr, getWormholeAddressesForMint, } from "./propellerUtils"; import { @@ -154,7 +158,8 @@ let propeller: web3.PublicKey; let propellerSender: web3.PublicKey; let propellerRedeemer: web3.PublicKey; let propellerRedeemerEscrowAccount: web3.PublicKey; -const propellerAdmin: web3.Keypair = web3.Keypair.generate(); +const propellerGovernanceKey: web3.Keypair = web3.Keypair.generate(); +const propellerPauseKey: web3.Keypair = web3.Keypair.generate(); let propellerFeeVault: web3.PublicKey; const initialMintAmount = new BN(100_000_000_000_000); @@ -188,7 +193,7 @@ let userUsdcAtaAddr: web3.PublicKey; let userUsdtAtaAddr: web3.PublicKey; let userSwimUsdAtaAddr: web3.PublicKey; -let flagshipPool: web3.PublicKey; +let swimUsdPool: web3.PublicKey; // let flagshipPoolData: SwimPoolState; // let poolAuth: web3.PublicKey; const swimUsdMint: web3.PublicKey = swimUsdKeypair.publicKey; @@ -199,7 +204,7 @@ const metapoolMint1Authority = userKeypair; const metapoolMint1Decimal = 8; const metapoolMintKeypairs = [metapoolMint0Keypair, metapoolMint1Keypair]; const metapoolMintDecimals = [mintDecimal, metapoolMint1Decimal]; -// const metapoolMintAuthorities = [flagshipPool, metapoolMint1Authority]; +// const metapoolMintAuthorities = [swimUsdPool, metapoolMint1Authority]; const metapoolLpMintKeypair = web3.Keypair.generate(); // const metapoolLpMint = metapoolLpMintKeypair.publicKey; @@ -215,7 +220,7 @@ let marginalPricePoolToken1Account: web3.PublicKey; let marginalPricePoolLpMint: web3.PublicKey; const marginalPricePoolTokenMint = usdcKeypair.publicKey; -let outputTokenIdMappingAddrs: ReadonlyMap; +let toTokenNumberMapAddrs: ReadonlyMap; let wormholeAddresses: WormholeAddresses; let custody: web3.PublicKey; @@ -254,6 +259,7 @@ let propellerEngineSwimUsdFeeAccount: web3.PublicKey; let aggregatorAccount: AggregatorAccount; let aggregator: PublicKey; +let feeTrackingAccts: FeeTrackingAccounts; describe("propeller", () => { beforeAll(async () => { @@ -278,7 +284,7 @@ describe("propeller", () => { console.info(`initializing two pool v2`); ({ - poolPubkey: flagshipPool, + poolPubkey: swimUsdPool, poolTokenAccounts: [poolUsdcAtaAddr, poolUsdtAtaAddr], governanceFeeAccount: flagshipPoolGovernanceFeeAcct, } = await setupPoolPrereqs( @@ -317,10 +323,10 @@ describe("propeller", () => { } const pool = pubkeys.pool; console.info( - `poolKey: ${pool.toBase58()}, expected: ${flagshipPool.toBase58()}`, + `poolKey: ${pool.toBase58()}, expected: ${swimUsdPool.toBase58()}`, ); - // expect(pool.toBase58()).toEqual(flagshipPool.toBase58()); + // expect(pool.toBase58()).toEqual(swimUsdPool.toBase58()); const initFlagshipPoolTxnSig: string = await initFlagshipPoolTxn.rpc( rpcCommitmentConfig, ); @@ -332,7 +338,7 @@ describe("propeller", () => { `flagshipPoolData: ${JSON.stringify(flagshipPoolData, null, 2)}`, ); - marginalPricePool = flagshipPool; + marginalPricePool = swimUsdPool; // const calculatedSwimPoolPda = await web3.PublicKey.createProgramAddress( // [ @@ -343,7 +349,7 @@ describe("propeller", () => { // ], // twoPoolProgram.programId, // ); - // expect(flagshipPool.toBase58()).toEqual(calculatedSwimPoolPda.toBase58()); + // expect(swimUsdPool.toBase58()).toEqual(calculatedSwimPoolPda.toBase58()); console.info(`setting up user token accounts for flagship pool`); ({ @@ -374,7 +380,7 @@ describe("propeller", () => { } `); - const seedFlagshipPoolAmounts = [ + const seedSwimUsdPoolAmounts = [ new BN(50_000_000_000_000), new BN(50_000_000_000_000), ]; @@ -384,30 +390,26 @@ describe("propeller", () => { // inputAmounts, // minimumMintAmount, // }; - const [flagshipPoolAddApproveIxs, flagshipPoolAddRevokeIxs] = + const [seedSwimUsdPoolApproveIxs, seedSwimUsdPoolRevokeIxs] = await getApproveAndRevokeIxs( splToken, [userUsdcAtaAddr, userUsdtAtaAddr], - seedFlagshipPoolAmounts, + seedSwimUsdPoolAmounts, userTransferAuthority.publicKey, payer, ); + const seedFlagshipAccts = await getAddOrRemoveAccounts( + swimUsdPool, + provider.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const addTxn = twoPoolProgram.methods - .add(seedFlagshipPoolAmounts, minimumMintAmount) - .accounts({ - poolTokenAccount0: poolUsdcAtaAddr, - poolTokenAccount1: poolUsdtAtaAddr, - lpMint: swimUsdKeypair.publicKey, - governanceFee: flagshipPoolGovernanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userUsdcAtaAddr, - userTokenAccount1: userUsdtAtaAddr, - userLpTokenAccount: userSwimUsdAtaAddr, - tokenProgram: splToken.programId, - }) - .preInstructions([...flagshipPoolAddApproveIxs]) - .postInstructions([...flagshipPoolAddRevokeIxs]) + .add(seedSwimUsdPoolAmounts, minimumMintAmount) + .accounts(seedFlagshipAccts) + .preInstructions([...seedSwimUsdPoolApproveIxs]) + .postInstructions([...seedSwimUsdPoolRevokeIxs]) .signers([userTransferAuthority]); // .rpc(rpcCommitmentConfig); @@ -418,7 +420,7 @@ describe("propeller", () => { console.info(`addTxSig: ${addTxnSig}`); - marginalPricePool = flagshipPool; + marginalPricePool = swimUsdPool; marginalPricePoolToken0Account = poolUsdcAtaAddr; marginalPricePoolToken1Account = poolUsdtAtaAddr; marginalPricePoolLpMint = swimUsdKeypair.publicKey; @@ -443,7 +445,7 @@ describe("propeller", () => { splToken, metapoolMintKeypairs, metapoolMintDecimals, - [flagshipPool, metapoolMint1Authority.publicKey], + [swimUsdPool, metapoolMint1Authority.publicKey], // [metapoolMint1Keypair], // [metapoolMint1Decimal], // [metapoolMint1Authority.publicKey], @@ -585,20 +587,15 @@ describe("propeller", () => { userTransferAuthority.publicKey, payer, ); + const seedMetapoolAccts = await getAddOrRemoveAccounts( + metapool, + provider.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const seedMetapoolTxn = await twoPoolProgram.methods .add(inputAmounts, minimumMintAmount) - .accounts({ - // propeller: propeller, - poolTokenAccount0: metapoolPoolToken0Ata, - poolTokenAccount1: metapoolPoolToken1Ata, - lpMint: metapoolLpMintKeypair.publicKey, - governanceFee: metapoolGovernanceFeeAta, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userMetapoolTokenAccount0, - userTokenAccount1: userMetapoolTokenAccount1, - userLpTokenAccount: userMetapoolLpTokenAccount, - tokenProgram: splToken.programId, - }) + .accounts(seedMetapoolAccts) .preInstructions([...approveIxs]) .postInstructions([...revokeIxs]) .signers([userTransferAuthority]) @@ -714,30 +711,6 @@ describe("propeller", () => { await seedWormholeCustody(); console.info(`finished seeing wormhole custody`); - - console.info(`setting up switchboard`); - - // // If fails, fallback to looking for a local env file - // try { - // switchboard = await SwitchboardTestContext.loadFromEnv(provider); - // console.info(`set up switchboard`); - // const aggregatorAccount = await switchboard.createStaticFeed(100); - // aggregator = aggregatorAccount.publicKey; - // // switchboard = await SwitchboardTestContext.loadDevnetQueue( - // // provider, - // // "F8ce7MsckeZAbAGmxjJNetxYXQa9mKr9nnrC3qKubyYy" - // // ); - // // aggregatorKey = DEFAULT_SOL_USD_FEED; - // console.info("local env detected"); - // return; - // } catch (error: any) { - // console.info(`Error: SBV2 Localnet - ${JSON.stringify(error.message)}`); - // throw new Error( - // `Failed to load localenv SwitchboardTestContext: ${JSON.stringify( - // error.message, - // )}`, - // ); - // } }, 50000); describe("propellerEngine CompleteWithPayload and ProcessSwimPayload", () => { @@ -766,16 +739,11 @@ describe("propeller", () => { propellerEngineKeypair, ]); - const [expectedFeeTracker, bump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("fee"), - swimUsdMint.toBuffer(), - propellerEngineKeypair.publicKey.toBuffer(), - ], - propellerProgram.programId, - ); + const [expectedFeeTracker, bump] = await getFeeTrackerPda( + swimUsdMint, + propellerEngineKeypair.publicKey, + propellerProgram.programId, + ); if (!initializeFeeTrackersPubkeys.feeTracker) { throw new Error("feeTracker is undefined"); @@ -789,7 +757,7 @@ describe("propeller", () => { initializeFeeTrackersPubkeys.feeTracker, ); expect(feeTrackerAccount.bump).toEqual(bump); - expect(feeTrackerAccount.payer.toBase58()).toEqual( + expect(feeTrackerAccount.feesRecipient.toBase58()).toEqual( propellerEngineKeypair.publicKey.toBase58(), ); expect(feeTrackerAccount.feesMint.toBase58()).toEqual( @@ -853,7 +821,7 @@ describe("propeller", () => { swimUsdKeypair.publicKey.toBuffer(), CHAIN_ID_SOLANA, propellerProgram.programId, - ethRoutingContract, + evmRoutingContractBuffer, encodeSwimPayload(swimPayload), ), ); @@ -938,9 +906,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: wormholeAddresses.custody, @@ -952,26 +920,27 @@ describe("propeller", () => { tokenProgram: splToken.programId, tokenBridge, }, - feeTracker: propellerEngineFeeTracker, - aggregator, + feeTracking: feeTrackingAccts, + // feeTracker: propellerEngineFeeTracker, + // aggregator, // marginalPricePool: { // pool: marginalPricePool, // poolToken0Account: marginalPricePoolToken0Account, // poolToken1Account: marginalPricePoolToken1Account, // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, // }, // twoPoolProgram: twoPoolProgram.programId, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, memo: MEMO_PROGRAM_ID, }) - .preInstructions([setComputeUnitLimitIx]) - .signers([propellerEngineKeypair]); + .preInstructions([setComputeUnitLimitIx]); const completeNativeWithPayloadPubkeys = await completeNativeWithPayloadIxs.pubkeys(); @@ -1180,7 +1149,7 @@ describe("propeller", () => { }); it("creates owner token accounts(no-op)", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolToken0Mint = usdcKeypair.publicKey; const poolToken1Mint = usdtKeypair.publicKey; const lpMint = swimUsdMint; @@ -1205,8 +1174,7 @@ describe("propeller", () => { payer: propellerEngineKeypair.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, + claim: wormholeClaim, swimPayloadMessage, // tokenIdMap: ? @@ -1222,12 +1190,22 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: marginalPricePoolToken0Account, - marginalPricePoolToken1Account: marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // // marginalPricePool: marginalPricePool, + // // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // // marginalPricePoolLpMint: marginalPricePoolLpMint, + // // twoPoolProgram: twoPoolProgram.programId, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, }) .preInstructions([setComputeUnitLimitIx]) .rpc(); @@ -1252,7 +1230,7 @@ describe("propeller", () => { }); it("processes swim payload", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolTokenAccount0 = poolUsdcAtaAddr; const poolTokenAccount1 = poolUsdtAtaAddr; const lpMint = swimUsdMint; @@ -1271,8 +1249,6 @@ describe("propeller", () => { ) ).feesOwed; - const userTransferAuthority = web3.Keypair.generate(); - const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, @@ -1288,7 +1264,7 @@ describe("propeller", () => { )}`, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -1304,7 +1280,7 @@ describe("propeller", () => { const processSwimPayloadPubkeys = await propellerEnginePropellerProgram.methods .processSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, minOutputAmount, ) .accounts({ @@ -1322,7 +1298,6 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, userTokenAccount0, userTokenAccount1, userLpTokenAccount, @@ -1334,65 +1309,79 @@ describe("propeller", () => { const propellerProcessSwimPayloadIxs = propellerEnginePropellerProgram.methods .propellerProcessSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, ) .accounts({ processSwimPayload: processSwimPayloadPubkeys, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + feeTracking: feeTrackingAccts, + // fees: { + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, owner, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) - .signers([userTransferAuthority, propellerEngineKeypair]); + .signers([propellerEngineKeypair]); const propellerProcessSwimPayloadPubkeys = await propellerProcessSwimPayloadIxs.pubkeys(); console.info( `${JSON.stringify(propellerProcessSwimPayloadPubkeys, null, 2)}`, ); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } + const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); - const expectedTokenIdMap = - outputTokenIdMappingAddrs.get(targetTokenId); - if (!expectedTokenIdMap) { - throw new Error("expectedTokenIdMap not found"); + const expectedTokenNumberMap = + toTokenNumberMapAddrs.get(targetTokenId); + if (!expectedTokenNumberMap) { + throw new Error("expectedTokenNumberMap not found"); } - const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch( - expectedTokenIdMap, + const expectedTokenNumberMapAcct = + await propellerProgram.account.tokenNumberMap.fetch( + expectedTokenNumberMap, ); console.info(` calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} - expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : - ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} + expectedTokenIdMapAcct: ${expectedTokenNumberMap.toBase58()} : + ${JSON.stringify(expectedTokenNumberMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; - expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); + const derivedTokenNumberMap = + processSwimPayloadPubkeys.tokenNumberMap; + expect(derivedTokenNumberMap).toEqual(expectedTokenNumberMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); } @@ -1557,7 +1546,7 @@ describe("propeller", () => { swimUsdKeypair.publicKey.toBuffer(), CHAIN_ID_SOLANA, propellerProgram.programId, - ethRoutingContract, + evmRoutingContractBuffer, encodeSwimPayload(swimPayload), ), ); @@ -1631,9 +1620,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -1646,22 +1635,24 @@ describe("propeller", () => { tokenProgram: splToken.programId, tokenBridge, }, - feeTracker: propellerEngineFeeTracker, - aggregator, + feeTracking: feeTrackingAccts, + // feeTracker: propellerEngineFeeTracker, + // aggregator, // marginalPricePool: { // pool: marginalPricePool, // poolToken0Account: marginalPricePoolToken0Account, // poolToken1Account: marginalPricePoolToken1Account, // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, // }, // twoPoolProgram: twoPoolProgram.programId, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) @@ -1869,17 +1860,33 @@ describe("propeller", () => { }); it("creates owner token accounts", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolToken0Mint = usdcKeypair.publicKey; const poolToken1Mint = usdtKeypair.publicKey; const lpMint = swimUsdMint; + const swimPayloadMessageDataTransferAmountBefore = ( + await propellerEnginePropellerProgram.account.swimPayloadMessage.fetch( + swimPayloadMessage, + ) + ).transferAmount; + const userAtas = await Promise.all([ + getAssociatedTokenAddress(usdcKeypair.publicKey, owner), + getAssociatedTokenAddress(usdtKeypair.publicKey, owner), + getAssociatedTokenAddress(swimUsdKeypair.publicKey, owner), + ]); const [userTokenAccount0, userTokenAccount1, userLpTokenAccount] = - await Promise.all([ - getAssociatedTokenAddress(usdcKeypair.publicKey, owner), - getAssociatedTokenAddress(usdtKeypair.publicKey, owner), - getAssociatedTokenAddress(swimUsdKeypair.publicKey, owner), - ]); + userAtas; + const userAtaInitCount = ( + await Promise.all( + userAtas.map(async (ata) => { + return await splToken.account.token.fetchNullable(ata); + }), + ) + ).reduce((acc, ata) => { + return acc + (ata === null ? 1 : 0); + }, 0); + expect(userAtaInitCount).toEqual(3); const propellerFeeVaultBalanceBefore = ( await splToken.account.token.fetch(propellerFeeVault) ).amount; @@ -1897,8 +1904,7 @@ describe("propeller", () => { payer: propellerEngineKeypair.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, + claim: wormholeClaim, swimPayloadMessage, // tokenIdMap: ? @@ -1917,16 +1923,57 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: marginalPricePoolToken0Account, - marginalPricePoolToken1Account: marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // aggregator, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) .rpc(); + const userAtaAccountLength = ( + await connection.getAccountInfo(userAtas[0]) + ).data.length; + const userAtaRentExemption = + await connection.getMinimumBalanceForRentExemption( + userAtaAccountLength, + ); + const totalRentExemptionInLamports = new BN(userAtaInitCount).mul( + new BN(userAtaRentExemption), + ); + const expectedFeesInLamports = new BN(userAtaInitCount) + .mul(initAtaFee) + .add(totalRentExemptionInLamports); + + const feeSwimUsdBn = await convertLamportsToSwimUsdAtomic( + expectedFeesInLamports, + ); + + console.info(` + userAtaInitCount: ${userAtaInitCount} + * userAtaRentExemption: ${userAtaRentExemption} + = totalRentExemptionInLamports: ${totalRentExemptionInLamports.toString()} + + (initAtaFee: ${initAtaFee.toString()} + * userAtaInitCount: ${userAtaInitCount}) + + totalRentExemptionInLamports: ${totalRentExemptionInLamports.toString()} + = expectedFeesInLamports: ${expectedFeesInLamports.toString()} + + feeSwimUsdBn = ${feeSwimUsdBn.toString()} + `); + const propellerFeeVaultBalanceAfter = ( await splToken.account.token.fetch(propellerFeeVault) ).amount; @@ -1944,6 +1991,23 @@ describe("propeller", () => { propellerEngineFeeTrackerFeesOwedBefore, ), ).toBeTruthy(); + + const fees = propellerEngineFeeTrackerFeesOwedAfter.sub( + propellerEngineFeeTrackerFeesOwedBefore, + ); + expect(fees.eq(feeSwimUsdBn)).toBeTruthy(); + + const swimPayloadMessageDataTransferAmountAfter = ( + await propellerEnginePropellerProgram.account.swimPayloadMessage.fetch( + swimPayloadMessage, + ) + ).transferAmount; + + expect( + swimPayloadMessageDataTransferAmountAfter.eq( + swimPayloadMessageDataTransferAmountBefore.sub(feeSwimUsdBn), + ), + ).toBeTruthy(); const userTokenAccount0Data = await splToken.account.token.fetch( userTokenAccount0, ); @@ -1968,7 +2032,7 @@ describe("propeller", () => { }); it("processes swim payload", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolTokenAccount0 = poolUsdcAtaAddr; const poolTokenAccount1 = poolUsdtAtaAddr; const lpMint = swimUsdMint; @@ -1990,12 +2054,11 @@ describe("propeller", () => { ) ).feesOwed; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -2010,33 +2073,19 @@ describe("propeller", () => { await splToken.account.token.fetch(userTokenAccount1) ).amount; const [calculatedSwimClaim, calculatedSwimClaimBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("claim"), - wormholeClaim.toBuffer(), - ], - propellerProgram.programId, - ); + await getSwimClaimPda(wormholeClaim, propellerProgram.programId); - const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + const [calculatedTokenNumberMap, calculatedTokenNumberMapBump] = + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); + const processSwimPayloadPubkeys = await propellerEnginePropellerProgram.methods .processSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, new BN(0), ) .accounts({ @@ -2049,21 +2098,16 @@ describe("propeller", () => { swimPayloadMessageAccount.swimPayloadMessagePayer, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - // tokenIdMap: calculatedTokenIdMap, + // tokenIdMap: calculatedTokenNumberMap, pool, poolTokenAccount0, poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0, - userTokenAccount1, - userLpTokenAccount, tokenProgram: splToken.programId, - twoPoolProgram: twoPoolProgram.programId, systemProgram: web3.SystemProgram.programId, }) @@ -2075,7 +2119,7 @@ describe("propeller", () => { const propellerProcessSwimPayloadIxs = propellerEnginePropellerProgram.methods .propellerProcessSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, ) .accounts({ // Note: anchor can't autoderive nested accounts. @@ -2088,7 +2132,7 @@ describe("propeller", () => { // swimPayloadMessage, // redeemer: propellerRedeemer, // redeemerEscrow: propellerRedeemerEscrowAccount, - // tokenIdMap: calculatedTokenIdMap, + // tokenIdMap: calculatedTokenNumberMap, // pool, // poolTokenAccount0, // poolTokenAccount1, @@ -2107,46 +2151,66 @@ describe("propeller", () => { // systemProgram: web3.SystemProgram.programId, // }, processSwimPayload: processSwimPayloadPubkeys, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, + feeTracking: feeTrackingAccts, + // fees: { + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // }, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, owner, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) - .signers([userTransferAuthority, propellerEngineKeypair]); + .signers([propellerEngineKeypair]); const propellerProcessSwimPayloadPubkeys = await propellerProcessSwimPayloadIxs.pubkeys(); console.info( `${JSON.stringify(propellerProcessSwimPayloadPubkeys, null, 2)}`, ); - // if (!propellerProcessSwimPayloadPubkeys.tokenIdMap) { + // if (!propellerProcessSwimPayloadPubkeys.tokenNumberMap) { // throw new Error("tokenIdMap not derived"); // } - const expectedTokenIdMap = - outputTokenIdMappingAddrs.get(targetTokenId); - if (!expectedTokenIdMap) { - throw new Error("expectedTokenIdMap not found"); + const expectedTokenNumberMap = + toTokenNumberMapAddrs.get(targetTokenId); + if (!expectedTokenNumberMap) { + throw new Error("expectedTokenNumberMap not found"); } - const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch( - expectedTokenIdMap, + const expectedTokenNumberMapAcct = + await propellerProgram.account.tokenNumberMap.fetch( + expectedTokenNumberMap, ); console.info(` - calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} - calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} - expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : - ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} + calculatedTokenIdMap: ${calculatedTokenNumberMap.toBase58()} + calculatedTokenIdMapBump: ${calculatedTokenNumberMapBump} + expectedTokenIdMapAcct: ${expectedTokenNumberMap.toBase58()} : + ${JSON.stringify(expectedTokenNumberMapAcct, null, 2)} `); - expect(calculatedTokenIdMap).toEqual(expectedTokenIdMap); + expect(calculatedTokenNumberMap).toEqual(expectedTokenNumberMap); const processSwimPayloadTxnSig: string = await propellerProcessSwimPayloadIxs.rpc(); @@ -2378,7 +2442,7 @@ describe("propeller", () => { swimUsdKeypair.publicKey.toBuffer(), CHAIN_ID_SOLANA, propellerProgram.programId, - ethRoutingContract, + evmRoutingContractBuffer, encodeSwimPayload(swimPayload), ), ); @@ -2445,9 +2509,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -2459,14 +2523,21 @@ describe("propeller", () => { tokenProgram: splToken.programId, tokenBridge, }, - feeTracker: propellerEngineFeeTracker, - aggregator, - - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: marginalPricePoolToken0Account, - marginalPricePoolToken1Account: marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) @@ -2663,7 +2734,7 @@ describe("propeller", () => { ) ).feesOwed; - [invalidTokenIdMapAddr] = await getTargetTokenIdMapAddr( + [invalidTokenIdMapAddr] = await getToTokenNumberMapAddr( propeller, targetTokenId, propellerEnginePropellerProgram.programId, @@ -2679,8 +2750,7 @@ describe("propeller", () => { payer: propellerEngineKeypair.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, + claim: wormholeClaim, swimPayloadMessage, tokenIdMap: invalidTokenIdMapAddr, @@ -2691,12 +2761,15 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: marginalPricePoolToken0Account, - marginalPricePoolToken1Account: marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + // aggregator, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, rent: web3.SYSVAR_RENT_PUBKEY, }) .preInstructions([setComputeUnitLimitIx]) @@ -2739,7 +2812,6 @@ describe("propeller", () => { ) ).feesOwed; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, @@ -2767,24 +2839,26 @@ describe("propeller", () => { swimPayloadMessageAccount.swimPayloadMessagePayer, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - tokenIdMap: invalidTokenIdMapAddr, - userTransferAuthority: userTransferAuthority.publicKey, + tokenNumberMap: invalidTokenIdMapAddr, userSwimUsdAta: ownerSwimUsdAta, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - twoPoolProgram: twoPoolProgram.programId, + systemProgram: web3.SystemProgram.programId, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: marginalPricePoolToken0Account, - marginalPricePoolToken1Account: marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, + feeTracking: feeTrackingAccts, + + // aggregator, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, owner, }) .preInstructions([setComputeUnitLimitIx]) - .signers([userTransferAuthority, propellerEngineKeypair]); + .signers([propellerEngineKeypair]); const propellerProcessSwimPayloadPubkeys = await propellerProcessSwimPayloadIxs.pubkeys(); @@ -2992,7 +3066,7 @@ describe("propeller", () => { // swimUsdKeypair.publicKey.toBuffer(), // CHAIN_ID_SOLANA, // propellerProgram.programId, - // ethRoutingContract, + // evmRoutingContractBuffer, // encodeSwimPayload(swimPayload), // ), // ); @@ -3041,7 +3115,7 @@ describe("propeller", () => { // message: wormholeMessage, // claim: wormholeClaim, // endpoint: ethEndpointAccount, - // to: propellerRedeemerEscrowAccount, + // redeemerEscrow: propellerRedeemerEscrowAccount, // redeemer: propellerRedeemer, // feeRecipient: userSwimUsdAtaAddr, // // feeRecipient: propellerRedeemerEscrowAccount, @@ -3184,7 +3258,7 @@ describe("propeller", () => { // // eslint-disable-next-line jest/no-commented-out-tests // it("processes swim payload", async () => { - // const pool = flagshipPool; + // const pool = swimUsdPool; // const poolTokenAccount0 = poolUsdcAtaAddr; // const poolTokenAccount1 = poolUsdtAtaAddr; // const lpMint = swimUsdMint; @@ -3243,7 +3317,7 @@ describe("propeller", () => { // // const processSwimPayloadPubkeys = await processSwimPayload.pubkeys(); // console.info(`${JSON.stringify(processSwimPayloadPubkeys, null, 2)}`); - // if (!processSwimPayloadPubkeys.tokenIdMap) { + // if (!processSwimPayloadPubkeys.tokenNumberMap) { // throw new Error("tokenIdMap not derived"); // } // @@ -3263,19 +3337,19 @@ describe("propeller", () => { // ); // // const expectedTokenIdMap = - // outputTokenIdMappingAddrs.get(targetTokenId); + // toTokenNumberMapAddrs.get(targetTokenId); // if (!expectedTokenIdMap) { // throw new Error("expectedTokenIdMap not found"); // } // const expectedTokenIdMapAcct = - // await propellerProgram.account.tokenIdMap.fetch(expectedTokenIdMap); + // await propellerProgram.account.tokenNumberMap.fetch(expectedTokenIdMap); // console.info(` // calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} // calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} // expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : // ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} // `); - // const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + // const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; // expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); // if (!processSwimPayloadPubkeys.swimClaim) { // throw new Error("swimClaim key not derived"); @@ -3356,7 +3430,7 @@ describe("propeller", () => { // let wormholeClaim: web3.PublicKey; // let wormholeMessage: web3.PublicKey; // let swimPayloadMessage: web3.PublicKey; - // const targetTokenId = metapoolMint1OutputTokenIndex; + // const targetTokenId = metapoolMint1ToTokenNumber; // const memoBuffer = createMemoId(); // // const memo = "e45794d6c5a2750b"; // @@ -3408,7 +3482,7 @@ describe("propeller", () => { // swimUsdKeypair.publicKey.toBuffer(), // CHAIN_ID_SOLANA, // propellerProgram.programId, - // ethRoutingContract, + // evmRoutingContractBuffer, // encodeSwimPayload(swimPayload), // ), // ); @@ -3457,7 +3531,7 @@ describe("propeller", () => { // message: wormholeMessage, // claim: wormholeClaim, // endpoint: ethEndpointAccount, - // to: propellerRedeemerEscrowAccount, + // redeemerEscrow: propellerRedeemerEscrowAccount, // redeemer: propellerRedeemer, // feeRecipient: userSwimUsdAtaAddr, // // feeRecipient: propellerRedeemerEscrowAccount, @@ -3657,7 +3731,7 @@ describe("propeller", () => { // // const processSwimPayloadPubkeys = await processSwimPayload.pubkeys(); // console.info(`${JSON.stringify(processSwimPayloadPubkeys, null, 2)}`); - // if (!processSwimPayloadPubkeys.tokenIdMap) { + // if (!processSwimPayloadPubkeys.tokenNumberMap) { // throw new Error("tokenIdMap not derived"); // } // const [calculatedTokenIdMap, calculatedTokenIdMapBump] = @@ -3676,19 +3750,19 @@ describe("propeller", () => { // ); // // const expectedTokenIdMap = - // outputTokenIdMappingAddrs.get(targetTokenId); + // toTokenNumberMapAddrs.get(targetTokenId); // if (!expectedTokenIdMap) { // throw new Error("expectedTokenIdMap not found"); // } // const expectedTokenIdMapAcct = - // await propellerProgram.account.tokenIdMap.fetch(expectedTokenIdMap); + // await propellerProgram.account.tokenNumberMap.fetch(expectedTokenIdMap); // console.info(` // calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} // calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} // expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : // ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} // `); - // const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + // const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; // expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); // if (!processSwimPayloadPubkeys.swimClaim) { // throw new Error("swimClaim key not derived"); @@ -3822,7 +3896,7 @@ describe("propeller", () => { swimUsdKeypair.publicKey.toBuffer(), CHAIN_ID_SOLANA, propellerProgram.programId, - ethRoutingContract, + evmRoutingContractBuffer, encodeSwimPayload(swimPayload), ), ); @@ -3907,9 +3981,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -3921,22 +3995,24 @@ describe("propeller", () => { tokenProgram: splToken.programId, tokenBridge, }, - feeTracker: propellerEngineFeeTracker, - aggregator, + feeTracking: feeTrackingAccts, + // aggregator, + // feeTracker: propellerEngineFeeTracker, // marginalPricePool: { // pool: marginalPricePool, // poolToken0Account: marginalPricePoolToken0Account, // poolToken1Account: marginalPricePoolToken1Account, // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, // }, // twoPoolProgram: twoPoolProgram.programId, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) @@ -4146,7 +4222,7 @@ describe("propeller", () => { }); it("creates owner token accounts", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolToken0Mint = usdcKeypair.publicKey; const poolToken1Mint = usdtKeypair.publicKey; const lpMint = swimUsdMint; @@ -4174,8 +4250,7 @@ describe("propeller", () => { payer: propellerEngineKeypair.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, + claim: wormholeClaim, swimPayloadMessage, // tokenIdMap: ? @@ -4194,12 +4269,22 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: marginalPricePoolToken0Account, - marginalPricePoolToken1Account: marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // aggregator, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) .rpc(); @@ -4245,7 +4330,7 @@ describe("propeller", () => { }); it("processes swim payload", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolTokenAccount0 = poolUsdcAtaAddr; const poolTokenAccount1 = poolUsdtAtaAddr; const lpMint = swimUsdMint; @@ -4268,12 +4353,11 @@ describe("propeller", () => { ) ).feesOwed; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -4287,7 +4371,7 @@ describe("propeller", () => { const processSwimPayloadPubkeys = await propellerEnginePropellerProgram.methods .processSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, new BN(0), ) .accounts({ @@ -4305,15 +4389,10 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0, - userTokenAccount1, - userLpTokenAccount, tokenProgram: splToken.programId, - twoPoolProgram: twoPoolProgram.programId, systemProgram: web3.SystemProgram.programId, }) @@ -4322,55 +4401,67 @@ describe("propeller", () => { const propellerProcessSwimPayloadIxs = propellerEnginePropellerProgram.methods .propellerProcessSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, ) .accounts({ processSwimPayload: processSwimPayloadPubkeys, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, + feeTracking: feeTrackingAccts, + // fees: { + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // }, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, owner, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) - .signers([userTransferAuthority, propellerEngineKeypair]); + .signers([propellerEngineKeypair]); const propellerProcessSwimPayloadPubkeys = await propellerProcessSwimPayloadIxs.pubkeys(); console.info( `${JSON.stringify(propellerProcessSwimPayloadPubkeys, null, 2)}`, ); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } + const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); - const expectedTokenIdMap = - outputTokenIdMappingAddrs.get(targetTokenId); + const expectedTokenIdMap = toTokenNumberMapAddrs.get(targetTokenId); if (!expectedTokenIdMap) { throw new Error("expectedTokenIdMap not found"); } const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch( + await propellerProgram.account.tokenNumberMap.fetch( expectedTokenIdMap, ); console.info(` @@ -4379,7 +4470,7 @@ describe("propeller", () => { expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); @@ -4608,7 +4699,6 @@ describe("propeller", () => { let txns: readonly web3.Transaction[] = []; let txnIdx = 0; let propellerRedeemerEscrowAccountBefore: BN; - const userTransferAuthority = web3.Keypair.generate(); beforeAll(async () => { tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( @@ -4622,7 +4712,7 @@ describe("propeller", () => { swimUsdKeypair.publicKey.toBuffer(), CHAIN_ID_SOLANA, propellerProgram.programId, - ethRoutingContract, + evmRoutingContractBuffer, encodeSwimPayload(swimPayload), ), ); @@ -4663,7 +4753,6 @@ describe("propeller", () => { twoPoolProgram, splToken, aggregator, - userTransferAuthority, ); console.info(`txns: ${JSON.stringify(txns)}`); }); @@ -4909,8 +4998,8 @@ describe("propeller", () => { // getAssociatedTokenAddress(usdtKeypair.publicKey, owner), // getAssociatedTokenAddress(swimUsdKeypair.publicKey, owner), // ]); - const userPoolAtas = await getOwnerTokenAccountsForPool( - flagshipPool, + const userPoolAtas = await getOwnerAtaAddrsForPool( + swimUsdPool, owner, twoPoolProgram, ); @@ -4974,8 +5063,8 @@ describe("propeller", () => { `[Ricky] swimPayloadMessage: ${swimPayloadMessage.toString()}`, ); const userBalanceBefore = await connection.getBalance(owner); - const userPoolAtas = await getOwnerTokenAccountsForPool( - flagshipPool, + const userPoolAtas = await getOwnerAtaAddrsForPool( + swimUsdPool, owner, twoPoolProgram, ); @@ -5027,7 +5116,6 @@ describe("propeller", () => { const processSwimPayloadTxnSig = await propellerEngineAnchorProvider.sendAndConfirm( processSwimPayloadTxn, - [userTransferAuthority], ); console.info(`processSwimPayloadTxnSig: ${processSwimPayloadTxnSig}`); @@ -5168,7 +5256,6 @@ describe("propeller", () => { let txns: readonly web3.Transaction[] = []; let txnIdx = 0; let propellerRedeemerEscrowAccountBefore: BN; - const userTransferAuthority = web3.Keypair.generate(); beforeAll(async () => { tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( @@ -5182,7 +5269,7 @@ describe("propeller", () => { swimUsdKeypair.publicKey.toBuffer(), CHAIN_ID_SOLANA, propellerProgram.programId, - ethRoutingContract, + evmRoutingContractBuffer, encodeSwimPayload(swimPayload), ), ); @@ -5223,7 +5310,6 @@ describe("propeller", () => { twoPoolProgram, splToken, aggregator, - userTransferAuthority, ); console.info(`txns: ${JSON.stringify(txns)}`); }); @@ -5482,7 +5568,7 @@ describe("propeller", () => { ) ).feesOwed; - [invalidTokenIdMapAddr] = await getTargetTokenIdMapAddr( + [invalidTokenIdMapAddr] = await getToTokenNumberMapAddr( propeller, targetTokenId, propellerEnginePropellerProgram.programId, @@ -5574,7 +5660,6 @@ describe("propeller", () => { const processSwimPayloadTxnSig = await propellerEngineAnchorProvider.sendAndConfirm( processSwimPayloadTxn, - [userTransferAuthority], ); console.info(`processSwimPayloadTxnSig: ${processSwimPayloadTxnSig}`); @@ -5737,7 +5822,7 @@ describe("propeller", () => { swimUsdKeypair.publicKey.toBuffer(), CHAIN_ID_SOLANA, propellerProgram.programId, - ethRoutingContract, + evmRoutingContractBuffer, encodeSwimPayload(swimPayload), ), ); @@ -5811,9 +5896,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -5825,16 +5910,23 @@ describe("propeller", () => { tokenProgram: splToken.programId, tokenBridge, }, - feeTracker: propellerEngineFeeTracker, - aggregator, - - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // aggregator, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) @@ -6033,7 +6125,7 @@ describe("propeller", () => { }); it("creates owner token accounts", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolToken0Mint = usdcKeypair.publicKey; const poolToken1Mint = usdtKeypair.publicKey; const lpMint = swimUsdMint; @@ -6062,8 +6154,7 @@ describe("propeller", () => { payer: propellerEngineKeypair.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, + claim: wormholeClaim, swimPayloadMessage, // tokenIdMap: ? @@ -6082,14 +6173,24 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // aggregator, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) .rpc(); @@ -6136,7 +6237,7 @@ describe("propeller", () => { }); it("processes swim payload", async () => { - const pool = flagshipPool; + const pool = swimUsdPool; const poolTokenAccount0 = poolUsdcAtaAddr; const poolTokenAccount1 = poolUsdtAtaAddr; const lpMint = swimUsdMint; @@ -6162,12 +6263,11 @@ describe("propeller", () => { ) ).feesOwed; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -6181,7 +6281,7 @@ describe("propeller", () => { const processSwimPayloadPubkeys = await propellerEnginePropellerProgram.methods .processSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, new BN(0), ) .accounts({ @@ -6199,12 +6299,8 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0, - userTokenAccount1, - userLpTokenAccount, tokenProgram: splToken.programId, twoPoolProgram: twoPoolProgram.programId, @@ -6215,55 +6311,56 @@ describe("propeller", () => { const propellerProcessSwimPayloadIxs = propellerEnginePropellerProgram.methods .propellerProcessSwimPayload( - swimPayloadMessageAccountTargetTokenId, + swimPayloadMessageAccountToTokenNumber, ) .accounts({ processSwimPayload: processSwimPayloadPubkeys, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, - aggregator, - marginalPricePool: marginalPricePool, - marginalPricePoolToken0Account: - marginalPricePoolToken0Account, - marginalPricePoolToken1Account: - marginalPricePoolToken1Account, - marginalPricePoolLpMint: marginalPricePoolLpMint, + feeTracking: feeTrackingAccts, + // fees: { + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: + // marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: + // marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, owner, memo: MEMO_PROGRAM_ID, }) .preInstructions([setComputeUnitLimitIx]) - .signers([userTransferAuthority, propellerEngineKeypair]); + .signers([propellerEngineKeypair]); const propellerProcessSwimPayloadPubkeys = await propellerProcessSwimPayloadIxs.pubkeys(); console.info( `${JSON.stringify(propellerProcessSwimPayloadPubkeys, null, 2)}`, ); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); - const expectedTokenIdMap = - outputTokenIdMappingAddrs.get(targetTokenId); + const expectedTokenIdMap = toTokenNumberMapAddrs.get(targetTokenId); if (!expectedTokenIdMap) { throw new Error("expectedTokenIdMap not found"); } const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch( + await propellerProgram.account.tokenNumberMap.fetch( expectedTokenIdMap, ); console.info(` @@ -6272,7 +6369,7 @@ describe("propeller", () => { expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); @@ -6532,7 +6629,7 @@ describe("propeller", () => { // ], // propellerProgramId, // ); -// const tokenIdMapData = await propellerProgram.account.tokenIdMap.fetch( +// const tokenIdMapData = await propellerProgram.account.tokenNumberMap.fetch( // tokenIdMap, // ); // const poolKey = tokenIdMapData.pool; @@ -6582,7 +6679,7 @@ describe("propeller", () => { // // const setupFlagshipPool = async () => { // ({ -// poolPubkey: flagshipPool, +// poolPubkey: swimUsdPool, // poolTokenAccounts: [poolUsdcAtaAddr, poolUsdtAtaAddr], // governanceFeeAccount: flagshipPoolGovernanceFeeAcct, // } = await setupPoolPrereqs( @@ -6622,10 +6719,10 @@ describe("propeller", () => { // } // const pool = pubkeys.pool; // console.info( -// `poolKey: ${pool.toBase58()}, expected: ${flagshipPool.toBase58()}`, +// `poolKey: ${pool.toBase58()}, expected: ${swimUsdPool.toBase58()}`, // ); // -// expect(pool.toBase58()).toBe(flagshipPool.toBase58()); +// expect(pool.toBase58()).toBe(swimUsdPool.toBase58()); // const initFlagshipPoolTxnSig = await initFlagshipPoolTxn.rpc( // rpcCommitmentConfig, // ); @@ -6637,7 +6734,7 @@ describe("propeller", () => { // `flagshipPoolData: ${JSON.stringify(flagshipPoolData, null, 2)}`, // ); // -// marginalPricePool = flagshipPool; +// marginalPricePool = swimUsdPool; // marginalPricePoolToken0Account = poolUsdcAtaAddr; // marginalPricePoolToken1Account = poolUsdtAtaAddr; // marginalPricePoolLpMint = swimUsdKeypair.publicKey; @@ -6651,7 +6748,7 @@ describe("propeller", () => { // ], // twoPoolProgram.programId, // ); -// expect(flagshipPool.toBase58()).toBe(calculatedSwimPoolPda.toBase58()); +// expect(swimUsdPool.toBase58()).toBe(calculatedSwimPoolPda.toBase58()); // // console.info(`setting up user token accounts for flagship pool`); // ({ @@ -6674,7 +6771,7 @@ describe("propeller", () => { // `done setting up flagship pool and relevant user token accounts`, // ); // console.info(` -// flagshipPool: ${JSON.stringify(flagshipPoolData, null, 2)} +// swimUsdPool: ${JSON.stringify(flagshipPoolData, null, 2)} // user: { // userUsdcAtaAddr: ${userUsdcAtaAddr.toBase58()} // userUsdtAtaAddr: ${userUsdtAtaAddr.toBase58()} @@ -6745,7 +6842,7 @@ describe("propeller", () => { // splToken, // metapoolMintKeypairs, // metapoolMintDecimals, -// [flagshipPool, metapoolMint1Authority.publicKey], +// [swimUsdPool, metapoolMint1Authority.publicKey], // // [metapoolMintKeypair1], // // [metapoolMint1Decimal], // // [metapoolMint1Authority.publicKey], @@ -6921,34 +7018,32 @@ const initializePropeller = async () => { completeWithPayloadFee, initAtaFee, processSwimPayloadFee, - // propellerMinTransferAmount, - // propellerEthMinTransferAmount, marginalPricePool, marginalPricePoolTokenIndex, marginalPricePoolTokenMint, - // evmRoutingContractAddress: ethRoutingContract, - // evmRoutingContractAddress: ethRoutingContractEthUint8Arr + maxStaleness, }; const tx = propellerProgram.methods .initialize(initializeParams) .accounts({ propellerRedeemerEscrow: propellerRedeemerEscrowAddr, propellerFeeVault, - admin: propellerAdmin.publicKey, + governanceKey: propellerGovernanceKey.publicKey, + pauseKey: propellerPauseKey.publicKey, swimUsdMint: swimUsdMint, payer: userKeypair.publicKey, tokenProgram: TOKEN_PROGRAM_ID, associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, systemProgram: web3.SystemProgram.programId, rent: web3.SYSVAR_RENT_PUBKEY, - pool: flagshipPool, + pool: swimUsdPool, poolTokenMint0: usdcKeypair.publicKey, poolTokenMint1: usdtKeypair.publicKey, lpMint: swimUsdKeypair.publicKey, twoPoolProgram: twoPoolProgram.programId, aggregator, }) - .signers([propellerAdmin]); + .signers([propellerGovernanceKey, propellerPauseKey]); const pubkeys = await tx.pubkeys(); console.info(`pubkeys: ${JSON.stringify(pubkeys, null, 2)}`); @@ -7002,105 +7097,114 @@ const initializePropeller = async () => { propellerRedeemer: ${propellerRedeemer.toBase58()} propellerRedeemerEscrowAccount: ${propellerRedeemerEscrowAccount.toBase58()} `); + feeTrackingAccts = await getFeeTrackingAccounts( + propeller, + propellerEngineKeypair.publicKey, + propellerEnginePropellerProgram, + twoPoolProgram, + ); }; const createTokenIdMaps = async () => { - const swimUsdTokenIdMap = { - pool: flagshipPool, + const swimUsdTokenNumberMap = { + pool: swimUsdPool, poolTokenIndex: 0, poolTokenMint: swimUsdKeypair.publicKey, - poolIx: { transfer: {} }, + toTokenStep: { transfer: {} }, }; - const usdcTokenIdMap = { - pool: flagshipPool, + const usdcTokenNumberMap = { + pool: swimUsdPool, poolTokenIndex: usdcPoolTokenIndex, poolTokenMint: usdcKeypair.publicKey, - poolIx: { removeExactBurn: {} }, + toTokenStep: { removeExactBurn: {} }, }; - const usdtTokenIdMap = { - pool: flagshipPool, + const usdtTokenNumberMap = { + pool: swimUsdPool, poolTokenIndex: usdtPoolTokenIndex, poolTokenMint: usdtKeypair.publicKey, - poolIx: { removeExactBurn: {} }, + toTokenStep: { removeExactBurn: {} }, }; - const metapoolMint1TokenIdMap = { + const metapoolMint1TokenNumberMap = { pool: metapool, poolTokenIndex: metapoolMint1PoolTokenIndex, poolTokenMint: metapoolMint1Keypair.publicKey, - poolIx: { swapExactInput: {} }, + toTokenStep: { swapExactInput: {} }, }; - const outputTokenIdMapAddrEntries = await Promise.all( + const toTokenNumberMapAddrEntries = await Promise.all( [ { - outputTokenIndex: SWIM_USD_TO_TOKEN_NUMBER, - tokenIdMap: swimUsdTokenIdMap, + toTokenNumber: SWIM_USD_TO_TOKEN_NUMBER, + tokenNumberMap: swimUsdTokenNumberMap, + }, + { + toTokenNumber: USDC_TO_TOKEN_NUMBER, + tokenNumberMap: usdcTokenNumberMap, + }, + { + toTokenNumber: USDT_TO_TOKEN_NUMBER, + tokenNumberMap: usdtTokenNumberMap, }, - { outputTokenIndex: USDC_TO_TOKEN_NUMBER, tokenIdMap: usdcTokenIdMap }, - { outputTokenIndex: USDT_TO_TOKEN_NUMBER, tokenIdMap: usdtTokenIdMap }, { - outputTokenIndex: metapoolMint1OutputTokenIndex, - tokenIdMap: metapoolMint1TokenIdMap, + toTokenNumber: metapoolMint1ToTokenNumber, + tokenNumberMap: metapoolMint1TokenNumberMap, }, - ].map(async ({ outputTokenIndex, tokenIdMap }) => { - const createTokenIdMappingTxn = propellerProgram.methods - .createTokenIdMap( - outputTokenIndex, - tokenIdMap.pool, - tokenIdMap.poolTokenIndex, - tokenIdMap.poolTokenMint, - tokenIdMap.poolIx, + ].map(async ({ toTokenNumber, tokenNumberMap }) => { + console.info(`creating tokenNumberMap for ${toTokenNumber}`); + const createTokenNumberMapTxn = propellerProgram.methods + .createTokenNumberMap( + toTokenNumber, + tokenNumberMap.pool, + tokenNumberMap.poolTokenIndex, + tokenNumberMap.poolTokenMint, + tokenNumberMap.toTokenStep, ) .accounts({ propeller, - admin: propellerAdmin.publicKey, - payer: userKeypair.publicKey, + governanceKey: propellerGovernanceKey.publicKey, + payer: payer.publicKey, systemProgram: web3.SystemProgram.programId, // rent: web3.SYSVAR_RENT_PUBKEY, - pool: tokenIdMap.pool, + pool: tokenNumberMap.pool, twoPoolProgram: twoPoolProgram.programId, }) - .signers([propellerAdmin]); + .signers([propellerGovernanceKey]); - const pubkeys = await createTokenIdMappingTxn.pubkeys(); - if (!pubkeys.tokenIdMap) { + const pubkeys = await createTokenNumberMapTxn.pubkeys(); + if (!pubkeys.tokenNumberMap) { throw new Error("Token Id Map PDA Address was not auto-derived"); } - const tokenIdMapAddr = pubkeys.tokenIdMap; - expect(tokenIdMapAddr).toBeTruthy(); + const tokenNumberMapAddr = pubkeys.tokenNumberMap; + expect(tokenNumberMapAddr).toBeTruthy(); const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(outputTokenIndex).toArrayLike(Buffer, "le", 2), - // byteify.serializeUint16(usdcOutputTokenIndex), - ], + await getToTokenNumberMapAddr( + propeller, + toTokenNumber, propellerProgram.programId, ); - expect(tokenIdMapAddr).toEqual(calculatedTokenIdMap); - await createTokenIdMappingTxn.rpc(); + expect(tokenNumberMapAddr).toEqual(calculatedTokenIdMap); + await createTokenNumberMapTxn.rpc(); - const fetchedTokenIdMap = await propellerProgram.account.tokenIdMap.fetch( - tokenIdMapAddr, - ); - expect(fetchedTokenIdMap.outputTokenIndex).toEqual(outputTokenIndex); - expect(fetchedTokenIdMap.pool).toEqual(tokenIdMap.pool); + const fetchedTokenIdMap = + await propellerProgram.account.tokenNumberMap.fetch(tokenNumberMapAddr); + expect(fetchedTokenIdMap.toTokenNumber).toEqual(toTokenNumber); + expect(fetchedTokenIdMap.pool).toEqual(tokenNumberMap.pool); expect(fetchedTokenIdMap.poolTokenIndex).toEqual( - tokenIdMap.poolTokenIndex, + tokenNumberMap.poolTokenIndex, + ); + expect(fetchedTokenIdMap.poolTokenMint).toEqual( + tokenNumberMap.poolTokenMint, ); - expect(fetchedTokenIdMap.poolTokenMint).toEqual(tokenIdMap.poolTokenMint); - expect(fetchedTokenIdMap.poolIx).toEqual(tokenIdMap.poolIx); + expect(fetchedTokenIdMap.toTokenStep).toEqual(tokenNumberMap.toTokenStep); expect(fetchedTokenIdMap.bump).toEqual(calculatedTokenIdMapBump); - return { outputTokenIndex, tokenIdMapAddr }; + return { toTokenNumber, tokenNumberMapAddr }; }), ); - outputTokenIdMappingAddrs = new Map( - outputTokenIdMapAddrEntries.map(({ outputTokenIndex, tokenIdMapAddr }) => { - return [outputTokenIndex, tokenIdMapAddr]; + toTokenNumberMapAddrs = new Map( + toTokenNumberMapAddrEntries.map(({ toTokenNumber, tokenNumberMapAddr }) => { + return [toTokenNumber, tokenNumberMapAddr]; }), ); }; @@ -7112,12 +7216,12 @@ const createTargetChainMaps = async () => { .createTargetChainMap(targetChainId, address) .accounts({ propeller, - admin: propellerAdmin.publicKey, + governanceKey: propellerGovernanceKey.publicKey, payer: propellerEngineKeypair.publicKey, systemProgram: web3.SystemProgram.programId, // rent: web3.SYSVAR_RENT_PUBKEY, }) - .signers([propellerAdmin]); + .signers([propellerGovernanceKey]); const createTargetChainMapPubkeys = await createTargetChainMap.pubkeys(); await createTargetChainMap.rpc(); @@ -7149,8 +7253,14 @@ const seedWormholeCustody = async () => { const transferAmount = userLpTokenBalanceBefore.div(new BN(2)); const wormholeMessage = web3.Keypair.generate(); + const nonce = createNonce().readUInt32LE(0); const crossChainTransferNativeTxnSig = await propellerProgram.methods - .crossChainTransferNativeWithPayload(transferAmount, CHAIN_ID_ETH, evmOwner) + .crossChainTransferNativeWithPayload( + nonce, + transferAmount, + CHAIN_ID_ETH, + evmOwner, + ) .accounts({ propeller, payer: payer.publicKey, @@ -7217,6 +7327,37 @@ const convertLamportsToSwimUsdAtomic = async (feeInLamports: BN) => { return new BN(feeSwimUsdAtomic.toNumber()); }; +const getExpectedFeesForCreateUserAtasInLamports = async ( + swimUsdMintKey: web3.PublicKey, + swimPayloadMessage: web3.PublicKey, +) => { + const swimPayloadMessageAcctData = + await propellerProgram.account.swimPayloadMessage.fetch(swimPayloadMessage); + const swimPayloadOwner = swimPayloadMessageAcctData.owner; + const outputTokenIndex = swimPayloadMessageAcctData.toTokenNumber; + const propellerAddr = await getPropellerPda( + swimUsdMintKey, + propellerProgram.programId, + ); + const [tokenIdMapAddr] = await getToTokenNumberMapAddr( + propellerAddr, + outputTokenIndex, + propellerProgram.programId, + ); + const tokenNumberMapAcctData = + await propellerProgram.account.tokenNumberMap.fetch(tokenIdMapAddr); + const tokenNumberMapPool = tokenNumberMapAcctData.pool; + const poolAcctData = await twoPoolProgram.account.twoPool.fetch( + tokenNumberMapPool, + ); + const poolMints = [...poolAcctData.tokenMintKeys, poolAcctData.lpMintKey]; + const userAtas = await Promise.all( + poolMints.map(async (mint) => { + return await getAssociatedTokenAddress(mint, swimPayloadOwner); + }), + ); +}; + function createMemoId() { const SWIM_MEMO_LENGTH = 16; // NOTE: Please always use random bytes to avoid conflicts with other users diff --git a/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts b/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts index 2e7e467a0..65c6d7f37 100644 --- a/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts +++ b/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts @@ -1,4 +1,4 @@ -import crypto from "crypto"; +import * as crypto from "crypto"; import { CHAIN_ID_ETH, @@ -7,6 +7,7 @@ import { getClaimAddressSolana, postVaaSolanaWithRetry, setDefaultWasm, + tryNativeToHexString, uint8ArrayToHex, } from "@certusone/wormhole-sdk"; import { parseUnits } from "@ethersproject/units"; @@ -28,6 +29,7 @@ import { TOKEN_PROGRAM_ID, getAccount, getAssociatedTokenAddress, + getOrCreateAssociatedTokenAccount, } from "@solana/spl-token"; import { PublicKey } from "@solana/web3.js"; @@ -37,25 +39,29 @@ import { getApproveAndRevokeIxs, idl } from "../../index"; import { DEFAULT_SOL_USD_FEED, PROPELLER_PID, + SWIM_MEMO_LENGTH, SWIM_USD_TO_TOKEN_NUMBER, TWO_POOL_PID, USDC_TO_TOKEN_NUMBER, USDT_TO_TOKEN_NUMBER, ampFactor, + bscTokenBridge, commitment, completeWithPayloadFee, - ethRoutingContract, - ethRoutingContractEthHexStr, ethTokenBridge, + ethTokenBridgeNativeStr, evmOwner, + evmRoutingContractBuffer, + evmRoutingContractHexStr, evmTargetTokenId, gasKickstartAmount, governanceFee, initAtaFee, lpFee, marginalPricePoolTokenIndex, - metapoolMint1OutputTokenIndex, + maxStaleness, metapoolMint1PoolTokenIndex, + metapoolMint1ToTokenNumber, postVaaFee, processSwimPayloadFee, routingContracts, @@ -68,22 +74,33 @@ import { usdtPoolTokenIndex, } from "../consts"; import { + getAddOrRemoveAccounts, + getPoolTokenAccountBalances, getPoolUserBalances, + getUserAtaBalancesForPool, printBeforeAndAfterPoolUserBalances, printPoolUserBalances, setupPoolPrereqs, setupUserAssociatedTokenAccts, } from "../twoPool/poolTestUtils"; +import type { WormholeAddresses } from "./propellerUtils"; import { + DummyForeignRoutingProgram, encodeSwimPayload, formatParsedTokenTransferWithSwimPayloadPostedMessage, + getCompleteNativeWithPayloadTxn, + getProcessSwimPayloadTxn, getPropellerPda, getPropellerRedeemerPda, getPropellerSenderPda, getSwimClaimPda, getSwimPayloadMessagePda, + getTargetChainMapAddr, + getToTokenNumberMapAddr, + getWormholeAddressesForMint, parseTokenTransferWithSwimPayloadPostedMessage, + postVaaToSolana, } from "./propellerUtils"; import { deriveEndpointPda, @@ -97,23 +114,7 @@ import { signAndEncodeVaa, } from "./wormholeUtils"; -// this just breaks everything for some reason... -// think it was something related to the cjs/esm stuff for spl-memo -// -// const MEMO_PROGRAM_ID: PublicKey = new PublicKey( -// "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", -// ); setDefaultWasm("node"); -// const pr2 = new AnchorProvider( -// connection, -// provider.wallet, -// { -// commitment: "confirmed", -// } -// ) -// const process = require("process"); -// const url = process.env.ANCHOR_PROVIDER_URL; -// const provider = AnchorProvider.local(url, {commitment: "confirmed"}); const envProvider = AnchorProvider.env(); const provider = new AnchorProvider( @@ -142,7 +143,7 @@ const wormhole = WORMHOLE_CORE_BRIDGE; const tokenBridge = WORMHOLE_TOKEN_BRIDGE; // start these higher to avoid issues when running this suite & engine.test.ts suite in one go -let ethTokenBridgeSequence = 1000; +const ethTokenBridgeSequence = 1000; // let ethTokenBridgeSequence = BigInt(0); let metapool: web3.PublicKey; @@ -156,7 +157,8 @@ let propeller: web3.PublicKey; let propellerSender: web3.PublicKey; let propellerRedeemer: web3.PublicKey; let propellerRedeemerEscrowAccount: web3.PublicKey; -const propellerAdmin: web3.Keypair = web3.Keypair.generate(); +const propellerGovernanceKey: web3.Keypair = web3.Keypair.generate(); +const propellerPauseKey: web3.Keypair = web3.Keypair.generate(); let propellerFeeVault: web3.PublicKey; const dummyUser = payer; @@ -180,8 +182,8 @@ const poolMintKeypairs = [usdcKeypair, usdtKeypair]; const poolMintDecimals = [mintDecimal, mintDecimal]; const poolMintAuthorities = [payer, payer]; const swimUsdKeypair = web3.Keypair.generate(); -const governanceKeypair = web3.Keypair.generate(); -const pauseKeypair = web3.Keypair.generate(); +const poolGovernanceKeypair = web3.Keypair.generate(); +const poolPauseKeypair = web3.Keypair.generate(); let poolUsdcAtaAddr: web3.PublicKey; let poolUsdtAtaAddr: web3.PublicKey; @@ -195,7 +197,7 @@ let flagshipPool: web3.PublicKey; // let flagshipPoolData: SwimPoolState; // let poolAuth: web3.PublicKey; const swimUsdMint: web3.PublicKey = swimUsdKeypair.publicKey; - +let wormholeAddresses: WormholeAddresses; const metapoolMint0Keypair = swimUsdKeypair; const metapoolMint1Keypair = web3.Keypair.generate(); const metapoolMint1Authority = payer; @@ -247,6 +249,16 @@ let tokenBridgeConfig: web3.PublicKey; let custodySigner: web3.PublicKey; const aggregator: web3.PublicKey = DEFAULT_SOL_USD_FEED; +const dummyEthTokenBridge: DummyForeignRoutingProgram = + new DummyForeignRoutingProgram( + ethTokenBridgeNativeStr, + CHAIN_ID_ETH, + ethTokenBridgeSequence, + swimUsdMint.toBuffer(), + propellerProgram.programId, + evmRoutingContractBuffer, + ); + describe("propeller", () => { beforeAll(async () => { console.info(`initializing two pool v2`); @@ -261,7 +273,7 @@ describe("propeller", () => { poolMintDecimals, poolMintAuthorities.map((k) => k.publicKey), swimUsdKeypair.publicKey, - governanceKeypair.publicKey, + poolGovernanceKeypair.publicKey, )); const initFlagshipPoolTxn = twoPoolProgram.methods // .initialize(params) @@ -273,8 +285,8 @@ describe("propeller", () => { lpMint: swimUsdKeypair.publicKey, poolTokenAccount0: poolUsdcAtaAddr, poolTokenAccount1: poolUsdtAtaAddr, - pauseKey: pauseKeypair.publicKey, - governanceAccount: governanceKeypair.publicKey, + pauseKey: poolPauseKeypair.publicKey, + governanceAccount: poolGovernanceKeypair.publicKey, governanceFeeAccount: flagshipPoolGovernanceFeeAcct, tokenProgram: splToken.programId, associatedTokenProgram: splAssociatedToken.programId, @@ -372,7 +384,7 @@ describe("propeller", () => { // [metapoolMint1Decimal], // [metapoolMint1Authority.publicKey], metapoolLpMintKeypair.publicKey, - governanceKeypair.publicKey, + poolGovernanceKeypair.publicKey, )); const initMetapoolTxn = twoPoolProgram.methods @@ -385,8 +397,8 @@ describe("propeller", () => { lpMint: metapoolLpMintKeypair.publicKey, poolTokenAccount0: metapoolPoolToken0Ata, poolTokenAccount1: metapoolPoolToken1Ata, - pauseKey: pauseKeypair.publicKey, - governanceAccount: governanceKeypair.publicKey, + pauseKey: poolPauseKeypair.publicKey, + governanceAccount: poolGovernanceKeypair.publicKey, governanceFeeAccount: metapoolGovernanceFeeAta, tokenProgram: splToken.programId, associatedTokenProgram: splAssociatedToken.programId, @@ -474,76 +486,24 @@ describe("propeller", () => { } `); - // [custodyOrWrappedMeta] = await (async () => { - // const mintInfo = await getMint(program.provider.connection, swimUsdMint); - // if (mintInfo.mintAuthority! === tokenMintSigner) { - // //First derive the Wrapped Mint Key - // //[Ricky] - this call is propellerLpAta wormhole-sdk - // const nativeInfo = await getOriginalAssetSol( - // program.provider.connection, - // tokenBridge.toString(), - // swimUsdMint.toString() - // ); - // const [wrappedMintKey] = await web3.PublicKey.findProgramAddress( - // [ - // Buffer.from("wrapped"), - // // serializeuint16 as uint8array - // // ` data.token_chain.to_be_bytes().to_vec(),` - // serializeUint16(nativeInfo.chainId as number), - // swimUsdMint.toBytes() - // ], - // tokenBridge - // ); - // //Then derive the Wrapped Meta Key - // return await web3.PublicKey.findProgramAddress([Buffer.from("meta"), wrappedMintKey.toBytes()], tokenBridge); - // } else { - // // transfer native sol asset - // return await web3.PublicKey.findProgramAddress([swimUsdMint.toBytes()], tokenBridge); - // } - // })(); - - // note - there's also wasm generated helper methods to derive these addresses as well. - // assuming always sending solana native token so this will be custody. - [custody] = await (async () => { - return await web3.PublicKey.findProgramAddress( - [swimUsdMint.toBytes()], - tokenBridge, - ); - })(); - - [wormholeConfig] = await web3.PublicKey.findProgramAddress( - [Buffer.from("Bridge")], - wormhole, - ); - [wormholeFeeCollector] = await web3.PublicKey.findProgramAddress( - [Buffer.from("fee_collector")], - wormhole, - ); - // wh functions return in a hex string format - // wormholeEmitter = new web3.PublicKey( - // tryHexToNativeString(await getEmitterAddressSolana(tokenBridge.toBase58()), CHAIN_ID_SOLANA) - // ); - [wormholeEmitter] = await web3.PublicKey.findProgramAddress( - [Buffer.from("emitter")], - tokenBridge, - ); - [wormholeSequence] = await web3.PublicKey.findProgramAddress( - [Buffer.from("Sequence"), wormholeEmitter.toBytes()], - wormhole, + wormholeAddresses = await getWormholeAddressesForMint( + WORMHOLE_CORE_BRIDGE, + WORMHOLE_TOKEN_BRIDGE, + swimUsdMint, + ethTokenBridge, + bscTokenBridge, ); - [authoritySigner] = await web3.PublicKey.findProgramAddress( - [Buffer.from("authority_signer")], - tokenBridge, - ); - [tokenBridgeConfig] = await web3.PublicKey.findProgramAddress( - [Buffer.from("config")], - tokenBridge, - ); - [custodySigner] = await web3.PublicKey.findProgramAddress( - [Buffer.from("custody_signer")], - tokenBridge, - ); + ({ + authoritySigner, + custody, + custodySigner, + tokenBridgeConfig, + wormholeConfig, + wormholeEmitter, + wormholeFeeCollector, + wormholeSequence, + } = wormholeAddresses); console.info(` custodyOrWrappedMeta: ${custody.toString()} @@ -591,7 +551,8 @@ describe("propeller", () => { marginalPricePool, marginalPricePoolTokenIndex, marginalPricePoolTokenMint, - // evmRoutingContractAddress: ethRoutingContract, + maxStaleness, + // evmRoutingContractAddress: evmRoutingContractBuffer, // evmRoutingContractAddress: ethRoutingContractEthUint8Arr }; const tx = propellerProgram.methods @@ -599,7 +560,8 @@ describe("propeller", () => { .accounts({ propellerRedeemerEscrow: propellerRedeemerEscrowAddr, propellerFeeVault, - admin: propellerAdmin.publicKey, + governanceKey: propellerGovernanceKey.publicKey, + pauseKey: propellerPauseKey.publicKey, swimUsdMint: swimUsdMint, payer: payer.publicKey, tokenProgram: TOKEN_PROGRAM_ID, @@ -613,7 +575,7 @@ describe("propeller", () => { twoPoolProgram: twoPoolProgram.programId, aggregator, }) - .signers([propellerAdmin]); + .signers([propellerGovernanceKey, propellerPauseKey]); const pubkeys = await tx.pubkeys(); console.info(`pubkeys: ${JSON.stringify(pubkeys, null, 2)}`); @@ -674,7 +636,9 @@ describe("propeller", () => { propeller, ); console.info(`propellerData: ${JSON.stringify(propellerData)}`); - expect(propellerData.admin).toEqual(propellerAdmin.publicKey); + expect(propellerData.governanceKey).toEqual( + propellerGovernanceKey.publicKey, + ); expect(propellerData.swimUsdMint).toEqual(swimUsdMint); console.info( @@ -697,164 +661,221 @@ describe("propeller", () => { `); }); - it("Creates Token Id Mappings", async () => { - const swimUsdTokenIdMap = { + it("Creates Token Number Maps", async () => { + const swimUsdTokenNumberMap = { pool: flagshipPool, poolTokenIndex: 0, poolTokenMint: swimUsdKeypair.publicKey, - poolIx: { transfer: {} }, + toTokenStep: { transfer: {} }, }; - const usdcTokenIdMap = { + const usdcTokenNumberMap = { pool: flagshipPool, poolTokenIndex: usdcPoolTokenIndex, poolTokenMint: usdcKeypair.publicKey, - poolIx: { removeExactBurn: {} }, + toTokenStep: { removeExactBurn: {} }, }; - const usdtTokenIdMap = { + const usdtTokenNumberMap = { pool: flagshipPool, poolTokenIndex: usdtPoolTokenIndex, poolTokenMint: usdtKeypair.publicKey, - poolIx: { removeExactBurn: {} }, + toTokenStep: { removeExactBurn: {} }, }; - const metapoolMint1TokenIdMap = { + const metapoolMint1TokenNumberMap = { pool: metapool, poolTokenIndex: metapoolMint1PoolTokenIndex, poolTokenMint: metapoolMint1Keypair.publicKey, - poolIx: { swapExactInput: {} }, + toTokenStep: { swapExactInput: {} }, }; - const outputTokenIdMapAddrEntries = await Promise.all( + const toTokenNumberMapAddrEntries = await Promise.all( [ { - outputTokenIndex: SWIM_USD_TO_TOKEN_NUMBER, - tokenIdMap: swimUsdTokenIdMap, + toTokenNumber: SWIM_USD_TO_TOKEN_NUMBER, + tokenNumberMap: swimUsdTokenNumberMap, }, - { outputTokenIndex: USDC_TO_TOKEN_NUMBER, tokenIdMap: usdcTokenIdMap }, - { outputTokenIndex: USDT_TO_TOKEN_NUMBER, tokenIdMap: usdtTokenIdMap }, { - outputTokenIndex: metapoolMint1OutputTokenIndex, - tokenIdMap: metapoolMint1TokenIdMap, + toTokenNumber: USDC_TO_TOKEN_NUMBER, + tokenNumberMap: usdcTokenNumberMap, }, - ].map(async ({ outputTokenIndex, tokenIdMap }) => { - const createTokenIdMappingTxn = propellerProgram.methods - .createTokenIdMap( - outputTokenIndex, - tokenIdMap.pool, - tokenIdMap.poolTokenIndex, - tokenIdMap.poolTokenMint, - tokenIdMap.poolIx, + { + toTokenNumber: USDT_TO_TOKEN_NUMBER, + tokenNumberMap: usdtTokenNumberMap, + }, + { + toTokenNumber: metapoolMint1ToTokenNumber, + tokenNumberMap: metapoolMint1TokenNumberMap, + }, + ].map(async ({ toTokenNumber, tokenNumberMap }) => { + console.info(`creating tokenNumberMap for ${toTokenNumber}`); + const createTokenNumberMapTxn = propellerProgram.methods + .createTokenNumberMap( + toTokenNumber, + tokenNumberMap.pool, + tokenNumberMap.poolTokenIndex, + tokenNumberMap.poolTokenMint, + tokenNumberMap.toTokenStep, ) .accounts({ propeller, - admin: propellerAdmin.publicKey, + governanceKey: propellerGovernanceKey.publicKey, payer: payer.publicKey, systemProgram: web3.SystemProgram.programId, // rent: web3.SYSVAR_RENT_PUBKEY, - pool: tokenIdMap.pool, + pool: tokenNumberMap.pool, twoPoolProgram: twoPoolProgram.programId, }) - .signers([propellerAdmin]); + .signers([propellerGovernanceKey]); - const pubkeys = await createTokenIdMappingTxn.pubkeys(); - if (!pubkeys.tokenIdMap) { + const pubkeys = await createTokenNumberMapTxn.pubkeys(); + if (!pubkeys.tokenNumberMap) { throw new Error("Token Id Map PDA Address was not auto-derived"); } - const tokenIdMapAddr = pubkeys.tokenIdMap; - expect(tokenIdMapAddr).toBeTruthy(); + const tokenNumberMapAddr = pubkeys.tokenNumberMap; + expect(tokenNumberMapAddr).toBeTruthy(); const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(outputTokenIndex).toArrayLike(Buffer, "le", 2), - // byteify.serializeUint16(usdcOutputTokenIndex), - ], + await getToTokenNumberMapAddr( + propeller, + toTokenNumber, propellerProgram.programId, ); - expect(tokenIdMapAddr).toEqual(calculatedTokenIdMap); - await createTokenIdMappingTxn.rpc(); + + expect(tokenNumberMapAddr).toEqual(calculatedTokenIdMap); + await createTokenNumberMapTxn.rpc(); const fetchedTokenIdMap = - await propellerProgram.account.tokenIdMap.fetch(tokenIdMapAddr); - expect(fetchedTokenIdMap.outputTokenIndex).toEqual(outputTokenIndex); - expect(fetchedTokenIdMap.pool).toEqual(tokenIdMap.pool); + await propellerProgram.account.tokenNumberMap.fetch( + tokenNumberMapAddr, + ); + expect(fetchedTokenIdMap.toTokenNumber).toEqual(toTokenNumber); + expect(fetchedTokenIdMap.pool).toEqual(tokenNumberMap.pool); expect(fetchedTokenIdMap.poolTokenIndex).toEqual( - tokenIdMap.poolTokenIndex, + tokenNumberMap.poolTokenIndex, ); expect(fetchedTokenIdMap.poolTokenMint).toEqual( - tokenIdMap.poolTokenMint, + tokenNumberMap.poolTokenMint, + ); + expect(fetchedTokenIdMap.toTokenStep).toEqual( + tokenNumberMap.toTokenStep, ); - expect(fetchedTokenIdMap.poolIx).toEqual(tokenIdMap.poolIx); expect(fetchedTokenIdMap.bump).toEqual(calculatedTokenIdMapBump); - return { outputTokenIndex, tokenIdMapAddr }; + return { toTokenNumber, tokenNumberMapAddr }; }), ); outputTokenIdMappingAddrs = new Map( - outputTokenIdMapAddrEntries.map( - ({ outputTokenIndex, tokenIdMapAddr }) => { - return [outputTokenIndex, tokenIdMapAddr]; + toTokenNumberMapAddrEntries.map( + ({ toTokenNumber, tokenNumberMapAddr }) => { + return [toTokenNumber, tokenNumberMapAddr]; }, ), ); for (const [ - outputTokenIndex, - tokenIdMapAddr, + toTokenNumber, + tokenNumberMapAddr, ] of outputTokenIdMappingAddrs.entries()) { console.info(` - outputTokenIndex: ${outputTokenIndex} - tokenIdMapAddr: ${tokenIdMapAddr.toBase58()} - tokenIdMap: ${JSON.stringify( - await propellerProgram.account.tokenIdMap.fetch(tokenIdMapAddr), + toTokenNumber: ${toTokenNumber} + tokenNumberMapAddr: ${tokenNumberMapAddr.toBase58()} + tokenNumberMap: ${JSON.stringify( + await propellerProgram.account.tokenNumberMap.fetch( + tokenNumberMapAddr, + ), )} `); } }); - it("Creates target chain maps", async () => { - const targetChainMaps = await Promise.all( - routingContracts.map(async ({ targetChainId, address }) => { - const createTargetChainMap = propellerProgram.methods - .createTargetChainMap(targetChainId, address) - .accounts({ - propeller, - admin: propellerAdmin.publicKey, - payer: payer.publicKey, - systemProgram: web3.SystemProgram.programId, - // rent: web3.SYSVAR_RENT_PUBKEY, - }) - .signers([propellerAdmin]); - - const createTargetChainMapPubkeys = - await createTargetChainMap.pubkeys(); - await createTargetChainMap.rpc(); - const targetChainMapData = - await propellerProgram.account.targetChainMap.fetch( - createTargetChainMapPubkeys.targetChainMap, + describe("Target Chain Maps", () => { + it("Creates target chain maps", async () => { + const targetChainMaps = await Promise.all( + routingContracts.map(async ({ targetChainId, address }) => { + const createTargetChainMap = propellerProgram.methods + .createTargetChainMap(targetChainId, address) + .accounts({ + propeller, + governanceKey: propellerGovernanceKey.publicKey, + payer: payer.publicKey, + systemProgram: web3.SystemProgram.programId, + // rent: web3.SYSVAR_RENT_PUBKEY, + }) + .signers([propellerGovernanceKey]); + + const createTargetChainMapPubkeys = + await createTargetChainMap.pubkeys(); + await createTargetChainMap.rpc(); + const targetChainMapData = + await propellerProgram.account.targetChainMap.fetch( + createTargetChainMapPubkeys.targetChainMap, + ); + return { + targetChainId, + targetAddress: address, + targetChainMapData, + }; + }), + ); + targetChainMaps.forEach( + ({ targetChainId, targetAddress, targetChainMapData }) => { + expect(targetChainMapData.targetChain).toEqual(targetChainId); + expect(Buffer.from(targetChainMapData.targetAddress)).toEqual( + targetAddress, ); - return { - targetChainId, - targetAddress: address, + }, + ); + }); + it("Updates target chain maps", async () => { + const targetChain = CHAIN_ID_ETH; + const [targetChainMapAddr] = await getTargetChainMapAddr( + propeller, + targetChain, + propellerProgram.programId, + ); + const newTargetAddressNativeStr = + "0x280999aB9aBfDe9DC5CE7aFB25497d6BB3e8bDD5"; + const newTargetAddress = Buffer.from( + tryNativeToHexString(newTargetAddressNativeStr, targetChain), + "hex", + ); + await propellerProgram.methods + .updateTargetChainMap(targetChain, newTargetAddress) + .accounts({ + propeller, + governanceKey: propellerGovernanceKey.publicKey, + payer: payer.publicKey, + targetChainMap: targetChainMapAddr, + }) + .signers([propellerGovernanceKey]) + .rpc(); - targetChainMapData, - }; - }), - ); - targetChainMaps.forEach( - ({ targetChainId, targetAddress, targetChainMapData }) => { - expect(targetChainMapData.targetChain).toEqual(targetChainId); - expect(Buffer.from(targetChainMapData.targetAddress)).toEqual( - targetAddress, - ); - }, - ); + const targetChainMapDataAfter = + await propellerProgram.account.targetChainMap.fetch(targetChainMapAddr); + expect(Buffer.from(targetChainMapDataAfter.targetAddress)).toEqual( + newTargetAddress, + ); + + await propellerProgram.methods + .updateTargetChainMap(targetChain, evmRoutingContractBuffer) + .accounts({ + propeller, + governanceKey: propellerGovernanceKey.publicKey, + payer: payer.publicKey, + targetChainMap: targetChainMapAddr, + }) + .signers([propellerGovernanceKey]) + .rpc(); + + const targetChainMapDataAfter2 = + await propellerProgram.account.targetChainMap.fetch(targetChainMapAddr); + expect(Buffer.from(targetChainMapDataAfter2.targetAddress)).toEqual( + evmRoutingContractBuffer, + ); + }); }); - describe("Propeller Pool Ixs", () => { - describe("Propeller Flagship Pool ixs", () => { - it("Cross Chain Add", async () => { + describe("InitToSwimUSD", () => { + describe("for swimUSD pool", () => { + it("crossChainInitToSwimUsd", async () => { const poolUserBalancesBefore = await getPoolUserBalances( splToken, twoPoolProgram, @@ -888,7 +909,7 @@ describe("propeller", () => { const memoStr = createMemoId(); const addTxn = propellerProgram.methods - .crossChainAdd(inputAmounts, minimumMintAmount) + .crossChainInitToSwimUsd(inputAmounts, minimumMintAmount) .accounts({ propeller: propeller, poolTokenAccount0: poolUsdcAtaAddr, @@ -977,7 +998,7 @@ describe("propeller", () => { expect(previousDepthAfter.gt(previousDepthBefore)).toEqual(true); await checkTxnLogsForMemo(addTxnSig, memoStr); }); - describe("Propeller Add", () => { + describe("propellerInitToSwimUsd", () => { it("executes successfully", async () => { const poolUserBalancesBefore = await getPoolUserBalances( splToken, @@ -1005,7 +1026,7 @@ describe("propeller", () => { const maxFee = new BN(100); const addTxn = propellerProgram.methods - .propellerAdd(inputAmounts, maxFee) + .propellerInitToSwimUsd(inputAmounts, maxFee) .accounts({ propeller: propeller, poolTokenAccount0: poolUsdcAtaAddr, @@ -1117,7 +1138,7 @@ describe("propeller", () => { const maxFee = new BN(50_000_000_000); await expect(() => { return propellerProgram.methods - .propellerAdd(inputAmounts, maxFee) + .propellerInitToSwimUsd(inputAmounts, maxFee) .accounts({ propeller: propeller, poolTokenAccount0: poolUsdcAtaAddr, @@ -1142,8 +1163,7 @@ describe("propeller", () => { }); }); }); - - describe("Propeller Metapool Pool ixs", () => { + describe("for metapool", () => { beforeAll(async () => { console.info("seeding metapool"); const poolUserBalancesBefore = await getPoolUserBalances( @@ -1193,10 +1213,68 @@ describe("propeller", () => { userTransferAuthority.publicKey, payer, ); + const seedMetapoolAccts = await getAddOrRemoveAccounts( + metapool, + payer.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const seedMetapoolTxn = await twoPoolProgram.methods .add(inputAmounts, minimumMintAmount) + .accounts(seedMetapoolAccts) + .preInstructions([...approveIxs]) + .postInstructions([...revokeIxs]) + .signers([userTransferAuthority]) + .rpc(); + + console.info(`seedMetapoolTxn: ${seedMetapoolTxn}`); + const poolUserBalancesAfter = await getPoolUserBalances( + splToken, + twoPoolProgram, + metapoolPoolToken0Ata, + metapoolPoolToken1Ata, + metapoolGovernanceFeeAta, + userMetapoolTokenAccount0, + userMetapoolTokenAccount1, + userMetapoolLpTokenAccount, + metapool, + metapoolLpMintKeypair.publicKey, + ); + printBeforeAndAfterPoolUserBalances("seeding metapool", [ + poolUserBalancesBefore, + poolUserBalancesAfter, + ]); + }); + it("crossChainInitToSwimUsd", async () => { + const poolUserBalancesBefore = await getPoolUserBalances( + splToken, + twoPoolProgram, + metapoolPoolToken0Ata, + metapoolPoolToken1Ata, + metapoolGovernanceFeeAta, + userMetapoolTokenAccount0, + userMetapoolTokenAccount1, + userMetapoolLpTokenAccount, + metapool, + metapoolLpMintKeypair.publicKey, + ); + printPoolUserBalances("poolUserBalancesBefore", poolUserBalancesBefore); + const exactInputAmounts = [new BN(0), new BN(100_000)]; + const minimumOutputAmount = new BN(0); + const userTransferAuthority = web3.Keypair.generate(); + const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( + splToken, + [userMetapoolTokenAccount0, userMetapoolTokenAccount1], + exactInputAmounts, + userTransferAuthority.publicKey, + payer, + ); + const memoStr = createMemoId(); + + const swapExactInputTxnSig = await propellerProgram.methods + .crossChainInitToSwimUsd(exactInputAmounts, minimumOutputAmount) .accounts({ - // propeller: propeller, + propeller, poolTokenAccount0: metapoolPoolToken0Ata, poolTokenAccount1: metapoolPoolToken1Ata, lpMint: metapoolLpMintKeypair.publicKey, @@ -1206,13 +1284,19 @@ describe("propeller", () => { userTokenAccount1: userMetapoolTokenAccount1, userLpTokenAccount: userMetapoolLpTokenAccount, tokenProgram: splToken.programId, + twoPoolProgram: twoPoolProgram.programId, }) .preInstructions([...approveIxs]) - .postInstructions([...revokeIxs]) + .postInstructions([ + ...revokeIxs, + createMemoInstruction(memoStr.toString("hex")), + ]) .signers([userTransferAuthority]) - .rpc(); + .rpc(rpcCommitmentConfig); + + console.info(`swapExactInputTxnSig: ${swapExactInputTxnSig}`); + await checkTxnLogsForMemo(swapExactInputTxnSig, memoStr); - console.info(`seedMetapoolTxn: ${seedMetapoolTxn}`); const poolUserBalancesAfter = await getPoolUserBalances( splToken, twoPoolProgram, @@ -1225,13 +1309,65 @@ describe("propeller", () => { metapool, metapoolLpMintKeypair.publicKey, ); - printBeforeAndAfterPoolUserBalances("seeding metapool", [ - poolUserBalancesBefore, - poolUserBalancesAfter, - ]); + console.info(`swapExactInput (non propeller-enabled) balances`); + printBeforeAndAfterPoolUserBalances( + "swapExactInput (non propeller-enabled) balances", + [poolUserBalancesBefore, poolUserBalancesAfter], + ); + + const { + poolTokenBalances: [ + poolToken0AtaBalanceBefore, + poolToken1AtaBalanceBefore, + ], + governanceFeeBalance: governanceFeeBalanceBefore, + userTokenBalances: [ + userToken0AtaBalanceBefore, + userToken1AtaBalanceBefore, + ], + userLpTokenBalance: userLpTokenBalanceBefore, + previousDepth: previousDepthBefore, + } = poolUserBalancesBefore; + + const { + poolTokenBalances: [ + poolToken0AtaBalanceAfter, + poolToken1AtaBalanceAfter, + ], + governanceFeeBalance: governanceFeeBalanceAfter, + userTokenBalances: [ + userToken0AtaBalanceAfter, + userToken1AtaBalanceAfter, + ], + userLpTokenBalance: userLpTokenBalanceAfter, + previousDepth: previousDepthAfter, + } = poolUserBalancesAfter; + + expect( + poolToken0AtaBalanceAfter.lt(poolToken0AtaBalanceBefore), + ).toEqual(true); + expect( + poolToken1AtaBalanceAfter.gt(poolToken1AtaBalanceBefore), + ).toEqual(true); + + expect( + governanceFeeBalanceAfter.gt(governanceFeeBalanceBefore), + ).toEqual(true); + expect( + userToken0AtaBalanceAfter.gt(userToken0AtaBalanceBefore), + ).toEqual(true); + expect( + userToken1AtaBalanceAfter.eq( + userToken1AtaBalanceBefore.sub(exactInputAmounts[1]), + ), + ).toEqual(true); + expect(userLpTokenBalanceAfter.eq(userLpTokenBalanceBefore)).toEqual( + true, + ); + expect(!previousDepthAfter.eq(previousDepthBefore)).toBeTruthy(); }); - describe("Swap Exact Input", () => { - it("CrossChainSwapExactInput", async () => { + describe("propellerInitToSwimUsd", () => { + it("executes successfully", async () => { const poolUserBalancesBefore = await getPoolUserBalances( splToken, twoPoolProgram, @@ -1248,21 +1384,23 @@ describe("propeller", () => { "poolUserBalancesBefore", poolUserBalancesBefore, ); - const exactInputAmount = new BN(100_000); - const minimumOutputAmount = new BN(0); + const exactInputAmounts = [new BN(0), new BN(100_000)]; + const maxFee = new BN(0); const userTransferAuthority = web3.Keypair.generate(); const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( splToken, - [userMetapoolTokenAccount1], - [exactInputAmount], + [userMetapoolTokenAccount0, userMetapoolTokenAccount1], + exactInputAmounts, userTransferAuthority.publicKey, payer, ); + const memoStr = createMemoId(); const swapExactInputTxn = propellerProgram.methods - .crossChainSwapExactInput(exactInputAmount, minimumOutputAmount) + .propellerInitToSwimUsd(exactInputAmounts, maxFee) .accounts({ + propeller, poolTokenAccount0: metapoolPoolToken0Ata, poolTokenAccount1: metapoolPoolToken1Ata, lpMint: metapoolLpMintKeypair.publicKey, @@ -1270,9 +1408,9 @@ describe("propeller", () => { userTransferAuthority: userTransferAuthority.publicKey, userTokenAccount0: userMetapoolTokenAccount0, userTokenAccount1: userMetapoolTokenAccount1, + userLpTokenAccount: userMetapoolLpTokenAccount, tokenProgram: splToken.programId, twoPoolProgram: twoPoolProgram.programId, - swimUsdMint: swimUsdMint, }) .preInstructions([...approveIxs]) .postInstructions([ @@ -1358,7 +1496,7 @@ describe("propeller", () => { ).toEqual(true); expect( userToken1AtaBalanceAfter.eq( - userToken1AtaBalanceBefore.sub(exactInputAmount), + userToken1AtaBalanceBefore.sub(exactInputAmounts[1]), ), ).toEqual(true); expect(userLpTokenBalanceAfter.eq(userLpTokenBalanceBefore)).toEqual( @@ -1366,179 +1504,43 @@ describe("propeller", () => { ); expect(!previousDepthAfter.eq(previousDepthBefore)).toBeTruthy(); }); - describe("PropellerSwapExactInput", () => { - it("executes successfully", async () => { - const poolUserBalancesBefore = await getPoolUserBalances( - splToken, - twoPoolProgram, - metapoolPoolToken0Ata, - metapoolPoolToken1Ata, - metapoolGovernanceFeeAta, - userMetapoolTokenAccount0, - userMetapoolTokenAccount1, - userMetapoolLpTokenAccount, - metapool, - metapoolLpMintKeypair.publicKey, - ); - printPoolUserBalances( - "poolUserBalancesBefore", - poolUserBalancesBefore, - ); - const exactInputAmount = new BN(100_000); - const maxFee = new BN(0); - const userTransferAuthority = web3.Keypair.generate(); - const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( - splToken, - [userMetapoolTokenAccount1], - [exactInputAmount], - userTransferAuthority.publicKey, - payer, - ); - const memoStr = createMemoId(); - - const swapExactInputTxn = propellerProgram.methods - .propellerSwapExactInput(exactInputAmount, maxFee) - .accounts({ - poolTokenAccount0: metapoolPoolToken0Ata, - poolTokenAccount1: metapoolPoolToken1Ata, - lpMint: metapoolLpMintKeypair.publicKey, - governanceFee: metapoolGovernanceFeeAta, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userMetapoolTokenAccount0, - userTokenAccount1: userMetapoolTokenAccount1, - tokenProgram: splToken.programId, - twoPoolProgram: twoPoolProgram.programId, - swimUsdMint: swimUsdMint, - }) - .preInstructions([...approveIxs]) - .postInstructions([ - ...revokeIxs, - createMemoInstruction(memoStr.toString("hex")), - ]) - .signers([userTransferAuthority]); - // .rpc(rpcCommitmentConfig); - - const swapExactInputTxnPubkeys = await swapExactInputTxn.pubkeys(); - console.info( - `swapExactInputTxPubkeys: ${JSON.stringify( - swapExactInputTxnPubkeys, - null, - 2, - )}`, - ); - - const swapExactInputTxnSig = await swapExactInputTxn.rpc( - rpcCommitmentConfig, - ); - console.info(`swapExactInputTxnSig: ${swapExactInputTxnSig}`); - await checkTxnLogsForMemo(swapExactInputTxnSig, memoStr); - - const poolUserBalancesAfter = await getPoolUserBalances( - splToken, - twoPoolProgram, - metapoolPoolToken0Ata, - metapoolPoolToken1Ata, - metapoolGovernanceFeeAta, - userMetapoolTokenAccount0, - userMetapoolTokenAccount1, - userMetapoolLpTokenAccount, - metapool, - metapoolLpMintKeypair.publicKey, - ); - console.info(`swapExactInput (non propeller-enabled) balances`); - printBeforeAndAfterPoolUserBalances( - "swapExactInput (non propeller-enabled) balances", - [poolUserBalancesBefore, poolUserBalancesAfter], - ); - - const { - poolTokenBalances: [ - poolToken0AtaBalanceBefore, - poolToken1AtaBalanceBefore, - ], - governanceFeeBalance: governanceFeeBalanceBefore, - userTokenBalances: [ - userToken0AtaBalanceBefore, - userToken1AtaBalanceBefore, - ], - userLpTokenBalance: userLpTokenBalanceBefore, - previousDepth: previousDepthBefore, - } = poolUserBalancesBefore; - - const { - poolTokenBalances: [ - poolToken0AtaBalanceAfter, - poolToken1AtaBalanceAfter, - ], - governanceFeeBalance: governanceFeeBalanceAfter, - userTokenBalances: [ - userToken0AtaBalanceAfter, - userToken1AtaBalanceAfter, - ], - userLpTokenBalance: userLpTokenBalanceAfter, - previousDepth: previousDepthAfter, - } = poolUserBalancesAfter; - - expect( - poolToken0AtaBalanceAfter.lt(poolToken0AtaBalanceBefore), - ).toEqual(true); - expect( - poolToken1AtaBalanceAfter.gt(poolToken1AtaBalanceBefore), - ).toEqual(true); - - expect( - governanceFeeBalanceAfter.gt(governanceFeeBalanceBefore), - ).toEqual(true); - expect( - userToken0AtaBalanceAfter.gt(userToken0AtaBalanceBefore), - ).toEqual(true); - expect( - userToken1AtaBalanceAfter.eq( - userToken1AtaBalanceBefore.sub(exactInputAmount), - ), - ).toEqual(true); - expect( - userLpTokenBalanceAfter.eq(userLpTokenBalanceBefore), - ).toEqual(true); - expect(!previousDepthAfter.eq(previousDepthBefore)).toBeTruthy(); - }); - it("fails if output amount < max fee", async () => { - const exactInputAmount = new BN(100_000); - const maxFee = new BN(100_000_000_000); - const userTransferAuthority = web3.Keypair.generate(); - const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( - splToken, - [userMetapoolTokenAccount1], - [exactInputAmount], - userTransferAuthority.publicKey, - payer, - ); - const memoStr = createMemoId(); + it("fails if output amount < max fee", async () => { + const exactInputAmounts = [new BN(0), new BN(100_000_000)]; + const maxFee = new BN(100_000_000_000); + const userTransferAuthority = web3.Keypair.generate(); + const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( + splToken, + [userMetapoolTokenAccount0, userMetapoolTokenAccount1], + exactInputAmounts, + userTransferAuthority.publicKey, + payer, + ); + const memoStr = createMemoId(); - const swapExactInputTxn = propellerProgram.methods - .propellerSwapExactInput(exactInputAmount, maxFee) - .accounts({ - poolTokenAccount0: metapoolPoolToken0Ata, - poolTokenAccount1: metapoolPoolToken1Ata, - lpMint: metapoolLpMintKeypair.publicKey, - governanceFee: metapoolGovernanceFeeAta, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userMetapoolTokenAccount0, - userTokenAccount1: userMetapoolTokenAccount1, - tokenProgram: splToken.programId, - twoPoolProgram: twoPoolProgram.programId, - swimUsdMint: swimUsdMint, - }) - .preInstructions([...approveIxs]) - .postInstructions([ - ...revokeIxs, - createMemoInstruction(memoStr.toString("hex")), - ]) - .signers([userTransferAuthority]); - await expect(swapExactInputTxn.rpc()).rejects.toThrow( - "Insufficient Amount being transferred", - ); - }); + const swapExactInputTxn = propellerProgram.methods + .propellerInitToSwimUsd(exactInputAmounts, maxFee) + .accounts({ + propeller, + poolTokenAccount0: metapoolPoolToken0Ata, + poolTokenAccount1: metapoolPoolToken1Ata, + lpMint: metapoolLpMintKeypair.publicKey, + governanceFee: metapoolGovernanceFeeAta, + userTransferAuthority: userTransferAuthority.publicKey, + userTokenAccount0: userMetapoolTokenAccount0, + userTokenAccount1: userMetapoolTokenAccount1, + userLpTokenAccount: userMetapoolLpTokenAccount, + tokenProgram: splToken.programId, + twoPoolProgram: twoPoolProgram.programId, + }) + .preInstructions([...approveIxs]) + .postInstructions([ + ...revokeIxs, + createMemoInstruction(memoStr.toString("hex")), + ]) + .signers([userTransferAuthority]); + await expect(swapExactInputTxn.rpc()).rejects.toThrow( + "Insufficient Amount being transferred", + ); }); }); }); @@ -1564,13 +1566,18 @@ describe("propeller", () => { const memo = createMemoId(); const wormholeMessage = web3.Keypair.generate(); - const nonceBefore = ( - await propellerProgram.account.propeller.fetch(propeller) - ).nonce; + const nonce = createNonce().readUInt32LE(0); + const targetChain = CHAIN_ID_ETH; + const [targetChainMap] = await getTargetChainMapAddr( + propeller, + targetChain, + propellerProgram.programId, + ); const transferNativeTxn = await propellerProgram.methods .crossChainTransferNativeWithPayload( + nonce, transferAmount, - CHAIN_ID_ETH, + targetChain, evmOwner, ) .accounts({ @@ -1596,6 +1603,7 @@ describe("propeller", () => { // systemProgram, wormhole, tokenProgram: splToken.programId, + targetChainMap, }) .preInstructions([setComputeUnitLimitIx]) .postInstructions([createMemoInstruction(memo.toString("hex"))]) @@ -1607,11 +1615,6 @@ describe("propeller", () => { rpcCommitmentConfig, ); - const nonceAfter = ( - await propellerProgram.account.propeller.fetch(propeller) - ).nonce; - expect(nonceAfter).toEqual(nonceBefore + 1); - const transferNativeTxnSize = transferNativeTxn.serialize().length; console.info(`transferNativeTxnSize txnSize: ${transferNativeTxnSize}`); await connection.confirmTransaction({ @@ -1668,7 +1671,7 @@ describe("propeller", () => { const tokenTransferTo = tokenTransferMessage.tokenTransfer.to; expect(uint8ArrayToHex(tokenTransferTo)).toEqual( - ethRoutingContractEthHexStr, + evmRoutingContractHexStr, ); const formattedTokenTransferMessage = formatParsedTokenTransferPostedMessage(tokenTransferMessage); @@ -1679,32 +1682,12 @@ describe("propeller", () => { 2, )}`, ); - expect(tokenTransferMessage.core.nonce).toEqual(nonceBefore); expect(swimPayload.owner).toEqual(evmOwner); expect(swimPayload.propellerEnabled).toBeUndefined(); expect(swimPayload.gasKickstart).toBeUndefined(); expect(swimPayload.maxFee).toBeUndefined(); expect(swimPayload.targetTokenId).toBeUndefined(); expect(swimPayload.memo).toBeUndefined(); - // this fails due to capitalization - // expect(formattedTokenTransferMessage.tokenTransfer.to).to.equal(ethRoutingContractStr); - // console.info(` - // tokenTransferMessage: ${JSON.stringify(tokenTransferMessage, null ,2)} - // `) - // console.info(`postedMessage:\n${JSON.stringify(postedMessage)}`); - // console.info(`postedMessagePayload:\n${JSON.stringify(postedMessagePayload)}`); - // const { - // payload: swimPayload - // } = postedMessagePayload; - // console.info(` - // evmOwnerEthHexStr: ${evmOwnerEthHexStr} - // evmOwner(Buffer.toString('hex')): ${evmOwner.toString('hex')} - // evmTargetTokenAddrEthHexStr: ${evmTargetTokenAddrEthHexStr} - // evmTargetTokenAddr(Buffer.toString('hex')): ${evmTargetTokenAddr.toString('hex')} - // `); - // console.info(`swimPayload:\n${JSON.stringify(swimPayload)}`); - // const transferAmountBuffer = transferAmount.toBuffer('be'); - // console.info(`transferAmountBufferHexStr: ${transferAmountBuffer.toString('hex')}`); }); it("calls propellerTransferNativeWithPayload with memo", async () => { @@ -1728,18 +1711,24 @@ describe("propeller", () => { const wormholeMessage = web3.Keypair.generate(); const gasKickstart = false; const maxFee = new BN(100_000); - const nonceBefore = ( - await propellerProgram.account.propeller.fetch(propeller) - ).nonce; + const nonce = createNonce().readUInt32LE(0); + + const targetChain = CHAIN_ID_ETH; + const [targetChainMap] = await getTargetChainMapAddr( + propeller, + targetChain, + propellerProgram.programId, + ); const propellerTransferNativeTxn = await propellerProgram.methods .propellerTransferNativeWithPayload( + nonce, transferAmount, - CHAIN_ID_ETH, + targetChain, evmOwner, gasKickstart, maxFee, evmTargetTokenId, - Buffer.from(memo, "hex"), + memo, ) .accounts({ propeller, @@ -1764,6 +1753,7 @@ describe("propeller", () => { // systemProgram, wormhole, tokenProgram: splToken.programId, + targetChainMap, }) .preInstructions([setComputeUnitLimitIx]) .postInstructions([createMemoInstruction(memo.toString("hex"))]) @@ -1775,11 +1765,6 @@ describe("propeller", () => { rpcCommitmentConfig, ); - const nonceAfter = ( - await propellerProgram.account.propeller.fetch(propeller) - ).nonce; - expect(nonceAfter).toEqual(nonceBefore + 1); - const transferNativeTxnSize = propellerTransferNativeTxn.serialize().length; console.info(`transferNativeTxnSize txnSize: ${transferNativeTxnSize}`); @@ -1838,7 +1823,7 @@ describe("propeller", () => { expect(swimPayload.maxFee.eq(maxFee)).toBeTruthy(); const tokenTransferTo = tokenTransferMessage.tokenTransfer.to; expect(uint8ArrayToHex(tokenTransferTo)).toEqual( - ethRoutingContractEthHexStr, + evmRoutingContractHexStr, ); const formattedTokenTransferMessage = formatParsedTokenTransferPostedMessage(tokenTransferMessage); @@ -1849,36 +1834,13 @@ describe("propeller", () => { 2, )}`, ); - expect(tokenTransferMessage.core.nonce).toEqual(nonceBefore); expect(swimPayload.owner).toEqual(evmOwner); expect(swimPayload.propellerEnabled).toEqual(true); expect(swimPayload.gasKickstart).toEqual(gasKickstart); expect(swimPayload.maxFee).toEqual(maxFee); expect(swimPayload.targetTokenId).toEqual(evmTargetTokenId); expect(swimPayload.memo.toString("hex")).toEqual(memo.toString("hex")); - await checkTxnLogsForMemo( - propellerTransferNativeTxnSig, - memo.toString("hex"), - ); - // this fails due to capitalization - // expect(formattedTokenTransferMessage.tokenTransfer.to).to.equal(ethRoutingContractStr); - // console.info(` - // tokenTransferMessage: ${JSON.stringify(tokenTransferMessage, null ,2)} - // `) - // console.info(`postedMessage:\n${JSON.stringify(postedMessage)}`); - // console.info(`postedMessagePayload:\n${JSON.stringify(postedMessagePayload)}`); - // const { - // payload: swimPayload - // } = postedMessagePayload; - // console.info(` - // evmOwnerEthHexStr: ${evmOwnerEthHexStr} - // evmOwner(Buffer.toString('hex')): ${evmOwner.toString('hex')} - // evmTargetTokenAddrEthHexStr: ${evmTargetTokenAddrEthHexStr} - // evmTargetTokenAddr(Buffer.toString('hex')): ${evmTargetTokenAddr.toString('hex')} - // `); - // console.info(`swimPayload:\n${JSON.stringify(swimPayload)}`); - // const transferAmountBuffer = transferAmount.toBuffer('be'); - // console.info(`transferAmountBufferHexStr: ${transferAmountBuffer.toString('hex')}`); + await checkTxnLogsForMemo(propellerTransferNativeTxnSig, memo); }); it("calls propellerTransferNativeWithPayload without memo", async () => { @@ -1901,13 +1863,19 @@ describe("propeller", () => { const wormholeMessage = web3.Keypair.generate(); const gasKickstart = false; const maxFee = new BN(100_000); - const nonceBefore = ( - await propellerProgram.account.propeller.fetch(propeller) - ).nonce; + const nonce = createNonce().readUInt32LE(0); + const targetChain = CHAIN_ID_ETH; + const [targetChainMap] = await getTargetChainMapAddr( + propeller, + targetChain, + propellerProgram.programId, + ); + const propellerTransferNativeTxn = await propellerProgram.methods .propellerTransferNativeWithPayload( + nonce, transferAmount, - CHAIN_ID_ETH, + targetChain, evmOwner, gasKickstart, maxFee, @@ -1937,6 +1905,7 @@ describe("propeller", () => { // systemProgram, wormhole, tokenProgram: splToken.programId, + targetChainMap, }) .preInstructions([setComputeUnitLimitIx]) .transaction(); @@ -1947,10 +1916,6 @@ describe("propeller", () => { rpcCommitmentConfig, ); - const nonceAfter = ( - await propellerProgram.account.propeller.fetch(propeller) - ).nonce; - expect(nonceAfter).toEqual(nonceBefore + 1); const transferNativeTxnSize = propellerTransferNativeTxn.serialize().length; console.info(`transferNativeTxnSize txnSize: ${transferNativeTxnSize}`); @@ -2009,7 +1974,7 @@ describe("propeller", () => { expect(swimPayload.maxFee.eq(maxFee)).toBeTruthy(); const tokenTransferTo = tokenTransferMessage.tokenTransfer.to; expect(uint8ArrayToHex(tokenTransferTo)).toEqual( - ethRoutingContractEthHexStr, + evmRoutingContractHexStr, ); const formattedTokenTransferMessage = formatParsedTokenTransferPostedMessage(tokenTransferMessage); @@ -2020,50 +1985,200 @@ describe("propeller", () => { 2, )}`, ); - expect(tokenTransferMessage.core.nonce).toEqual(nonceBefore); expect(swimPayload.owner).toEqual(evmOwner); expect(swimPayload.propellerEnabled).toEqual(true); expect(swimPayload.gasKickstart).toEqual(gasKickstart); expect(swimPayload.maxFee).toEqual(maxFee); expect(swimPayload.targetTokenId).toEqual(evmTargetTokenId); expect(swimPayload.memo).toBeUndefined(); - await checkTxnLogsForMemo( - transferNativeTxnSig, - memo.toString("hex"), - false, - ); - // this fails due to capitalization - // expect(formattedTokenTransferMessage.tokenTransfer.to).to.equal(ethRoutingContractStr); - // console.info(` - // tokenTransferMessage: ${JSON.stringify(tokenTransferMessage, null ,2)} - // `) - // console.info(`postedMessage:\n${JSON.stringify(postedMessage)}`); - // console.info(`postedMessagePayload:\n${JSON.stringify(postedMessagePayload)}`); - // const { - // payload: swimPayload - // } = postedMessagePayload; - // console.info(` - // evmOwnerEthHexStr: ${evmOwnerEthHexStr} - // evmOwner(Buffer.toString('hex')): ${evmOwner.toString('hex')} - // evmTargetTokenAddrEthHexStr: ${evmTargetTokenAddrEthHexStr} - // evmTargetTokenAddr(Buffer.toString('hex')): ${evmTargetTokenAddr.toString('hex')} - // `); - // console.info(`swimPayload:\n${JSON.stringify(swimPayload)}`); - // const transferAmountBuffer = transferAmount.toBuffer('be'); - // console.info(`transferAmountBufferHexStr: ${transferAmountBuffer.toString('hex')}`); + await checkTxnLogsForMemo(transferNativeTxnSig, memo, false); }); - it("Fails propellerTransferNativeWithPayload if transferAmount <= maxFee for targetChain", async () => { - const transferAmount = new BN(100_000_000); + describe("error cases", () => { + it("Fails propellerTransferNativeWithPayload if transferAmount <= maxFee for targetChain", async () => { + const transferAmount = new BN(100_000_000); + + const wormholeMessage = web3.Keypair.generate(); + const gasKickstart = false; + const maxFee = transferAmount.add(new BN(1)); + const nonce = createNonce().readUInt32LE(0); + await expect(() => { + return propellerProgram.methods + .propellerTransferNativeWithPayload( + nonce, + transferAmount, + CHAIN_ID_ETH, + evmOwner, + gasKickstart, + maxFee, + evmTargetTokenId, + null, + ) + .accounts({ + propeller, + payer: payer.publicKey, + tokenBridgeConfig, + userSwimUsdAta: userSwimUsdAtaAddr, + swimUsdMint: swimUsdMint, + custody, + tokenBridge, + custodySigner, + authoritySigner, + wormholeConfig, + wormholeMessage: wormholeMessage.publicKey, + wormholeEmitter, + wormholeSequence, + wormholeFeeCollector, + clock: web3.SYSVAR_CLOCK_PUBKEY, + // autoderived + // sender + rent: web3.SYSVAR_RENT_PUBKEY, + // autoderived + // systemProgram, + wormhole, + tokenProgram: splToken.programId, + }) + .signers([wormholeMessage]) + .preInstructions([setComputeUnitLimitIx]) + .rpc(); + }).rejects.toThrow("Insufficient Amount being transferred"); + }); + it("fails if targetChain is paused", async () => { + const targetChain = CHAIN_ID_ETH; + let isPaused = true; + const [targetChainMapAddr] = await getTargetChainMapAddr( + propeller, + targetChain, + propellerProgram.programId, + ); + await propellerProgram.methods + .targetChainMapSetPaused(targetChain, isPaused) + .accounts({ + propeller, + pauseKey: propellerPauseKey.publicKey, + payer: payer.publicKey, + targetChainMap: targetChainMapAddr, + }) + .signers([propellerPauseKey]) + .rpc(); - const wormholeMessage = web3.Keypair.generate(); - const gasKickstart = false; - const maxFee = transferAmount.add(new BN(1)); - await expect(() => { - return propellerProgram.methods + const targetChainMapData = + await propellerProgram.account.targetChainMap.fetch( + targetChainMapAddr, + ); + expect(targetChainMapData.isPaused).toEqual(isPaused); + const transferAmount = new BN(100_000_000); + + const memo = createMemoId(); + let wormholeMessage = web3.Keypair.generate(); + + const nonce = createNonce().readUInt32LE(0); + await expect(() => { + return propellerProgram.methods + .crossChainTransferNativeWithPayload( + nonce, + transferAmount, + targetChain, + evmOwner, + ) + .accounts({ + propeller, + payer: payer.publicKey, + tokenBridgeConfig, + userSwimUsdAta: userSwimUsdAtaAddr, + swimUsdMint: swimUsdMint, + custody, + tokenBridge, + custodySigner, + authoritySigner, + wormholeConfig, + wormholeMessage: wormholeMessage.publicKey, + wormholeEmitter, + wormholeSequence, + wormholeFeeCollector, + clock: web3.SYSVAR_CLOCK_PUBKEY, + // autoderived + // sender + rent: web3.SYSVAR_RENT_PUBKEY, + // autoderived + // systemProgram, + wormhole, + tokenProgram: splToken.programId, + }) + .preInstructions([setComputeUnitLimitIx]) + .postInstructions([createMemoInstruction(memo.toString("hex"))]) + .signers([wormholeMessage]) + .rpc(); + }).rejects.toThrow("Target Chain is paused"); + + wormholeMessage = web3.Keypair.generate(); + const gasKickstart = false; + const maxFee = new BN(100_000); + + await expect(() => { + return propellerProgram.methods + .propellerTransferNativeWithPayload( + nonce, + transferAmount, + targetChain, + evmOwner, + gasKickstart, + maxFee, + evmTargetTokenId, + null, + ) + .accounts({ + propeller, + payer: payer.publicKey, + tokenBridgeConfig, + userSwimUsdAta: userSwimUsdAtaAddr, + swimUsdMint: swimUsdMint, + custody, + tokenBridge, + custodySigner, + authoritySigner, + wormholeConfig, + wormholeMessage: wormholeMessage.publicKey, + wormholeEmitter, + wormholeSequence, + wormholeFeeCollector, + clock: web3.SYSVAR_CLOCK_PUBKEY, + // autoderived + // sender + rent: web3.SYSVAR_RENT_PUBKEY, + // autoderived + // systemProgram, + wormhole, + tokenProgram: splToken.programId, + }) + .preInstructions([setComputeUnitLimitIx]) + .signers([wormholeMessage]) + .rpc(); + }).rejects.toThrow("Target Chain is paused"); + + isPaused = false; + await propellerProgram.methods + .targetChainMapSetPaused(targetChain, isPaused) + .accounts({ + propeller, + pauseKey: propellerPauseKey.publicKey, + payer: payer.publicKey, + targetChainMap: targetChainMapAddr, + }) + .signers([propellerPauseKey]) + .rpc(); + + const targetChainMapDataAfter = + await propellerProgram.account.targetChainMap.fetch( + targetChainMapAddr, + ); + expect(targetChainMapDataAfter.isPaused).toEqual(isPaused); + + await propellerProgram.methods .propellerTransferNativeWithPayload( + nonce, transferAmount, - CHAIN_ID_ETH, + targetChain, evmOwner, gasKickstart, maxFee, @@ -2094,10 +2209,10 @@ describe("propeller", () => { wormhole, tokenProgram: splToken.programId, }) - .signers([wormholeMessage]) .preInstructions([setComputeUnitLimitIx]) + .signers([wormholeMessage]) .rpc(); - }).rejects.toThrow("Insufficient Amount being transferred"); + }); }); describe("User submitted CompleteWithPayload and ProcessSwimPayload", () => { @@ -2115,48 +2230,17 @@ describe("propeller", () => { it("mocks token transfer with payload then verifySig & postVaa then executes CompleteWithPayload", async () => { // const maxFee = new BN(1_000_000_000); const swimPayload = { - version: swimPayloadVersion, - owner: provider.publicKey.toBuffer(), - // owner: owner.toBuffer(), - // propellerEnabled, - // gasKickstart, - // maxFee, - // targetTokenId, - // memo: memoBuffer, - }; - // - // const swimPayload = { - // version: swimPayloadVersion, - // targetTokenId, - // owner: provider.publicKey.toBuffer(), - // memo: memoBuffer, - // propellerEnabled, - // gasKickstart, - // }; - const amount = parseUnits("1", mintDecimal); - console.info(`amount: ${amount.toString()}`); - /** - * this is encoding a token transfer from eth routing contract - * with a swimUSD token address that originated on solana - * the same as initializing a `TransferWrappedWithPayload` from eth - */ + version: swimPayloadVersion, + owner: provider.publicKey.toBuffer(), + }; - const nonce = createNonce().readUInt32LE(0); - const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( - 0, - nonce, - CHAIN_ID_ETH as number, - ethTokenBridge, - BigInt(++ethTokenBridgeSequence), - encodeTokenTransferWithPayload( + const amount = parseUnits("1", mintDecimal); + console.info(`amount: ${amount.toString()}`); + const tokenTransferWithPayloadSignedVaa = + dummyEthTokenBridge.createSignedTokenTransferWithSwimPayloadVAA( amount.toString(), - swimUsdKeypair.publicKey.toBuffer(), - CHAIN_ID_SOLANA, - propellerProgram.programId, - ethRoutingContract, - encodeSwimPayload(swimPayload), - ), - ); + swimPayload, + ); const propellerRedeemerEscrowAccountBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) ).amount; @@ -2218,14 +2302,9 @@ describe("propeller", () => { message: wormholeMessage, claim: wormholeClaim, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - // this is only used in propellerCompleteNativeWithPayload - // but must still be passed. - // tokenBridge.completeNativeWithPayload just checks mint is - // correct. - feeRecipient: userSwimUsdAtaAddr, - // feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, custody: custody, swimUsdMint: swimUsdMint, custodySigner, @@ -2289,17 +2368,16 @@ describe("propeller", () => { expect(swimPayloadMessageAccount.bump).toEqual( expectedSwimPayloadMessageBump, ); - // expect(swimPayloadMessageAccount.whMessage).toEqual(wormholeMessage); expect(swimPayloadMessageAccount.claim).toEqual(wormholeClaim); expect( Buffer.from(swimPayloadMessageAccount.vaaEmitterAddress), - ).toEqual(ethTokenBridge); + ).toEqual(dummyEthTokenBridge.emitterAddress); expect(swimPayloadMessageAccount.vaaEmitterChain).toEqual( - CHAIN_ID_ETH, + dummyEthTokenBridge.emitterChain, ); expect( swimPayloadMessageAccount.vaaSequence.eq( - new BN(ethTokenBridgeSequence), + new BN(dummyEthTokenBridge.sequence), ), ).toBeTruthy(); expect( @@ -2307,14 +2385,7 @@ describe("propeller", () => { new BN(amount.toString()), ), ).toBeTruthy(); - // const { - // swimPayloadVersion, - // targetTokenId, - // owner, - // memo, - // propellerEnabled, - // gasKickstart - // } = swimPayloadMessageAccount.swimPayload; + expect(swimPayloadMessageAccount.swimPayloadVersion).toEqual( swimPayloadVersion, ); @@ -2375,7 +2446,6 @@ describe("propeller", () => { const userTokenAccount1 = userUsdtAtaAddr; const userLpTokenAccount = userSwimUsdAtaAddr; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, @@ -2408,7 +2478,6 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, userTokenAccount0, userTokenAccount1, userLpTokenAccount, @@ -2417,27 +2486,18 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) - .postInstructions([createMemoInstruction(memoStr.toString("hex"))]) - .signers([userTransferAuthority]); + .postInstructions([createMemoInstruction(memoStr.toString("hex"))]); const processSwimPayloadPubkeys = await processSwimPayloadIxs.pubkeys(); console.info(`${JSON.stringify(processSwimPayloadPubkeys, null, 2)}`); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountTargetTokenId, propellerProgram.programId, ); @@ -2447,14 +2507,16 @@ describe("propeller", () => { throw new Error("expectedTokenIdMap not found"); } const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch(expectedTokenIdMap); + await propellerProgram.account.tokenNumberMap.fetch( + expectedTokenIdMap, + ); console.info(` calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); @@ -2533,28 +2595,12 @@ describe("propeller", () => { const amount = parseUnits("1", mintDecimal); console.info(`amount: ${amount.toString()}`); - /** - * this is encoding a token transfer from eth routing contract - * with a swimUSD token address that originated on solana - * the same as initializing a `TransferWrappedWithPayload` from eth - */ - const nonce = createNonce().readUInt32LE(0); - const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( - 0, - nonce, - CHAIN_ID_ETH as number, - ethTokenBridge, - BigInt(++ethTokenBridgeSequence), - encodeTokenTransferWithPayload( + const tokenTransferWithPayloadSignedVaa = + dummyEthTokenBridge.createSignedTokenTransferWithSwimPayloadVAA( amount.toString(), - swimUsdKeypair.publicKey.toBuffer(), - CHAIN_ID_SOLANA, - propellerProgram.programId, - ethRoutingContract, - encodeSwimPayload(swimPayload), - ), - ); + swimPayload, + ); const propellerRedeemerEscrowAccountBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) ).amount; @@ -2600,14 +2646,9 @@ describe("propeller", () => { message: wormholeMessage, claim: wormholeClaim, endpoint: endpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - // this is only used in propellerCompleteNativeWithPayload - // but must still be passed. - // tokenBridge.completeNativeWithPayload just checks mint is - // correct. - feeRecipient: userSwimUsdAtaAddr, - // feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, custody: custody, swimUsdMint: swimUsdMint, custodySigner, @@ -2675,13 +2716,13 @@ describe("propeller", () => { expect(swimPayloadMessageAccount.claim).toEqual(wormholeClaim); expect( Buffer.from(swimPayloadMessageAccount.vaaEmitterAddress), - ).toEqual(ethTokenBridge); + ).toEqual(dummyEthTokenBridge.emitterAddress); expect(swimPayloadMessageAccount.vaaEmitterChain).toEqual( - CHAIN_ID_ETH, + dummyEthTokenBridge.emitterChain, ); expect( swimPayloadMessageAccount.vaaSequence.eq( - new BN(ethTokenBridgeSequence), + new BN(dummyEthTokenBridge.sequence), ), ).toBeTruthy(); expect( @@ -2753,12 +2794,11 @@ describe("propeller", () => { const userTokenAccount1 = userUsdtAtaAddr; const userLpTokenAccount = userSwimUsdAtaAddr; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -2789,7 +2829,6 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, userTokenAccount0, userTokenAccount1, userLpTokenAccount, @@ -2798,44 +2837,36 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) - .postInstructions([createMemoInstruction(memoStr.toString("hex"))]) - .signers([userTransferAuthority]); + .postInstructions([createMemoInstruction(memoStr.toString("hex"))]); const processSwimPayloadPubkeys = await processSwimPayload.pubkeys(); console.info(`${JSON.stringify(processSwimPayloadPubkeys, null, 2)}`); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); - const expectedTokenIdMap = outputTokenIdMappingAddrs.get(targetTokenId); if (!expectedTokenIdMap) { throw new Error("expectedTokenIdMap not found"); } const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch(expectedTokenIdMap); + await propellerProgram.account.tokenNumberMap.fetch( + expectedTokenIdMap, + ); console.info(` calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); @@ -2908,7 +2939,7 @@ describe("propeller", () => { let wormholeClaim: web3.PublicKey; let wormholeMessage: web3.PublicKey; let swimPayloadMessage: web3.PublicKey; - const targetTokenId = metapoolMint1OutputTokenIndex; + const targetTokenId = metapoolMint1ToTokenNumber; const memoStr = createMemoId(); // const memo = "e45794d6c5a2750b"; @@ -2920,28 +2951,12 @@ describe("propeller", () => { const amount = parseUnits("1", mintDecimal); console.info(`amount: ${amount.toString()}`); - /** - * this is encoding a token transfer from eth routing contract - * with a swimUSD token address that originated on solana - * the same as initializing a `TransferWrappedWithPayload` from eth - */ - const nonce = createNonce().readUInt32LE(0); - const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( - 0, - nonce, - CHAIN_ID_ETH as number, - ethTokenBridge, - BigInt(++ethTokenBridgeSequence), - encodeTokenTransferWithPayload( + const tokenTransferWithPayloadSignedVaa = + dummyEthTokenBridge.createSignedTokenTransferWithSwimPayloadVAA( amount.toString(), - swimUsdKeypair.publicKey.toBuffer(), - CHAIN_ID_SOLANA, - propellerProgram.programId, - ethRoutingContract, - encodeSwimPayload(swimPayload), - ), - ); + swimPayload, + ); const propellerRedeemerEscrowAccountBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) ).amount; @@ -2987,14 +3002,9 @@ describe("propeller", () => { message: wormholeMessage, claim: wormholeClaim, endpoint: endpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - // this is only used in propellerCompleteNativeWithPayload - // but must still be passed. - // tokenBridge.completeNativeWithPayload just checks mint is - // correct. - feeRecipient: userSwimUsdAtaAddr, - // feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, custody: custody, swimUsdMint: swimUsdMint, custodySigner, @@ -3061,13 +3071,13 @@ describe("propeller", () => { expect(swimPayloadMessageAccount.claim).toEqual(wormholeClaim); expect( Buffer.from(swimPayloadMessageAccount.vaaEmitterAddress), - ).toEqual(ethTokenBridge); + ).toEqual(dummyEthTokenBridge.emitterAddress); expect(swimPayloadMessageAccount.vaaEmitterChain).toEqual( - CHAIN_ID_ETH, + dummyEthTokenBridge.emitterChain, ); expect( swimPayloadMessageAccount.vaaSequence.eq( - new BN(ethTokenBridgeSequence), + new BN(dummyEthTokenBridge.sequence), ), ).toBeTruthy(); expect( @@ -3132,12 +3142,11 @@ describe("propeller", () => { // user's lp token account. should be unchanged. const userLpTokenAccount = userMetapoolLpTokenAccount; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -3168,7 +3177,6 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, userTokenAccount0, userTokenAccount1, userLpTokenAccount, @@ -3177,27 +3185,17 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) - .postInstructions([createMemoInstruction(memoStr.toString("hex"))]) - - .signers([userTransferAuthority]); + .postInstructions([createMemoInstruction(memoStr.toString("hex"))]); const processSwimPayloadPubkeys = await processSwimPayload.pubkeys(); console.info(`${JSON.stringify(processSwimPayloadPubkeys, null, 2)}`); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); @@ -3207,14 +3205,16 @@ describe("propeller", () => { throw new Error("expectedTokenIdMap not found"); } const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch(expectedTokenIdMap); + await propellerProgram.account.tokenNumberMap.fetch( + expectedTokenIdMap, + ); console.info(` calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); @@ -3324,21 +3324,26 @@ describe("propeller", () => { */ const nonce = createNonce().readUInt32LE(0); - const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( - 0, - nonce, - CHAIN_ID_ETH as number, - ethTokenBridge, - BigInt(++ethTokenBridgeSequence), - encodeTokenTransferWithPayload( + // const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( + // 0, + // nonce, + // CHAIN_ID_ETH as number, + // ethTokenBridge, + // BigInt(++ethTokenBridgeSequence), + // encodeTokenTransferWithPayload( + // amount.toString(), + // swimUsdKeypair.publicKey.toBuffer(), + // CHAIN_ID_SOLANA, + // propellerProgram.programId, + // evmRoutingContractBuffer, + // encodeSwimPayload(swimPayload), + // ), + // ); + const tokenTransferWithPayloadSignedVaa = + dummyEthTokenBridge.createSignedTokenTransferWithSwimPayloadVAA( amount.toString(), - swimUsdKeypair.publicKey.toBuffer(), - CHAIN_ID_SOLANA, - propellerProgram.programId, - ethRoutingContract, - encodeSwimPayload(swimPayload), - ), - ); + swimPayload, + ); const propellerRedeemerEscrowAccountBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) ).amount; @@ -3384,14 +3389,9 @@ describe("propeller", () => { message: wormholeMessage, claim: wormholeClaim, endpoint: endpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - // this is only used in propellerCompleteNativeWithPayload - // but must still be passed. - // tokenBridge.completeNativeWithPayload just checks mint is - // correct. - feeRecipient: userSwimUsdAtaAddr, - // feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, custody: custody, swimUsdMint: swimUsdMint, custodySigner, @@ -3460,13 +3460,13 @@ describe("propeller", () => { expect(swimPayloadMessageAccount.claim).toEqual(wormholeClaim); expect( Buffer.from(swimPayloadMessageAccount.vaaEmitterAddress), - ).toEqual(ethTokenBridge); + ).toEqual(dummyEthTokenBridge.emitterAddress); expect(swimPayloadMessageAccount.vaaEmitterChain).toEqual( - CHAIN_ID_ETH, + dummyEthTokenBridge.emitterChain, ); expect( swimPayloadMessageAccount.vaaSequence.eq( - new BN(ethTokenBridgeSequence), + new BN(dummyEthTokenBridge.sequence), ), ).toBeTruthy(); expect( @@ -3538,12 +3538,11 @@ describe("propeller", () => { const userTokenAccount1 = userUsdtAtaAddr; const userLpTokenAccount = userSwimUsdAtaAddr; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -3573,7 +3572,6 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, userTokenAccount0, userTokenAccount1, userLpTokenAccount, @@ -3582,27 +3580,19 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) - .postInstructions([createMemoInstruction(memoStr.toString("hex"))]) - .signers([userTransferAuthority]); + .postInstructions([createMemoInstruction(memoStr.toString("hex"))]); const processSwimPayloadPubkeys = await processSwimPayloadIxs.pubkeys(); console.info(`${JSON.stringify(processSwimPayloadPubkeys, null, 2)}`); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } + const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); @@ -3612,14 +3602,16 @@ describe("propeller", () => { throw new Error("expectedTokenIdMap not found"); } const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch(expectedTokenIdMap); + await propellerProgram.account.tokenNumberMap.fetch( + expectedTokenIdMap, + ); console.info(` calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); @@ -3720,21 +3712,26 @@ describe("propeller", () => { */ const nonce = createNonce().readUInt32LE(0); - const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( - 0, - nonce, - CHAIN_ID_ETH as number, - ethTokenBridge, - BigInt(++ethTokenBridgeSequence), - encodeTokenTransferWithPayload( + // const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( + // 0, + // nonce, + // CHAIN_ID_ETH as number, + // ethTokenBridge, + // BigInt(++ethTokenBridgeSequence), + // encodeTokenTransferWithPayload( + // amount.toString(), + // swimUsdKeypair.publicKey.toBuffer(), + // CHAIN_ID_SOLANA, + // propellerProgram.programId, + // evmRoutingContractBuffer, + // encodeSwimPayload(swimPayload), + // ), + // ); + const tokenTransferWithPayloadSignedVaa = + dummyEthTokenBridge.createSignedTokenTransferWithSwimPayloadVAA( amount.toString(), - swimUsdKeypair.publicKey.toBuffer(), - CHAIN_ID_SOLANA, - propellerProgram.programId, - ethRoutingContract, - encodeSwimPayload(swimPayload), - ), - ); + swimPayload, + ); const propellerRedeemerEscrowAccountBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) ).amount; @@ -3799,14 +3796,9 @@ describe("propeller", () => { message: wormholeMessage, claim: wormholeClaim, endpoint: endpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - // this is only used in propellerCompleteNativeWithPayload - // but must still be passed. - // tokenBridge.completeNativeWithPayload just checks mint is - // correct. - feeRecipient: userSwimUsdAtaAddr, - // feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, custody: custody, swimUsdMint: swimUsdMint, custodySigner, @@ -3874,13 +3866,13 @@ describe("propeller", () => { expect(swimPayloadMessageAccount.claim).toEqual(wormholeClaim); expect( Buffer.from(swimPayloadMessageAccount.vaaEmitterAddress), - ).toEqual(ethTokenBridge); + ).toEqual(dummyEthTokenBridge.emitterAddress); expect(swimPayloadMessageAccount.vaaEmitterChain).toEqual( - CHAIN_ID_ETH, + dummyEthTokenBridge.emitterChain, ); expect( swimPayloadMessageAccount.vaaSequence.eq( - new BN(ethTokenBridgeSequence), + new BN(dummyEthTokenBridge.sequence), ), ).toBeTruthy(); expect( @@ -3952,12 +3944,11 @@ describe("propeller", () => { const userTokenAccount1 = userUsdtAtaAddr; const userLpTokenAccount = userSwimUsdAtaAddr; - const userTransferAuthority = web3.Keypair.generate(); const swimPayloadMessageAccount = await propellerProgram.account.swimPayloadMessage.fetch( swimPayloadMessage, ); - const swimPayloadMessageAccountTargetTokenId = + const swimPayloadMessageAccountToTokenNumber = swimPayloadMessageAccount.targetTokenId; const propellerRedeemerEscrowBalanceBefore = ( await splToken.account.token.fetch(propellerRedeemerEscrowAccount) @@ -3988,7 +3979,6 @@ describe("propeller", () => { poolTokenAccount1, lpMint, governanceFee: governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, userTokenAccount0, userTokenAccount1, userLpTokenAccount, @@ -3997,27 +3987,18 @@ describe("propeller", () => { systemProgram: web3.SystemProgram.programId, }) .preInstructions([setComputeUnitLimitIx]) - .postInstructions([createMemoInstruction(memoStr.toString("hex"))]) - .signers([userTransferAuthority]); + .postInstructions([createMemoInstruction(memoStr.toString("hex"))]); const processSwimPayloadPubkeys = await processSwimPayload.pubkeys(); console.info(`${JSON.stringify(processSwimPayloadPubkeys, null, 2)}`); - if (!processSwimPayloadPubkeys.tokenIdMap) { + if (!processSwimPayloadPubkeys.tokenNumberMap) { throw new Error("tokenIdMap not derived"); } const [calculatedTokenIdMap, calculatedTokenIdMapBump] = - await web3.PublicKey.findProgramAddress( - [ - Buffer.from("propeller"), - Buffer.from("token_id"), - propeller.toBuffer(), - new BN(swimPayloadMessageAccountTargetTokenId).toArrayLike( - Buffer, - "le", - 2, - ), - ], + await getToTokenNumberMapAddr( + propeller, + swimPayloadMessageAccountToTokenNumber, propellerProgram.programId, ); @@ -4027,14 +4008,16 @@ describe("propeller", () => { throw new Error("expectedTokenIdMap not found"); } const expectedTokenIdMapAcct = - await propellerProgram.account.tokenIdMap.fetch(expectedTokenIdMap); + await propellerProgram.account.tokenNumberMap.fetch( + expectedTokenIdMap, + ); console.info(` calculatedTokenIdMap: ${calculatedTokenIdMap.toBase58()} calculatedTokenIdMapBump: ${calculatedTokenIdMapBump} expectedTokenIdMapAcct: ${expectedTokenIdMap.toBase58()} : ${JSON.stringify(expectedTokenIdMapAcct, null, 2)} `); - const derivedTokenIdMap = processSwimPayloadPubkeys.tokenIdMap; + const derivedTokenIdMap = processSwimPayloadPubkeys.tokenNumberMap; expect(derivedTokenIdMap).toEqual(expectedTokenIdMap); if (!processSwimPayloadPubkeys.swimClaim) { throw new Error("swimClaim key not derived"); @@ -4105,9 +4088,534 @@ describe("propeller", () => { }); }); + describe("Updating/Closing Token Number Maps", () => { + let newMetapool: web3.PublicKey; + let newMetapoolToken0Ata: web3.PublicKey; + let newMetapoolToken1Ata: web3.PublicKey; + const newMetapoolLpMintKeypair = web3.Keypair.generate(); + let newMetapoolGovernanceFeeAta: web3.PublicKey; + let userNewMetapoolLpTokenAta: web3.PublicKey; + + // intiialize new metapool with same token_mint_keys as previous metapool but new lp token + beforeAll(async () => { + ({ + poolPubkey: newMetapool, + poolTokenAccounts: [newMetapoolToken0Ata, newMetapoolToken1Ata], + governanceFeeAccount: newMetapoolGovernanceFeeAta, + } = await setupPoolPrereqs( + twoPoolProgram, + splToken, + metapoolMintKeypairs, + metapoolMintDecimals, + [flagshipPool, metapoolMint1Authority.publicKey], + newMetapoolLpMintKeypair.publicKey, + poolGovernanceKeypair.publicKey, + )); + + await twoPoolProgram.methods + .initialize(ampFactor, lpFee, governanceFee) + .accounts({ + payer: provider.publicKey, + poolMint0: metapoolMintKeypairs[0].publicKey, + poolMint1: metapoolMintKeypairs[1].publicKey, + lpMint: newMetapoolLpMintKeypair.publicKey, + poolTokenAccount0: newMetapoolToken0Ata, + poolTokenAccount1: newMetapoolToken1Ata, + pauseKey: poolPauseKeypair.publicKey, + governanceAccount: poolGovernanceKeypair.publicKey, + governanceFeeAccount: newMetapoolGovernanceFeeAta, + tokenProgram: splToken.programId, + associatedTokenProgram: splAssociatedToken.programId, + systemProgram: web3.SystemProgram.programId, + rent: web3.SYSVAR_RENT_PUBKEY, + }) + .signers([newMetapoolLpMintKeypair]) + .rpc(); + const newMetapoolData = await twoPoolProgram.account.twoPool.fetch( + newMetapool, + ); + console.info(` + newMetapool: ${newMetapool.toBase58()} + newMetapoolData: ${JSON.stringify(newMetapoolData, null, 2)} + `); + console.info("seeding new metapool"); + userNewMetapoolLpTokenAta = ( + await getOrCreateAssociatedTokenAccount( + connection, + payer, + newMetapoolLpMintKeypair.publicKey, + dummyUser.publicKey, + ) + ).address; + const uiInputAmounts: readonly BN[] = [new BN(2_000), new BN(1_500)]; + const inputAmounts: readonly BN[] = uiInputAmounts.map( + (bn: BN, i: number) => { + const powerOfTen = new BN(10).pow(new BN(metapoolMintDecimals[i])); + return bn.mul(powerOfTen); + }, + ); + const minimumMintAmount = new BN(0); + // const addParams = { + // inputAmounts, + // minimumMintAmount, + // }; + const userTransferAuthority = web3.Keypair.generate(); + const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( + splToken, + [userMetapoolTokenAccount0, userMetapoolTokenAccount1], + inputAmounts, + userTransferAuthority.publicKey, + payer, + ); + const seedNewMetapoolAccts = await getAddOrRemoveAccounts( + newMetapool, + dummyUser.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); + const seedNewMetapoolTxn = await twoPoolProgram.methods + .add(inputAmounts, minimumMintAmount) + .accounts(seedNewMetapoolAccts) + .preInstructions([...approveIxs]) + .postInstructions([...revokeIxs]) + .signers([userTransferAuthority]) + .rpc(); + + console.info(`seedNewMetapoolTxn: ${seedNewMetapoolTxn}`); + const newMetapoolTokenAccountAmounts = ( + await Promise.all( + newMetapoolData.tokenKeys.map(async (key) => { + return await splToken.account.token.fetch(key); + }), + ) + ).map((account) => account.amount.toString()); + + console.info(` + newMetapoolTokenAccountAmounts: ${newMetapoolTokenAccountAmounts.toString()} + `); + // seed new metapool + // await twoPoolProgram.methods.add().accounts({}).rpc(); + }); + + it("Can update a token number map", async () => { + const [tokenNumberMapAddr] = await getToTokenNumberMapAddr( + propeller, + metapoolMint1ToTokenNumber, + propellerProgram.programId, + ); + const tokenNumberMapDataBefore = + await propellerProgram.account.tokenNumberMap.fetch(tokenNumberMapAddr); + expect(tokenNumberMapDataBefore.toTokenNumber).toEqual( + metapoolMint1ToTokenNumber, + ); + expect(tokenNumberMapDataBefore.poolTokenMint).toEqual( + metapoolMintKeypairs[1].publicKey, + ); + expect(tokenNumberMapDataBefore.pool).toEqual(metapool); + expect(tokenNumberMapDataBefore.poolTokenIndex).toEqual(1); + const poolTokenIndex = 1; + const poolTokenMint = metapoolMint1Keypair.publicKey; + const toTokenStep = { swapExactInput: {} }; + await propellerProgram.methods + .updateTokenNumberMap( + metapoolMint1ToTokenNumber, + poolTokenIndex, + poolTokenMint, + toTokenStep, + ) + .accounts({ + propeller, + governanceKey: propellerGovernanceKey.publicKey, + payer: payer.publicKey, + // rent: web3.SYSVAR_RENT_PUBKEY, + pool: newMetapool, + twoPoolProgram: twoPoolProgram.programId, + }) + .signers([propellerGovernanceKey]) + .rpc(); + // create new metapool with same metapoolMint1 + + const tokenNumberMapDataAfter = + await propellerProgram.account.tokenNumberMap.fetch(tokenNumberMapAddr); + expect(tokenNumberMapDataAfter.toTokenNumber).toEqual( + metapoolMint1ToTokenNumber, + ); + expect(tokenNumberMapDataAfter.poolTokenMint).toEqual( + metapoolMintKeypairs[1].publicKey, + ); + expect(tokenNumberMapDataAfter.pool).toEqual(newMetapool); + expect(tokenNumberMapDataAfter.poolTokenIndex).toEqual(1); + + const oldMetapoolTokenAccountBalancesBefore = + await getPoolTokenAccountBalances(metapool, twoPoolProgram, splToken); + + const newMetapoolTokenAccountBalancesBefore = + await getPoolTokenAccountBalances( + newMetapool, + twoPoolProgram, + splToken, + ); + + // + + const targetTokenId = metapoolMint1ToTokenNumber; + const memoStr = createMemoId(); + + const swimPayload = { + version: swimPayloadVersion, + owner: provider.publicKey.toBuffer(), + }; + + const amount = parseUnits("1", mintDecimal); + console.info(`amount: ${amount.toString()}`); + /** + * this is encoding a token transfer from eth routing contract + * with a swimUSD token address that originated on solana + * the same as initializing a `TransferWrappedWithPayload` from eth + */ + + const nonce = createNonce().readUInt32LE(0); + // const tokenTransferWithPayloadSignedVaa = signAndEncodeVaa( + // 0, + // nonce, + // CHAIN_ID_ETH as number, + // ethTokenBridge, + // BigInt(++ethTokenBridgeSequence), + // encodeTokenTransferWithPayload( + // amount.toString(), + // swimUsdKeypair.publicKey.toBuffer(), + // CHAIN_ID_SOLANA, + // propellerProgram.programId, + // evmRoutingContractBuffer, + // encodeSwimPayload(swimPayload), + // ), + // ); + const tokenTransferWithPayloadSignedVaa = + dummyEthTokenBridge.createSignedTokenTransferWithSwimPayloadVAA( + amount.toString(), + swimPayload, + ); + const propellerRedeemerEscrowAccountBefore = ( + await splToken.account.token.fetch(propellerRedeemerEscrowAccount) + ).amount; + + const newMetapoolUserAtaBalancesBefore = await getUserAtaBalancesForPool( + dummyUser.publicKey, + newMetapool, + twoPoolProgram, + splToken, + ); + + await postVaaSolanaWithRetry( + connection, + async (tx) => { + return provider.wallet.signTransaction(tx); + }, + WORMHOLE_CORE_BRIDGE.toBase58(), + payer.publicKey.toBase58(), + tokenTransferWithPayloadSignedVaa, + 10, + ); + + const [wormholeMessage] = await deriveMessagePda( + tokenTransferWithPayloadSignedVaa, + WORMHOLE_CORE_BRIDGE, + ); + + const [endpointAccount] = await deriveEndpointPda( + CHAIN_ID_ETH, + ethTokenBridge, + // parsedVaa.emitterChain, + // parsedVaa.emitterAddress, + WORMHOLE_TOKEN_BRIDGE, + ); + console.info(`endpointAccount: ${endpointAccount.toBase58()}`); + const wormholeClaim = await getClaimAddressSolana( + WORMHOLE_TOKEN_BRIDGE.toBase58(), + tokenTransferWithPayloadSignedVaa, + ); + + const completeNativeWithPayloadMetapoolIxs = propellerProgram.methods + .completeNativeWithPayload() + .accounts({ + propeller, + payer: payer.publicKey, + tokenBridgeConfig, + // userTokenBridgeAccount: userLpTokenAccount.address, + message: wormholeMessage, + claim: wormholeClaim, + endpoint: endpointAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, + redeemer: propellerRedeemer, + feeVault: propellerFeeVault, + custody: custody, + swimUsdMint: swimUsdMint, + custodySigner, + rent: web3.SYSVAR_RENT_PUBKEY, + systemProgram: web3.SystemProgram.programId, + wormhole, + tokenProgram: splToken.programId, + tokenBridge, + }) + .preInstructions([setComputeUnitLimitIx]) + .postInstructions([createMemoInstruction(memoStr.toString("hex"))]); + + const completeNativeWithPayloadMetapoolPubkeys = + await completeNativeWithPayloadMetapoolIxs.pubkeys(); + + if (!completeNativeWithPayloadMetapoolPubkeys.swimPayloadMessage) { + throw new Error("swimPayloadMessage key not derived"); + } + + const swimPayloadMessage = + completeNativeWithPayloadMetapoolPubkeys.swimPayloadMessage; + + const completeNativeWithPayloadMetapoolTxn = + await completeNativeWithPayloadMetapoolIxs.transaction(); + const transferNativeTxnSig = await provider.sendAndConfirm( + completeNativeWithPayloadMetapoolTxn, + [payer], + { + skipPreflight: true, + }, + ); + + const swimPayloadMessageAccount = + await propellerProgram.account.swimPayloadMessage.fetch( + completeNativeWithPayloadMetapoolPubkeys.swimPayloadMessage, + ); + + await connection.confirmTransaction({ + signature: transferNativeTxnSig, + ...(await connection.getLatestBlockhash()), + }); + + const propellerRedeemerEscrowAccountAfter = ( + await splToken.account.token.fetch(propellerRedeemerEscrowAccount) + ).amount; + + expect( + propellerRedeemerEscrowAccountAfter.gt( + propellerRedeemerEscrowAccountBefore, + ), + ).toEqual(true); + await checkTxnLogsForMemo(transferNativeTxnSig, memoStr); + + const minOutputAmount = new BN(0); + await propellerProgram.methods + .processSwimPayload(targetTokenId, minOutputAmount) + .accounts({ + propeller, + payer: payer.publicKey, + claim: wormholeClaim, + swimPayloadMessage, + swimPayloadMessagePayer: + swimPayloadMessageAccount.swimPayloadMessagePayer, + redeemer: propellerRedeemer, + redeemerEscrow: propellerRedeemerEscrowAccount, + // tokenIdMap: ? + pool: newMetapool, + poolTokenAccount0: newMetapoolToken0Ata, + poolTokenAccount1: newMetapoolToken1Ata, + lpMint: newMetapoolLpMintKeypair.publicKey, + governanceFee: newMetapoolGovernanceFeeAta, + userTokenAccount0: userMetapoolTokenAccount0, + userTokenAccount1: userMetapoolTokenAccount1, + userLpTokenAccount: userNewMetapoolLpTokenAta, + tokenProgram: splToken.programId, + twoPoolProgram: twoPoolProgram.programId, + systemProgram: web3.SystemProgram.programId, + }) + .preInstructions([setComputeUnitLimitIx]) + .postInstructions([createMemoInstruction(memoStr.toString("hex"))]) + .rpc(); + + const oldMetapoolTokenAccountBalancesAfter = + await getPoolTokenAccountBalances(metapool, twoPoolProgram, splToken); + + const newMetapoolTokenAccountBalancesAfter = + await getPoolTokenAccountBalances( + newMetapool, + twoPoolProgram, + splToken, + ); + + expect( + oldMetapoolTokenAccountBalancesAfter[0].eq( + oldMetapoolTokenAccountBalancesBefore[0], + ), + ).toBeTruthy(); + expect( + oldMetapoolTokenAccountBalancesAfter[1].eq( + oldMetapoolTokenAccountBalancesBefore[1], + ), + ).toBeTruthy(); + expect( + newMetapoolTokenAccountBalancesAfter[0].gt( + newMetapoolTokenAccountBalancesBefore[0], + ), + ).toBeTruthy(); + expect( + newMetapoolTokenAccountBalancesAfter[1].lt( + newMetapoolTokenAccountBalancesBefore[1], + ), + ).toBeTruthy(); + + const newMetapoolUserAtaBalancesAfter = await getUserAtaBalancesForPool( + dummyUser.publicKey, + newMetapool, + twoPoolProgram, + splToken, + ); + + expect( + newMetapoolUserAtaBalancesBefore[0].eq( + newMetapoolUserAtaBalancesAfter[0], + ), + ).toBeTruthy(); + // should have received metapool token 1 + expect( + newMetapoolUserAtaBalancesBefore[1].lt( + newMetapoolUserAtaBalancesAfter[1], + ), + ).toBeTruthy(); + expect( + newMetapoolUserAtaBalancesBefore[2].eq( + newMetapoolUserAtaBalancesAfter[2], + ), + ).toBeTruthy(); + }); + it("Can close and re-create a token number map", async () => { + const [tokenNumberMapAddr] = await getToTokenNumberMapAddr( + propeller, + metapoolMint1ToTokenNumber, + propellerProgram.programId, + ); + await propellerProgram.methods + .closeTokenNumberMap(metapoolMint1ToTokenNumber) + .accounts({ + propeller, + governanceKey: propellerGovernanceKey.publicKey, + payer: payer.publicKey, + tokenNumberMap: tokenNumberMapAddr, + }) + .signers([propellerGovernanceKey]) + .rpc(); + const tokenNumberMapDataAfterClose = + await propellerProgram.account.tokenNumberMap.fetchNullable( + tokenNumberMapAddr, + ); + expect(tokenNumberMapDataAfterClose).toBeNull(); + + const swimPayload = { + version: swimPayloadVersion, + owner: provider.publicKey.toBuffer(), + }; + + const amount = parseUnits("1", mintDecimal); + console.info(`amount: ${amount.toString()}`); + + const tokenTransferWithPayloadSignedVaa = + dummyEthTokenBridge.createSignedTokenTransferWithSwimPayloadVAA( + amount.toString(), + swimPayload, + ); + await postVaaToSolana( + tokenTransferWithPayloadSignedVaa, + provider, + wormhole, + ); + + const completeNativeWithPayloadTxn = + await getCompleteNativeWithPayloadTxn( + tokenTransferWithPayloadSignedVaa, + wormhole, + tokenBridge, + propellerProgram, + ); + await provider.sendAndConfirm(completeNativeWithPayloadTxn); + + await expect(() => { + return getProcessSwimPayloadTxn( + metapoolMint1ToTokenNumber, + new BN(0), + tokenTransferWithPayloadSignedVaa, + wormhole, + tokenBridge, + propellerProgram, + twoPoolProgram, + splToken, + ); + }).rejects.toThrow("Token number map does not exist"); + + //recreate tokenNumberMap with original metapool + const metapoolMint1TokenNumberMap = { + pool: metapool, + poolTokenIndex: metapoolMint1PoolTokenIndex, + poolTokenMint: metapoolMint1Keypair.publicKey, + toTokenStep: { swapExactInput: {} }, + }; + + await propellerProgram.methods + .createTokenNumberMap( + metapoolMint1ToTokenNumber, + metapoolMint1TokenNumberMap.pool, + metapoolMint1TokenNumberMap.poolTokenIndex, + metapoolMint1TokenNumberMap.poolTokenMint, + metapoolMint1TokenNumberMap.toTokenStep, + ) + .accounts({ + propeller, + governanceKey: propellerGovernanceKey.publicKey, + payer: payer.publicKey, + systemProgram: web3.SystemProgram.programId, + // rent: web3.SYSVAR_RENT_PUBKEY, + pool: metapool, + twoPoolProgram: twoPoolProgram.programId, + }) + .signers([propellerGovernanceKey]) + .rpc(); + + const tokenNumberMapDataAfterRecreate = + await propellerProgram.account.tokenNumberMap.fetch(tokenNumberMapAddr); + expect(tokenNumberMapDataAfterRecreate.pool.toBase58()).toEqual( + metapool.toBase58(), + ); + const originalMetapoolAtaBalancesBefore = + await getPoolTokenAccountBalances(metapool, twoPoolProgram, splToken); + const processSwimPayloadTxn = await getProcessSwimPayloadTxn( + metapoolMint1ToTokenNumber, + new BN(0), + tokenTransferWithPayloadSignedVaa, + wormhole, + tokenBridge, + propellerProgram, + twoPoolProgram, + splToken, + ); + await provider.sendAndConfirm(processSwimPayloadTxn); + const originalMetapoolAtaBalancesAfter = + await getPoolTokenAccountBalances(metapool, twoPoolProgram, splToken); + //original metapool token[0] (swimUSD) balance should go up + expect( + originalMetapoolAtaBalancesAfter[0].gt( + originalMetapoolAtaBalancesBefore[0], + ), + ).toBeTruthy(); + // pool token[1] balance should go down + expect( + originalMetapoolAtaBalancesAfter[1].lt( + originalMetapoolAtaBalancesBefore[1], + ), + ).toBeTruthy(); + }); // end of close/recreate token number map test + }); + + describe("Updating fees & oracles", () => {}); + async function checkTxnLogsForMemo( txSig: string, - memoStr: Buffer, + memoBuffer: Buffer, exists = true, ) { console.info(`txSig: ${txSig}`); @@ -4129,13 +4637,12 @@ describe("propeller", () => { const memoLogFound = txnLogs.some( (log) => log.startsWith("Program log: Memo") && - log.includes(memoStr.toString("hex")), + log.includes(memoBuffer.toString("hex")), ); expect(memoLogFound).toEqual(exists); } function createMemoId() { - const SWIM_MEMO_LENGTH = 16; // NOTE: Please always use random bytes to avoid conflicts with other users return crypto.randomBytes(SWIM_MEMO_LENGTH); // return (++memoId).toString().padStart(16, "0"); diff --git a/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts b/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts index 3de28449e..bd703d084 100644 --- a/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts +++ b/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts @@ -2,20 +2,29 @@ import type { ChainId, ChainName } from "@certusone/wormhole-sdk"; import { CHAIN_ID_BSC, CHAIN_ID_ETH, + CHAIN_ID_SOLANA, + createNonce, getClaimAddressSolana, + postVaaSolanaWithRetry, + tryNativeToHexString, tryUint8ArrayToNative, } from "@certusone/wormhole-sdk"; -import type { Program, SplToken } from "@project-serum/anchor"; +import { uint8ArrayToHex } from "@certusone/wormhole-sdk/lib/cjs/utils/array"; +import type { AnchorProvider, Program, SplToken } from "@project-serum/anchor"; import { BN, web3 } from "@project-serum/anchor"; -import { MEMO_PROGRAM_ID } from "@solana/spl-memo"; +import { MEMO_PROGRAM_ID, createMemoInstruction } from "@solana/spl-memo"; import { ASSOCIATED_TOKEN_PROGRAM_ID, + TOKEN_PROGRAM_ID, + createAssociatedTokenAccountInstruction, getAssociatedTokenAddress, } from "@solana/spl-token"; import { BigNumber } from "ethers"; import type { Propeller } from "../../artifacts/propeller"; import type { TwoPool } from "../../artifacts/two_pool"; +import { setComputeUnitLimitIx } from "../consts"; +import { getOwnerAtaAddrsForPool } from "../twoPool/poolTestUtils"; import type { ParsedTokenTransferPostedMessage, @@ -24,11 +33,13 @@ import type { import { deriveEndpointPda, deriveMessagePda, + encodeTokenTransferWithPayload, formatParsedTokenTransferPostedMessage, formatParsedTokenTransferSignedVaa, parseTokenTransferPostedMessage, parseTokenTransferSignedVaa, } from "./tokenBridgeUtils"; +import { signAndEncodeVaa } from "./wormholeUtils"; export async function getPropellerPda( mint: web3.PublicKey, @@ -61,6 +72,22 @@ export async function getPropellerSenderPda( )[0]; } +export const getFeeTrackerPda = async ( + swimUsdMint: web3.PublicKey, + feesRecipient: web3.PublicKey, + programId: web3.PublicKey, +): Promise => { + return await web3.PublicKey.findProgramAddress( + [ + Buffer.from("propeller"), + Buffer.from("fee"), + swimUsdMint.toBuffer(), + feesRecipient.toBuffer(), + ], + programId, + ); +}; + export async function getSwimClaimPda( wormholeClaim: web3.PublicKey, propellerProgramId: web3.PublicKey, @@ -84,117 +111,6 @@ export const getSwimPayloadMessagePda = async ( propellerProgramId, ); }; -// -// async function addToPool( -// provider: anchor.AnchorProvider, -// pool: web3.PublicKey, -// poolState: SwimPoolState, -// userTokenAccounts: web3.PublicKey[], -// userLpTokenAccount: web3.PublicKey, -// inputAmounts: anchor.BN[], -// minimumMintAmount: anchor.BN, -// tokenAccountOwner: web3.Keypair, -// delegate: web3.PublicKey, -// ): Promise { -// // let userMetapoolTokenAccountAmounts = await Promise.all( -// // userMetapoolTokenAccounts.map(async (account) => { -// // return (await splToken.account.token.fetch(account)).amount; -// // })); -// // console.log(`userMetapoolTokenAccountAmounts: ${userMetapoolTokenAccountAmounts}`) -// // let userMetapoolTokenAccounts0Amount = await splToken.account.token.fetch(userMetapoolTokenAccounts[0]); -// // let inputAmounts = [new anchor.BN(100), new anchor.BN(100)]; -// -// const addMetapoolIx = addToPoolIx( -// { -// provider, -// pool, -// poolState, -// userTokenAccounts, -// userLpTokenAccount, -// inputAmounts, -// minimumMintAmount, -// }); -// const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( -// anchor.BN.max(inputAmounts[0]!, inputAmounts[1]!), -// tokenAccountOwner.publicKey, -// delegate, -// userTokenAccounts -// ); -// const seedMetapoolTxn = new web3.Transaction() -// .add(...approveIxs!) -// .add(addMetapoolIx) -// .add(...revokeIxs!); -// seedMetapoolTxn.recentBlockhash = (await connection.getLatestBlockhash()).blockhash -// return await provider.sendAndConfirm( -// seedMetapoolTxn, -// [dummyUser], -// { -// skipPreflight: true, -// } -// ); -// } -// -// async function createAtaAndMint(mint: web3.PublicKey) { -// const tokenAccount = await getOrCreateAssociatedTokenAccount( -// connection, -// payer, -// mint, -// dummyUser.publicKey, -// false -// ); -// const mintTxn = await mintTo( -// connection, -// payer, -// mint, -// tokenAccount.address, -// payer, -// initialMintAmount, -// ); -// -// await connection.confirmTransaction({ -// signature: mintTxn, -// ...(await connection.getLatestBlockhash()) -// }) -// console.log(`minted to user_token_account: ${tokenAccount.address.toBase58()}`); -// return tokenAccount -// } - -// const parseTokenTransferWithSwimPayloadPostedMessage = async (arr: Buffer) => { -// const {parse_posted_message} = await importCoreWasm(); -// const postedMessage = parse_posted_message(arr); -// const tokenTransfer = parseTransferWithPayload(Buffer.from(postedMessage.payload)); -// // console.log(`swimPayloadRawBufferStr: ${tokenTransfer.payload.toString()}`); -// const swimPayload = parseSwimPayload(tokenTransfer.payload); -// return { -// ...postedMessage, -// vaa_signature_account: new web3.PublicKey(postedMessage.vaa_signature_account).toBase58(), -// // emitter_address: tryHexToNativeAssetString(postedMessage.emitter_address, postedMessage.emitter_chain), -// // emitter_address: new web3.PublicKey(postedMessage.emitter_address).toBase58(), -// emitter_address: tryUint8ArrayToNative(postedMessage.emitter_address, postedMessage.emitter_chain), -// -// payload: { -// ...tokenTransfer, -// amount: tokenTransfer.amount.toString(), -// originAddress: tryHexToNativeAssetString(tokenTransfer.originAddress, tokenTransfer.originChain), -// originChain: toChainName(tokenTransfer.originChain), -// targetAddress: tryHexToNativeAssetString(tokenTransfer.targetAddress, tokenTransfer.targetChain), -// targetChain: toChainName(tokenTransfer.targetChain), -// fromAddress: tryHexToNativeAssetString(tokenTransfer.fromAddress, postedMessage.emitter_chain), -// payload: { -// ...swimPayload, -// minOutputAmount: swimPayload.minOutputAmount.toString(), -// memo: swimPayload.memo.toString(), -// propellerMinThreshold: swimPayload.propellerMinThreshold.toString(), -// // propellerFee: swimPayload.propellerFee.toString(), -// // targetTokenStr: swimPayload.targetToken.toString(), -// // targetTokenHexStr: swimPayload.targetToken.toString("hex"), -// // ownerStr: swimPayload.owner.toString(), -// ownerNativeStr: tryHexToNativeAssetString(swimPayload.owner.toString("hex"), tokenTransfer.targetChain), -// // ownerHexStr: swimPayload.owner.toString("hex"), -// } -// } -// } -// } // 1 byte - swim internal payload version number // 32 bytes - logical owner/recipient (will use ATA of owner and token on Solana) @@ -414,14 +330,20 @@ export const formatSwimPayload = ( swimPayload: ParsedSwimPayload, chain: ChainId | ChainName, ) => { - return { + const formattedBaseSwimPayload = { ...swimPayload, - // minOutputAmount: swimPayload.minOutputAmount.toString(), - memo: swimPayload.memo !== undefined ? swimPayload.memo.toString() : "", - // minThreshold: swimPayload.minThreshold.toString(), owner: tryUint8ArrayToNative(swimPayload.owner, chain), - maxFee: - swimPayload.maxFee !== undefined ? swimPayload.maxFee.toString() : "", + }; + const formattedMaxFee = swimPayload.maxFee + ? { maxFee: swimPayload.maxFee.toString() } + : {}; + const formattedMemo = swimPayload.memo + ? { memo: uint8ArrayToHex(swimPayload.memo) } + : {}; + return { + ...formattedBaseSwimPayload, + ...formattedMaxFee, + ...formattedMemo, }; }; @@ -459,23 +381,23 @@ export const formatParsedTokenTransferWithSwimPayloadPostedMessage = ( }; }; -export const getTargetTokenIdMapAddr = async ( - propeller: web3.PublicKey, - targetTokenId: number, +export const getToTokenNumberMapAddr = async ( + propellerState: web3.PublicKey, + toTokenNumber: number, propellerProgramId: web3.PublicKey, ) => { return await web3.PublicKey.findProgramAddress( [ Buffer.from("propeller"), Buffer.from("token_id"), - propeller.toBuffer(), - new BN(targetTokenId).toArrayLike(Buffer, "le", 2), + propellerState.toBuffer(), + new BN(toTokenNumber).toArrayLike(Buffer, "le", 2), ], propellerProgramId, ); }; -export const getTargetChainIdMapAddr = async ( +export const getTargetChainMapAddr = async ( propeller: web3.PublicKey, targetChain: number, propellerProgramId: web3.PublicKey, @@ -542,7 +464,6 @@ export const generatePropellerEngineTxns = async ( twoPoolProgram: Program, splToken: Program, aggregator: web3.PublicKey, - userTransferAuthority: web3.Keypair, ): Promise => { let txns: readonly web3.Transaction[] = []; const { @@ -626,20 +547,6 @@ export const generatePropellerEngineTxns = async ( tokenTransferWithPayloadSignedVaa, ); - //TODO: https://solanacookbook.com/references/basic-transactions.html#how-to-change-compute-budget-fee-priority-for-a-transaction - // const modifyComputeUnits = web3.ComputeBudgetProgram.setComputeUnitLimit({ - // units: 900000 - // }); - // - // const addPriorityFee = web3.ComputeBudgetProgram.setComputeUnitPrice({ - // microLamports: 1 - // }); - - const requestUnitsIx = web3.ComputeBudgetProgram.requestUnits({ - // units: 420690, - units: 900000, - additionalFee: 0, - }); const propellerData = await propellerProgram.account.propeller.fetch( propeller, ); @@ -657,9 +564,9 @@ export const generatePropellerEngineTxns = async ( claim: wormholeClaim, swimPayloadMessage: swimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, custody: custody, swimUsdMint: swimUsdMint, custodySigner, @@ -673,6 +580,12 @@ export const generatePropellerEngineTxns = async ( console.info(` completePubkeys: ${JSON.stringify(completePubkeys, null, 2)} `); + const feeTrackingAccts = await getFeeTrackingAccounts( + propeller, + payer.publicKey, + propellerProgram, + twoPoolProgram, + ); const completeNativeWithPayloadIxs = propellerProgram.methods .propellerCompleteNativeWithPayload() @@ -680,22 +593,24 @@ export const generatePropellerEngineTxns = async ( // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore completeNativeWithPayload: completePubkeys, - feeTracker: propellerEngineFeeTracker, - aggregator, + feeTracking: feeTrackingAccts, + // aggregator, + // feeTracker: propellerEngineFeeTracker, // marginalPricePool: { // pool: marginalPricePoolInfo.pool, // poolToken0Account: marginalPricePoolInfo.token0Account, // poolToken1Account: marginalPricePoolInfo.token1Account, // lpMint: marginalPricePoolInfo.lpMint, + // twoPoolProgram: twoPoolProgram.programId, // }, - marginalPricePool: marginalPricePoolInfo.pool, - marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, - marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, - marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, - twoPoolProgram: twoPoolProgram.programId, + // marginalPricePool: marginalPricePoolInfo.pool, + // marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, + // marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, + // marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, + // twoPoolProgram: twoPoolProgram.programId, memo: MEMO_PROGRAM_ID, }) - .preInstructions([requestUnitsIx]) + .preInstructions([setComputeUnitLimitIx]) .signers([payer]); const completeNativeWithPayloadPubkeys = @@ -711,17 +626,19 @@ export const generatePropellerEngineTxns = async ( const completeNativeWithPayloadTxn = await completeNativeWithPayloadIxs.transaction(); txns = [completeNativeWithPayloadTxn]; - const targetTokenId = swimPayload.targetTokenId; - if (!targetTokenId) { + const toTokenNumber = swimPayload.targetTokenId; + if (!toTokenNumber) { throw new Error("No target token id"); } - const [tokenIdMapAddr] = await getTargetTokenIdMapAddr( + const [tokenNumberMapAddr] = await getToTokenNumberMapAddr( propeller, - targetTokenId, + toTokenNumber, propellerProgram.programId, ); - const tokenIdMapData = - await propellerProgram.account.tokenIdMap.fetchNullable(tokenIdMapAddr); + const tokenNumberMapData = + await propellerProgram.account.tokenNumberMap.fetchNullable( + tokenNumberMapAddr, + ); const owner = new web3.PublicKey(swimPayload.owner); // const userTransferAuthority = web3.Keypair.generate(); const [swimClaim] = await getSwimClaimPda( @@ -729,9 +646,9 @@ export const generatePropellerEngineTxns = async ( propellerProgram.programId, ); - if (!tokenIdMapData) { + if (!tokenNumberMapData) { console.info( - `invalid tokenIdMap. targetTokenId: ${targetTokenId.toString()}. Generating fallback transactions`, + `invalid tokenNumberMap. toTokenNumber: ${toTokenNumber.toString()}. Generating fallback transactions`, ); const userSwimUsdAta: web3.PublicKey = await getAssociatedTokenAddress( @@ -749,11 +666,10 @@ export const generatePropellerEngineTxns = async ( payer: payer.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, + claim: wormholeClaim, swimPayloadMessage, - tokenIdMap: tokenIdMapAddr, + tokenIdMap: tokenNumberMapAddr, swimUsdMint: swimUsdMint, owner, ownerSwimUsdAta: userSwimUsdAta, @@ -761,12 +677,15 @@ export const generatePropellerEngineTxns = async ( systemProgram: web3.SystemProgram.programId, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - aggregator, - marginalPricePool: marginalPricePoolInfo.pool, - marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, - marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, - marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // aggregator, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: marginalPricePoolInfo.pool, + // marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, + // marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, + // marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, + // twoPoolProgram: twoPoolProgram.programId, rent: web3.SYSVAR_RENT_PUBKEY, }) .transaction(); @@ -785,45 +704,39 @@ export const generatePropellerEngineTxns = async ( swimPayloadMessagePayer: payer.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - tokenIdMap: tokenIdMapAddr, - userTransferAuthority: userTransferAuthority.publicKey, + tokenNumberMap: tokenNumberMapAddr, userSwimUsdAta: userSwimUsdAta, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - twoPoolProgram: twoPoolProgram.programId, systemProgram: web3.SystemProgram.programId, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, - aggregator, - marginalPricePool: marginalPricePoolInfo.pool, - marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, - marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, - marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, + feeTracking: feeTrackingAccts, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // aggregator, + // marginalPricePool: marginalPricePoolInfo.pool, + // marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, + // marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, + // marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, + // twoPoolProgram: twoPoolProgram.programId, owner, }) - .preInstructions([requestUnitsIx]) - .signers([userTransferAuthority, payer]) + .preInstructions([setComputeUnitLimitIx]) + .signers([payer]) .transaction(); txns = [...txns, propellerProcessSwimPayloadFallbackTxn]; } else { - const tokenIdMapPoolAddr = tokenIdMapData.pool; - const tokenIdMapPoolData = await twoPoolProgram.account.twoPool.fetch( + const tokenIdMapPoolAddr = tokenNumberMapData.pool; + const tokenNumberMapPoolData = await twoPoolProgram.account.twoPool.fetch( tokenIdMapPoolAddr, ); - const tokenIdMapPoolInfo = { + const tokenNumberMapPoolInfo = { pool: tokenIdMapPoolAddr, - tokenMints: tokenIdMapPoolData.tokenMintKeys, - tokenAccounts: tokenIdMapPoolData.tokenKeys, - lpMint: tokenIdMapPoolData.lpMintKey, - governanceFeeAcct: tokenIdMapPoolData.governanceFeeKey, + tokenMints: tokenNumberMapPoolData.tokenMintKeys, + tokenAccounts: tokenNumberMapPoolData.tokenKeys, + lpMint: tokenNumberMapPoolData.lpMintKey, + governanceFeeAcct: tokenNumberMapPoolData.governanceFeeKey, }; - // const mints = [...tokenIdMapPoolInfo.tokenMints, tokenIdMapPoolInfo.lpMint]; - // const ownerAtaAddrs = await Promise.all( - // mints.map(async (mint) => { - // return await getAssociatedTokenAddress(mint, owner); - // }), - // ); - const ownerAtaAddrs = await getOwnerTokenAccountsForPool( + const ownerAtaAddrs = await getOwnerAtaAddrsForPool( tokenIdMapPoolAddr, owner, twoPoolProgram, @@ -850,15 +763,14 @@ export const generatePropellerEngineTxns = async ( payer: payer.publicKey, redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, + claim: wormholeClaim, swimPayloadMessage, - tokenIdMap: tokenIdMapAddr, - pool: tokenIdMapPoolInfo.pool, - poolToken0Mint: tokenIdMapPoolInfo.tokenMints[0], - poolToken1Mint: tokenIdMapPoolInfo.tokenMints[1], - poolLpMint: tokenIdMapPoolInfo.lpMint, + tokenNumberMap: tokenNumberMapAddr, + pool: tokenNumberMapPoolInfo.pool, + poolToken0Mint: tokenNumberMapPoolInfo.tokenMints[0], + poolToken1Mint: tokenNumberMapPoolInfo.tokenMints[1], + poolLpMint: tokenNumberMapPoolInfo.lpMint, user: owner, userPoolToken0Account: ownerAtaAddrs[0], userPoolToken1Account: ownerAtaAddrs[1], @@ -867,19 +779,29 @@ export const generatePropellerEngineTxns = async ( systemProgram: web3.SystemProgram.programId, tokenProgram: splToken.programId, memo: MEMO_PROGRAM_ID, - aggregator, - marginalPricePool: marginalPricePoolInfo.pool, - marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, - marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, - marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, - twoPoolProgram: twoPoolProgram.programId, + feeTracking: feeTrackingAccts, + // aggregator, + // feeVault: propellerFeeVault, + // feeTracker: propellerEngineFeeTracker, + // marginalPricePool: { + // pool: marginalPricePoolInfo.pool, + // poolToken0Account: marginalPricePoolInfo.token0Account, + // poolToken1Account: marginalPricePoolInfo.token1Account, + // lpMint: marginalPricePoolInfo.lpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePoolInfo.pool, + // marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, + // marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, + // marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, + // twoPoolProgram: twoPoolProgram.programId, }) - .preInstructions([requestUnitsIx]) + .preInstructions([setComputeUnitLimitIx]) .transaction(); txns = [...txns, createOwnerAtasTxn]; } const processSwimPayloadPubkeys = await propellerProgram.methods - .processSwimPayload(targetTokenId, new BN(0)) + .processSwimPayload(toTokenNumber, new BN(0)) .accounts({ propeller, payer: payer.publicKey, @@ -890,12 +812,11 @@ export const generatePropellerEngineTxns = async ( redeemer: propellerRedeemer, redeemerEscrow: propellerRedeemerEscrowAccount, // tokenIdMap: ? - pool: tokenIdMapPoolInfo.pool, - poolTokenAccount0: tokenIdMapPoolInfo.tokenAccounts[0], - poolTokenAccount1: tokenIdMapPoolInfo.tokenAccounts[1], - lpMint: tokenIdMapPoolInfo.lpMint, - governanceFee: tokenIdMapPoolInfo.governanceFeeAcct, - userTransferAuthority: userTransferAuthority.publicKey, + pool: tokenNumberMapPoolInfo.pool, + poolTokenAccount0: tokenNumberMapPoolInfo.tokenAccounts[0], + poolTokenAccount1: tokenNumberMapPoolInfo.tokenAccounts[1], + lpMint: tokenNumberMapPoolInfo.lpMint, + governanceFee: tokenNumberMapPoolInfo.governanceFeeAcct, userTokenAccount0: ownerAtaAddrs[0], userTokenAccount1: ownerAtaAddrs[1], userLpTokenAccount: ownerAtaAddrs[2], @@ -904,51 +825,19 @@ export const generatePropellerEngineTxns = async ( systemProgram: web3.SystemProgram.programId, }) .pubkeys(); + const propellerProcessSwimPayloadTxn = await propellerProgram.methods - .propellerProcessSwimPayload(targetTokenId) + .propellerProcessSwimPayload(toTokenNumber) .accounts({ - // processSwimPayload: { - // propeller, - // payer: payer.publicKey, - // message: wormholeMessage, - // claim: wormholeClaim, - // swimPayloadMessage, - // swimClaim, - // redeemer: propellerRedeemer, - // redeemerEscrow: propellerRedeemerEscrowAccount, - // // tokenIdMap: ? - // pool: tokenIdMapPoolInfo.pool, - // poolTokenAccount0: tokenIdMapPoolInfo.tokenAccounts[0], - // poolTokenAccount1: tokenIdMapPoolInfo.tokenAccounts[1], - // lpMint: tokenIdMapPoolInfo.lpMint, - // governanceFee: tokenIdMapPoolInfo.governanceFeeAcct, - // userTransferAuthority: userTransferAuthority.publicKey, - // // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - // userTokenAccount0: ownerAtaAddrs[0], - // // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - // userTokenAccount1: ownerAtaAddrs[1], - // // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - // userLpTokenAccount: ownerAtaAddrs[2], - // tokenProgram: splToken.programId, - // memo: MEMO_PROGRAM_ID, - // twoPoolProgram: twoPoolProgram.programId, - // systemProgram: web3.SystemProgram.programId, - // }, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore processSwimPayload: processSwimPayloadPubkeys, - feeVault: propellerFeeVault, - feeTracker: propellerEngineFeeTracker, - aggregator, - marginalPricePool: marginalPricePoolInfo.pool, - marginalPricePoolToken0Account: marginalPricePoolInfo.token0Account, - marginalPricePoolToken1Account: marginalPricePoolInfo.token1Account, - marginalPricePoolLpMint: marginalPricePoolInfo.lpMint, + feeTracking: feeTrackingAccts, owner, memo: MEMO_PROGRAM_ID, }) - .preInstructions([requestUnitsIx]) - .signers([userTransferAuthority, payer]) + .preInstructions([setComputeUnitLimitIx]) + .signers([payer]) .transaction(); txns = [...txns, propellerProcessSwimPayloadTxn]; } @@ -956,6 +845,417 @@ export const generatePropellerEngineTxns = async ( return txns; }; +export const getCompleteNativeWithPayloadAccounts = async ( + tokenTransferWithPayloadSignedVaa: Buffer, + wormhole: web3.PublicKey, + tokenBridge: web3.PublicKey, + propellerProgram: Program, +) => { + const { tokenTransferVaa } = parseTokenTransferWithSwimPayloadSignedVaa( + tokenTransferWithPayloadSignedVaa, + ); + const [wormholeMessage] = await deriveMessagePda( + tokenTransferWithPayloadSignedVaa, + wormhole, + ); + const wormholeClaim = await getClaimAddressSolana( + tokenBridge.toBase58(), + tokenTransferWithPayloadSignedVaa, + ); + const [swimPayloadMessage] = await getSwimPayloadMessagePda( + wormholeClaim, + propellerProgram.programId, + ); + + const [endpoint] = await deriveEndpointPda( + tokenTransferVaa.core.emitterChain, + tokenTransferVaa.core.emitterAddress, + tokenBridge, + ); + const swimUsdMint = new web3.PublicKey( + tryUint8ArrayToNative( + tokenTransferVaa.tokenTransfer.tokenAddress, + tokenTransferVaa.tokenTransfer.tokenChain, + ), + ); + const [custody] = await web3.PublicKey.findProgramAddress( + [swimUsdMint.toBytes()], + tokenBridge, + ); + + const [tokenBridgeConfig] = await web3.PublicKey.findProgramAddress( + [Buffer.from("config")], + tokenBridge, + ); + const [custodySigner] = await web3.PublicKey.findProgramAddress( + [Buffer.from("custody_signer")], + tokenBridge, + ); + + const propeller = await getPropellerPda( + swimUsdMint, + propellerProgram.programId, + ); + + const propellerData = await propellerProgram.account.propeller.fetch( + propeller, + ); + const propellerFeeVault = propellerData.feeVault; + + const propellerRedeemer = await getPropellerRedeemerPda( + propellerProgram.programId, + ); + + const propellerRedeemerEscrowAccount = await getAssociatedTokenAddress( + swimUsdMint, + propellerRedeemer, + true, + ); + + return propellerProgram.methods + .completeNativeWithPayload() + .accounts({ + propeller, + payer: propellerProgram.provider.publicKey, + tokenBridgeConfig, + message: wormholeMessage, + claim: wormholeClaim, + swimPayloadMessage: swimPayloadMessage, + endpoint: endpoint, + redeemerEscrow: propellerRedeemerEscrowAccount, + redeemer: propellerRedeemer, + feeVault: propellerFeeVault, + custody: custody, + swimUsdMint: swimUsdMint, + custodySigner, + rent: web3.SYSVAR_RENT_PUBKEY, + systemProgram: web3.SystemProgram.programId, + wormhole: wormhole, + tokenProgram: TOKEN_PROGRAM_ID, + tokenBridge: tokenBridge, + }) + .pubkeys(); +}; + +export const getCompleteNativeWithPayloadTxn = async ( + tokenTransferWithPayloadSignedVaa: Buffer, + wormhole: web3.PublicKey, + tokenBridge: web3.PublicKey, + propellerProgram: Program, + memo?: Buffer, +): Promise => { + const completeNativeWithPayloadAccounts = + await getCompleteNativeWithPayloadAccounts( + tokenTransferWithPayloadSignedVaa, + wormhole, + tokenBridge, + propellerProgram, + ); + console.info(` + completeNativeWithPayloadAccounts: ${JSON.stringify( + completeNativeWithPayloadAccounts, + null, + 2, + )} + `); + const postIxs = + typeof memo !== "undefined" + ? [createMemoInstruction(memo.toString("hex"))] + : []; + return await propellerProgram.methods + .completeNativeWithPayload() + .accounts({ + ...completeNativeWithPayloadAccounts, + }) + .preInstructions([setComputeUnitLimitIx]) + .postInstructions(postIxs) + .transaction(); +}; + +export const getProcessSwimPayloadTxn = async ( + toTokenNumber: number, + minOutputAmount: BN, + tokenTransferWithPayloadSignedVaa: Buffer, + wormhole: web3.PublicKey, + tokenBridge: web3.PublicKey, + propellerProgram: Program, + twoPoolProgram: Program, + splToken: Program, + memo?: Buffer, +) => { + const { tokenTransferVaa } = parseTokenTransferWithSwimPayloadSignedVaa( + tokenTransferWithPayloadSignedVaa, + ); + const wormholeClaim = await getClaimAddressSolana( + tokenBridge.toBase58(), + tokenTransferWithPayloadSignedVaa, + ); + const [swimPayloadMessage] = await getSwimPayloadMessagePda( + wormholeClaim, + propellerProgram.programId, + ); + + const swimUsdMint = new web3.PublicKey( + tryUint8ArrayToNative( + tokenTransferVaa.tokenTransfer.tokenAddress, + tokenTransferVaa.tokenTransfer.tokenChain, + ), + ); + const propeller = await getPropellerPda( + swimUsdMint, + propellerProgram.programId, + ); + const swimPayloadMessageData = + await propellerProgram.account.swimPayloadMessage.fetch(swimPayloadMessage); + + const propellerRedeemer = await getPropellerRedeemerPda( + propellerProgram.programId, + ); + + const propellerRedeemerEscrowAccount = await getAssociatedTokenAddress( + swimUsdMint, + propellerRedeemer, + true, + ); + const [tokenNumberMapAddr] = await getToTokenNumberMapAddr( + propeller, + toTokenNumber, + propellerProgram.programId, + ); + const tokenNumberMapData = + await propellerProgram.account.tokenNumberMap.fetchNullable( + tokenNumberMapAddr, + ); + //TODO: variant for fallback should be handled in propellerProcessSwimPayload? + if (!tokenNumberMapData) { + throw new Error("Token number map does not exist"); + } + const tokenNumberMapPool = tokenNumberMapData.pool; + const poolData = await twoPoolProgram.account.twoPool.fetch( + tokenNumberMapPool, + ); + const poolMints = [...poolData.tokenMintKeys, poolData.lpMintKey]; + const userAtasForPool = await Promise.all( + poolMints.map(async (mint) => { + const userAta = await getAssociatedTokenAddress( + mint, + swimPayloadMessageData.owner, + ); + const userAtaData = await splToken.account.token.fetchNullable(userAta); + return { + mint, + userAta, + userAtaData, + }; + }), + ); + + const preIxs = userAtasForPool + .filter(({ userAtaData }) => userAtaData === null) + .map(({ mint, userAta }) => { + return createAssociatedTokenAccountInstruction( + propellerProgram.provider.publicKey, + userAta, + swimPayloadMessageData.owner, + mint, + ); + }); + return propellerProgram.methods + .processSwimPayload(toTokenNumber, minOutputAmount) + .accounts({ + propeller, + payer: propellerProgram.provider.publicKey, + claim: wormholeClaim, + swimPayloadMessage, + swimPayloadMessagePayer: swimPayloadMessageData.swimPayloadMessagePayer, + redeemer: propellerRedeemer, + redeemerEscrow: propellerRedeemerEscrowAccount, + // tokenIdMap: ? + pool: tokenNumberMapPool, + poolTokenAccount0: poolData.tokenKeys[0], + poolTokenAccount1: poolData.tokenKeys[1], + lpMint: poolData.lpMintKey, + governanceFee: poolData.governanceFeeKey, + userTokenAccount0: userAtasForPool[0].userAta, + userTokenAccount1: userAtasForPool[1].userAta, + userLpTokenAccount: userAtasForPool[2].userAta, + tokenProgram: splToken.programId, + twoPoolProgram: twoPoolProgram.programId, + systemProgram: web3.SystemProgram.programId, + }) + .preInstructions(preIxs) + .postInstructions(memo ? [createMemoInstruction(memo.toString("hex"))] : []) + .transaction(); +}; + +export const getPropellerCompleteNativeWithPayloadTxn = async ( + tokenTransferWithPayloadSignedVaa: Buffer, + wormholeAddresses: WormholeAddresses, + memoStr: string | null, + propellerProgram: Program, + twoPoolProgram: Program, +): Promise => { + // const { + // custody, + // custodySigner, + // ethEndpointAccount, + // tokenBridge, + // tokenBridgeConfig, + // wormhole, + // } = wormholeAddresses; + // const [wormholeMessage] = await deriveMessagePda( + // tokenTransferWithPayloadSignedVaa, + // wormhole, + // ); + // const wormholeClaim = await getClaimAddressSolana( + // tokenBridge.toBase58(), + // tokenTransferWithPayloadSignedVaa, + // ); + // const [swimPayloadMessage] = await getSwimPayloadMessagePda( + // wormholeClaim, + // propellerProgram.programId, + // ); + const { tokenTransferVaa } = parseTokenTransferWithSwimPayloadSignedVaa( + tokenTransferWithPayloadSignedVaa, + ); + + const swimUsdMint = new web3.PublicKey( + tryUint8ArrayToNative( + tokenTransferVaa.tokenTransfer.tokenAddress, + tokenTransferVaa.tokenTransfer.tokenChain, + ), + ); + + const propeller = await getPropellerPda( + swimUsdMint, + propellerProgram.programId, + ); + + const propellerData = await propellerProgram.account.propeller.fetch( + propeller, + ); + // const propellerFeeVault = propellerData.feeVault; + // + // const propellerRedeemer = await getPropellerRedeemerPda( + // propellerProgram.programId, + // ); + // + // const propellerRedeemerEscrowAccount = await getAssociatedTokenAddress( + // swimUsdMint, + // propellerRedeemer, + // true, + // ); + + // const completeNativeWithPayload = propellerProgram.methods + // .completeNativeWithPayload() + // .accounts({ + // propeller, + // payer: propellerProgram.provider.publicKey, + // tokenBridgeConfig, + // message: wormholeMessage, + // claim: wormholeClaim, + // swimPayloadMessage: swimPayloadMessage, + // endpoint: ethEndpointAccount, + // redeemerEscrow: propellerRedeemerEscrowAccount, + // redeemer: propellerRedeemer, + // feeVault: propellerFeeVault, + // custody: custody, + // swimUsdMint: swimUsdMint, + // custodySigner, + // rent: web3.SYSVAR_RENT_PUBKEY, + // systemProgram: web3.SystemProgram.programId, + // wormhole: wormholeAddresses.wormhole, + // tokenProgram: TOKEN_PROGRAM_ID, + // tokenBridge: wormholeAddresses.tokenBridge, + // }); + + const completeNativeWithPayload = await getCompleteNativeWithPayloadAccounts( + tokenTransferWithPayloadSignedVaa, + wormholeAddresses.wormhole, + wormholeAddresses.tokenBridge, + // wormholeAddresses, + propellerProgram, + ); + + // if (!propellerEnabled) { + // return completeNativeWithPayload + // .preInstructions([setComputeUnitLimitIx]) + // .postInstructions( + // memoStr !== null ? [createMemoInstruction(memoStr)] : [], + // ) + // .transaction(); + // } else { + // const completeNativeWithPayloadPubkeys = + // await completeNativeWithPayload.pubkeys(); + // const [feeTracker] = await getFeeTrackerPda( + // swimUsdMint, + // propellerProgram.provider.publicKey!, + // propellerProgram.programId, + // ); + // const aggregator = propellerData.aggregator; + // const marginalPricePool = propellerData.marginalPricePool; + // const marginalPricePoolData = await twoPoolProgram.account.twoPool.fetch( + // marginalPricePool, + // ); + // const marginalPricePoolToken0Account = marginalPricePoolData.tokenKeys[0]; + // const marginalPricePoolToken1Account = marginalPricePoolData.tokenKeys[1]; + // const marginalPricePoolLpMint = marginalPricePoolData.lpMintKey; + // + // return propellerProgram.methods + // .propellerCompleteNativeWithPayload() + // .accounts({ + // // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // // @ts-ignore + // completeNativeWithPayload: completeNativeWithPayloadPubkeys, + // feeTracker: feeTracker, + // aggregator, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // memo: MEMO_PROGRAM_ID, + // }) + // .preInstructions([setComputeUnitLimitIx]) + // .transaction(); + // } + const completeNativeWithPayloadPubkeys = + await completeNativeWithPayload.pubkeys(); + + + const feeTrackingAccts = await getFeeTrackingAccounts( + propeller, + propellerProgram.provider.publicKey!, + propellerProgram, + twoPoolProgram, + ); + return propellerProgram.methods + .propellerCompleteNativeWithPayload() + .accounts({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + completeNativeWithPayload: completeNativeWithPayloadPubkeys, + feeTracking: feeTrackingAccts, + // feeTracker: feeTracker, + // aggregator, + // marginalPricePool: { + // pool: marginalPricePool, + // poolToken0Account: marginalPricePoolToken0Account, + // poolToken1Account: marginalPricePoolToken1Account, + // lpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + // }, + // marginalPricePool: marginalPricePool, + // marginalPricePoolToken0Account: marginalPricePoolToken0Account, + // marginalPricePoolToken1Account: marginalPricePoolToken1Account, + // marginalPricePoolLpMint: marginalPricePoolLpMint, + // twoPoolProgram: twoPoolProgram.programId, + memo: MEMO_PROGRAM_ID, + }) + .preInstructions([setComputeUnitLimitIx]) + .transaction(); +}; + const getMarginalPricePoolInfo = async ( propeller: web3.PublicKey, propellerProgram: Program, @@ -1048,23 +1348,143 @@ export const getWormholeAddressesForMint = async ( }; }; -export const getOwnerTokenAccountsForPool = async ( - pool: web3.PublicKey, - owner: web3.PublicKey, +export interface FeeTrackingAccounts { + readonly feeVault: web3.PublicKey; + readonly feeTracker: web3.PublicKey; + readonly aggregator: web3.PublicKey; + readonly marginalPricePool: MarginalPricePoolAccounts; +} + +export interface MarginalPricePoolAccounts { + readonly pool: web3.PublicKey; + readonly poolToken0Account: web3.PublicKey; + readonly poolToken1Account: web3.PublicKey; + readonly lpMint: web3.PublicKey; + readonly twoPoolProgram: web3.PublicKey; +} + +//Note: should be able to just use the propellerProgram.provider as payer +// but just being explicit here for safety. +export const getFeeTrackingAccounts = async ( + propeller: web3.PublicKey, + payer: web3.PublicKey, + propellerProgram: Program, twoPoolProgram: Program, -): Promise => { - const tokenIdMapPoolData = await twoPoolProgram.account.twoPool.fetch(pool); - const tokenIdMapPoolInfo = { - pool, - tokenMints: tokenIdMapPoolData.tokenMintKeys, - tokenAccounts: tokenIdMapPoolData.tokenKeys, - lpMint: tokenIdMapPoolData.lpMintKey, - governanceFeeAcct: tokenIdMapPoolData.governanceFeeKey, +): Promise => { + const propellerData = await propellerProgram.account.propeller.fetch( + propeller, + ); + const feeVault = propellerData.feeVault; + const swimUsdMint = propellerData.swimUsdMint; + const [feeTracker] = await getFeeTrackerPda( + swimUsdMint, + payer, + propellerProgram.programId, + ); + const aggregator = propellerData.aggregator; + const marginalPricePool = propellerData.marginalPricePool; + const marginalPricePoolData = await twoPoolProgram.account.twoPool.fetch( + marginalPricePool, + ); + return { + feeVault, + feeTracker, + aggregator, + marginalPricePool: { + pool: marginalPricePool, + poolToken0Account: marginalPricePoolData.tokenKeys[0], + poolToken1Account: marginalPricePoolData.tokenKeys[1], + lpMint: marginalPricePoolData.lpMintKey, + twoPoolProgram: twoPoolProgram.programId, + }, }; - const mints = [...tokenIdMapPoolInfo.tokenMints, tokenIdMapPoolInfo.lpMint]; - return await Promise.all( - mints.map(async (mint) => { - return await getAssociatedTokenAddress(mint, owner); - }), +}; + + + +export const postVaaToSolana = async ( + tokenTransferWithPayloadSignedVaa: Buffer, + provider: AnchorProvider, + wormhole: web3.PublicKey, +) => { + await postVaaSolanaWithRetry( + provider.connection, + async (tx) => { + return provider.wallet.signTransaction(tx); + }, + wormhole.toBase58(), + provider.publicKey.toBase58(), + tokenTransferWithPayloadSignedVaa, + 10, ); }; + +export class DummyForeignRoutingProgram { + private _sequence: number; + private readonly _emitterChain: ChainId; + private readonly _emitterAddress: Buffer; + private readonly tokenAddress: Buffer; + private readonly to: web3.PublicKey; + private readonly from: Buffer; + + public constructor( + address: string, + emitterChain: ChainId, + startingSequence = 0, + tokenAddress: Buffer, + to: web3.PublicKey, + from: Buffer, + ) { + this._emitterChain = emitterChain; + this._emitterAddress = Buffer.from( + tryNativeToHexString(address, this._emitterChain), + "hex", + ); + + // uptick this + this._sequence = startingSequence; + this.tokenAddress = tokenAddress; + this.to = to; + this.from = from; + } + + public get emitterChain(): ChainId { + return this._emitterChain; + } + + public get emitterAddress(): Buffer { + return this._emitterAddress; + } + + public get sequence(): number { + return this._sequence; + } + + /** + * It creates a signed token transfer with a swim payload from this to + * solana. + * @param {string} amount - The amount of tokens to transfer. + * @param {Buffer} swimPayload - This is the payload that will be sent to the SWIM server. + * @returns A signed token transfer with a swim payload. + */ + public createSignedTokenTransferWithSwimPayloadVAA( + amount: string, + swimPayload: ParsedSwimPayload, + ) { + return signAndEncodeVaa( + 0, + createNonce().readUInt32LE(0), + this._emitterChain as number, + this._emitterAddress, + BigInt(++this._sequence), + encodeTokenTransferWithPayload( + amount.toString(), + this.tokenAddress, + CHAIN_ID_SOLANA, + this.to, + this.from, + encodeSwimPayload(swimPayload), + ), + ); + } +} diff --git a/packages/solana-contracts/src/__tests__/propeller/tokenBridgeUtils.ts b/packages/solana-contracts/src/__tests__/propeller/tokenBridgeUtils.ts index ab8e83508..a6f82766d 100644 --- a/packages/solana-contracts/src/__tests__/propeller/tokenBridgeUtils.ts +++ b/packages/solana-contracts/src/__tests__/propeller/tokenBridgeUtils.ts @@ -1,30 +1,22 @@ import type { ChainId } from "@certusone/wormhole-sdk"; -import { - CHAIN_ID_SOLANA, - toChainName, - tryNativeToHexString, - tryUint8ArrayToNative, -} from "@certusone/wormhole-sdk"; +import { CHAIN_ID_SOLANA, toChainName, tryNativeToHexString, tryUint8ArrayToNative } from "@certusone/wormhole-sdk"; import { BN, web3 } from "@project-serum/anchor"; // eslint-disable-next-line import/order import * as byteify from "byteify"; // import { toBigNumberHex } from "./utils"; - import type { BigNumberish } from "ethers"; import { BigNumber } from "ethers"; // import { PostVaaMethod } from "./types"; import keccak256 from "keccak256"; - +import type { ParsedPostedMessage, ParsedVaa } from "./wormholeUtils"; import { - WORMHOLE_TOKEN_BRIDGE, formatParsedVaa, formatPostedMessage, parsePostedMessage, parseVaa, - // signAndEncodeVaa, + WORMHOLE_TOKEN_BRIDGE, } from "./wormholeUtils"; -import type { ParsedPostedMessage, ParsedVaa } from "./wormholeUtils"; export function toBigNumberHex(value: BigNumberish, numBytes: number): string { return BigNumber.from(value) @@ -285,7 +277,6 @@ export const deriveMessagePda = async ( signedVaa: Buffer, programId: web3.PublicKey, ) => { - const hash = hashVaa(signedVaa); // const hexHash = await getSignedVAAHash(signedVaa); // const hash2 = Buffer.from(hexToUint8Array(hexHash)); // console.info(` @@ -302,6 +293,7 @@ export const deriveMessagePda = async ( // hash2: ${hash2} // hash2BufferHex: ${Buffer.from(hash2).toString("hex")} // `); + const hash = hashVaa(signedVaa); return await web3.PublicKey.findProgramAddress( [Buffer.from("PostedVAA"), hash], programId, @@ -402,6 +394,7 @@ export async function parseTokenTransferPostedMessage( postedMessage: Buffer, ): Promise { const parsed = await parsePostedMessage(postedMessage); + console.info(`finished parsePostedMessage`); const data = parsed.data; const tokenTransfer = parseTokenTransfer(data); return { @@ -673,3 +666,4 @@ export async function getMintMetaPdas(mintKey: web3.PublicKey) { // ); // } // } + diff --git a/packages/solana-contracts/src/__tests__/twoPool/pool.test.ts b/packages/solana-contracts/src/__tests__/twoPool/pool.test.ts index 0b6682968..4aef97ff6 100644 --- a/packages/solana-contracts/src/__tests__/twoPool/pool.test.ts +++ b/packages/solana-contracts/src/__tests__/twoPool/pool.test.ts @@ -35,6 +35,8 @@ import { parsePoolAccount } from "../../poolDecoder"; import { TWO_POOL_PID } from "../consts"; import { + getAddOrRemoveAccounts, + getSwapAccounts, setupPoolPrereqs, setupUserAssociatedTokenAccts, } from "./poolTestUtils"; @@ -184,7 +186,7 @@ describe("TwoPool", () => { }); describe("Defi Instructions", () => { - it("Can add to pool", async () => { + it.skip("Can add to pool", async () => { const previousDepthBefore = ( await twoPoolProgram.account.twoPool.fetch(flagshipPool) ).previousDepth; @@ -203,20 +205,71 @@ describe("TwoPool", () => { userTransferAuthority.publicKey, payer, ); + const addOrRemoveAccounts = await getAddOrRemoveAccounts( + flagshipPool, + provider.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const tx = await twoPoolProgram.methods // .add(addParams) .add(inputAmounts, minimumMintAmount) - .accounts({ - poolTokenAccount0: poolUsdcAtaAddr, - poolTokenAccount1: poolUsdtAtaAddr, - lpMint: swimUsdKeypair.publicKey, - governanceFee: governanceFeeAddr, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userUsdcAtaAddr, - userTokenAccount1: userUsdtAtaAddr, - userLpTokenAccount: userSwimUsdAtaAddr, - tokenProgram: splToken.programId, - }) + .accounts(addOrRemoveAccounts) + .preInstructions([...approveIxs]) + .postInstructions([...revokeIxs]) + .signers([userTransferAuthority]) + .rpc(); + + console.info("Your transaction signature", tx); + + const userLpTokenAccountBalance = ( + await splToken.account.token.fetch(userSwimUsdAtaAddr) + ).amount; + console.info( + `userLpTokenAccountBalance: ${userLpTokenAccountBalance.toString()}`, + ); + + expect(userLpTokenAccountBalance.gt(new BN(0))).toBeTruthy(); + const previousDepthAfter = ( + await twoPoolProgram.account.twoPool.fetch(flagshipPool) + ).previousDepth; + console.info(` + previousDepth + Before: ${previousDepthBefore.toString()} + After: ${previousDepthAfter.toString()} + `); + expect(previousDepthAfter.gt(previousDepthBefore)).toBeTruthy(); + }); + it("Can add_new to pool", async () => { + const previousDepthBefore = ( + await twoPoolProgram.account.twoPool.fetch(flagshipPool) + ).previousDepth; + + const inputAmounts = [new BN(100_000_000), new BN(100_000_000)]; + const minimumMintAmount = new BN(0); + // const addParams = { + // inputAmounts, + // minimumMintAmount, + // }; + const userTransferAuthority = Keypair.generate(); + const [approveIxs, revokeIxs] = await getApproveAndRevokeIxs( + splToken, + [userUsdcAtaAddr, userUsdtAtaAddr], + inputAmounts, + userTransferAuthority.publicKey, + payer, + ); + + const addOrRemoveAccounts = await getAddOrRemoveAccounts( + flagshipPool, + provider.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); + const tx = await twoPoolProgram.methods + // .add(addParams) + .add(inputAmounts, minimumMintAmount) + .accounts(addOrRemoveAccounts) .preInstructions([...approveIxs]) .postInstructions([...revokeIxs]) .signers([userTransferAuthority]) @@ -472,20 +525,16 @@ describe("TwoPool", () => { payer, ); + const addOrRemoveAccounts = await getAddOrRemoveAccounts( + flagshipPool, + provider.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const tx = await twoPoolProgram.methods // .removeUniform(removeUniformParams) .removeUniform(exactBurnAmount, minimumOutputAmounts) - .accounts({ - poolTokenAccount0: poolUsdcAtaAddr, - poolTokenAccount1: poolUsdtAtaAddr, - lpMint: swimUsdKeypair.publicKey, - governanceFee: governanceFeeAddr, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userUsdcAtaAddr, - userTokenAccount1: userUsdtAtaAddr, - userLpTokenAccount: userSwimUsdAtaAddr, - tokenProgram: splToken.programId, - }) + .accounts(addOrRemoveAccounts) .preInstructions([...approveIxs]) .postInstructions([...revokeIxs]) .signers([userTransferAuthority]) @@ -582,21 +631,16 @@ describe("TwoPool", () => { userTransferAuthority.publicKey, payer, ); - + const addOrRemoveAccounts = await getAddOrRemoveAccounts( + flagshipPool, + provider.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const tx = await twoPoolProgram.methods // .removeExactBurn(removeExactBurnParams) .removeExactBurn(exactBurnAmount, outputTokenIndex, minimumOutputAmount) - .accounts({ - poolTokenAccount0: poolUsdcAtaAddr, - poolTokenAccount1: poolUsdtAtaAddr, - lpMint: swimUsdKeypair.publicKey, - governanceFee: governanceFeeAddr, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userUsdcAtaAddr, - userTokenAccount1: userUsdtAtaAddr, - userLpTokenAccount: userSwimUsdAtaAddr, - tokenProgram: splToken.programId, - }) + .accounts(addOrRemoveAccounts) .preInstructions([...approveIxs]) .postInstructions([...revokeIxs]) .signers([userTransferAuthority]) @@ -695,20 +739,16 @@ describe("TwoPool", () => { payer, ); + const addOrRemoveAccounts = await getAddOrRemoveAccounts( + flagshipPool, + provider.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const tx = await twoPoolProgram.methods // .removeExactOutput(removeExactOutputParams) .removeExactOutput(maximumBurnAmount, exactOutputAmounts) - .accounts({ - poolTokenAccount0: poolUsdcAtaAddr, - poolTokenAccount1: poolUsdtAtaAddr, - lpMint: swimUsdKeypair.publicKey, - governanceFee: governanceFeeAddr, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: userUsdcAtaAddr, - userTokenAccount1: userUsdtAtaAddr, - userLpTokenAccount: userSwimUsdAtaAddr, - tokenProgram: splToken.programId, - }) + .accounts(addOrRemoveAccounts) .preInstructions([...approveIxs]) .postInstructions([...revokeIxs]) .signers([userTransferAuthority]) @@ -856,10 +896,8 @@ describe("TwoPool", () => { // .adjustAmpFactor(params) .adjustAmpFactor(targetTs, targetValue) .accounts({ - commonGovernance: { - pool: flagshipPool, - governance: governanceKeypair.publicKey, - }, + pool: flagshipPool, + governanceKey: governanceKeypair.publicKey, }) .signers([governanceKeypair]) .rpc(); @@ -979,10 +1017,11 @@ describe("TwoPool", () => { const changePauseKeyTxn = await twoPoolProgram.methods .changePauseKey(newPauseKeypair.publicKey) .accounts({ - commonGovernance: { + governance: { pool: flagshipPool, - governance: governanceKeypair.publicKey, + governanceKey: governanceKeypair.publicKey, }, + newPauseKey: newPauseKeypair.publicKey, }) .signers([governanceKeypair]) .rpc(); @@ -1056,10 +1095,8 @@ describe("TwoPool", () => { // .prepareFeeChange(params) .prepareFeeChange(newLpFee, newGovernanceFee) .accounts({ - commonGovernance: { - pool: flagshipPool, - governance: governanceKeypair.publicKey, - }, + pool: flagshipPool, + governanceKey: governanceKeypair.publicKey, }) .signers([governanceKeypair]) .rpc(); @@ -1077,17 +1114,19 @@ describe("TwoPool", () => { }); it("Can prepare governance transitions", async () => { - const upcomingGovernanceKey = Keypair.generate().publicKey; + const upcomingGovernanceKeypair = Keypair.generate(); + const upcomingGovernanceKey = upcomingGovernanceKeypair.publicKey; // const params = { // upcomingGovernanceKey, // }; const prepareGovernanceTransitionTxn = await twoPoolProgram.methods .prepareGovernanceTransition(upcomingGovernanceKey) .accounts({ - commonGovernance: { + governance: { pool: flagshipPool, - governance: governanceKeypair.publicKey, + governanceKey: governanceKeypair.publicKey, }, + upcomingGovernanceKey: upcomingGovernanceKey, }) .signers([governanceKeypair]) .rpc(); @@ -1117,9 +1156,9 @@ describe("TwoPool", () => { const changeGovFeeTxn = await twoPoolProgram.methods .changeGovernanceFeeAccount(newGovernanceFeeKey) .accounts({ - commonGovernance: { + governance: { pool: flagshipPool, - governance: governanceKeypair.publicKey, + governanceKey: governanceKeypair.publicKey, }, newGovernanceFee: newGovernanceFeeKey, }) @@ -1142,9 +1181,9 @@ describe("TwoPool", () => { return twoPoolProgram.methods .changeGovernanceFeeAccount(newGovernanceFeeKey) .accounts({ - commonGovernance: { + governance: { pool: flagshipPool, - governance: governanceKeypair.publicKey, + governanceKey: governanceKeypair.publicKey, }, newGovernanceFee: newGovernanceFeeKey, }) @@ -1157,7 +1196,7 @@ describe("TwoPool", () => { // await twoPoolProgram.methods // .changeGovernanceFeeAccount(newGovernanceFeeKey) // .accounts({ - // commonGovernance: { + // governance: { // pool: flagshipPool, // governance: governanceKeypair.publicKey, // }, @@ -1226,9 +1265,9 @@ describe("TwoPool", () => { // .createLpMetadata(params) .createLpMetadata(data, isMutable, updateAuthorityIsSigner) .accounts({ - commonGovernance: { + governance: { pool: flagshipPool, - governance: governanceKeypair.publicKey, + governanceKey: governanceKeypair.publicKey, }, createMetadataAccounts: { metadata: metadataPda, @@ -1292,9 +1331,9 @@ describe("TwoPool", () => { isMutable, ) .accounts({ - commonGovernance: { + governance: { pool: flagshipPool, - governance: governanceKeypair.publicKey, + governanceKey: governanceKeypair.publicKey, }, updateMetadataAccounts: { metadata: metadataPda, @@ -1354,9 +1393,9 @@ describe("TwoPool", () => { isMutable, ) .accounts({ - commonGovernance: { + governance: { pool: flagshipPool, - governance: governanceKeypair.publicKey, + governanceKey: governanceKeypair.publicKey, }, updateMetadataAccounts: { metadata: metadataPda, @@ -1449,20 +1488,16 @@ describe("TwoPool", () => { userTransferAuthority.publicKey, newUser, ); + const addOrRemoveAccounts = await getAddOrRemoveAccounts( + flagshipPool, + newUser.publicKey, + userTransferAuthority.publicKey, + twoPoolProgram, + ); const tx = await twoPoolProgram.methods // .removeExactBurn(removeExactBurnParams) .removeExactBurn(exactBurnAmount, outputTokenIndex, minimumOutputAmount) - .accounts({ - poolTokenAccount0: poolUsdcAtaAddr, - poolTokenAccount1: poolUsdtAtaAddr, - lpMint: swimUsdKeypair.publicKey, - governanceFee: governanceFeeAddr, - userTransferAuthority: userTransferAuthority.publicKey, - userTokenAccount0: newUserAtaAddrs[0], - userTokenAccount1: newUserAtaAddrs[1], - userLpTokenAccount: newUserAtaAddrs[2], - tokenProgram: splToken.programId, - }) + .accounts(addOrRemoveAccounts) .preInstructions([...createUserAtaIxs, ...approveIxs]) .postInstructions([...revokeIxs]) .signers([payer, userTransferAuthority, newUser]) diff --git a/packages/solana-contracts/src/__tests__/twoPool/poolTestUtils.ts b/packages/solana-contracts/src/__tests__/twoPool/poolTestUtils.ts index 9c935a4ee..c6a131695 100644 --- a/packages/solana-contracts/src/__tests__/twoPool/poolTestUtils.ts +++ b/packages/solana-contracts/src/__tests__/twoPool/poolTestUtils.ts @@ -1,12 +1,13 @@ -import type { BN, Program, SplToken } from "@project-serum/anchor"; +import type { BN, Program, Program, SplToken } from "@project-serum/anchor"; import { web3 } from "@project-serum/anchor"; import { + TOKEN_PROGRAM_ID, getAssociatedTokenAddress, getOrCreateAssociatedTokenAccount, } from "@solana/spl-token"; import type { Commitment, ConfirmOptions } from "@solana/web3.js"; -import type { TwoPool } from "../../artifacts/two_pool"; +import type { TwoPool, TwoPool } from "../../artifacts/two_pool"; /** * It initializes the mints for the tokens that will be used in the pool, and then *CALCULATES* the pool token accounts and @@ -375,3 +376,148 @@ export function printBeforeAndAfterPoolUserBalances( after: ${previousDepthAfter.toString()} `); } + +export const getPoolTokenKeys = async ( + pool: web3.PublicKey, + twoPoolProgram: Program, +) => { + const poolInfo = await twoPoolProgram.account.twoPool.fetch(pool); + return poolInfo.tokenKeys; +}; + +export const getPoolTokenAccountBalances = async ( + pool: web3.PublicKey, + twoPoolProgram: Program, + splToken: Program, +): Promise => { + const poolTokenKeys = await getPoolTokenKeys(pool, twoPoolProgram); + const poolAtaData = await Promise.all( + poolTokenKeys.map(async (tokenKey) => { + return splToken.account.token.fetch(tokenKey); + }), + ); + return poolAtaData.map((ata) => ata.amount); +}; + +export const getOwnerAtaAddrsForPool = async ( + pool: web3.PublicKey, + owner: web3.PublicKey, + twoPoolProgram: Program, +): Promise => { + const tokenIdMapPoolData = await twoPoolProgram.account.twoPool.fetch(pool); + const tokenIdMapPoolInfo = { + pool, + tokenMints: tokenIdMapPoolData.tokenMintKeys, + tokenAccounts: tokenIdMapPoolData.tokenKeys, + lpMint: tokenIdMapPoolData.lpMintKey, + governanceFeeAcct: tokenIdMapPoolData.governanceFeeKey, + }; + const mints = [...tokenIdMapPoolInfo.tokenMints, tokenIdMapPoolInfo.lpMint]; + return await Promise.all( + mints.map(async (mint) => { + return await getAssociatedTokenAddress(mint, owner); + }), + ); +}; + +export const getUserAtaDataForPool = async ( + user: web3.PublicKey, + pool: web3.PublicKey, + twoPoolProgram: Program, + splToken: Program, +) => { + const userAtaAddrs = await getOwnerAtaAddrsForPool( + pool, + user, + twoPoolProgram, + ); + return await Promise.all( + userAtaAddrs.map(async (ataAddr) => { + return splToken.account.token.fetch(ataAddr); + }), + ); +}; + +export const getNullableUserAtaDataForPool = async ( + user: web3.PublicKey, + pool: web3.PublicKey, + twoPoolProgram: Program, + splToken: Program, +) => { + const userAtaAddrs = await getOwnerAtaAddrsForPool( + pool, + user, + twoPoolProgram, + ); + return await Promise.all( + userAtaAddrs.map(async (ataAddr) => { + const data = await splToken.account.token.fetchNullable(ataAddr); + return { + ataAddr, + data, + }; + }), + ); +}; + +export const getUserAtaBalancesForPool = async ( + user: web3.PublicKey, + pool: web3.PublicKey, + twoPoolProgram: Program, + splToken: Program, +) => { + const userTokenAccounts = await getUserAtaDataForPool( + user, + pool, + twoPoolProgram, + splToken, + ); + return userTokenAccounts.map((ata) => ata.amount); +}; + +export const getSwapAccounts = async ( + pool: web3.PublicKey, + user: web3.PublicKey, + userTransferAuthority: web3.PublicKey, + twoPoolProgram: Program, +) => { + const poolData = await twoPoolProgram.account.twoPool.fetch(pool); + const [userTokenAccount0, userTokenAccount1] = await getOwnerAtaAddrsForPool( + pool, + user, + twoPoolProgram, + ); + return { + pool, + poolTokenAccount0: poolData.tokenKeys[0], + poolTokenAccount1: poolData.tokenKeys[1], + lpMint: poolData.lpMintKey, + governanceFee: poolData.governanceFeeKey, + userTransferAuthority, + userTokenAccount0, + userTokenAccount1, + tokenProgram: TOKEN_PROGRAM_ID, + }; +}; + +export const getAddOrRemoveAccounts = async ( + pool: web3.PublicKey, + user: web3.PublicKey, + userTransferAuthority: web3.PublicKey, + twoPoolProgram: Program, +) => { + const swapAccounts = await getSwapAccounts( + pool, + user, + userTransferAuthority, + twoPoolProgram, + ); + const userLpTokenAccount = await getAssociatedTokenAddress( + swapAccounts.lpMint, + user, + ); + return { + swap: { ...swapAccounts }, + userLpTokenAccount, + }; +}; diff --git a/packages/solana-contracts/src/artifacts/propeller.json b/packages/solana-contracts/src/artifacts/propeller.json index 1a897df40..6f91399dc 100644 --- a/packages/solana-contracts/src/artifacts/propeller.json +++ b/packages/solana-contracts/src/artifacts/propeller.json @@ -64,7 +64,12 @@ "isSigner": false }, { - "name": "admin", + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "pauseKey", "isMut": false, "isSigner": true }, @@ -171,7 +176,204 @@ ] }, { - "name": "createTokenIdMap", + "name": "setPaused", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "paused", + "type": "bool" + } + ] + }, + { + "name": "changePauseKey", + "accounts": [ + { + "name": "governance", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ] + }, + { + "name": "newPauseKey", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "newPauseKey", + "type": "publicKey" + } + ] + }, + { + "name": "prepareGovernanceTransition", + "accounts": [ + { + "name": "governance", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ] + }, + { + "name": "upcomingGovernanceKey", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "upcomingGovernanceKey", + "type": "publicKey" + } + ] + }, + { + "name": "enactGovernanceTransition", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "updateFees", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "feeUpdates", + "type": { + "defined": "FeeUpdates" + } + } + ] + }, + { + "name": "createTokenNumberMap", "accounts": [ { "name": "propeller", @@ -194,7 +396,7 @@ } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -217,12 +419,12 @@ { "kind": "arg", "type": "publicKey", - "path": "pool" + "path": "pool.token_mint_keys [0]" }, { "kind": "arg", "type": "publicKey", - "path": "pool" + "path": "pool.token_mint_keys [1]" }, { "kind": "arg", @@ -238,7 +440,7 @@ } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -262,7 +464,7 @@ { "kind": "arg", "type": "u16", - "path": "target_token_index" + "path": "to_token_number" } ] } @@ -280,7 +482,7 @@ ], "args": [ { - "name": "targetTokenIndex", + "name": "toTokenNumber", "type": "u16" }, { @@ -296,15 +498,15 @@ "type": "publicKey" }, { - "name": "poolIx", + "name": "toTokenStep", "type": { - "defined": "PoolInstruction" + "defined": "ToTokenStep" } } ] }, { - "name": "createTargetChainMap", + "name": "updateTokenNumberMap", "accounts": [ { "name": "propeller", @@ -327,7 +529,7 @@ } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -337,7 +539,12 @@ "isSigner": true }, { - "name": "targetChainMap", + "name": "pool", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -347,6 +554,11 @@ "type": "string", "value": "propeller" }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, { "kind": "account", "type": "publicKey", @@ -356,35 +568,40 @@ { "kind": "arg", "type": "u16", - "path": "target_chain" + "path": "to_token_number" } ] } }, { - "name": "systemProgram", + "name": "twoPoolProgram", "isMut": false, "isSigner": false } ], "args": [ { - "name": "targetChain", + "name": "toTokenNumber", "type": "u16" }, { - "name": "targetAddress", + "name": "poolTokenIndex", + "type": "u8" + }, + { + "name": "poolTokenMint", + "type": "publicKey" + }, + { + "name": "toTokenStep", "type": { - "array": [ - "u8", - 32 - ] + "defined": "ToTokenStep" } } ] }, { - "name": "updateTargetChainMap", + "name": "closeTokenNumberMap", "accounts": [ { "name": "propeller", @@ -407,7 +624,7 @@ } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -417,7 +634,7 @@ "isSigner": true }, { - "name": "targetChainMap", + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -427,6 +644,11 @@ "type": "string", "value": "propeller" }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, { "kind": "account", "type": "publicKey", @@ -434,34 +656,26 @@ "path": "propeller" }, { - "kind": "account", + "kind": "arg", "type": "u16", - "account": "TargetChainMap", - "path": "target_chain_map.target_chain" + "path": "to_token_number" } ] } - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false } ], "args": [ { - "name": "routingContract", - "type": { - "array": [ - "u8", - 32 - ] - } + "name": "toTokenNumber", + "type": "u16" } ] }, { - "name": "initializeFeeTracker", + "name": "createTargetChainMap", + "docs": [ + "Target Chain Map *" + ], "accounts": [ { "name": "propeller", @@ -477,14 +691,24 @@ { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "swim_usd_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } }, { - "name": "feeTracker", + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", "isMut": true, "isSigner": false, "pda": { @@ -495,9 +719,224 @@ "value": "propeller" }, { - "kind": "const", - "type": "string", - "value": "fee" + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" + } + ] + } + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "targetChain", + "type": "u16" + }, + { + "name": "targetAddress", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + }, + { + "name": "updateTargetChainMap", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" + } + ] + } + } + ], + "args": [ + { + "name": "targetChain", + "type": "u16" + }, + { + "name": "routingContract", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + }, + { + "name": "targetChainMapSetPaused", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" + } + ] + } + } + ], + "args": [ + { + "name": "targetChain", + "type": "u16" + }, + { + "name": "isPaused", + "type": "bool" + } + ] + }, + { + "name": "initializeFeeTracker", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ] + } + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "fee" }, { "kind": "account", @@ -580,7 +1019,7 @@ "kind": "account", "type": "publicKey", "account": "FeeTracker", - "path": "fee_tracker.payer" + "path": "fee_tracker.fees_recipient" } ] } @@ -609,7 +1048,10 @@ "args": [] }, { - "name": "crossChainAdd", + "name": "crossChainInitToSwimUsd", + "docs": [ + "Sending" + ], "accounts": [ { "name": "propeller", @@ -625,8 +1067,8 @@ { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } @@ -706,7 +1148,10 @@ { "name": "userLpTokenAccount", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "validation will be checked in the pool CPI call anyways" + ] }, { "name": "tokenProgram", @@ -737,7 +1182,7 @@ "returns": "u64" }, { - "name": "propellerAdd", + "name": "propellerInitToSwimUsd", "accounts": [ { "name": "propeller", @@ -753,8 +1198,8 @@ { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } @@ -834,7 +1279,10 @@ { "name": "userLpTokenAccount", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "validation will be checked in the pool CPI call anyways" + ] }, { "name": "tokenProgram", @@ -865,11 +1313,11 @@ "returns": "u64" }, { - "name": "crossChainSwapExactInput", + "name": "crossChainTransferNativeWithPayload", "accounts": [ { "name": "propeller", - "isMut": false, + "isMut": true, "isSigner": false, "pda": { "seeds": [ @@ -881,265 +1329,19 @@ { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" + "account": "Mint", + "path": "swim_usd_mint" } ] } }, { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "swimUsdMint", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "exactInputAmount", - "type": "u64" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ], - "returns": "u64" - }, - { - "name": "propellerSwapExactInput", - "accounts": [ - { - "name": "propeller", - "isMut": false, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - } - ] - } - }, - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "swimUsdMint", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "exactInputAmount", - "type": "u64" - }, - { - "name": "maxFee", - "type": "u64" - } - ], - "returns": "u64" - }, - { - "name": "crossChainTransferNativeWithPayload", - "accounts": [ - { - "name": "propeller", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "swim_usd_mint" - } - ] - } - }, - { - "name": "payer", - "isMut": true, - "isSigner": true - }, - { - "name": "tokenBridgeConfig", + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenBridgeConfig", "isMut": true, "isSigner": false, "pda": { @@ -1153,8 +1355,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1231,8 +1432,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1256,8 +1456,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1281,8 +1480,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1301,8 +1499,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1406,6 +1603,10 @@ } ], "args": [ + { + "name": "nonce", + "type": "u32" + }, { "name": "amount", "type": "u64" @@ -1463,8 +1664,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1541,8 +1741,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1566,8 +1765,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1591,8 +1789,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1611,8 +1808,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1716,6 +1912,10 @@ } ], "args": [ + { + "name": "nonce", + "type": "u32" + }, { "name": "amount", "type": "u64" @@ -1755,6 +1955,9 @@ }, { "name": "completeNativeWithPayload", + "docs": [ + "Receiving" + ], "accounts": [ { "name": "propeller", @@ -1796,8 +1999,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1823,20 +2025,24 @@ "seeds = [", "vaa.emitter_address, vaa.emitter_chain, vaa.sequence", "],", - "seeds::program = token_bridge" + "seeds::program = token_bridge", + "checked in CPI" ] }, { "name": "endpoint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "checked in CPI" + ] }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -1862,18 +2068,58 @@ } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" + "recipient of fees for executing complete transfer (e.g. relayer)", + "this is only used in `propellerCompleteNativeWithPayload`." ] }, + { + "name": "swimPayloadMessage", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "swim_payload" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claim" + } + ] + } + }, { "name": "custody", "isMut": true, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { "name": "swimUsdMint", @@ -1883,56 +2129,46 @@ { "name": "custodySigner", "isMut": false, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "custody_signer" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { - "name": "rent", + "name": "wormhole", "isMut": false, "isSigner": false }, { - "name": "systemProgram", + "name": "tokenProgram", "isMut": false, "isSigner": false }, { - "name": "wormhole", + "name": "tokenBridge", "isMut": false, "isSigner": false }, { - "name": "tokenProgram", + "name": "systemProgram", "isMut": false, "isSigner": false }, { - "name": "tokenBridge", + "name": "rent", "isMut": false, "isSigner": false - }, - { - "name": "swimPayloadMessage", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "swim_payload" - }, - { - "kind": "account", - "type": "publicKey", - "path": "claim" - } - ] - } } ], "args": [] @@ -2083,7 +2319,7 @@ "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -2107,7 +2343,7 @@ { "kind": "arg", "type": "u16", - "path": "target_token_id" + "path": "to_token_number" } ] } @@ -2169,11 +2405,6 @@ "isMut": true, "isSigner": false }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, { "name": "userTokenAccount0", "isMut": true, @@ -2207,7 +2438,7 @@ ], "args": [ { - "name": "targetTokenId", + "name": "toTokenNumber", "type": "u16" }, { @@ -2263,8 +2494,7 @@ "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -2290,20 +2520,24 @@ "seeds = [", "vaa.emitter_address, vaa.emitter_chain, vaa.sequence", "],", - "seeds::program = token_bridge" + "seeds::program = token_bridge", + "checked in CPI" ] }, { "name": "endpoint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "checked in CPI" + ] }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -2329,18 +2563,58 @@ } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" + "recipient of fees for executing complete transfer (e.g. relayer)", + "this is only used in `propellerCompleteNativeWithPayload`." ] }, + { + "name": "swimPayloadMessage", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "swim_payload" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claim" + } + ] + } + }, { "name": "custody", "isMut": true, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { "name": "swimUsdMint", @@ -2350,173 +2624,152 @@ { "name": "custodySigner", "isMut": false, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "custody_signer" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { - "name": "rent", + "name": "wormhole", "isMut": false, "isSigner": false }, { - "name": "systemProgram", + "name": "tokenProgram", "isMut": false, "isSigner": false }, { - "name": "wormhole", + "name": "tokenBridge", "isMut": false, "isSigner": false }, { - "name": "tokenProgram", + "name": "systemProgram", "isMut": false, "isSigner": false }, { - "name": "tokenBridge", + "name": "rent", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", "isMut": false, "isSigner": false }, { - "name": "swimPayloadMessage", + "name": "feeVault", "isMut": true, "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "swim_payload" - }, - { - "kind": "account", - "type": "publicKey", - "path": "claim" + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } } - ] - } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } ] }, { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": { - "defined": "Box>" - }, - "account": "CompleteNativeWithPayload", - "path": "complete_native_with_payload.swim_usd_mint" - }, - { - "kind": "account", - "type": { - "defined": "Signer<'info>" - }, - "account": "CompleteNativeWithPayload", - "path": "complete_native_with_payload.payer" - } - ] - } - }, - { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "memo", - "isMut": false, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "propellerCreateOwnerTokenAccounts", - "docs": [ - "Valid target_token_id *" - ], - "accounts": [ - { - "name": "propeller", - "isMut": false, + "name": "memo", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "propellerCreateOwnerTokenAccounts", + "docs": [ + "Valid target_token_id *" + ], + "accounts": [ + { + "name": "propeller", + "isMut": false, "isSigner": false, "pda": { "seeds": [ @@ -2565,41 +2818,6 @@ "isMut": true, "isSigner": false }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, { "name": "claim", "isMut": false, @@ -2663,7 +2881,7 @@ } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -2726,7 +2944,8 @@ "programId": { "kind": "account", "type": "publicKey", - "path": "two_pool_program" + "account": "FeeTracking", + "path": "fee_tracking" } } }, @@ -2781,66 +3000,90 @@ "isSigner": false }, { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false + ] }, { "name": "memo", @@ -3003,7 +3246,7 @@ "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -3027,7 +3270,7 @@ { "kind": "arg", "type": "u16", - "path": "target_token_id" + "path": "to_token_number" } ] } @@ -3089,11 +3332,6 @@ "isMut": true, "isSigner": false }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, { "name": "userTokenAccount0", "isMut": true, @@ -3127,116 +3365,98 @@ ] }, { - "name": "aggregator", - "isMut": false, - "isSigner": false, - "docs": [ - "Assuming that USD:USDC 1:1" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] + } ] }, { - "name": "feeVault", + "name": "owner", "isMut": true, "isSigner": false, "docs": [ - "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" - ] - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "ProcessSwimPayload", - "path": "process_swim_payload" - }, - { - "kind": "account", - "type": { - "defined": "Signer<'info>" - }, - "account": "ProcessSwimPayload", - "path": "process_swim_payload.payer" - } - ] - } - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": { - "defined": "Program<'info,two_pool::program::TwoPool>" - }, - "account": "ProcessSwimPayload", - "path": "process_swim_payload.two_pool_program" - } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "owner", - "isMut": true, - "isSigner": false, - "docs": [ - "This is for transferring lamports for kickstart" + "This is for transferring lamports for kickstart", + "TODO: force this to be system account?" ] }, { @@ -3247,7 +3467,7 @@ ], "args": [ { - "name": "targetTokenId", + "name": "toTokenNumber", "type": "u16" } ], @@ -3312,41 +3532,6 @@ "isMut": true, "isSigner": false }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, { "name": "claim", "isMut": false, @@ -3414,9 +3599,35 @@ "isMut": false, "isSigner": false, "docs": [ - "deseraizlied as a `TokenIdMap`. if it does exist, then engine should have called", + "deserialized as a `TokenIdMap`. if it does exist, then engine should have called", "propeller_create_owner_token_accounts instead" - ] + ], + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "account", + "type": "u16", + "account": "SwimPayloadMessage", + "path": "swim_payload_message.target_token_id" + } + ] + } }, { "name": "swimUsdMint", @@ -3453,66 +3664,90 @@ "isSigner": false }, { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false + ] }, { "name": "memo", @@ -3677,140 +3912,146 @@ "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "docs": [ - "deserialized as a `TokenIdMap`. if it does exist, then engine should have called", + "deserialized as a `TokenNumberMap`. if it does exist, then engine should have called", "propeller_create_owner_token_accounts instead" - ] - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userSwimUsdAta", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "memo", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false, - "docs": [ - "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" - ] - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, + ], "pda": { "seeds": [ { "kind": "const", "type": "string", - "value": "two_pool" + "value": "propeller" }, { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" + "kind": "const", + "type": "string", + "value": "token_id" }, { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" + "account": "Propeller", + "path": "propeller" }, { "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" + "type": "u16", + "account": "SwimPayloadMessage", + "path": "swim_payload_message.target_token_id" } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } + ] } }, { - "name": "marginalPricePoolToken0Account", + "name": "userSwimUsdAta", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", "isMut": false, "isSigner": false }, { - "name": "marginalPricePoolToken1Account", + "name": "memo", "isMut": false, "isSigner": false }, { - "name": "marginalPricePoolLpMint", + "name": "systemProgram", "isMut": false, "isSigner": false }, + { + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] + } + ] + }, { "name": "owner", "isMut": true, @@ -3835,7 +4076,7 @@ "type": "u8" }, { - "name": "payer", + "name": "feesRecipient", "type": "publicKey" }, { @@ -3870,17 +4111,25 @@ 32 ] } + }, + { + "name": "isPaused", + "type": "bool" } ] } }, { - "name": "TokenIdMap", + "name": "TokenNumberMap", "type": { "kind": "struct", "fields": [ { - "name": "outputTokenIndex", + "name": "bump", + "type": "u8" + }, + { + "name": "toTokenNumber", "type": "u16" }, { @@ -3896,14 +4145,10 @@ "type": "publicKey" }, { - "name": "poolIx", + "name": "toTokenStep", "type": { - "defined": "PoolInstruction" + "defined": "ToTokenStep" } - }, - { - "name": "bump", - "type": "u8" } ] } @@ -3918,19 +4163,23 @@ "type": "u8" }, { - "name": "nonce", - "type": "u32" + "name": "isPaused", + "type": "bool" }, { - "name": "admin", + "name": "governanceKey", "type": "publicKey" }, { - "name": "wormhole", + "name": "preparedGovernanceKey", "type": "publicKey" }, { - "name": "tokenBridge", + "name": "governanceTransitionTs", + "type": "i64" + }, + { + "name": "pauseKey", "type": "publicKey" }, { @@ -3975,6 +4224,10 @@ }, { "name": "marginalPricePool", + "docs": [ + "pool used to get marginal price of swimUSD -> stablecoin for gas conversion", + "e.g. usdc-usdt pool" + ], "type": "publicKey" }, { @@ -3992,6 +4245,10 @@ { "name": "aggregator", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4092,6 +4349,82 @@ } ], "types": [ + { + "name": "FeeUpdates", + "type": { + "kind": "struct", + "fields": [ + { + "name": "secpVerifyInitFee", + "type": { + "option": "u64" + } + }, + { + "name": "secpVerifyFee", + "type": { + "option": "u64" + } + }, + { + "name": "postVaaFee", + "type": { + "option": "u64" + } + }, + { + "name": "initAtaFee", + "type": { + "option": "u64" + } + }, + { + "name": "completeWithPayloadFee", + "type": { + "option": "u64" + } + }, + { + "name": "processSwimPayloadFee", + "type": { + "option": "u64" + } + } + ] + } + }, + { + "name": "GasOracleUpdates", + "type": { + "kind": "struct", + "fields": [ + { + "name": "aggregator", + "type": { + "option": "publicKey" + } + }, + { + "name": "maxStaleness", + "type": { + "option": "i64" + } + }, + { + "name": "maxConfidenceInterval", + "type": { + "option": "i64" + } + }, + { + "name": "fallbackOracle", + "type": { + "option": "publicKey" + } + } + ] + } + }, { "name": "InitializeParams", "type": { @@ -4136,6 +4469,10 @@ { "name": "marginalPricePoolTokenMint", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4443,7 +4780,7 @@ } }, { - "name": "PoolInstruction", + "name": "ToTokenStep", "type": { "kind": "enum", "variants": [ @@ -4460,15 +4797,15 @@ } }, { - "name": "SwimPayloadVersion", + "name": "ToSwimUsdStep", "type": { "kind": "enum", "variants": [ { - "name": "V0" + "name": "Add" }, { - "name": "V1" + "name": "SwapExactInput" } ] } @@ -4578,198 +4915,273 @@ }, { "code": 6011, + "name": "InvalidPoolForInitToSwimUsd", + "msg": "Invalid Pool for Init To SwimUSD" + }, + { + "code": 6012, "name": "InvalidCpiReturnProgramId", "msg": "Incorrect ProgramId for CPI return value" }, { - "code": 6012, + "code": 6013, "name": "InvalidCpiReturnValue", "msg": "Invalid CPI Return value" }, { - "code": 6013, + "code": 6014, "name": "InvalidMint", "msg": "Invalid Mint" }, { - "code": 6014, + "code": 6015, "name": "InvalidAddAndWormholeTransferMint", "msg": "Invalid Mint for AddAndWormholeTransfer" }, { - "code": 6015, + "code": 6016, "name": "InvalidSwapExactInputOutputTokenIndex", "msg": "Invalid output token index for SwapExactInput params" }, { - "code": 6016, + "code": 6017, "name": "InvalidSwapExactInputInputAmount", "msg": "Invalid input amount for SwapExactInput params" }, { - "code": 6017, + "code": 6018, "name": "InvalidSwimUsdMint", "msg": "Invalid SwimUSD Mint" }, { - "code": 6018, + "code": 6019, "name": "InvalidPayloadTypeInVaa", "msg": "Invalid Payload Type in VAA" }, { - "code": 6019, + "code": 6020, "name": "SerializeError", "msg": "Serializing error" }, { - "code": 6020, + "code": 6021, "name": "DeserializeError", "msg": "Deserializing error" }, { - "code": 6021, + "code": 6022, "name": "UserRedeemerSignatureNotDetected", "msg": "User redeemer needs to be signer" }, { - "code": 6022, + "code": 6023, "name": "InvalidSwitchboardAccount", "msg": "Not a valid Switchboard account" }, { - "code": 6023, + "code": 6024, "name": "StaleFeed", - "msg": "Switchboard feed has not been updated in 5 minutes" + "msg": "Switchboard feed value is stale " }, { - "code": 6024, + "code": 6025, "name": "ConfidenceIntervalExceeded", "msg": "Switchboard feed exceeded provided confidence interval" }, { - "code": 6025, + "code": 6026, "name": "InsufficientAmount", "msg": "Insufficient Amount being transferred" }, - { - "code": 6026, - "name": "InvalidClaimData", - "msg": "Invalid claim data" - }, { "code": 6027, - "name": "ClaimNotClaimed", - "msg": "Claim Account not claimed" + "name": "InvalidWormholeClaimAccount", + "msg": "Invalid Wormhole Claim Account" }, { "code": 6028, - "name": "InvalidPropellerAdmin", - "msg": "Invalid Propeller Admin" + "name": "InvalidClaimData", + "msg": "Invalid claim data" }, { "code": 6029, - "name": "InvalidTokenIdMapPool", - "msg": "Invalid Pool for Token Id Map" + "name": "ClaimNotClaimed", + "msg": "Claim Account not claimed" }, { "code": 6030, - "name": "InvalidOutputTokenIndex", - "msg": "Invalid Output Token Index" + "name": "InvalidPropellerGovernanceKey", + "msg": "Invalid Propeller GovernanceKey" }, { "code": 6031, - "name": "InvalidTokenIdMapPoolTokenIndex", - "msg": "Invalid Pool Token Index for Token Id Map" + "name": "InvalidPropellerPauseKey", + "msg": "Invalid Propeller Pause Key" }, { "code": 6032, - "name": "InvalidTokenIdMapPoolTokenMint", - "msg": "Invalid Pool Token Mint for Token Id Map" + "name": "InvalidTokenNumberMapPool", + "msg": "Invalid Pool for Token Number Map" }, { "code": 6033, - "name": "InvalidTokenIdMapPoolIx", - "msg": "Invalid Pool Ix for Token Id Map" + "name": "InvalidOutputTokenIndex", + "msg": "Invalid Output Token Index" }, { "code": 6034, + "name": "InvalidTokenNumberMapPoolTokenIndex", + "msg": "Invalid Pool Token Index for Token Number Map" + }, + { + "code": 6035, + "name": "InvalidTokenNumberMapPoolTokenMint", + "msg": "Invalid Pool Token Mint for Token Number Map" + }, + { + "code": 6036, + "name": "InvalidTokenNumberMapToTokenStep", + "msg": "Invalid To Token Step for Token Number Map" + }, + { + "code": 6037, "name": "InvalidSwimPayloadGasKickstart", "msg": "Invalid Gas Kickstart parameter in Swim Payload" }, { - "code": 6035, + "code": 6038, + "name": "InvalidMarginalPricePool", + "msg": "Invalid Marginal Price Pool" + }, + { + "code": 6039, "name": "InvalidMarginalPricePoolAccounts", "msg": "Invalid Marginal Price Pool Accounts" }, { - "code": 6036, + "code": 6040, "name": "NotPropellerEnabled", "msg": "Propeller Not Enabled in payload" }, { - "code": 6037, + "code": 6041, "name": "InvalidRoutingContractAddress", "msg": "Invalid Routing Contract Address" }, { - "code": 6038, + "code": 6042, "name": "IntegerOverflow", "msg": "Integer Overflow" }, { - "code": 6039, + "code": 6043, "name": "ConversionError", "msg": "Conversion Error" }, { - "code": 6040, + "code": 6044, "name": "UnableToRetrieveSwimUsdMintDecimals", "msg": "Unable to retrieve SwimUSD mint decimals from marginal price pool information" }, { - "code": 6041, + "code": 6045, "name": "InvalidMetapoolTokenMint", "msg": "Invalid Metapool Token Mint. token_mint[0] should == swim_usd_mint" }, { - "code": 6042, + "code": 6046, "name": "UnableToDeserializeTokenAccount", "msg": "Unable to deserialize account info as token account" }, { - "code": 6043, + "code": 6047, "name": "InvalidTokenAccountDataLen", "msg": "Invalid token account data length. != 0 && != TokenAccount::LEN" }, { - "code": 6044, + "code": 6048, "name": "PayerInsufficientFundsForGasKickstart", "msg": "Payer has insufficient funds for gas kickstart" }, { - "code": 6045, + "code": 6049, "name": "IncorrectOwnerForCreateTokenAccount", "msg": "Owner of token account != swimPayload.owner" }, { - "code": 6046, - "name": "TokenIdMapExists", - "msg": "TokenIdMap exists. Please use the correct instruction" - }, - { - "code": 6047, - "name": "InvalidTokenIdMapAccountAddress", - "msg": "Invalid address for TokenIdMap account" + "code": 6050, + "name": "TokenNumberMapExists", + "msg": "TokenNumberMap exists. Please use the correct instruction" }, { - "code": 6048, + "code": 6051, "name": "InvalidSwimPayloadVersion", "msg": "Invalid Swim Payload version" }, { - "code": 6049, + "code": 6052, "name": "InvalidAggregator", "msg": "Invalid Aggregator" + }, + { + "code": 6053, + "name": "InvalidFeeVault", + "msg": "Invalid Fee Vault" + }, + { + "code": 6054, + "name": "InvalidMemo", + "msg": "Invalid Memo" + }, + { + "code": 6055, + "name": "ToTokenNumberMismatch", + "msg": "ToTokenNumber does not match SwimPayload.to_tokenNumber" + }, + { + "code": 6056, + "name": "IsPaused", + "msg": "Routing Contract is paused" + }, + { + "code": 6057, + "name": "TargetChainIsPaused", + "msg": "Target Chain is paused" + }, + { + "code": 6058, + "name": "InvalidTargetChainMap", + "msg": "Invalid Target Chain Map" + }, + { + "code": 6059, + "name": "InvalidSwimPayloadMessagePayer", + "msg": "Invalid SwimPayloadMessagePayer" + }, + { + "code": 6060, + "name": "InvalidNewPauseKey", + "msg": "Invalid New Pause Key for UpdatePauseKey" + }, + { + "code": 6061, + "name": "InvalidUpcomingGovernanceKey", + "msg": "Invalid Upcoming Governance Key for Prepare Governance Transition" + }, + { + "code": 6062, + "name": "InvalidEnact", + "msg": "Invalid Enact Governance Transition" + }, + { + "code": 6063, + "name": "InsufficientDelay", + "msg": "Insufficient Delay for Enact Governance Transition" + }, + { + "code": 6064, + "name": "InvalidFeeTracker", + "msg": "Invalid Fee Tracker" } ] } \ No newline at end of file diff --git a/packages/solana-contracts/src/artifacts/propeller.ts b/packages/solana-contracts/src/artifacts/propeller.ts index 24795a3ee..ff99d41c9 100644 --- a/packages/solana-contracts/src/artifacts/propeller.ts +++ b/packages/solana-contracts/src/artifacts/propeller.ts @@ -64,7 +64,12 @@ export type Propeller = { "isSigner": false }, { - "name": "admin", + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "pauseKey", "isMut": false, "isSigner": true }, @@ -171,7 +176,204 @@ export type Propeller = { ] }, { - "name": "createTokenIdMap", + "name": "setPaused", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "paused", + "type": "bool" + } + ] + }, + { + "name": "changePauseKey", + "accounts": [ + { + "name": "governance", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ] + }, + { + "name": "newPauseKey", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "newPauseKey", + "type": "publicKey" + } + ] + }, + { + "name": "prepareGovernanceTransition", + "accounts": [ + { + "name": "governance", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ] + }, + { + "name": "upcomingGovernanceKey", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "upcomingGovernanceKey", + "type": "publicKey" + } + ] + }, + { + "name": "enactGovernanceTransition", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "updateFees", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "feeUpdates", + "type": { + "defined": "FeeUpdates" + } + } + ] + }, + { + "name": "createTokenNumberMap", "accounts": [ { "name": "propeller", @@ -194,7 +396,7 @@ export type Propeller = { } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -217,12 +419,12 @@ export type Propeller = { { "kind": "arg", "type": "publicKey", - "path": "pool" + "path": "pool.token_mint_keys [0]" }, { "kind": "arg", "type": "publicKey", - "path": "pool" + "path": "pool.token_mint_keys [1]" }, { "kind": "arg", @@ -238,7 +440,7 @@ export type Propeller = { } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -262,7 +464,7 @@ export type Propeller = { { "kind": "arg", "type": "u16", - "path": "target_token_index" + "path": "to_token_number" } ] } @@ -280,7 +482,7 @@ export type Propeller = { ], "args": [ { - "name": "targetTokenIndex", + "name": "toTokenNumber", "type": "u16" }, { @@ -296,15 +498,15 @@ export type Propeller = { "type": "publicKey" }, { - "name": "poolIx", + "name": "toTokenStep", "type": { - "defined": "PoolInstruction" + "defined": "ToTokenStep" } } ] }, { - "name": "createTargetChainMap", + "name": "updateTokenNumberMap", "accounts": [ { "name": "propeller", @@ -327,7 +529,7 @@ export type Propeller = { } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -337,7 +539,12 @@ export type Propeller = { "isSigner": true }, { - "name": "targetChainMap", + "name": "pool", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -347,6 +554,11 @@ export type Propeller = { "type": "string", "value": "propeller" }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, { "kind": "account", "type": "publicKey", @@ -356,35 +568,40 @@ export type Propeller = { { "kind": "arg", "type": "u16", - "path": "target_chain" + "path": "to_token_number" } ] } }, { - "name": "systemProgram", + "name": "twoPoolProgram", "isMut": false, "isSigner": false } ], "args": [ { - "name": "targetChain", + "name": "toTokenNumber", "type": "u16" }, { - "name": "targetAddress", + "name": "poolTokenIndex", + "type": "u8" + }, + { + "name": "poolTokenMint", + "type": "publicKey" + }, + { + "name": "toTokenStep", "type": { - "array": [ - "u8", - 32 - ] + "defined": "ToTokenStep" } } ] }, { - "name": "updateTargetChainMap", + "name": "closeTokenNumberMap", "accounts": [ { "name": "propeller", @@ -407,7 +624,7 @@ export type Propeller = { } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -417,7 +634,7 @@ export type Propeller = { "isSigner": true }, { - "name": "targetChainMap", + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -427,6 +644,11 @@ export type Propeller = { "type": "string", "value": "propeller" }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, { "kind": "account", "type": "publicKey", @@ -434,34 +656,26 @@ export type Propeller = { "path": "propeller" }, { - "kind": "account", + "kind": "arg", "type": "u16", - "account": "TargetChainMap", - "path": "target_chain_map.target_chain" + "path": "to_token_number" } ] } - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false } ], "args": [ { - "name": "routingContract", - "type": { - "array": [ - "u8", - 32 - ] - } + "name": "toTokenNumber", + "type": "u16" } ] }, { - "name": "initializeFeeTracker", + "name": "createTargetChainMap", + "docs": [ + "Target Chain Map *" + ], "accounts": [ { "name": "propeller", @@ -477,14 +691,24 @@ export type Propeller = { { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "swim_usd_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } }, { - "name": "feeTracker", + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", "isMut": true, "isSigner": false, "pda": { @@ -495,9 +719,224 @@ export type Propeller = { "value": "propeller" }, { - "kind": "const", - "type": "string", - "value": "fee" + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" + } + ] + } + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "targetChain", + "type": "u16" + }, + { + "name": "targetAddress", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + }, + { + "name": "updateTargetChainMap", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" + } + ] + } + } + ], + "args": [ + { + "name": "targetChain", + "type": "u16" + }, + { + "name": "routingContract", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + }, + { + "name": "targetChainMapSetPaused", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" + } + ] + } + } + ], + "args": [ + { + "name": "targetChain", + "type": "u16" + }, + { + "name": "isPaused", + "type": "bool" + } + ] + }, + { + "name": "initializeFeeTracker", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ] + } + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "fee" }, { "kind": "account", @@ -580,7 +1019,7 @@ export type Propeller = { "kind": "account", "type": "publicKey", "account": "FeeTracker", - "path": "fee_tracker.payer" + "path": "fee_tracker.fees_recipient" } ] } @@ -609,7 +1048,10 @@ export type Propeller = { "args": [] }, { - "name": "crossChainAdd", + "name": "crossChainInitToSwimUsd", + "docs": [ + "Sending" + ], "accounts": [ { "name": "propeller", @@ -625,8 +1067,8 @@ export type Propeller = { { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } @@ -706,7 +1148,10 @@ export type Propeller = { { "name": "userLpTokenAccount", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "validation will be checked in the pool CPI call anyways" + ] }, { "name": "tokenProgram", @@ -737,7 +1182,7 @@ export type Propeller = { "returns": "u64" }, { - "name": "propellerAdd", + "name": "propellerInitToSwimUsd", "accounts": [ { "name": "propeller", @@ -753,8 +1198,8 @@ export type Propeller = { { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } @@ -834,7 +1279,10 @@ export type Propeller = { { "name": "userLpTokenAccount", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "validation will be checked in the pool CPI call anyways" + ] }, { "name": "tokenProgram", @@ -865,11 +1313,11 @@ export type Propeller = { "returns": "u64" }, { - "name": "crossChainSwapExactInput", + "name": "crossChainTransferNativeWithPayload", "accounts": [ { "name": "propeller", - "isMut": false, + "isMut": true, "isSigner": false, "pda": { "seeds": [ @@ -881,265 +1329,19 @@ export type Propeller = { { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" + "account": "Mint", + "path": "swim_usd_mint" } ] } }, { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "swimUsdMint", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "exactInputAmount", - "type": "u64" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ], - "returns": "u64" - }, - { - "name": "propellerSwapExactInput", - "accounts": [ - { - "name": "propeller", - "isMut": false, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - } - ] - } - }, - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "swimUsdMint", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "exactInputAmount", - "type": "u64" - }, - { - "name": "maxFee", - "type": "u64" - } - ], - "returns": "u64" - }, - { - "name": "crossChainTransferNativeWithPayload", - "accounts": [ - { - "name": "propeller", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "swim_usd_mint" - } - ] - } - }, - { - "name": "payer", - "isMut": true, - "isSigner": true - }, - { - "name": "tokenBridgeConfig", + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenBridgeConfig", "isMut": true, "isSigner": false, "pda": { @@ -1153,8 +1355,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1231,8 +1432,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1256,8 +1456,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1281,8 +1480,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1301,8 +1499,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1406,6 +1603,10 @@ export type Propeller = { } ], "args": [ + { + "name": "nonce", + "type": "u32" + }, { "name": "amount", "type": "u64" @@ -1463,8 +1664,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1541,8 +1741,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1566,8 +1765,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1591,8 +1789,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1611,8 +1808,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -1716,6 +1912,10 @@ export type Propeller = { } ], "args": [ + { + "name": "nonce", + "type": "u32" + }, { "name": "amount", "type": "u64" @@ -1755,6 +1955,9 @@ export type Propeller = { }, { "name": "completeNativeWithPayload", + "docs": [ + "Receiving" + ], "accounts": [ { "name": "propeller", @@ -1796,8 +1999,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -1823,20 +2025,24 @@ export type Propeller = { "seeds = [", "vaa.emitter_address, vaa.emitter_chain, vaa.sequence", "],", - "seeds::program = token_bridge" + "seeds::program = token_bridge", + "checked in CPI" ] }, { "name": "endpoint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "checked in CPI" + ] }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -1862,18 +2068,58 @@ export type Propeller = { } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" + "recipient of fees for executing complete transfer (e.g. relayer)", + "this is only used in `propellerCompleteNativeWithPayload`." ] }, + { + "name": "swimPayloadMessage", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "swim_payload" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claim" + } + ] + } + }, { "name": "custody", "isMut": true, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { "name": "swimUsdMint", @@ -1883,56 +2129,46 @@ export type Propeller = { { "name": "custodySigner", "isMut": false, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "custody_signer" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { - "name": "rent", + "name": "wormhole", "isMut": false, "isSigner": false }, { - "name": "systemProgram", + "name": "tokenProgram", "isMut": false, "isSigner": false }, { - "name": "wormhole", + "name": "tokenBridge", "isMut": false, "isSigner": false }, { - "name": "tokenProgram", + "name": "systemProgram", "isMut": false, "isSigner": false }, { - "name": "tokenBridge", + "name": "rent", "isMut": false, "isSigner": false - }, - { - "name": "swimPayloadMessage", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "swim_payload" - }, - { - "kind": "account", - "type": "publicKey", - "path": "claim" - } - ] - } } ], "args": [] @@ -2083,7 +2319,7 @@ export type Propeller = { "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -2107,7 +2343,7 @@ export type Propeller = { { "kind": "arg", "type": "u16", - "path": "target_token_id" + "path": "to_token_number" } ] } @@ -2169,11 +2405,6 @@ export type Propeller = { "isMut": true, "isSigner": false }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, { "name": "userTokenAccount0", "isMut": true, @@ -2207,7 +2438,7 @@ export type Propeller = { ], "args": [ { - "name": "targetTokenId", + "name": "toTokenNumber", "type": "u16" }, { @@ -2263,8 +2494,7 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -2290,20 +2520,24 @@ export type Propeller = { "seeds = [", "vaa.emitter_address, vaa.emitter_chain, vaa.sequence", "],", - "seeds::program = token_bridge" + "seeds::program = token_bridge", + "checked in CPI" ] }, { "name": "endpoint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "checked in CPI" + ] }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -2329,18 +2563,58 @@ export type Propeller = { } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" + "recipient of fees for executing complete transfer (e.g. relayer)", + "this is only used in `propellerCompleteNativeWithPayload`." ] }, + { + "name": "swimPayloadMessage", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "swim_payload" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claim" + } + ] + } + }, { "name": "custody", "isMut": true, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { "name": "swimUsdMint", @@ -2350,173 +2624,152 @@ export type Propeller = { { "name": "custodySigner", "isMut": false, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "custody_signer" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { - "name": "rent", + "name": "wormhole", "isMut": false, "isSigner": false }, { - "name": "systemProgram", + "name": "tokenProgram", "isMut": false, "isSigner": false }, { - "name": "wormhole", + "name": "tokenBridge", "isMut": false, "isSigner": false }, { - "name": "tokenProgram", + "name": "systemProgram", "isMut": false, "isSigner": false }, { - "name": "tokenBridge", + "name": "rent", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", "isMut": false, "isSigner": false }, { - "name": "swimPayloadMessage", + "name": "feeVault", "isMut": true, "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "swim_payload" - }, - { - "kind": "account", - "type": "publicKey", - "path": "claim" + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } } - ] - } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } ] }, { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": { - "defined": "Box>" - }, - "account": "CompleteNativeWithPayload", - "path": "complete_native_with_payload.swim_usd_mint" - }, - { - "kind": "account", - "type": { - "defined": "Signer<'info>" - }, - "account": "CompleteNativeWithPayload", - "path": "complete_native_with_payload.payer" - } - ] - } - }, - { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "memo", - "isMut": false, - "isSigner": false - } - ], - "args": [] - }, - { - "name": "propellerCreateOwnerTokenAccounts", - "docs": [ - "Valid target_token_id *" - ], - "accounts": [ - { - "name": "propeller", - "isMut": false, + "name": "memo", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "propellerCreateOwnerTokenAccounts", + "docs": [ + "Valid target_token_id *" + ], + "accounts": [ + { + "name": "propeller", + "isMut": false, "isSigner": false, "pda": { "seeds": [ @@ -2565,41 +2818,6 @@ export type Propeller = { "isMut": true, "isSigner": false }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, { "name": "claim", "isMut": false, @@ -2663,7 +2881,7 @@ export type Propeller = { } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -2726,7 +2944,8 @@ export type Propeller = { "programId": { "kind": "account", "type": "publicKey", - "path": "two_pool_program" + "account": "FeeTracking", + "path": "fee_tracking" } } }, @@ -2781,66 +3000,90 @@ export type Propeller = { "isSigner": false }, { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false + ] }, { "name": "memo", @@ -3003,7 +3246,7 @@ export type Propeller = { "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -3027,7 +3270,7 @@ export type Propeller = { { "kind": "arg", "type": "u16", - "path": "target_token_id" + "path": "to_token_number" } ] } @@ -3089,11 +3332,6 @@ export type Propeller = { "isMut": true, "isSigner": false }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, { "name": "userTokenAccount0", "isMut": true, @@ -3127,116 +3365,98 @@ export type Propeller = { ] }, { - "name": "aggregator", - "isMut": false, - "isSigner": false, - "docs": [ - "Assuming that USD:USDC 1:1" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] + } ] }, { - "name": "feeVault", + "name": "owner", "isMut": true, "isSigner": false, "docs": [ - "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" - ] - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "ProcessSwimPayload", - "path": "process_swim_payload" - }, - { - "kind": "account", - "type": { - "defined": "Signer<'info>" - }, - "account": "ProcessSwimPayload", - "path": "process_swim_payload.payer" - } - ] - } - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": { - "defined": "Program<'info,two_pool::program::TwoPool>" - }, - "account": "ProcessSwimPayload", - "path": "process_swim_payload.two_pool_program" - } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "owner", - "isMut": true, - "isSigner": false, - "docs": [ - "This is for transferring lamports for kickstart" + "This is for transferring lamports for kickstart", + "TODO: force this to be system account?" ] }, { @@ -3247,7 +3467,7 @@ export type Propeller = { ], "args": [ { - "name": "targetTokenId", + "name": "toTokenNumber", "type": "u16" } ], @@ -3312,41 +3532,6 @@ export type Propeller = { "isMut": true, "isSigner": false }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, { "name": "claim", "isMut": false, @@ -3414,9 +3599,35 @@ export type Propeller = { "isMut": false, "isSigner": false, "docs": [ - "deseraizlied as a `TokenIdMap`. if it does exist, then engine should have called", + "deserialized as a `TokenIdMap`. if it does exist, then engine should have called", "propeller_create_owner_token_accounts instead" - ] + ], + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "account", + "type": "u16", + "account": "SwimPayloadMessage", + "path": "swim_payload_message.target_token_id" + } + ] + } }, { "name": "swimUsdMint", @@ -3453,66 +3664,90 @@ export type Propeller = { "isSigner": false }, { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false + ] }, { "name": "memo", @@ -3677,18 +3912,39 @@ export type Propeller = { "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "docs": [ - "deserialized as a `TokenIdMap`. if it does exist, then engine should have called", + "deserialized as a `TokenNumberMap`. if it does exist, then engine should have called", "propeller_create_owner_token_accounts instead" - ] - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true + ], + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "account", + "type": "u16", + "account": "SwimPayloadMessage", + "path": "swim_payload_message.target_token_id" + } + ] + } }, { "name": "userSwimUsdAta", @@ -3700,11 +3956,6 @@ export type Propeller = { "isMut": false, "isSigner": false }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, { "name": "memo", "isMut": false, @@ -3716,100 +3967,90 @@ export type Propeller = { "isSigner": false }, { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false, - "docs": [ - "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" - ] - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false + ] }, { "name": "owner", @@ -3835,7 +4076,7 @@ export type Propeller = { "type": "u8" }, { - "name": "payer", + "name": "feesRecipient", "type": "publicKey" }, { @@ -3870,17 +4111,25 @@ export type Propeller = { 32 ] } + }, + { + "name": "isPaused", + "type": "bool" } ] } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "type": { "kind": "struct", "fields": [ { - "name": "outputTokenIndex", + "name": "bump", + "type": "u8" + }, + { + "name": "toTokenNumber", "type": "u16" }, { @@ -3896,14 +4145,10 @@ export type Propeller = { "type": "publicKey" }, { - "name": "poolIx", + "name": "toTokenStep", "type": { - "defined": "PoolInstruction" + "defined": "ToTokenStep" } - }, - { - "name": "bump", - "type": "u8" } ] } @@ -3918,19 +4163,23 @@ export type Propeller = { "type": "u8" }, { - "name": "nonce", - "type": "u32" + "name": "isPaused", + "type": "bool" }, { - "name": "admin", + "name": "governanceKey", "type": "publicKey" }, { - "name": "wormhole", + "name": "preparedGovernanceKey", "type": "publicKey" }, { - "name": "tokenBridge", + "name": "governanceTransitionTs", + "type": "i64" + }, + { + "name": "pauseKey", "type": "publicKey" }, { @@ -3975,6 +4224,10 @@ export type Propeller = { }, { "name": "marginalPricePool", + "docs": [ + "pool used to get marginal price of swimUSD -> stablecoin for gas conversion", + "e.g. usdc-usdt pool" + ], "type": "publicKey" }, { @@ -3992,6 +4245,10 @@ export type Propeller = { { "name": "aggregator", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4092,6 +4349,82 @@ export type Propeller = { } ], "types": [ + { + "name": "FeeUpdates", + "type": { + "kind": "struct", + "fields": [ + { + "name": "secpVerifyInitFee", + "type": { + "option": "u64" + } + }, + { + "name": "secpVerifyFee", + "type": { + "option": "u64" + } + }, + { + "name": "postVaaFee", + "type": { + "option": "u64" + } + }, + { + "name": "initAtaFee", + "type": { + "option": "u64" + } + }, + { + "name": "completeWithPayloadFee", + "type": { + "option": "u64" + } + }, + { + "name": "processSwimPayloadFee", + "type": { + "option": "u64" + } + } + ] + } + }, + { + "name": "GasOracleUpdates", + "type": { + "kind": "struct", + "fields": [ + { + "name": "aggregator", + "type": { + "option": "publicKey" + } + }, + { + "name": "maxStaleness", + "type": { + "option": "i64" + } + }, + { + "name": "maxConfidenceInterval", + "type": { + "option": "i64" + } + }, + { + "name": "fallbackOracle", + "type": { + "option": "publicKey" + } + } + ] + } + }, { "name": "InitializeParams", "type": { @@ -4136,6 +4469,10 @@ export type Propeller = { { "name": "marginalPricePoolTokenMint", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4443,7 +4780,7 @@ export type Propeller = { } }, { - "name": "PoolInstruction", + "name": "ToTokenStep", "type": { "kind": "enum", "variants": [ @@ -4460,15 +4797,15 @@ export type Propeller = { } }, { - "name": "SwimPayloadVersion", + "name": "ToSwimUsdStep", "type": { "kind": "enum", "variants": [ { - "name": "V0" + "name": "Add" }, { - "name": "V1" + "name": "SwapExactInput" } ] } @@ -4578,198 +4915,273 @@ export type Propeller = { }, { "code": 6011, + "name": "InvalidPoolForInitToSwimUsd", + "msg": "Invalid Pool for Init To SwimUSD" + }, + { + "code": 6012, "name": "InvalidCpiReturnProgramId", "msg": "Incorrect ProgramId for CPI return value" }, { - "code": 6012, + "code": 6013, "name": "InvalidCpiReturnValue", "msg": "Invalid CPI Return value" }, { - "code": 6013, + "code": 6014, "name": "InvalidMint", "msg": "Invalid Mint" }, { - "code": 6014, + "code": 6015, "name": "InvalidAddAndWormholeTransferMint", "msg": "Invalid Mint for AddAndWormholeTransfer" }, { - "code": 6015, + "code": 6016, "name": "InvalidSwapExactInputOutputTokenIndex", "msg": "Invalid output token index for SwapExactInput params" }, { - "code": 6016, + "code": 6017, "name": "InvalidSwapExactInputInputAmount", "msg": "Invalid input amount for SwapExactInput params" }, { - "code": 6017, + "code": 6018, "name": "InvalidSwimUsdMint", "msg": "Invalid SwimUSD Mint" }, { - "code": 6018, + "code": 6019, "name": "InvalidPayloadTypeInVaa", "msg": "Invalid Payload Type in VAA" }, { - "code": 6019, + "code": 6020, "name": "SerializeError", "msg": "Serializing error" }, { - "code": 6020, + "code": 6021, "name": "DeserializeError", "msg": "Deserializing error" }, { - "code": 6021, + "code": 6022, "name": "UserRedeemerSignatureNotDetected", "msg": "User redeemer needs to be signer" }, { - "code": 6022, + "code": 6023, "name": "InvalidSwitchboardAccount", "msg": "Not a valid Switchboard account" }, { - "code": 6023, + "code": 6024, "name": "StaleFeed", - "msg": "Switchboard feed has not been updated in 5 minutes" + "msg": "Switchboard feed value is stale " }, { - "code": 6024, + "code": 6025, "name": "ConfidenceIntervalExceeded", "msg": "Switchboard feed exceeded provided confidence interval" }, { - "code": 6025, + "code": 6026, "name": "InsufficientAmount", "msg": "Insufficient Amount being transferred" }, - { - "code": 6026, - "name": "InvalidClaimData", - "msg": "Invalid claim data" - }, { "code": 6027, - "name": "ClaimNotClaimed", - "msg": "Claim Account not claimed" + "name": "InvalidWormholeClaimAccount", + "msg": "Invalid Wormhole Claim Account" }, { "code": 6028, - "name": "InvalidPropellerAdmin", - "msg": "Invalid Propeller Admin" + "name": "InvalidClaimData", + "msg": "Invalid claim data" }, { "code": 6029, - "name": "InvalidTokenIdMapPool", - "msg": "Invalid Pool for Token Id Map" + "name": "ClaimNotClaimed", + "msg": "Claim Account not claimed" }, { "code": 6030, - "name": "InvalidOutputTokenIndex", - "msg": "Invalid Output Token Index" + "name": "InvalidPropellerGovernanceKey", + "msg": "Invalid Propeller GovernanceKey" }, { "code": 6031, - "name": "InvalidTokenIdMapPoolTokenIndex", - "msg": "Invalid Pool Token Index for Token Id Map" + "name": "InvalidPropellerPauseKey", + "msg": "Invalid Propeller Pause Key" }, { "code": 6032, - "name": "InvalidTokenIdMapPoolTokenMint", - "msg": "Invalid Pool Token Mint for Token Id Map" + "name": "InvalidTokenNumberMapPool", + "msg": "Invalid Pool for Token Number Map" }, { "code": 6033, - "name": "InvalidTokenIdMapPoolIx", - "msg": "Invalid Pool Ix for Token Id Map" + "name": "InvalidOutputTokenIndex", + "msg": "Invalid Output Token Index" }, { "code": 6034, - "name": "InvalidSwimPayloadGasKickstart", - "msg": "Invalid Gas Kickstart parameter in Swim Payload" + "name": "InvalidTokenNumberMapPoolTokenIndex", + "msg": "Invalid Pool Token Index for Token Number Map" }, { "code": 6035, - "name": "InvalidMarginalPricePoolAccounts", - "msg": "Invalid Marginal Price Pool Accounts" + "name": "InvalidTokenNumberMapPoolTokenMint", + "msg": "Invalid Pool Token Mint for Token Number Map" }, { "code": 6036, - "name": "NotPropellerEnabled", - "msg": "Propeller Not Enabled in payload" + "name": "InvalidTokenNumberMapToTokenStep", + "msg": "Invalid To Token Step for Token Number Map" }, { "code": 6037, - "name": "InvalidRoutingContractAddress", - "msg": "Invalid Routing Contract Address" + "name": "InvalidSwimPayloadGasKickstart", + "msg": "Invalid Gas Kickstart parameter in Swim Payload" }, { "code": 6038, - "name": "IntegerOverflow", - "msg": "Integer Overflow" + "name": "InvalidMarginalPricePool", + "msg": "Invalid Marginal Price Pool" }, { "code": 6039, - "name": "ConversionError", - "msg": "Conversion Error" + "name": "InvalidMarginalPricePoolAccounts", + "msg": "Invalid Marginal Price Pool Accounts" }, { "code": 6040, - "name": "UnableToRetrieveSwimUsdMintDecimals", - "msg": "Unable to retrieve SwimUSD mint decimals from marginal price pool information" + "name": "NotPropellerEnabled", + "msg": "Propeller Not Enabled in payload" }, { "code": 6041, + "name": "InvalidRoutingContractAddress", + "msg": "Invalid Routing Contract Address" + }, + { + "code": 6042, + "name": "IntegerOverflow", + "msg": "Integer Overflow" + }, + { + "code": 6043, + "name": "ConversionError", + "msg": "Conversion Error" + }, + { + "code": 6044, + "name": "UnableToRetrieveSwimUsdMintDecimals", + "msg": "Unable to retrieve SwimUSD mint decimals from marginal price pool information" + }, + { + "code": 6045, "name": "InvalidMetapoolTokenMint", "msg": "Invalid Metapool Token Mint. token_mint[0] should == swim_usd_mint" }, { - "code": 6042, + "code": 6046, "name": "UnableToDeserializeTokenAccount", "msg": "Unable to deserialize account info as token account" }, { - "code": 6043, + "code": 6047, "name": "InvalidTokenAccountDataLen", "msg": "Invalid token account data length. != 0 && != TokenAccount::LEN" }, { - "code": 6044, + "code": 6048, "name": "PayerInsufficientFundsForGasKickstart", "msg": "Payer has insufficient funds for gas kickstart" }, { - "code": 6045, + "code": 6049, "name": "IncorrectOwnerForCreateTokenAccount", "msg": "Owner of token account != swimPayload.owner" }, { - "code": 6046, - "name": "TokenIdMapExists", - "msg": "TokenIdMap exists. Please use the correct instruction" - }, - { - "code": 6047, - "name": "InvalidTokenIdMapAccountAddress", - "msg": "Invalid address for TokenIdMap account" + "code": 6050, + "name": "TokenNumberMapExists", + "msg": "TokenNumberMap exists. Please use the correct instruction" }, { - "code": 6048, + "code": 6051, "name": "InvalidSwimPayloadVersion", "msg": "Invalid Swim Payload version" }, { - "code": 6049, + "code": 6052, "name": "InvalidAggregator", "msg": "Invalid Aggregator" + }, + { + "code": 6053, + "name": "InvalidFeeVault", + "msg": "Invalid Fee Vault" + }, + { + "code": 6054, + "name": "InvalidMemo", + "msg": "Invalid Memo" + }, + { + "code": 6055, + "name": "ToTokenNumberMismatch", + "msg": "ToTokenNumber does not match SwimPayload.to_tokenNumber" + }, + { + "code": 6056, + "name": "IsPaused", + "msg": "Routing Contract is paused" + }, + { + "code": 6057, + "name": "TargetChainIsPaused", + "msg": "Target Chain is paused" + }, + { + "code": 6058, + "name": "InvalidTargetChainMap", + "msg": "Invalid Target Chain Map" + }, + { + "code": 6059, + "name": "InvalidSwimPayloadMessagePayer", + "msg": "Invalid SwimPayloadMessagePayer" + }, + { + "code": 6060, + "name": "InvalidNewPauseKey", + "msg": "Invalid New Pause Key for UpdatePauseKey" + }, + { + "code": 6061, + "name": "InvalidUpcomingGovernanceKey", + "msg": "Invalid Upcoming Governance Key for Prepare Governance Transition" + }, + { + "code": 6062, + "name": "InvalidEnact", + "msg": "Invalid Enact Governance Transition" + }, + { + "code": 6063, + "name": "InsufficientDelay", + "msg": "Insufficient Delay for Enact Governance Transition" + }, + { + "code": 6064, + "name": "InvalidFeeTracker", + "msg": "Invalid Fee Tracker" } ] }; @@ -4840,7 +5252,12 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "admin", + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "pauseKey", "isMut": false, "isSigner": true }, @@ -4947,7 +5364,204 @@ export const IDL: Propeller = { ] }, { - "name": "createTokenIdMap", + "name": "setPaused", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "paused", + "type": "bool" + } + ] + }, + { + "name": "changePauseKey", + "accounts": [ + { + "name": "governance", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ] + }, + { + "name": "newPauseKey", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "newPauseKey", + "type": "publicKey" + } + ] + }, + { + "name": "prepareGovernanceTransition", + "accounts": [ + { + "name": "governance", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ] + }, + { + "name": "upcomingGovernanceKey", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "upcomingGovernanceKey", + "type": "publicKey" + } + ] + }, + { + "name": "enactGovernanceTransition", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "updateFees", + "accounts": [ + { + "name": "propeller", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "feeUpdates", + "type": { + "defined": "FeeUpdates" + } + } + ] + }, + { + "name": "createTokenNumberMap", "accounts": [ { "name": "propeller", @@ -4970,7 +5584,7 @@ export const IDL: Propeller = { } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -4993,12 +5607,12 @@ export const IDL: Propeller = { { "kind": "arg", "type": "publicKey", - "path": "pool" + "path": "pool.token_mint_keys [0]" }, { "kind": "arg", "type": "publicKey", - "path": "pool" + "path": "pool.token_mint_keys [1]" }, { "kind": "arg", @@ -5014,7 +5628,7 @@ export const IDL: Propeller = { } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -5038,7 +5652,7 @@ export const IDL: Propeller = { { "kind": "arg", "type": "u16", - "path": "target_token_index" + "path": "to_token_number" } ] } @@ -5056,7 +5670,7 @@ export const IDL: Propeller = { ], "args": [ { - "name": "targetTokenIndex", + "name": "toTokenNumber", "type": "u16" }, { @@ -5072,15 +5686,15 @@ export const IDL: Propeller = { "type": "publicKey" }, { - "name": "poolIx", + "name": "toTokenStep", "type": { - "defined": "PoolInstruction" + "defined": "ToTokenStep" } } ] }, { - "name": "createTargetChainMap", + "name": "updateTokenNumberMap", "accounts": [ { "name": "propeller", @@ -5103,7 +5717,7 @@ export const IDL: Propeller = { } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -5113,7 +5727,12 @@ export const IDL: Propeller = { "isSigner": true }, { - "name": "targetChainMap", + "name": "pool", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenNumberMap", "isMut": true, "isSigner": false, "pda": { @@ -5123,6 +5742,11 @@ export const IDL: Propeller = { "type": "string", "value": "propeller" }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, { "kind": "account", "type": "publicKey", @@ -5132,7 +5756,166 @@ export const IDL: Propeller = { { "kind": "arg", "type": "u16", - "path": "target_chain" + "path": "to_token_number" + } + ] + } + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "toTokenNumber", + "type": "u16" + }, + { + "name": "poolTokenIndex", + "type": "u8" + }, + { + "name": "poolTokenMint", + "type": "publicKey" + }, + { + "name": "toTokenStep", + "type": { + "defined": "ToTokenStep" + } + } + ] + }, + { + "name": "closeTokenNumberMap", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "tokenNumberMap", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "to_token_number" + } + ] + } + } + ], + "args": [ + { + "name": "toTokenNumber", + "type": "u16" + } + ] + }, + { + "name": "createTargetChainMap", + "docs": [ + "Target Chain Map *" + ], + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" } ] } @@ -5183,7 +5966,7 @@ export const IDL: Propeller = { } }, { - "name": "admin", + "name": "governanceKey", "isMut": false, "isSigner": true }, @@ -5210,21 +5993,19 @@ export const IDL: Propeller = { "path": "propeller" }, { - "kind": "account", + "kind": "arg", "type": "u16", - "account": "TargetChainMap", - "path": "target_chain_map.target_chain" + "path": "target_chain" } ] } - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false } ], "args": [ + { + "name": "targetChain", + "type": "u16" + }, { "name": "routingContract", "type": { @@ -5236,6 +6017,76 @@ export const IDL: Propeller = { } ] }, + { + "name": "targetChainMapSetPaused", + "accounts": [ + { + "name": "propeller", + "isMut": false, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller.swim_usd_mint" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true + }, + { + "name": "targetChainMap", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "arg", + "type": "u16", + "path": "target_chain" + } + ] + } + } + ], + "args": [ + { + "name": "targetChain", + "type": "u16" + }, + { + "name": "isPaused", + "type": "bool" + } + ] + }, { "name": "initializeFeeTracker", "accounts": [ @@ -5356,7 +6207,7 @@ export const IDL: Propeller = { "kind": "account", "type": "publicKey", "account": "FeeTracker", - "path": "fee_tracker.payer" + "path": "fee_tracker.fees_recipient" } ] } @@ -5385,7 +6236,10 @@ export const IDL: Propeller = { "args": [] }, { - "name": "crossChainAdd", + "name": "crossChainInitToSwimUsd", + "docs": [ + "Sending" + ], "accounts": [ { "name": "propeller", @@ -5401,8 +6255,8 @@ export const IDL: Propeller = { { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } @@ -5482,7 +6336,10 @@ export const IDL: Propeller = { { "name": "userLpTokenAccount", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "validation will be checked in the pool CPI call anyways" + ] }, { "name": "tokenProgram", @@ -5513,7 +6370,7 @@ export const IDL: Propeller = { "returns": "u64" }, { - "name": "propellerAdd", + "name": "propellerInitToSwimUsd", "accounts": [ { "name": "propeller", @@ -5529,8 +6386,8 @@ export const IDL: Propeller = { { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "Propeller", + "path": "propeller.swim_usd_mint" } ] } @@ -5610,7 +6467,10 @@ export const IDL: Propeller = { { "name": "userLpTokenAccount", "isMut": true, - "isSigner": false + "isSigner": false, + "docs": [ + "validation will be checked in the pool CPI call anyways" + ] }, { "name": "tokenProgram", @@ -5641,11 +6501,11 @@ export const IDL: Propeller = { "returns": "u64" }, { - "name": "crossChainSwapExactInput", + "name": "crossChainTransferNativeWithPayload", "accounts": [ { "name": "propeller", - "isMut": false, + "isMut": true, "isSigner": false, "pda": { "seeds": [ @@ -5657,260 +6517,14 @@ export const IDL: Propeller = { { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" + "account": "Mint", + "path": "swim_usd_mint" } ] } }, { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "swimUsdMint", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "exactInputAmount", - "type": "u64" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ], - "returns": "u64" - }, - { - "name": "propellerSwapExactInput", - "accounts": [ - { - "name": "propeller", - "isMut": false, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - } - ] - } - }, - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "swimUsdMint", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "exactInputAmount", - "type": "u64" - }, - { - "name": "maxFee", - "type": "u64" - } - ], - "returns": "u64" - }, - { - "name": "crossChainTransferNativeWithPayload", - "accounts": [ - { - "name": "propeller", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "swim_usd_mint" - } - ] - } - }, - { - "name": "payer", + "name": "payer", "isMut": true, "isSigner": true }, @@ -5929,8 +6543,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -6007,8 +6620,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -6032,8 +6644,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -6057,8 +6668,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -6077,8 +6687,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -6182,6 +6791,10 @@ export const IDL: Propeller = { } ], "args": [ + { + "name": "nonce", + "type": "u32" + }, { "name": "amount", "type": "u64" @@ -6239,8 +6852,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -6317,8 +6929,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -6342,8 +6953,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -6367,8 +6977,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -6387,8 +6996,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "wormhole" } } }, @@ -6492,6 +7100,10 @@ export const IDL: Propeller = { } ], "args": [ + { + "name": "nonce", + "type": "u32" + }, { "name": "amount", "type": "u64" @@ -6531,6 +7143,9 @@ export const IDL: Propeller = { }, { "name": "completeNativeWithPayload", + "docs": [ + "Receiving" + ], "accounts": [ { "name": "propeller", @@ -6572,8 +7187,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -6599,20 +7213,24 @@ export const IDL: Propeller = { "seeds = [", "vaa.emitter_address, vaa.emitter_chain, vaa.sequence", "],", - "seeds::program = token_bridge" + "seeds::program = token_bridge", + "checked in CPI" ] }, { "name": "endpoint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "checked in CPI" + ] }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -6632,24 +7250,64 @@ export const IDL: Propeller = { { "kind": "const", "type": "string", - "value": "redeemer" + "value": "redeemer" + } + ] + } + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)", + "this is only used in `propellerCompleteNativeWithPayload`." + ] + }, + { + "name": "swimPayloadMessage", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "swim_payload" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claim" } ] } }, - { - "name": "feeRecipient", - "isMut": true, - "isSigner": false, - "docs": [ - "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" - ] - }, { "name": "custody", "isMut": true, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { "name": "swimUsdMint", @@ -6659,56 +7317,46 @@ export const IDL: Propeller = { { "name": "custodySigner", "isMut": false, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "custody_signer" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { - "name": "rent", + "name": "wormhole", "isMut": false, "isSigner": false }, { - "name": "systemProgram", + "name": "tokenProgram", "isMut": false, "isSigner": false }, { - "name": "wormhole", + "name": "tokenBridge", "isMut": false, "isSigner": false }, { - "name": "tokenProgram", + "name": "systemProgram", "isMut": false, "isSigner": false }, { - "name": "tokenBridge", + "name": "rent", "isMut": false, "isSigner": false - }, - { - "name": "swimPayloadMessage", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "swim_payload" - }, - { - "kind": "account", - "type": "publicKey", - "path": "claim" - } - ] - } } ], "args": [] @@ -6859,7 +7507,7 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -6883,7 +7531,7 @@ export const IDL: Propeller = { { "kind": "arg", "type": "u16", - "path": "target_token_id" + "path": "to_token_number" } ] } @@ -6945,11 +7593,6 @@ export const IDL: Propeller = { "isMut": true, "isSigner": false }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, { "name": "userTokenAccount0", "isMut": true, @@ -6983,7 +7626,7 @@ export const IDL: Propeller = { ], "args": [ { - "name": "targetTokenId", + "name": "toTokenNumber", "type": "u16" }, { @@ -7039,8 +7682,7 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "account": "Propeller", - "path": "propeller" + "path": "token_bridge" } } }, @@ -7066,20 +7708,24 @@ export const IDL: Propeller = { "seeds = [", "vaa.emitter_address, vaa.emitter_chain, vaa.sequence", "],", - "seeds::program = token_bridge" + "seeds::program = token_bridge", + "checked in CPI" ] }, { "name": "endpoint", "isMut": false, - "isSigner": false + "isSigner": false, + "docs": [ + "checked in CPI" + ] }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -7105,18 +7751,58 @@ export const IDL: Propeller = { } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" + "recipient of fees for executing complete transfer (e.g. relayer)", + "this is only used in `propellerCompleteNativeWithPayload`." ] }, + { + "name": "swimPayloadMessage", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "swim_payload" + }, + { + "kind": "account", + "type": "publicKey", + "path": "claim" + } + ] + } + }, { "name": "custody", "isMut": true, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "swim_usd_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { "name": "swimUsdMint", @@ -7126,17 +7812,21 @@ export const IDL: Propeller = { { "name": "custodySigner", "isMut": false, - "isSigner": false - }, - { - "name": "rent", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "custody_signer" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "token_bridge" + } + } }, { "name": "wormhole", @@ -7154,127 +7844,102 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "swimPayloadMessage", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "swim_payload" - }, - { - "kind": "account", - "type": "publicKey", - "path": "claim" - } - ] - } - } - ] - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": { - "defined": "Box>" - }, - "account": "CompleteNativeWithPayload", - "path": "complete_native_with_payload.swim_usd_mint" - }, - { - "kind": "account", - "type": { - "defined": "Signer<'info>" - }, - "account": "CompleteNativeWithPayload", - "path": "complete_native_with_payload.payer" - } - ] - } - }, - { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "rent", + "isMut": false, + "isSigner": false } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false + ] }, { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] + } + ] }, { "name": "memo", @@ -7341,41 +8006,6 @@ export const IDL: Propeller = { "isMut": true, "isSigner": false }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, { "name": "claim", "isMut": false, @@ -7439,7 +8069,7 @@ export const IDL: Propeller = { } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -7502,7 +8132,8 @@ export const IDL: Propeller = { "programId": { "kind": "account", "type": "publicKey", - "path": "two_pool_program" + "account": "FeeTracking", + "path": "fee_tracking" } } }, @@ -7557,66 +8188,90 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false + ] }, { "name": "memo", @@ -7779,7 +8434,7 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "pda": { @@ -7803,7 +8458,7 @@ export const IDL: Propeller = { { "kind": "arg", "type": "u16", - "path": "target_token_id" + "path": "to_token_number" } ] } @@ -7865,11 +8520,6 @@ export const IDL: Propeller = { "isMut": true, "isSigner": false }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, { "name": "userTokenAccount0", "isMut": true, @@ -7899,120 +8549,102 @@ export const IDL: Propeller = { "name": "systemProgram", "isMut": false, "isSigner": false - } - ] - }, - { - "name": "aggregator", - "isMut": false, - "isSigner": false, - "docs": [ - "Assuming that USD:USDC 1:1" - ] - }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false, - "docs": [ - "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" - ] - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "ProcessSwimPayload", - "path": "process_swim_payload" - }, - { - "kind": "account", - "type": { - "defined": "Signer<'info>" - }, - "account": "ProcessSwimPayload", - "path": "process_swim_payload.payer" - } - ] - } - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": { - "defined": "Program<'info,two_pool::program::TwoPool>" - }, - "account": "ProcessSwimPayload", - "path": "process_swim_payload.two_pool_program" - } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false + } + ] }, { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] + } + ] }, { "name": "owner", "isMut": true, "isSigner": false, "docs": [ - "This is for transferring lamports for kickstart" + "This is for transferring lamports for kickstart", + "TODO: force this to be system account?" ] }, { @@ -8023,7 +8655,7 @@ export const IDL: Propeller = { ], "args": [ { - "name": "targetTokenId", + "name": "toTokenNumber", "type": "u16" } ], @@ -8088,41 +8720,6 @@ export const IDL: Propeller = { "isMut": true, "isSigner": false }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, { "name": "claim", "isMut": false, @@ -8190,9 +8787,35 @@ export const IDL: Propeller = { "isMut": false, "isSigner": false, "docs": [ - "deseraizlied as a `TokenIdMap`. if it does exist, then engine should have called", + "deserialized as a `TokenIdMap`. if it does exist, then engine should have called", "propeller_create_owner_token_accounts instead" - ] + ], + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "propeller" + }, + { + "kind": "const", + "type": "string", + "value": "token_id" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Propeller", + "path": "propeller" + }, + { + "kind": "account", + "type": "u16", + "account": "SwimPayloadMessage", + "path": "swim_payload_message.target_token_id" + } + ] + } }, { "name": "swimUsdMint", @@ -8229,66 +8852,90 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" - } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] } - } - }, - { - "name": "marginalPricePoolToken0Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolToken1Account", - "isMut": false, - "isSigner": false - }, - { - "name": "marginalPricePoolLpMint", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false + ] }, { "name": "memo", @@ -8453,140 +9100,146 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "isMut": false, "isSigner": false, "docs": [ - "deserialized as a `TokenIdMap`. if it does exist, then engine should have called", + "deserialized as a `TokenNumberMap`. if it does exist, then engine should have called", "propeller_create_owner_token_accounts instead" - ] - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userSwimUsdAta", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "twoPoolProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "memo", - "isMut": false, - "isSigner": false - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - }, - { - "name": "aggregator", - "isMut": false, - "isSigner": false - }, - { - "name": "feeVault", - "isMut": true, - "isSigner": false, - "docs": [ - "this is \"to_fees\"", - "recipient of fees for executing complete transfer (e.g. relayer)" - ] - }, - { - "name": "feeTracker", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "propeller" - }, - { - "kind": "const", - "type": "string", - "value": "fee" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Propeller", - "path": "propeller.swim_usd_mint" - }, - { - "kind": "account", - "type": "publicKey", - "path": "payer" - } - ] - } - }, - { - "name": "marginalPricePool", - "isMut": true, - "isSigner": false, + ], "pda": { "seeds": [ { "kind": "const", "type": "string", - "value": "two_pool" + "value": "propeller" }, { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_0_account.mint" + "kind": "const", + "type": "string", + "value": "token_id" }, { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "marginal_price_pool_token_1_account.mint" + "account": "Propeller", + "path": "propeller" }, { "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "marginal_price_pool_lp_mint" + "type": "u16", + "account": "SwimPayloadMessage", + "path": "swim_payload_message.target_token_id" } - ], - "programId": { - "kind": "account", - "type": "publicKey", - "path": "two_pool_program" - } + ] } }, { - "name": "marginalPricePoolToken0Account", + "name": "userSwimUsdAta", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", "isMut": false, "isSigner": false }, { - "name": "marginalPricePoolToken1Account", + "name": "memo", "isMut": false, "isSigner": false }, { - "name": "marginalPricePoolLpMint", + "name": "systemProgram", "isMut": false, "isSigner": false }, + { + "name": "feeTracking", + "accounts": [ + { + "name": "aggregator", + "isMut": false, + "isSigner": false + }, + { + "name": "feeVault", + "isMut": true, + "isSigner": false, + "docs": [ + "this is \"to_fees\"", + "recipient of fees for executing complete transfer (e.g. relayer)" + ] + }, + { + "name": "feeTracker", + "isMut": true, + "isSigner": false + }, + { + "name": "marginalPricePool", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_0_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_1_account.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ], + "programId": { + "kind": "account", + "type": "publicKey", + "path": "two_pool_program" + } + } + }, + { + "name": "poolToken0Account", + "isMut": false, + "isSigner": false + }, + { + "name": "poolToken1Account", + "isMut": false, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": false, + "isSigner": false + }, + { + "name": "twoPoolProgram", + "isMut": false, + "isSigner": false + } + ] + } + ] + }, { "name": "owner", "isMut": true, @@ -8611,7 +9264,7 @@ export const IDL: Propeller = { "type": "u8" }, { - "name": "payer", + "name": "feesRecipient", "type": "publicKey" }, { @@ -8646,17 +9299,25 @@ export const IDL: Propeller = { 32 ] } + }, + { + "name": "isPaused", + "type": "bool" } ] } }, { - "name": "tokenIdMap", + "name": "tokenNumberMap", "type": { "kind": "struct", "fields": [ { - "name": "outputTokenIndex", + "name": "bump", + "type": "u8" + }, + { + "name": "toTokenNumber", "type": "u16" }, { @@ -8672,14 +9333,10 @@ export const IDL: Propeller = { "type": "publicKey" }, { - "name": "poolIx", + "name": "toTokenStep", "type": { - "defined": "PoolInstruction" + "defined": "ToTokenStep" } - }, - { - "name": "bump", - "type": "u8" } ] } @@ -8694,19 +9351,23 @@ export const IDL: Propeller = { "type": "u8" }, { - "name": "nonce", - "type": "u32" + "name": "isPaused", + "type": "bool" }, { - "name": "admin", + "name": "governanceKey", "type": "publicKey" }, { - "name": "wormhole", + "name": "preparedGovernanceKey", "type": "publicKey" }, { - "name": "tokenBridge", + "name": "governanceTransitionTs", + "type": "i64" + }, + { + "name": "pauseKey", "type": "publicKey" }, { @@ -8751,6 +9412,10 @@ export const IDL: Propeller = { }, { "name": "marginalPricePool", + "docs": [ + "pool used to get marginal price of swimUSD -> stablecoin for gas conversion", + "e.g. usdc-usdt pool" + ], "type": "publicKey" }, { @@ -8768,6 +9433,10 @@ export const IDL: Propeller = { { "name": "aggregator", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -8868,6 +9537,82 @@ export const IDL: Propeller = { } ], "types": [ + { + "name": "FeeUpdates", + "type": { + "kind": "struct", + "fields": [ + { + "name": "secpVerifyInitFee", + "type": { + "option": "u64" + } + }, + { + "name": "secpVerifyFee", + "type": { + "option": "u64" + } + }, + { + "name": "postVaaFee", + "type": { + "option": "u64" + } + }, + { + "name": "initAtaFee", + "type": { + "option": "u64" + } + }, + { + "name": "completeWithPayloadFee", + "type": { + "option": "u64" + } + }, + { + "name": "processSwimPayloadFee", + "type": { + "option": "u64" + } + } + ] + } + }, + { + "name": "GasOracleUpdates", + "type": { + "kind": "struct", + "fields": [ + { + "name": "aggregator", + "type": { + "option": "publicKey" + } + }, + { + "name": "maxStaleness", + "type": { + "option": "i64" + } + }, + { + "name": "maxConfidenceInterval", + "type": { + "option": "i64" + } + }, + { + "name": "fallbackOracle", + "type": { + "option": "publicKey" + } + } + ] + } + }, { "name": "InitializeParams", "type": { @@ -8912,6 +9657,10 @@ export const IDL: Propeller = { { "name": "marginalPricePoolTokenMint", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -9219,7 +9968,7 @@ export const IDL: Propeller = { } }, { - "name": "PoolInstruction", + "name": "ToTokenStep", "type": { "kind": "enum", "variants": [ @@ -9236,15 +9985,15 @@ export const IDL: Propeller = { } }, { - "name": "SwimPayloadVersion", + "name": "ToSwimUsdStep", "type": { "kind": "enum", "variants": [ { - "name": "V0" + "name": "Add" }, { - "name": "V1" + "name": "SwapExactInput" } ] } @@ -9354,198 +10103,273 @@ export const IDL: Propeller = { }, { "code": 6011, + "name": "InvalidPoolForInitToSwimUsd", + "msg": "Invalid Pool for Init To SwimUSD" + }, + { + "code": 6012, "name": "InvalidCpiReturnProgramId", "msg": "Incorrect ProgramId for CPI return value" }, { - "code": 6012, + "code": 6013, "name": "InvalidCpiReturnValue", "msg": "Invalid CPI Return value" }, { - "code": 6013, + "code": 6014, "name": "InvalidMint", "msg": "Invalid Mint" }, { - "code": 6014, + "code": 6015, "name": "InvalidAddAndWormholeTransferMint", "msg": "Invalid Mint for AddAndWormholeTransfer" }, { - "code": 6015, + "code": 6016, "name": "InvalidSwapExactInputOutputTokenIndex", "msg": "Invalid output token index for SwapExactInput params" }, { - "code": 6016, + "code": 6017, "name": "InvalidSwapExactInputInputAmount", "msg": "Invalid input amount for SwapExactInput params" }, { - "code": 6017, + "code": 6018, "name": "InvalidSwimUsdMint", "msg": "Invalid SwimUSD Mint" }, { - "code": 6018, + "code": 6019, "name": "InvalidPayloadTypeInVaa", "msg": "Invalid Payload Type in VAA" }, { - "code": 6019, + "code": 6020, "name": "SerializeError", "msg": "Serializing error" }, { - "code": 6020, + "code": 6021, "name": "DeserializeError", "msg": "Deserializing error" }, { - "code": 6021, + "code": 6022, "name": "UserRedeemerSignatureNotDetected", "msg": "User redeemer needs to be signer" }, { - "code": 6022, + "code": 6023, "name": "InvalidSwitchboardAccount", "msg": "Not a valid Switchboard account" }, { - "code": 6023, + "code": 6024, "name": "StaleFeed", - "msg": "Switchboard feed has not been updated in 5 minutes" + "msg": "Switchboard feed value is stale " }, { - "code": 6024, + "code": 6025, "name": "ConfidenceIntervalExceeded", "msg": "Switchboard feed exceeded provided confidence interval" }, { - "code": 6025, + "code": 6026, "name": "InsufficientAmount", "msg": "Insufficient Amount being transferred" }, - { - "code": 6026, - "name": "InvalidClaimData", - "msg": "Invalid claim data" - }, { "code": 6027, - "name": "ClaimNotClaimed", - "msg": "Claim Account not claimed" + "name": "InvalidWormholeClaimAccount", + "msg": "Invalid Wormhole Claim Account" }, { "code": 6028, - "name": "InvalidPropellerAdmin", - "msg": "Invalid Propeller Admin" + "name": "InvalidClaimData", + "msg": "Invalid claim data" }, { "code": 6029, - "name": "InvalidTokenIdMapPool", - "msg": "Invalid Pool for Token Id Map" + "name": "ClaimNotClaimed", + "msg": "Claim Account not claimed" }, { "code": 6030, - "name": "InvalidOutputTokenIndex", - "msg": "Invalid Output Token Index" + "name": "InvalidPropellerGovernanceKey", + "msg": "Invalid Propeller GovernanceKey" }, { "code": 6031, - "name": "InvalidTokenIdMapPoolTokenIndex", - "msg": "Invalid Pool Token Index for Token Id Map" + "name": "InvalidPropellerPauseKey", + "msg": "Invalid Propeller Pause Key" }, { "code": 6032, - "name": "InvalidTokenIdMapPoolTokenMint", - "msg": "Invalid Pool Token Mint for Token Id Map" + "name": "InvalidTokenNumberMapPool", + "msg": "Invalid Pool for Token Number Map" }, { "code": 6033, - "name": "InvalidTokenIdMapPoolIx", - "msg": "Invalid Pool Ix for Token Id Map" + "name": "InvalidOutputTokenIndex", + "msg": "Invalid Output Token Index" }, { "code": 6034, + "name": "InvalidTokenNumberMapPoolTokenIndex", + "msg": "Invalid Pool Token Index for Token Number Map" + }, + { + "code": 6035, + "name": "InvalidTokenNumberMapPoolTokenMint", + "msg": "Invalid Pool Token Mint for Token Number Map" + }, + { + "code": 6036, + "name": "InvalidTokenNumberMapToTokenStep", + "msg": "Invalid To Token Step for Token Number Map" + }, + { + "code": 6037, "name": "InvalidSwimPayloadGasKickstart", "msg": "Invalid Gas Kickstart parameter in Swim Payload" }, { - "code": 6035, + "code": 6038, + "name": "InvalidMarginalPricePool", + "msg": "Invalid Marginal Price Pool" + }, + { + "code": 6039, "name": "InvalidMarginalPricePoolAccounts", "msg": "Invalid Marginal Price Pool Accounts" }, { - "code": 6036, + "code": 6040, "name": "NotPropellerEnabled", "msg": "Propeller Not Enabled in payload" }, { - "code": 6037, + "code": 6041, "name": "InvalidRoutingContractAddress", "msg": "Invalid Routing Contract Address" }, { - "code": 6038, + "code": 6042, "name": "IntegerOverflow", "msg": "Integer Overflow" }, { - "code": 6039, + "code": 6043, "name": "ConversionError", "msg": "Conversion Error" }, { - "code": 6040, + "code": 6044, "name": "UnableToRetrieveSwimUsdMintDecimals", "msg": "Unable to retrieve SwimUSD mint decimals from marginal price pool information" }, { - "code": 6041, + "code": 6045, "name": "InvalidMetapoolTokenMint", "msg": "Invalid Metapool Token Mint. token_mint[0] should == swim_usd_mint" }, { - "code": 6042, + "code": 6046, "name": "UnableToDeserializeTokenAccount", "msg": "Unable to deserialize account info as token account" }, { - "code": 6043, + "code": 6047, "name": "InvalidTokenAccountDataLen", "msg": "Invalid token account data length. != 0 && != TokenAccount::LEN" }, { - "code": 6044, + "code": 6048, "name": "PayerInsufficientFundsForGasKickstart", "msg": "Payer has insufficient funds for gas kickstart" }, { - "code": 6045, + "code": 6049, "name": "IncorrectOwnerForCreateTokenAccount", "msg": "Owner of token account != swimPayload.owner" }, { - "code": 6046, - "name": "TokenIdMapExists", - "msg": "TokenIdMap exists. Please use the correct instruction" - }, - { - "code": 6047, - "name": "InvalidTokenIdMapAccountAddress", - "msg": "Invalid address for TokenIdMap account" + "code": 6050, + "name": "TokenNumberMapExists", + "msg": "TokenNumberMap exists. Please use the correct instruction" }, { - "code": 6048, + "code": 6051, "name": "InvalidSwimPayloadVersion", "msg": "Invalid Swim Payload version" }, { - "code": 6049, + "code": 6052, "name": "InvalidAggregator", "msg": "Invalid Aggregator" + }, + { + "code": 6053, + "name": "InvalidFeeVault", + "msg": "Invalid Fee Vault" + }, + { + "code": 6054, + "name": "InvalidMemo", + "msg": "Invalid Memo" + }, + { + "code": 6055, + "name": "ToTokenNumberMismatch", + "msg": "ToTokenNumber does not match SwimPayload.to_tokenNumber" + }, + { + "code": 6056, + "name": "IsPaused", + "msg": "Routing Contract is paused" + }, + { + "code": 6057, + "name": "TargetChainIsPaused", + "msg": "Target Chain is paused" + }, + { + "code": 6058, + "name": "InvalidTargetChainMap", + "msg": "Invalid Target Chain Map" + }, + { + "code": 6059, + "name": "InvalidSwimPayloadMessagePayer", + "msg": "Invalid SwimPayloadMessagePayer" + }, + { + "code": 6060, + "name": "InvalidNewPauseKey", + "msg": "Invalid New Pause Key for UpdatePauseKey" + }, + { + "code": 6061, + "name": "InvalidUpcomingGovernanceKey", + "msg": "Invalid Upcoming Governance Key for Prepare Governance Transition" + }, + { + "code": 6062, + "name": "InvalidEnact", + "msg": "Invalid Enact Governance Transition" + }, + { + "code": 6063, + "name": "InsufficientDelay", + "msg": "Insufficient Delay for Enact Governance Transition" + }, + { + "code": 6064, + "name": "InvalidFeeTracker", + "msg": "Invalid Fee Tracker" } ] }; diff --git a/packages/solana-contracts/src/artifacts/two_pool.json b/packages/solana-contracts/src/artifacts/two_pool.json index bbef5d41c..64afef5a0 100644 --- a/packages/solana-contracts/src/artifacts/two_pool.json +++ b/packages/solana-contracts/src/artifacts/two_pool.json @@ -128,81 +128,86 @@ "name": "add", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -422,81 +427,86 @@ "name": "removeUniform", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -522,81 +532,86 @@ "name": "removeExactBurn", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -619,81 +634,86 @@ "name": "removeExactOutput", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -777,153 +797,101 @@ } }, { - "name": "prepareGovernanceTransition", + "name": "adjustAmpFactor", "docs": [ "Governance Ixs *" ], "accounts": [ { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true } ], "args": [ { - "name": "upcomingGovernanceKey", - "type": "publicKey" - } - ] - }, - { - "name": "enactGovernanceTransition", - "accounts": [ + "name": "targetTs", + "type": "i64" + }, { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] - } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] + "name": "targetValue", + "type": { + "defined": "DecimalU64Anchor" + } } - ], - "args": [] + ] }, { "name": "prepareFeeChange", "accounts": [ { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true } ], "args": [ @@ -945,46 +913,41 @@ "name": "enactFeeChange", "accounts": [ { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true } ], "args": [] @@ -993,7 +956,7 @@ "name": "changeGovernanceFeeAccount", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -1028,7 +991,7 @@ } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -1048,10 +1011,10 @@ ] }, { - "name": "adjustAmpFactor", + "name": "prepareGovernanceTransition", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -1086,28 +1049,27 @@ } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } ] + }, + { + "name": "upcomingGovernanceKey", + "isMut": false, + "isSigner": false } ], "args": [ { - "name": "targetTs", - "type": "i64" - }, - { - "name": "targetValue", - "type": { - "defined": "DecimalU64Anchor" - } + "name": "upcomingGovernanceKey", + "type": "publicKey" } ] }, { - "name": "setPaused", + "name": "enactGovernanceTransition", "accounts": [ { "name": "pool", @@ -1142,23 +1104,18 @@ } }, { - "name": "pauseKey", + "name": "governanceKey", "isMut": false, "isSigner": true } ], - "args": [ - { - "name": "paused", - "type": "bool" - } - ] + "args": [] }, { "name": "changePauseKey", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -1193,11 +1150,16 @@ } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } ] + }, + { + "name": "newPauseKey", + "isMut": false, + "isSigner": false } ], "args": [ @@ -1207,11 +1169,59 @@ } ] }, + { + "name": "setPaused", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "paused", + "type": "bool" + } + ] + }, { "name": "createLpMetadata", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -1246,7 +1256,7 @@ } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -1319,7 +1329,7 @@ "name": "updateLpMetadata", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -1354,7 +1364,7 @@ } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -1545,29 +1555,141 @@ } }, { - "name": "targetTs", - "type": "i64" + "name": "targetTs", + "type": "i64" + } + ] + } + }, + { + "name": "AddParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "inputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "minimumMintAmount", + "type": "u64" + } + ] + } + }, + { + "name": "RemoveExactBurnParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "exactBurnAmount", + "type": "u64" + }, + { + "name": "outputTokenIndex", + "type": "u8" + }, + { + "name": "minimumOutputAmount", + "type": "u64" + } + ] + } + }, + { + "name": "RemoveExactOutputParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "maximumBurnAmount", + "type": "u64" + }, + { + "name": "exactOutputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + }, + { + "name": "RemoveUniformParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "exactBurnAmount", + "type": "u64" + }, + { + "name": "minimumOutputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + }, + { + "name": "SwapExactInputParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "exactInputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "outputTokenIndex", + "type": "u8" + }, + { + "name": "minimumOutputAmount", + "type": "u64" } ] } }, { - "name": "AddParams", + "name": "SwapExactOutputParams", "type": { "kind": "struct", "fields": [ { - "name": "inputAmounts", + "name": "maximumInputAmount", + "type": "u64" + }, + { + "name": "inputTokenIndex", + "type": "u8" + }, + { + "name": "exactOutputAmounts", "type": { "array": [ "u64", 2 ] } - }, - { - "name": "minimumMintAmount", - "type": "u64" } ] } @@ -1590,6 +1712,26 @@ ] } }, + { + "name": "PrepareFeeChangeParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lpFee", + "type": { + "defined": "DecimalU64Anchor" + } + }, + { + "name": "governanceFee", + "type": { + "defined": "DecimalU64Anchor" + } + } + ] + } + }, { "name": "CreateLpMetadataParams", "type": { @@ -1612,6 +1754,40 @@ ] } }, + { + "name": "UpdateLpMetadataParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "newUpdateAuthority", + "type": { + "option": "publicKey" + } + }, + { + "name": "data", + "type": { + "option": { + "defined": "AnchorDataV2" + } + } + }, + { + "name": "primarySaleHappened", + "type": { + "option": "bool" + } + }, + { + "name": "isMutable", + "type": { + "option": "bool" + } + } + ] + } + }, { "name": "AnchorDataV2", "type": { @@ -1741,60 +1917,6 @@ ] } }, - { - "name": "PrepareFeeChangeParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "lpFee", - "type": { - "defined": "DecimalU64Anchor" - } - }, - { - "name": "governanceFee", - "type": { - "defined": "DecimalU64Anchor" - } - } - ] - } - }, - { - "name": "UpdateLpMetadataParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "newUpdateAuthority", - "type": { - "option": "publicKey" - } - }, - { - "name": "data", - "type": { - "option": { - "defined": "AnchorDataV2" - } - } - }, - { - "name": "primarySaleHappened", - "type": { - "option": "bool" - } - }, - { - "name": "isMutable", - "type": { - "option": "bool" - } - } - ] - } - }, { "name": "InitializeParams", "type": { @@ -1821,118 +1943,6 @@ ] } }, - { - "name": "RemoveExactBurnParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "exactBurnAmount", - "type": "u64" - }, - { - "name": "outputTokenIndex", - "type": "u8" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ] - } - }, - { - "name": "RemoveExactOutputParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "maximumBurnAmount", - "type": "u64" - }, - { - "name": "exactOutputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - } - ] - } - }, - { - "name": "RemoveUniformParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "exactBurnAmount", - "type": "u64" - }, - { - "name": "minimumOutputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - } - ] - } - }, - { - "name": "SwapExactInputParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "exactInputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - }, - { - "name": "outputTokenIndex", - "type": "u8" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ] - } - }, - { - "name": "SwapExactOutputParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "maximumInputAmount", - "type": "u64" - }, - { - "name": "inputTokenIndex", - "type": "u8" - }, - { - "name": "exactOutputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - } - ] - } - }, { "name": "PoolFee", "type": { @@ -2158,28 +2168,43 @@ }, { "code": 6032, + "name": "InvalidNewPauseKey", + "msg": "Invalid New Pause Key" + }, + { + "code": 6033, "name": "InvalidSwitchboardAccount", "msg": "Not a valid Switchboard account" }, { - "code": 6033, + "code": 6034, "name": "StaleFeed", "msg": "Switchboard feed has not been updated in 5 minutes" }, { - "code": 6034, + "code": 6035, "name": "ConfidenceIntervalExceeded", "msg": "Switchboard feed exceeded provided confidence interval" }, { - "code": 6035, + "code": 6036, "name": "MaxDecimalsExceeded", "msg": "Maximum decimals exceeded" }, { - "code": 6036, + "code": 6037, "name": "ConversionError", "msg": "Conversion error" + }, + { + "code": 6038, + "name": "BurnAmountExceedsTotalSupply", + "msg": "Burn amount exceeds lp total supply" + }, + { + "code": 6039, + "name": "InvalidUpcomingGovernanceKey", + "msg": "Invalid Upcoming Governance Key" } ] } \ No newline at end of file diff --git a/packages/solana-contracts/src/artifacts/two_pool.ts b/packages/solana-contracts/src/artifacts/two_pool.ts index ff02cacb8..e92b8cbe4 100644 --- a/packages/solana-contracts/src/artifacts/two_pool.ts +++ b/packages/solana-contracts/src/artifacts/two_pool.ts @@ -128,81 +128,86 @@ export type TwoPool = { "name": "add", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -422,81 +427,86 @@ export type TwoPool = { "name": "removeUniform", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -522,81 +532,86 @@ export type TwoPool = { "name": "removeExactBurn", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -619,81 +634,86 @@ export type TwoPool = { "name": "removeExactOutput", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -777,66 +797,166 @@ export type TwoPool = { } }, { - "name": "prepareGovernanceTransition", + "name": "adjustAmpFactor", "docs": [ "Governance Ixs *" ], "accounts": [ { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true } ], "args": [ { - "name": "upcomingGovernanceKey", - "type": "publicKey" + "name": "targetTs", + "type": "i64" + }, + { + "name": "targetValue", + "type": { + "defined": "DecimalU64Anchor" + } } ] }, { - "name": "enactGovernanceTransition", + "name": "prepareFeeChange", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "lpFee", + "type": { + "defined": "DecimalU64Anchor" + } + }, + { + "name": "governanceFee", + "type": { + "defined": "DecimalU64Anchor" + } + } + ] + }, + { + "name": "enactFeeChange", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" + } + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "changeGovernanceFeeAccount", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -871,20 +991,30 @@ export type TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } ] + }, + { + "name": "newGovernanceFee", + "isMut": false, + "isSigner": false } ], - "args": [] + "args": [ + { + "name": "newGovernanceFeeKey", + "type": "publicKey" + } + ] }, { - "name": "prepareFeeChange", + "name": "prepareGovernanceTransition", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -919,33 +1049,73 @@ export type TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } ] + }, + { + "name": "upcomingGovernanceKey", + "isMut": false, + "isSigner": false } ], "args": [ { - "name": "lpFee", - "type": { - "defined": "DecimalU64Anchor" + "name": "upcomingGovernanceKey", + "type": "publicKey" + } + ] + }, + { + "name": "enactGovernanceTransition", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" + } + ] } }, { - "name": "governanceFee", - "type": { - "defined": "DecimalU64Anchor" - } + "name": "governanceKey", + "isMut": false, + "isSigner": true } - ] + ], + "args": [] }, { - "name": "enactFeeChange", + "name": "changePauseKey", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -980,132 +1150,25 @@ export type TwoPool = { } }, { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] - } - ], - "args": [] - }, - { - "name": "changeGovernanceFeeAccount", - "accounts": [ - { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] - } - }, - { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } ] }, { - "name": "newGovernanceFee", + "name": "newPauseKey", "isMut": false, "isSigner": false } ], "args": [ { - "name": "newGovernanceFeeKey", + "name": "newPauseKey", "type": "publicKey" } ] }, - { - "name": "adjustAmpFactor", - "accounts": [ - { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] - } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] - } - ], - "args": [ - { - "name": "targetTs", - "type": "i64" - }, - { - "name": "targetValue", - "type": { - "defined": "DecimalU64Anchor" - } - } - ] - }, { "name": "setPaused", "accounts": [ @@ -1154,64 +1217,11 @@ export type TwoPool = { } ] }, - { - "name": "changePauseKey", - "accounts": [ - { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] - } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] - } - ], - "args": [ - { - "name": "newPauseKey", - "type": "publicKey" - } - ] - }, { "name": "createLpMetadata", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -1246,7 +1256,7 @@ export type TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -1319,7 +1329,7 @@ export type TwoPool = { "name": "updateLpMetadata", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -1354,7 +1364,7 @@ export type TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -1573,190 +1583,173 @@ export type TwoPool = { } }, { - "name": "AdjustAmpFactorParams", + "name": "RemoveExactBurnParams", "type": { "kind": "struct", "fields": [ { - "name": "targetTs", - "type": "i64" + "name": "exactBurnAmount", + "type": "u64" }, { - "name": "targetValue", - "type": { - "defined": "DecimalU64Anchor" - } + "name": "outputTokenIndex", + "type": "u8" + }, + { + "name": "minimumOutputAmount", + "type": "u64" } ] } }, { - "name": "CreateLpMetadataParams", + "name": "RemoveExactOutputParams", "type": { "kind": "struct", "fields": [ { - "name": "data", + "name": "maximumBurnAmount", + "type": "u64" + }, + { + "name": "exactOutputAmounts", "type": { - "defined": "AnchorDataV2" + "array": [ + "u64", + 2 + ] } - }, + } + ] + } + }, + { + "name": "RemoveUniformParams", + "type": { + "kind": "struct", + "fields": [ { - "name": "isMutable", - "type": "bool" + "name": "exactBurnAmount", + "type": "u64" }, { - "name": "updateAuthorityIsSigner", - "type": "bool" + "name": "minimumOutputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } } ] } }, { - "name": "AnchorDataV2", + "name": "SwapExactInputParams", "type": { "kind": "struct", "fields": [ { - "name": "name", - "docs": [ - "The name of the asset" - ], - "type": "string" + "name": "exactInputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } }, { - "name": "symbol", - "docs": [ - "The symbol for the asset" - ], - "type": "string" + "name": "outputTokenIndex", + "type": "u8" }, { - "name": "uri", - "docs": [ - "URI pointing to JSON representing the asset" - ], - "type": "string" + "name": "minimumOutputAmount", + "type": "u64" + } + ] + } + }, + { + "name": "SwapExactOutputParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "maximumInputAmount", + "type": "u64" }, { - "name": "sellerFeeBasisPoints", - "docs": [ - "Royalty basis points that goes to creators in secondary sales (0-10000)" - ], - "type": "u16" + "name": "inputTokenIndex", + "type": "u8" }, { - "name": "creators", - "docs": [ - "Array of creators, optional" - ], - "type": { - "option": { - "vec": { - "defined": "AnchorCreator" - } - } - } - }, - { - "name": "collection", - "docs": [ - "Collection" - ], - "type": { - "option": { - "defined": "AnchorCollection" - } - } - }, - { - "name": "uses", - "docs": [ - "Uses" - ], + "name": "exactOutputAmounts", "type": { - "option": { - "defined": "AnchorUses" - } + "array": [ + "u64", + 2 + ] } } ] } }, { - "name": "AnchorCreator", + "name": "AdjustAmpFactorParams", "type": { "kind": "struct", "fields": [ { - "name": "address", - "type": "publicKey" - }, - { - "name": "verified", - "type": "bool" + "name": "targetTs", + "type": "i64" }, { - "name": "share", - "type": "u8" - } - ] - } - }, - { - "name": "AnchorUses", - "type": { - "kind": "struct", - "fields": [ - { - "name": "useMethod", + "name": "targetValue", "type": { - "defined": "AnchorUseMethod" + "defined": "DecimalU64Anchor" } - }, - { - "name": "remaining", - "type": "u64" - }, - { - "name": "total", - "type": "u64" } ] } }, { - "name": "AnchorCollection", + "name": "PrepareFeeChangeParams", "type": { "kind": "struct", "fields": [ { - "name": "verified", - "type": "bool" + "name": "lpFee", + "type": { + "defined": "DecimalU64Anchor" + } }, { - "name": "key", - "type": "publicKey" + "name": "governanceFee", + "type": { + "defined": "DecimalU64Anchor" + } } ] } }, { - "name": "PrepareFeeChangeParams", + "name": "CreateLpMetadataParams", "type": { "kind": "struct", "fields": [ { - "name": "lpFee", + "name": "data", "type": { - "defined": "DecimalU64Anchor" + "defined": "AnchorDataV2" } }, { - "name": "governanceFee", - "type": { - "defined": "DecimalU64Anchor" - } + "name": "isMutable", + "type": "bool" + }, + { + "name": "updateAuthorityIsSigner", + "type": "bool" } ] } @@ -1796,138 +1789,155 @@ export type TwoPool = { } }, { - "name": "InitializeParams", + "name": "AnchorDataV2", "type": { "kind": "struct", "fields": [ { - "name": "ampFactor", + "name": "name", + "docs": [ + "The name of the asset" + ], + "type": "string" + }, + { + "name": "symbol", + "docs": [ + "The symbol for the asset" + ], + "type": "string" + }, + { + "name": "uri", + "docs": [ + "URI pointing to JSON representing the asset" + ], + "type": "string" + }, + { + "name": "sellerFeeBasisPoints", + "docs": [ + "Royalty basis points that goes to creators in secondary sales (0-10000)" + ], + "type": "u16" + }, + { + "name": "creators", + "docs": [ + "Array of creators, optional" + ], "type": { - "defined": "DecimalU64Anchor" + "option": { + "vec": { + "defined": "AnchorCreator" + } + } } }, { - "name": "lpFee", + "name": "collection", + "docs": [ + "Collection" + ], "type": { - "defined": "DecimalU64Anchor" + "option": { + "defined": "AnchorCollection" + } } }, { - "name": "governanceFee", + "name": "uses", + "docs": [ + "Uses" + ], "type": { - "defined": "DecimalU64Anchor" + "option": { + "defined": "AnchorUses" + } } } ] } }, { - "name": "RemoveExactBurnParams", + "name": "AnchorCreator", "type": { "kind": "struct", "fields": [ { - "name": "exactBurnAmount", - "type": "u64" + "name": "address", + "type": "publicKey" }, { - "name": "outputTokenIndex", - "type": "u8" + "name": "verified", + "type": "bool" }, { - "name": "minimumOutputAmount", - "type": "u64" + "name": "share", + "type": "u8" } ] } }, { - "name": "RemoveExactOutputParams", + "name": "AnchorUses", "type": { "kind": "struct", "fields": [ { - "name": "maximumBurnAmount", - "type": "u64" - }, - { - "name": "exactOutputAmounts", + "name": "useMethod", "type": { - "array": [ - "u64", - 2 - ] + "defined": "AnchorUseMethod" } - } - ] - } - }, - { - "name": "RemoveUniformParams", - "type": { - "kind": "struct", - "fields": [ + }, { - "name": "exactBurnAmount", + "name": "remaining", "type": "u64" }, { - "name": "minimumOutputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } + "name": "total", + "type": "u64" } ] } }, { - "name": "SwapExactInputParams", + "name": "AnchorCollection", "type": { "kind": "struct", "fields": [ { - "name": "exactInputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - }, - { - "name": "outputTokenIndex", - "type": "u8" + "name": "verified", + "type": "bool" }, { - "name": "minimumOutputAmount", - "type": "u64" + "name": "key", + "type": "publicKey" } ] } }, { - "name": "SwapExactOutputParams", + "name": "InitializeParams", "type": { "kind": "struct", "fields": [ { - "name": "maximumInputAmount", - "type": "u64" + "name": "ampFactor", + "type": { + "defined": "DecimalU64Anchor" + } }, { - "name": "inputTokenIndex", - "type": "u8" + "name": "lpFee", + "type": { + "defined": "DecimalU64Anchor" + } }, { - "name": "exactOutputAmounts", + "name": "governanceFee", "type": { - "array": [ - "u64", - 2 - ] + "defined": "DecimalU64Anchor" } } ] @@ -2158,30 +2168,45 @@ export type TwoPool = { }, { "code": 6032, + "name": "InvalidNewPauseKey", + "msg": "Invalid New Pause Key" + }, + { + "code": 6033, "name": "InvalidSwitchboardAccount", "msg": "Not a valid Switchboard account" }, { - "code": 6033, + "code": 6034, "name": "StaleFeed", "msg": "Switchboard feed has not been updated in 5 minutes" }, { - "code": 6034, + "code": 6035, "name": "ConfidenceIntervalExceeded", "msg": "Switchboard feed exceeded provided confidence interval" }, { - "code": 6035, + "code": 6036, "name": "MaxDecimalsExceeded", "msg": "Maximum decimals exceeded" }, { - "code": 6036, + "code": 6037, "name": "ConversionError", "msg": "Conversion error" - } - ] + }, + { + "code": 6038, + "name": "BurnAmountExceedsTotalSupply", + "msg": "Burn amount exceeds lp total supply" + }, + { + "code": 6039, + "name": "InvalidUpcomingGovernanceKey", + "msg": "Invalid Upcoming Governance Key" + } + ] }; export const IDL: TwoPool = { @@ -2314,81 +2339,86 @@ export const IDL: TwoPool = { "name": "add", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } - }, - { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ @@ -2608,90 +2638,302 @@ export const IDL: TwoPool = { "name": "removeUniform", "accounts": [ { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" - }, - { - "kind": "account", - "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] } - ] - } + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { - "name": "poolTokenAccount0", + "name": "userLpTokenAccount", "isMut": true, "isSigner": false - }, + } + ], + "args": [ { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false + "name": "exactBurnAmount", + "type": "u64" }, { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, + "name": "minimumOutputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ], + "returns": { + "vec": "u64" + } + }, + { + "name": "removeExactBurn", + "accounts": [ { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", - "isMut": false, - "isSigner": true + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] + } + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { - "name": "userTokenAccount0", + "name": "userLpTokenAccount", "isMut": true, "isSigner": false + } + ], + "args": [ + { + "name": "exactBurnAmount", + "type": "u64" }, { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false + "name": "outputTokenIndex", + "type": "u8" }, { - "name": "userLpTokenAccount", - "isMut": true, - "isSigner": false + "name": "minimumOutputAmount", + "type": "u64" + } + ], + "returns": "u64" + }, + { + "name": "removeExactOutput", + "accounts": [ + { + "name": "swap", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_0.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TokenAccount", + "path": "pool_token_account_1.mint" + }, + { + "kind": "account", + "type": "publicKey", + "account": "Mint", + "path": "lp_mint" + } + ] + } + }, + { + "name": "poolTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "poolTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "lpMint", + "isMut": true, + "isSigner": false + }, + { + "name": "governanceFee", + "isMut": true, + "isSigner": false + }, + { + "name": "userTransferAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "userTokenAccount0", + "isMut": true, + "isSigner": false + }, + { + "name": "userTokenAccount1", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ] }, { - "name": "tokenProgram", - "isMut": false, + "name": "userLpTokenAccount", + "isMut": true, "isSigner": false } ], "args": [ { - "name": "exactBurnAmount", + "name": "maximumBurnAmount", "type": "u64" }, { - "name": "minimumOutputAmounts", + "name": "exactOutputAmounts", "type": { "array": [ "u64", @@ -2705,11 +2947,11 @@ export const IDL: TwoPool = { } }, { - "name": "removeExactBurn", + "name": "marginalPrices", "accounts": [ { "name": "pool", - "isMut": true, + "isMut": false, "isSigner": false, "pda": { "seeds": [ @@ -2741,68 +2983,35 @@ export const IDL: TwoPool = { }, { "name": "poolTokenAccount0", - "isMut": true, + "isMut": false, "isSigner": false }, { "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", "isMut": false, - "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "userLpTokenAccount", - "isMut": true, "isSigner": false }, { - "name": "tokenProgram", + "name": "lpMint", "isMut": false, "isSigner": false } ], - "args": [ - { - "name": "exactBurnAmount", - "type": "u64" - }, - { - "name": "outputTokenIndex", - "type": "u8" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ], - "returns": "u64" + "args": [], + "returns": { + "array": [ + { + "defined": "BorshDecimal" + }, + 2 + ] + } }, { - "name": "removeExactOutput", + "name": "adjustAmpFactor", + "docs": [ + "Governance Ixs *" + ], "accounts": [ { "name": "pool", @@ -2818,95 +3027,49 @@ export const IDL: TwoPool = { { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" + "account": "TwoPool", + "path": "pool" }, { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" + "account": "TwoPool", + "path": "pool" }, { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "TwoPool", + "path": "pool.lp_mint_key" } ] } }, { - "name": "poolTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "lpMint", - "isMut": true, - "isSigner": false - }, - { - "name": "governanceFee", - "isMut": true, - "isSigner": false - }, - { - "name": "userTransferAuthority", + "name": "governanceKey", "isMut": false, "isSigner": true - }, - { - "name": "userTokenAccount0", - "isMut": true, - "isSigner": false - }, - { - "name": "userTokenAccount1", - "isMut": true, - "isSigner": false - }, - { - "name": "userLpTokenAccount", - "isMut": true, - "isSigner": false - }, - { - "name": "tokenProgram", - "isMut": false, - "isSigner": false } ], "args": [ { - "name": "maximumBurnAmount", - "type": "u64" + "name": "targetTs", + "type": "i64" }, { - "name": "exactOutputAmounts", + "name": "targetValue", "type": { - "array": [ - "u64", - 2 - ] + "defined": "DecimalU64Anchor" } } - ], - "returns": { - "vec": "u64" - } + ] }, { - "name": "marginalPrices", + "name": "prepareFeeChange", "accounts": [ { "name": "pool", - "isMut": false, + "isMut": true, "isSigner": false, "pda": { "seeds": [ @@ -2918,198 +3081,28 @@ export const IDL: TwoPool = { { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_0.mint" + "account": "TwoPool", + "path": "pool" }, { "kind": "account", "type": "publicKey", - "account": "TokenAccount", - "path": "pool_token_account_1.mint" + "account": "TwoPool", + "path": "pool" }, { "kind": "account", "type": "publicKey", - "account": "Mint", - "path": "lp_mint" + "account": "TwoPool", + "path": "pool.lp_mint_key" } ] } }, { - "name": "poolTokenAccount0", - "isMut": false, - "isSigner": false - }, - { - "name": "poolTokenAccount1", - "isMut": false, - "isSigner": false - }, - { - "name": "lpMint", + "name": "governanceKey", "isMut": false, - "isSigner": false - } - ], - "args": [], - "returns": { - "array": [ - { - "defined": "BorshDecimal" - }, - 2 - ] - } - }, - { - "name": "prepareGovernanceTransition", - "docs": [ - "Governance Ixs *" - ], - "accounts": [ - { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] - } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] - } - ], - "args": [ - { - "name": "upcomingGovernanceKey", - "type": "publicKey" - } - ] - }, - { - "name": "enactGovernanceTransition", - "accounts": [ - { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] - } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] - } - ], - "args": [] - }, - { - "name": "prepareFeeChange", - "accounts": [ - { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] - } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] + "isSigner": true } ], "args": [ @@ -3131,46 +3124,41 @@ export const IDL: TwoPool = { "name": "enactFeeChange", "accounts": [ { - "name": "commonGovernance", - "accounts": [ - { - "name": "pool", - "isMut": true, - "isSigner": false, - "pda": { - "seeds": [ - { - "kind": "const", - "type": "string", - "value": "two_pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool" - }, - { - "kind": "account", - "type": "publicKey", - "account": "TwoPool", - "path": "pool.lp_mint_key" - } - ] + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" } - }, - { - "name": "governance", - "isMut": false, - "isSigner": true - } - ] + ] + } + }, + { + "name": "governanceKey", + "isMut": false, + "isSigner": true } ], "args": [] @@ -3179,7 +3167,7 @@ export const IDL: TwoPool = { "name": "changeGovernanceFeeAccount", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -3214,7 +3202,7 @@ export const IDL: TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -3234,10 +3222,10 @@ export const IDL: TwoPool = { ] }, { - "name": "adjustAmpFactor", + "name": "prepareGovernanceTransition", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -3272,28 +3260,27 @@ export const IDL: TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } ] + }, + { + "name": "upcomingGovernanceKey", + "isMut": false, + "isSigner": false } ], "args": [ { - "name": "targetTs", - "type": "i64" - }, - { - "name": "targetValue", - "type": { - "defined": "DecimalU64Anchor" - } + "name": "upcomingGovernanceKey", + "type": "publicKey" } ] }, { - "name": "setPaused", + "name": "enactGovernanceTransition", "accounts": [ { "name": "pool", @@ -3328,23 +3315,18 @@ export const IDL: TwoPool = { } }, { - "name": "pauseKey", + "name": "governanceKey", "isMut": false, "isSigner": true } ], - "args": [ - { - "name": "paused", - "type": "bool" - } - ] + "args": [] }, { "name": "changePauseKey", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -3379,11 +3361,16 @@ export const IDL: TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } ] + }, + { + "name": "newPauseKey", + "isMut": false, + "isSigner": false } ], "args": [ @@ -3393,11 +3380,59 @@ export const IDL: TwoPool = { } ] }, + { + "name": "setPaused", + "accounts": [ + { + "name": "pool", + "isMut": true, + "isSigner": false, + "pda": { + "seeds": [ + { + "kind": "const", + "type": "string", + "value": "two_pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool" + }, + { + "kind": "account", + "type": "publicKey", + "account": "TwoPool", + "path": "pool.lp_mint_key" + } + ] + } + }, + { + "name": "pauseKey", + "isMut": false, + "isSigner": true + } + ], + "args": [ + { + "name": "paused", + "type": "bool" + } + ] + }, { "name": "createLpMetadata", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -3432,7 +3467,7 @@ export const IDL: TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -3505,7 +3540,7 @@ export const IDL: TwoPool = { "name": "updateLpMetadata", "accounts": [ { - "name": "commonGovernance", + "name": "governance", "accounts": [ { "name": "pool", @@ -3540,7 +3575,7 @@ export const IDL: TwoPool = { } }, { - "name": "governance", + "name": "governanceKey", "isMut": false, "isSigner": true } @@ -3731,29 +3766,141 @@ export const IDL: TwoPool = { } }, { - "name": "targetTs", - "type": "i64" + "name": "targetTs", + "type": "i64" + } + ] + } + }, + { + "name": "AddParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "inputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "minimumMintAmount", + "type": "u64" + } + ] + } + }, + { + "name": "RemoveExactBurnParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "exactBurnAmount", + "type": "u64" + }, + { + "name": "outputTokenIndex", + "type": "u8" + }, + { + "name": "minimumOutputAmount", + "type": "u64" + } + ] + } + }, + { + "name": "RemoveExactOutputParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "maximumBurnAmount", + "type": "u64" + }, + { + "name": "exactOutputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + }, + { + "name": "RemoveUniformParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "exactBurnAmount", + "type": "u64" + }, + { + "name": "minimumOutputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + }, + { + "name": "SwapExactInputParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "exactInputAmounts", + "type": { + "array": [ + "u64", + 2 + ] + } + }, + { + "name": "outputTokenIndex", + "type": "u8" + }, + { + "name": "minimumOutputAmount", + "type": "u64" } ] } }, { - "name": "AddParams", + "name": "SwapExactOutputParams", "type": { "kind": "struct", "fields": [ { - "name": "inputAmounts", + "name": "maximumInputAmount", + "type": "u64" + }, + { + "name": "inputTokenIndex", + "type": "u8" + }, + { + "name": "exactOutputAmounts", "type": { "array": [ "u64", 2 ] } - }, - { - "name": "minimumMintAmount", - "type": "u64" } ] } @@ -3776,6 +3923,26 @@ export const IDL: TwoPool = { ] } }, + { + "name": "PrepareFeeChangeParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "lpFee", + "type": { + "defined": "DecimalU64Anchor" + } + }, + { + "name": "governanceFee", + "type": { + "defined": "DecimalU64Anchor" + } + } + ] + } + }, { "name": "CreateLpMetadataParams", "type": { @@ -3798,6 +3965,40 @@ export const IDL: TwoPool = { ] } }, + { + "name": "UpdateLpMetadataParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "newUpdateAuthority", + "type": { + "option": "publicKey" + } + }, + { + "name": "data", + "type": { + "option": { + "defined": "AnchorDataV2" + } + } + }, + { + "name": "primarySaleHappened", + "type": { + "option": "bool" + } + }, + { + "name": "isMutable", + "type": { + "option": "bool" + } + } + ] + } + }, { "name": "AnchorDataV2", "type": { @@ -3927,60 +4128,6 @@ export const IDL: TwoPool = { ] } }, - { - "name": "PrepareFeeChangeParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "lpFee", - "type": { - "defined": "DecimalU64Anchor" - } - }, - { - "name": "governanceFee", - "type": { - "defined": "DecimalU64Anchor" - } - } - ] - } - }, - { - "name": "UpdateLpMetadataParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "newUpdateAuthority", - "type": { - "option": "publicKey" - } - }, - { - "name": "data", - "type": { - "option": { - "defined": "AnchorDataV2" - } - } - }, - { - "name": "primarySaleHappened", - "type": { - "option": "bool" - } - }, - { - "name": "isMutable", - "type": { - "option": "bool" - } - } - ] - } - }, { "name": "InitializeParams", "type": { @@ -4007,118 +4154,6 @@ export const IDL: TwoPool = { ] } }, - { - "name": "RemoveExactBurnParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "exactBurnAmount", - "type": "u64" - }, - { - "name": "outputTokenIndex", - "type": "u8" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ] - } - }, - { - "name": "RemoveExactOutputParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "maximumBurnAmount", - "type": "u64" - }, - { - "name": "exactOutputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - } - ] - } - }, - { - "name": "RemoveUniformParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "exactBurnAmount", - "type": "u64" - }, - { - "name": "minimumOutputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - } - ] - } - }, - { - "name": "SwapExactInputParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "exactInputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - }, - { - "name": "outputTokenIndex", - "type": "u8" - }, - { - "name": "minimumOutputAmount", - "type": "u64" - } - ] - } - }, - { - "name": "SwapExactOutputParams", - "type": { - "kind": "struct", - "fields": [ - { - "name": "maximumInputAmount", - "type": "u64" - }, - { - "name": "inputTokenIndex", - "type": "u8" - }, - { - "name": "exactOutputAmounts", - "type": { - "array": [ - "u64", - 2 - ] - } - } - ] - } - }, { "name": "PoolFee", "type": { @@ -4344,28 +4379,43 @@ export const IDL: TwoPool = { }, { "code": 6032, + "name": "InvalidNewPauseKey", + "msg": "Invalid New Pause Key" + }, + { + "code": 6033, "name": "InvalidSwitchboardAccount", "msg": "Not a valid Switchboard account" }, { - "code": 6033, + "code": 6034, "name": "StaleFeed", "msg": "Switchboard feed has not been updated in 5 minutes" }, { - "code": 6034, + "code": 6035, "name": "ConfidenceIntervalExceeded", "msg": "Switchboard feed exceeded provided confidence interval" }, { - "code": 6035, + "code": 6036, "name": "MaxDecimalsExceeded", "msg": "Maximum decimals exceeded" }, { - "code": 6036, + "code": 6037, "name": "ConversionError", "msg": "Conversion error" + }, + { + "code": 6038, + "name": "BurnAmountExceedsTotalSupply", + "msg": "Burn amount exceeds lp total supply" + }, + { + "code": 6039, + "name": "InvalidUpcomingGovernanceKey", + "msg": "Invalid Upcoming Governance Key" } ] };