From 030eaa27279d19a8ca92d31c5e5d397d1425408c Mon Sep 17 00:00:00 2001 From: swimricky Date: Tue, 11 Oct 2022 20:03:20 -0400 Subject: [PATCH] refactor(solana-contracts): rename account names in completeWithPayload --- .../programs/propeller/src/error.rs | 6 + .../programs/propeller/src/fees.rs | 11 +- .../propeller/src/instructions/fee_tracker.rs | 19 +- .../propeller/src/instructions/initialize.rs | 1 + .../src/instructions/process_swim_payload.rs | 396 ++++++------------ .../wormhole/complete_native_with_payload.rs | 159 +++---- .../programs/propeller/src/state/propeller.rs | 34 +- .../scripts/initializePropeller.ts | 3 +- .../solana-contracts/src/__tests__/consts.ts | 3 +- .../src/__tests__/propeller/engine.test.ts | 44 +- .../src/__tests__/propeller/propeller.test.ts | 139 ++---- .../src/__tests__/propeller/propellerUtils.ts | 234 ++++++++++- .../src/artifacts/propeller.json | 62 ++- .../src/artifacts/propeller.ts | 124 ++++-- 14 files changed, 648 insertions(+), 587 deletions(-) diff --git a/packages/solana-contracts/programs/propeller/src/error.rs b/packages/solana-contracts/programs/propeller/src/error.rs index 9e4599a29..18b4963b6 100644 --- a/packages/solana-contracts/programs/propeller/src/error.rs +++ b/packages/solana-contracts/programs/propeller/src/error.rs @@ -108,6 +108,9 @@ pub enum PropellerError { #[msg("Invalid Gas Kickstart parameter in Swim Payload")] InvalidSwimPayloadGasKickstart, + #[msg("Invalid Marginal Price Pool")] + InvalidMarginalPricePool, + #[msg("Invalid Marginal Price Pool Accounts")] InvalidMarginalPricePoolAccounts, @@ -152,4 +155,7 @@ pub enum PropellerError { #[msg("Invalid Aggregator")] InvalidAggregator, + + #[msg("Invalid Fee Vault")] + InvalidFeeVault, } diff --git a/packages/solana-contracts/programs/propeller/src/fees.rs b/packages/solana-contracts/programs/propeller/src/fees.rs index b7ed50207..6ca08b179 100644 --- a/packages/solana-contracts/programs/propeller/src/fees.rs +++ b/packages/solana-contracts/programs/propeller/src/fees.rs @@ -1,12 +1,9 @@ use {crate::TOKEN_COUNT, anchor_lang::prelude::*, two_pool::BorshDecimal}; -pub trait Fees { +pub trait Fees<'info> { + /// Calculating the fees, including txn and rent exmptions, in lamports. fn calculate_fees_in_lamports(&self) -> Result; - fn convert_fees_to_swim_usd_atomic( - &self, - fee_in_lamports: u64, - marginal_prices: [BorshDecimal; TOKEN_COUNT], - max_staleness: i64, - ) -> Result; + fn get_marginal_prices(&self) -> Result<[BorshDecimal; TOKEN_COUNT]>; + fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64) -> Result; fn track_and_transfer_fees(&mut self, fees_in_swim_usd: u64) -> Result<()>; } 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..8bf0055cf 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/fee_tracker.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/fee_tracker.rs @@ -2,7 +2,7 @@ use { crate::{error::*, Propeller}, anchor_lang::prelude::*, anchor_spl::{ - associated_token::{create, AssociatedToken, Create}, + associated_token::{create, get_associated_token_address, AssociatedToken, Create}, token::{self, Mint, Token, TokenAccount, Transfer}, }, two_pool::state::TwoPool, @@ -50,7 +50,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 +59,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 +73,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 +89,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>, diff --git a/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs b/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs index c4182c5e9..c985d478e 100644 --- a/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs +++ b/packages/solana-contracts/programs/propeller/src/instructions/initialize.rs @@ -102,6 +102,7 @@ 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], } 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 f38ba9e90..09988b164 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 @@ -371,13 +371,14 @@ pub struct PropellerProcessSwimPayload<'info> { ///CHECK: account for getting gas -> USD price #[account( constraint = - *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount + *aggregator.to_account_info().owner == SWITCHBOARD_PROGRAM_ID @ PropellerError::InvalidSwitchboardAccount, + address = process_swim_payload.propeller.aggregator @ PropellerError::InvalidAggregator )] pub aggregator: AccountLoader<'info, AggregatorAccountData>, #[account( mut, - address = get_associated_token_address(&process_swim_payload.propeller.key(), &process_swim_payload.propeller.swim_usd_mint) + address = process_swim_payload.propeller.fee_vault @ PropellerError::InvalidFeeVault )] /// this is "to_fees" /// recipient of fees for executing complete transfer (e.g. relayer) @@ -416,7 +417,8 @@ pub struct PropellerProcessSwimPayload<'info> { marginal_price_pool_lp_mint.key().as_ref(), ], bump = marginal_price_pool.bump, - seeds::program = process_swim_payload.two_pool_program.key() + seeds::program = process_swim_payload.two_pool_program.key(), + address = process_swim_payload.propeller.marginal_price_pool @ PropellerError::InvalidMarginalPricePool )] pub marginal_price_pool: Box>, #[account(address = marginal_price_pool.token_keys[0])] @@ -474,52 +476,43 @@ impl<'info> PropellerProcessSwimPayload<'info> { Ok(()) } - /// Calculates, transfer and tracks fees - /// returns fees_in_token_bridge_mint - fn handle_fees(&mut self) -> Result { - let fees_in_lamports = self.calculate_fees_in_lamports()?; - let marginal_prices = two_pool::cpi::marginal_prices(CpiContext::new( - self.process_swim_payload.two_pool_program.to_account_info(), - 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(), - }, - ))?; - let fees_in_swim_usd_atomic = convert_fees_to_swim_usd_atomic_2( - fees_in_lamports, - &self.process_swim_payload.propeller, - &self.marginal_price_pool_lp_mint, - marginal_prices.get(), - &self.marginal_price_pool, - &self.aggregator, - i64::MAX, - )?; - // let fees_in_swim_usd_atomic = - // let fees_in_swim_usd_atomic = self.calculate_fees()?; + fn transfer_gas_kickstart(&self) -> Result<()> { 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]]], + 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() }, ), - fees_in_swim_usd_atomic, + propeller.gas_kickstart_amount, )?; - Ok(fees_in_swim_usd_atomic) + 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(memo.as_slice(), &[]); + invoke(&memo_ix, &[self.memo.to_account_info()])?; + } else { + msg!("memo is empty"); + } + Ok(()) + } +} + +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 @@ -552,10 +545,8 @@ impl<'info> PropellerProcessSwimPayload<'info> { Ok(fee_in_lamports) } - fn calculate_fees(&self) -> Result { - let fee_in_lamports = self.calculate_fees_in_lamports()?; - - let marginal_prices = two_pool::cpi::marginal_prices(CpiContext::new( + fn get_marginal_prices(&self) -> Result<[BorshDecimal; TOKEN_COUNT]> { + let result = two_pool::cpi::marginal_prices(CpiContext::new( self.process_swim_payload.two_pool_program.to_account_info(), two_pool::cpi::accounts::MarginalPrices { pool: self.marginal_price_pool.to_account_info(), @@ -564,52 +555,72 @@ impl<'info> PropellerProcessSwimPayload<'info> { lp_mint: self.marginal_price_pool_lp_mint.to_account_info(), }, ))?; + Ok(result.get()) + } + + fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64) -> Result { + msg!("fee_in_lamports: {:?}", fee_in_lamports); + let marginal_price_pool_lp_mint = &self.marginal_price_pool_lp_mint; + let propeller = &self.process_swim_payload.propeller; - let fees_in_swim_usd_atomic = convert_fees_to_swim_usd_atomic_2( - fee_in_lamports, - &self.process_swim_payload.propeller, - &self.marginal_price_pool_lp_mint, - marginal_prices.get(), + let max_staleness = propeller.max_staleness; + 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( &self.marginal_price_pool, - &self.aggregator, - i64::MAX, + &self.get_marginal_prices()?, + propeller, + &marginal_price_pool_lp_mint.key(), )?; - Ok(fees_in_swim_usd_atomic) - } - 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 + 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 = get_lamports_intermediate_token_price(&self.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, &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 ); - 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(()) + Ok(fee_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(memo.as_slice(), &[]); - invoke(&memo_ix, &[self.memo.to_account_info()])?; - } else { - msg!("memo is empty"); - } - Ok(()) + fn track_and_transfer_fees(&mut self, fees_in_swim_usd: u64) -> Result<()> { + let fee_tracker = &mut self.fee_tracker; + fee_tracker.fees_owed = + fee_tracker.fees_owed.checked_add(fees_in_swim_usd).ok_or(PropellerError::IntegerOverflow)?; + + 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_vault.to_account_info(), + authority: self.process_swim_payload.redeemer.to_account_info(), + }, + &[&[&b"redeemer".as_ref(), &[self.process_swim_payload.propeller.redeemer_bump]]], + ), + fees_in_swim_usd, + ) } } @@ -651,14 +662,15 @@ pub fn handle_propeller_process_swim_payload( 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.convert_fees_to_swim_usd_atomic(fees_in_lamports)?; + ctx.accounts.track_and_transfer_fees(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))?; + transfer_amount.checked_sub(fees_in_swim_usd_atomic).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"); @@ -682,8 +694,9 @@ 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 + has_one = marginal_price_pool @ PropellerError::InvalidMarginalPricePool, + has_one = aggregator @ PropellerError::InvalidAggregator, + has_one = fee_vault @ PropellerError::InvalidFeeVault, )] pub propeller: Box>, #[account(mut)] @@ -837,144 +850,6 @@ impl<'info> PropellerProcessSwimPayloadFallback<'info> { 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(); @@ -999,14 +874,6 @@ impl<'info> PropellerProcessSwimPayloadFallback<'info> { Ok(()) } - pub fn transfer_tokens(&self, transfer_amount: u64) -> Result { - self.transfer_swim_usd_tokens( - transfer_amount, - &self.redeemer.to_account_info(), - &[&[&b"redeemer".as_ref(), &[self.propeller.redeemer_bump]]], - ) - } - fn transfer_swim_usd_tokens( &self, transfer_amount: u64, @@ -1042,8 +909,7 @@ impl<'info> PropellerProcessSwimPayloadFallback<'info> { } } -/* -impl Fees for PropellerProcessSwimPayloadFallback { +impl<'info> Fees<'info> for PropellerProcessSwimPayloadFallback<'info> { fn calculate_fees_in_lamports(&self) -> Result { let rent = Rent::get()?; @@ -1051,12 +917,6 @@ impl Fees for PropellerProcessSwimPayloadFallback { 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 @@ -1079,21 +939,30 @@ impl Fees for PropellerProcessSwimPayloadFallback { Ok(fee_in_lamports) } - fn convert_fees_to_swim_usd_atomic( - &self, - fee_in_lamports: u64, - marginal_prices: [BorshDecimal; TOKEN_COUNT], - max_staleness: i64, - ) -> Result { + 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.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(), + }, + ))?; + Ok(result.get()) + } + + fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64) -> Result { msg!("fee_in_lamports: {:?}", fee_in_lamports); let marginal_price_pool_lp_mint = &self.marginal_price_pool_lp_mint; + let max_staleness = self.propeller.max_staleness; let swim_usd_mint_key = self.propeller.swim_usd_mint; // let marginal_prices = get_marginal_prices(cpi_ctx)?; let intermediate_token_price_decimal: Decimal = get_marginal_price_decimal( &self.marginal_price_pool, - &marginal_prices, + &self.get_marginal_prices()?, &self.propeller, &marginal_price_pool_lp_mint.key(), )?; @@ -1103,16 +972,14 @@ impl Fees for PropellerProcessSwimPayloadFallback { 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 lamports_intermediate_token_price = get_lamports_intermediate_token_price(&self.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)?; + 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 = @@ -1127,8 +994,7 @@ impl Fees for PropellerProcessSwimPayloadFallback { fee_in_swim_usd_decimal, fee_in_swim_usd_atomic ); - res = fee_in_swim_usd_atomic; - Ok(res) + Ok(fee_in_swim_usd_atomic) } fn track_and_transfer_fees(&mut self, fees_in_swim_usd: u64) -> Result<()> { @@ -1150,7 +1016,6 @@ impl Fees for PropellerProcessSwimPayloadFallback { ) } } - */ pub fn handle_propeller_process_swim_payload_fallback( ctx: Context, @@ -1172,14 +1037,15 @@ pub fn handle_propeller_process_swim_payload_fallback( 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)?; + ctx.accounts.track_and_transfer_fees(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))?; + transfer_amount.checked_sub(fees_in_swim_usd_atomic).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"); 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 c996c5135..893f335f8 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,3 +1,4 @@ +use crate::Fees; use { crate::{ constants::LAMPORTS_PER_SOL_DECIMAL, deserialize_message_payload, error::*, @@ -13,6 +14,7 @@ use { solana_program::{instruction::Instruction, program::invoke_signed, system_program, sysvar::SysvarId}, }, anchor_spl::{ + associated_token::get_associated_token_address, token, token::{Mint, Token, TokenAccount, Transfer}, }, @@ -32,6 +34,7 @@ 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, )] pub propeller: Box>, @@ -102,13 +105,12 @@ pub struct CompleteNativeWithPayload<'info> { /// CHECK: wormhole endpoint account. seeds = [ vaa.emitter_chain, vaa.emitter_address ] 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,21 +125,13 @@ 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. - - // TODO: rename to `fee_vault` and add has_one in propeller constraints? #[account( mut, token::mint = propeller.swim_usd_mint, )] /// this is "to_fees" /// recipient of fees for executing complete transfer (e.g. relayer) - pub fee_recipient: Box>, + pub fee_vault: Box>, // #[account(mut)] // /// this is "to_fees" // /// TODO: type as TokenAccount? @@ -195,9 +189,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 +209,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), @@ -381,13 +375,23 @@ impl<'info> PropellerCompleteNativeWithPayload<'info> { 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.complete_native_with_payload.fee_vault.key(), propeller.fee_vault); + require_keys_eq!(ctx.accounts.complete_native_with_payload.fee_vault.owner, propeller.key()); require_keys_eq!(ctx.accounts.aggregator.key(), propeller.aggregator, PropellerError::InvalidAggregator); Ok(()) } - fn calculate_fees(&self) -> Result { + fn log_memo(&self, memo: [u8; 16]) -> Result<()> { + if memo != [0u8; 16] { + let memo_ix = spl_memo::build_memo(memo.as_slice(), &[]); + invoke(&memo_ix, &[self.memo.to_account_info()])?; + } + Ok(()) + } +} + +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()); @@ -424,85 +428,86 @@ impl<'info> PropellerCompleteNativeWithPayload<'info> { complete_with_payload_fee, fee_in_lamports ); + Ok(fee_in_lamports) + } - let marginal_prices = get_marginal_prices(self.into_marginal_prices())?; + 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.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(), + }, + ))?; + Ok(result.get()) + } + + fn convert_fees_to_swim_usd_atomic(&self, fee_in_lamports: u64) -> Result { + msg!("fee_in_lamports: {:?}", fee_in_lamports); + let marginal_price_pool_lp_mint = &self.marginal_price_pool_lp_mint; + + let propeller = &self.complete_native_with_payload.propeller; + let max_staleness = propeller.max_staleness; + 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( &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, + &self.get_marginal_prices()?, + propeller, + &marginal_price_pool_lp_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 lamports_intermediate_token_price = get_lamports_intermediate_token_price(&self.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_mint_key = self.complete_native_with_payload.propeller.swim_usd_mint; + 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 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)?; + 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(fee_in_swim_usd_atomic) } - fn handle_fees(&mut self, fees_in_token_bridge_mint: u64) -> Result<()> { + 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_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(), - }; + fee_tracker.fees_owed = + fee_tracker.fees_owed.checked_add(fees_in_swim_usd).ok_or(PropellerError::IntegerOverflow)?; + token::transfer( CpiContext::new_with_signer( self.complete_native_with_payload.token_program.to_account_info(), - cpi_accounts, + 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<()> { @@ -526,9 +531,15 @@ 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.\ @@ -536,12 +547,12 @@ pub fn handle_propeller_complete_native_with_payload(ctx: Context Result { // let pubkey = Pubkey::from_str(CORE_BRIDGE_ADDRESS) diff --git a/packages/solana-contracts/scripts/initializePropeller.ts b/packages/solana-contracts/scripts/initializePropeller.ts index 8cc5eff4b..04190d7d8 100644 --- a/packages/solana-contracts/scripts/initializePropeller.ts +++ b/packages/solana-contracts/scripts/initializePropeller.ts @@ -33,7 +33,7 @@ import { TWO_POOL_PID, USDC_TO_TOKEN_NUMBER, USDT_TO_TOKEN_NUMBER, - setComputeUnitLimitIx, + setComputeUnitLimitIx, maxStaleness, } from "../src/__tests__/consts"; import { getPropellerPda, @@ -278,6 +278,7 @@ async function initializePropellerState(): Promise { ...DEFAULT_INIT_PROPELLER_PARAMS, marginalPricePool: swimUsdPoolInfo.address, marginalPricePoolTokenMint: swimUsdPoolInfo.tokenMints[0], + maxStaleness: maxStaleness, }; console.info(` propellerAddr: ${propellerAddr.toString()} diff --git a/packages/solana-contracts/src/__tests__/consts.ts b/packages/solana-contracts/src/__tests__/consts.ts index 4a0bcad4f..b46cdbe0c 100644 --- a/packages/solana-contracts/src/__tests__/consts.ts +++ b/packages/solana-contracts/src/__tests__/consts.ts @@ -9,7 +9,7 @@ import { LAMPORTS_PER_SOL } from "@solana/web3.js"; export const commitment = "confirmed" as web3.Commitment; export const rpcCommitmentConfig = { - commitment, + commitment: "confirmed", preflightCommitment: commitment, skipPreflight: true, }; @@ -74,6 +74,7 @@ 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 diff --git a/packages/solana-contracts/src/__tests__/propeller/engine.test.ts b/packages/solana-contracts/src/__tests__/propeller/engine.test.ts index a22e1f8fa..55840e6db 100644 --- a/packages/solana-contracts/src/__tests__/propeller/engine.test.ts +++ b/packages/solana-contracts/src/__tests__/propeller/engine.test.ts @@ -58,6 +58,7 @@ import { initAtaFee, lpFee, marginalPricePoolTokenIndex, + maxStaleness, metapoolMint1OutputTokenIndex, metapoolMint1PoolTokenIndex, postVaaFee, @@ -82,6 +83,7 @@ import type { WormholeAddresses } from "./propellerUtils"; import { encodeSwimPayload, generatePropellerEngineTxns, + getFeeTrackerPda, getOwnerTokenAccountsForPool, getPropellerPda, getPropellerRedeemerPda, @@ -765,16 +767,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"); @@ -788,7 +785,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( @@ -940,9 +937,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, @@ -1636,9 +1633,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -2444,9 +2441,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -3035,7 +3032,7 @@ describe("propeller", () => { // message: wormholeMessage, // claim: wormholeClaim, // endpoint: ethEndpointAccount, - // to: propellerRedeemerEscrowAccount, + // redeemerEscrow: propellerRedeemerEscrowAccount, // redeemer: propellerRedeemer, // feeRecipient: userSwimUsdAtaAddr, // // feeRecipient: propellerRedeemerEscrowAccount, @@ -3451,7 +3448,7 @@ describe("propeller", () => { // message: wormholeMessage, // claim: wormholeClaim, // endpoint: ethEndpointAccount, - // to: propellerRedeemerEscrowAccount, + // redeemerEscrow: propellerRedeemerEscrowAccount, // redeemer: propellerRedeemer, // feeRecipient: userSwimUsdAtaAddr, // // feeRecipient: propellerRedeemerEscrowAccount, @@ -3904,9 +3901,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -5795,9 +5792,9 @@ describe("propeller", () => { claim: wormholeClaim, swimPayloadMessage: expectedSwimPayloadMessage, endpoint: ethEndpointAccount, - to: propellerRedeemerEscrowAccount, + redeemerEscrow: propellerRedeemerEscrowAccount, redeemer: propellerRedeemer, - feeRecipient: propellerFeeVault, + feeVault: propellerFeeVault, // feeRecipient: propellerRedeemerEscrowAccount, // tokenBridgeMint, custody: custody, @@ -6889,6 +6886,7 @@ const initializePropeller = async () => { marginalPricePool, marginalPricePoolTokenIndex, marginalPricePoolTokenMint, + maxStaleness, // evmRoutingContractAddress: ethRoutingContract, // evmRoutingContractAddress: ethRoutingContractEthUint8Arr }; diff --git a/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts b/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts index 7f52f0e56..387a10c7e 100644 --- a/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts +++ b/packages/solana-contracts/src/__tests__/propeller/propeller.test.ts @@ -40,6 +40,7 @@ import { USDC_TO_TOKEN_NUMBER, USDT_TO_TOKEN_NUMBER, ampFactor, + bscTokenBridge, commitment, completeWithPayloadFee, ethRoutingContract, @@ -52,6 +53,7 @@ import { initAtaFee, lpFee, marginalPricePoolTokenIndex, + maxStaleness, metapoolMint1OutputTokenIndex, metapoolMint1PoolTokenIndex, postVaaFee, @@ -81,7 +83,8 @@ import { getPropellerSenderPda, getSwimClaimPda, getSwimPayloadMessagePda, - parseTokenTransferWithSwimPayloadPostedMessage, + getWormholeAddressesForMint, + parseTokenTransferWithSwimPayloadPostedMessage, WormholeAddresses, } from "./propellerUtils"; import { deriveEndpointPda, @@ -193,7 +196,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; @@ -473,76 +476,25 @@ 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, - ); - - [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, + wormholeAddresses = await getWormholeAddressesForMint( + WORMHOLE_CORE_BRIDGE, + WORMHOLE_TOKEN_BRIDGE, + swimUsdMint, + ethTokenBridge, + bscTokenBridge, ); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + ({ + authoritySigner, + custody, + custodySigner, + tokenBridgeConfig, + wormholeConfig, + wormholeEmitter, + wormholeFeeCollector, + wormholeSequence, + } = wormholeAddresses); console.info(` custodyOrWrappedMeta: ${custody.toString()} @@ -590,6 +542,7 @@ describe("propeller", () => { marginalPricePool, marginalPricePoolTokenIndex, marginalPricePoolTokenMint, + maxStaleness, // evmRoutingContractAddress: ethRoutingContract, // evmRoutingContractAddress: ethRoutingContractEthUint8Arr }; @@ -2191,6 +2144,7 @@ describe("propeller", () => { tokenTransferWithPayloadSignedVaa, ); + const completeNativeWithPayloadIxs = propellerProgram.methods .completeNativeWithPayload() .accounts({ @@ -2201,14 +2155,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, @@ -2601,14 +2550,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, @@ -3007,14 +2951,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, @@ -3402,14 +3341,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, @@ -3817,14 +3751,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, diff --git a/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts b/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts index 75c577b1f..06464d03c 100644 --- a/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts +++ b/packages/solana-contracts/src/__tests__/propeller/propellerUtils.ts @@ -3,19 +3,22 @@ import { CHAIN_ID_BSC, CHAIN_ID_ETH, getClaimAddressSolana, + tryHexToNativeString, tryUint8ArrayToNative, } from "@certusone/wormhole-sdk"; import type { 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, 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 type { ParsedTokenTransferPostedMessage, @@ -61,6 +64,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, @@ -656,9 +675,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, @@ -953,6 +972,215 @@ export const generatePropellerEngineTxns = async ( return txns; }; +export const getCompleteNativeWithPayloadAccounts = async ( + tokenTransferWithPayloadSignedVaa: Buffer, + wormholeAddresses: WormholeAddresses, + propellerProgram: Program, +) => { + 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, + ); + + return 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, + }); +}; +export const getCompleteNativeWithPayloadTxn = async ( + tokenTransferWithPayloadSignedVaa: Buffer, + wormholeAddresses: WormholeAddresses, + propellerEnabled: boolean, + 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, + 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 getMarginalPricePoolInfo = async ( propeller: web3.PublicKey, propellerProgram: Program, diff --git a/packages/solana-contracts/src/artifacts/propeller.json b/packages/solana-contracts/src/artifacts/propeller.json index 01a8b13e5..e2c3ebdc4 100644 --- a/packages/solana-contracts/src/artifacts/propeller.json +++ b/packages/solana-contracts/src/artifacts/propeller.json @@ -580,7 +580,7 @@ "kind": "account", "type": "publicKey", "account": "FeeTracker", - "path": "fee_tracker.payer" + "path": "fee_tracker.fees_recipient" } ] } @@ -1832,11 +1832,11 @@ "isSigner": false }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -1862,7 +1862,7 @@ } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ @@ -2294,11 +2294,11 @@ "isSigner": false }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -2324,7 +2324,7 @@ } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ @@ -3873,7 +3873,7 @@ "type": "u8" }, { - "name": "payer", + "name": "feesRecipient", "type": "publicKey" }, { @@ -4030,6 +4030,10 @@ { "name": "aggregator", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4174,6 +4178,10 @@ { "name": "marginalPricePoolTokenMint", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4736,78 +4744,88 @@ }, { "code": 6035, + "name": "InvalidMarginalPricePool", + "msg": "Invalid Marginal Price Pool" + }, + { + "code": 6036, "name": "InvalidMarginalPricePoolAccounts", "msg": "Invalid Marginal Price Pool Accounts" }, { - "code": 6036, + "code": 6037, "name": "NotPropellerEnabled", "msg": "Propeller Not Enabled in payload" }, { - "code": 6037, + "code": 6038, "name": "InvalidRoutingContractAddress", "msg": "Invalid Routing Contract Address" }, { - "code": 6038, + "code": 6039, "name": "IntegerOverflow", "msg": "Integer Overflow" }, { - "code": 6039, + "code": 6040, "name": "ConversionError", "msg": "Conversion Error" }, { - "code": 6040, + "code": 6041, "name": "UnableToRetrieveSwimUsdMintDecimals", "msg": "Unable to retrieve SwimUSD mint decimals from marginal price pool information" }, { - "code": 6041, + "code": 6042, "name": "InvalidMetapoolTokenMint", "msg": "Invalid Metapool Token Mint. token_mint[0] should == swim_usd_mint" }, { - "code": 6042, + "code": 6043, "name": "UnableToDeserializeTokenAccount", "msg": "Unable to deserialize account info as token account" }, { - "code": 6043, + "code": 6044, "name": "InvalidTokenAccountDataLen", "msg": "Invalid token account data length. != 0 && != TokenAccount::LEN" }, { - "code": 6044, + "code": 6045, "name": "PayerInsufficientFundsForGasKickstart", "msg": "Payer has insufficient funds for gas kickstart" }, { - "code": 6045, + "code": 6046, "name": "IncorrectOwnerForCreateTokenAccount", "msg": "Owner of token account != swimPayload.owner" }, { - "code": 6046, + "code": 6047, "name": "TokenIdMapExists", "msg": "TokenIdMap exists. Please use the correct instruction" }, { - "code": 6047, + "code": 6048, "name": "InvalidTokenIdMapAccountAddress", "msg": "Invalid address for TokenIdMap account" }, { - "code": 6048, + "code": 6049, "name": "InvalidSwimPayloadVersion", "msg": "Invalid Swim Payload version" }, { - "code": 6049, + "code": 6050, "name": "InvalidAggregator", "msg": "Invalid Aggregator" + }, + { + "code": 6051, + "name": "InvalidFeeVault", + "msg": "Invalid Fee Vault" } ] } \ 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 068abe656..86a32f67a 100644 --- a/packages/solana-contracts/src/artifacts/propeller.ts +++ b/packages/solana-contracts/src/artifacts/propeller.ts @@ -580,7 +580,7 @@ export type Propeller = { "kind": "account", "type": "publicKey", "account": "FeeTracker", - "path": "fee_tracker.payer" + "path": "fee_tracker.fees_recipient" } ] } @@ -1832,11 +1832,11 @@ export type Propeller = { "isSigner": false }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -1862,7 +1862,7 @@ export type Propeller = { } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ @@ -2294,11 +2294,11 @@ export type Propeller = { "isSigner": false }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -2324,7 +2324,7 @@ export type Propeller = { } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ @@ -3873,7 +3873,7 @@ export type Propeller = { "type": "u8" }, { - "name": "payer", + "name": "feesRecipient", "type": "publicKey" }, { @@ -4030,6 +4030,10 @@ export type Propeller = { { "name": "aggregator", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4174,6 +4178,10 @@ export type Propeller = { { "name": "marginalPricePoolTokenMint", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -4736,78 +4744,88 @@ export type Propeller = { }, { "code": 6035, + "name": "InvalidMarginalPricePool", + "msg": "Invalid Marginal Price Pool" + }, + { + "code": 6036, "name": "InvalidMarginalPricePoolAccounts", "msg": "Invalid Marginal Price Pool Accounts" }, { - "code": 6036, + "code": 6037, "name": "NotPropellerEnabled", "msg": "Propeller Not Enabled in payload" }, { - "code": 6037, + "code": 6038, "name": "InvalidRoutingContractAddress", "msg": "Invalid Routing Contract Address" }, { - "code": 6038, + "code": 6039, "name": "IntegerOverflow", "msg": "Integer Overflow" }, { - "code": 6039, + "code": 6040, "name": "ConversionError", "msg": "Conversion Error" }, { - "code": 6040, + "code": 6041, "name": "UnableToRetrieveSwimUsdMintDecimals", "msg": "Unable to retrieve SwimUSD mint decimals from marginal price pool information" }, { - "code": 6041, + "code": 6042, "name": "InvalidMetapoolTokenMint", "msg": "Invalid Metapool Token Mint. token_mint[0] should == swim_usd_mint" }, { - "code": 6042, + "code": 6043, "name": "UnableToDeserializeTokenAccount", "msg": "Unable to deserialize account info as token account" }, { - "code": 6043, + "code": 6044, "name": "InvalidTokenAccountDataLen", "msg": "Invalid token account data length. != 0 && != TokenAccount::LEN" }, { - "code": 6044, + "code": 6045, "name": "PayerInsufficientFundsForGasKickstart", "msg": "Payer has insufficient funds for gas kickstart" }, { - "code": 6045, + "code": 6046, "name": "IncorrectOwnerForCreateTokenAccount", "msg": "Owner of token account != swimPayload.owner" }, { - "code": 6046, + "code": 6047, "name": "TokenIdMapExists", "msg": "TokenIdMap exists. Please use the correct instruction" }, { - "code": 6047, + "code": 6048, "name": "InvalidTokenIdMapAccountAddress", "msg": "Invalid address for TokenIdMap account" }, { - "code": 6048, + "code": 6049, "name": "InvalidSwimPayloadVersion", "msg": "Invalid Swim Payload version" }, { - "code": 6049, + "code": 6050, "name": "InvalidAggregator", "msg": "Invalid Aggregator" + }, + { + "code": 6051, + "name": "InvalidFeeVault", + "msg": "Invalid Fee Vault" } ] }; @@ -5394,7 +5412,7 @@ export const IDL: Propeller = { "kind": "account", "type": "publicKey", "account": "FeeTracker", - "path": "fee_tracker.payer" + "path": "fee_tracker.fees_recipient" } ] } @@ -6646,11 +6664,11 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -6676,7 +6694,7 @@ export const IDL: Propeller = { } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ @@ -7108,11 +7126,11 @@ export const IDL: Propeller = { "isSigner": false }, { - "name": "to", + "name": "redeemerEscrow", "isMut": true, "isSigner": false, "docs": [ - "owned by redeemer. \"redeemerEscrow\"" + "`to` account in `CompleteNativeWithPayload`" ] }, { @@ -7138,7 +7156,7 @@ export const IDL: Propeller = { } }, { - "name": "feeRecipient", + "name": "feeVault", "isMut": true, "isSigner": false, "docs": [ @@ -8687,7 +8705,7 @@ export const IDL: Propeller = { "type": "u8" }, { - "name": "payer", + "name": "feesRecipient", "type": "publicKey" }, { @@ -8844,6 +8862,10 @@ export const IDL: Propeller = { { "name": "aggregator", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -8988,6 +9010,10 @@ export const IDL: Propeller = { { "name": "marginalPricePoolTokenMint", "type": "publicKey" + }, + { + "name": "maxStaleness", + "type": "i64" } ] } @@ -9550,78 +9576,88 @@ export const IDL: Propeller = { }, { "code": 6035, + "name": "InvalidMarginalPricePool", + "msg": "Invalid Marginal Price Pool" + }, + { + "code": 6036, "name": "InvalidMarginalPricePoolAccounts", "msg": "Invalid Marginal Price Pool Accounts" }, { - "code": 6036, + "code": 6037, "name": "NotPropellerEnabled", "msg": "Propeller Not Enabled in payload" }, { - "code": 6037, + "code": 6038, "name": "InvalidRoutingContractAddress", "msg": "Invalid Routing Contract Address" }, { - "code": 6038, + "code": 6039, "name": "IntegerOverflow", "msg": "Integer Overflow" }, { - "code": 6039, + "code": 6040, "name": "ConversionError", "msg": "Conversion Error" }, { - "code": 6040, + "code": 6041, "name": "UnableToRetrieveSwimUsdMintDecimals", "msg": "Unable to retrieve SwimUSD mint decimals from marginal price pool information" }, { - "code": 6041, + "code": 6042, "name": "InvalidMetapoolTokenMint", "msg": "Invalid Metapool Token Mint. token_mint[0] should == swim_usd_mint" }, { - "code": 6042, + "code": 6043, "name": "UnableToDeserializeTokenAccount", "msg": "Unable to deserialize account info as token account" }, { - "code": 6043, + "code": 6044, "name": "InvalidTokenAccountDataLen", "msg": "Invalid token account data length. != 0 && != TokenAccount::LEN" }, { - "code": 6044, + "code": 6045, "name": "PayerInsufficientFundsForGasKickstart", "msg": "Payer has insufficient funds for gas kickstart" }, { - "code": 6045, + "code": 6046, "name": "IncorrectOwnerForCreateTokenAccount", "msg": "Owner of token account != swimPayload.owner" }, { - "code": 6046, + "code": 6047, "name": "TokenIdMapExists", "msg": "TokenIdMap exists. Please use the correct instruction" }, { - "code": 6047, + "code": 6048, "name": "InvalidTokenIdMapAccountAddress", "msg": "Invalid address for TokenIdMap account" }, { - "code": 6048, + "code": 6049, "name": "InvalidSwimPayloadVersion", "msg": "Invalid Swim Payload version" }, { - "code": 6049, + "code": 6050, "name": "InvalidAggregator", "msg": "Invalid Aggregator" + }, + { + "code": 6051, + "name": "InvalidFeeVault", + "msg": "Invalid Fee Vault" } ] };