mod program_test; use { mpl_token_metadata::accounts::{MasterEdition, Metadata}, program_test::StablebondProgramTest, solana_program_test::{tokio, ProgramTestContext}, solana_sdk::{ signature::{Keypair, Signer}, transaction::Transaction, }, spl_associated_token_account::get_associated_token_address, stablebond_sdk::{ find_bond_pda, find_config_pda, find_delegate_pda, find_payment_feed_pda, instructions::{ InitializeBond, InitializeBondInstructionArgs, InitializeConfig, InitializeConfigInstructionArgs, InitializeDelegate, InitializePaymentFeed, InitializePaymentFeedInstructionArgs, UpdateBondPaymentFeed, }, types::{OracleSetup, PaymentFeedInfo, PaymentFeedType}, }, }; struct ExecuteTestContext { context: ProgramTestContext, admin: Keypair, bond_mint: Keypair, } async fn setup() -> ExecuteTestContext { let pt = StablebondProgramTest::start_new().await; let admin = pt.admin; let bond_mint = pt.bond_mints[0].insecure_clone(); let nft_collection_mint = pt.nft_collection_mint; let usdc_mint = pt.mints[0].payment_mint; let initialize_config_ix_args = InitializeConfigInstructionArgs { oracle: OracleSetup::Stub, usdc_mxn_payment_feed: pt.usdc_mxn_payment_feed.clone(), usdc_usd_payment_feed: pt.usdc_usd_payment_feed, }; let initialize_config_ix = InitializeConfig { admin_wallet: admin.pubkey(), config_account: find_config_pda().0, config_token_account: get_associated_token_address( &find_config_pda().0, &nft_collection_mint.pubkey(), ), mint_account: nft_collection_mint.pubkey(), payment_mint_account: pt.usdc_mxn_payment_feed.payment_mint, metadata_account: Metadata::find_pda(&nft_collection_mint.pubkey()).0, master_edition_account: MasterEdition::find_pda(&nft_collection_mint.pubkey()).0, usdc_mxn_feed_account: find_payment_feed_pda(PaymentFeedType::UsdcMxn).0, usdc_usd_feed_account: find_payment_feed_pda(PaymentFeedType::UsdcUsd).0, token_program: spl_token::id(), associated_token_program: spl_associated_token_account::id(), metadata_program: mpl_token_metadata::ID, system_program: solana_program::system_program::id(), } .instruction(initialize_config_ix_args); let initialize_delegate_ix = InitializeDelegate { config_account: find_config_pda().0, admin_wallet: admin.pubkey(), delegate_wallet: admin.pubkey(), delegate_account: find_delegate_pda(admin.pubkey()).0, system_program: solana_program::system_program::id(), } .instruction(); let ixs = &[initialize_config_ix, initialize_delegate_ix]; let mut context = pt.context; let transaction = Transaction::new_signed_with_payer( ixs, Some(&admin.pubkey()), &[&admin, &nft_collection_mint], context.last_blockhash, ); assert!(context .banks_client .process_transaction(transaction) .await .is_ok()); let payment_feed_info = PaymentFeedInfo { payment_feed_type: PaymentFeedType::SwitchboardUsdcMxn, payment_mint: usdc_mint, base_price_feed: Keypair::new().pubkey(), quote_price_feed: Keypair::new().pubkey(), }; let initialize_payment_feed_args = InitializePaymentFeedInstructionArgs { payment_feed_info }; let initialize_payment_feed_ix = InitializePaymentFeed { delegate_wallet: admin.pubkey(), delegate_account: find_delegate_pda(admin.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::SwitchboardUsdcMxn).0, payment_mint_account: usdc_mint, system_program: solana_program::system_program::id(), } .instruction(initialize_payment_feed_args); let transaction: Transaction = Transaction::new_signed_with_payer( &[initialize_payment_feed_ix], Some(&admin.pubkey()), &[&admin], context.last_blockhash, ); assert!(context .banks_client .process_transaction(transaction) .await .is_ok()); let payment_feed_info = PaymentFeedInfo { payment_feed_type: PaymentFeedType::SwitchboardUsdcUsd, payment_mint: usdc_mint, base_price_feed: Keypair::new().pubkey(), quote_price_feed: Keypair::new().pubkey(), }; let initialize_payment_feed_args = InitializePaymentFeedInstructionArgs { payment_feed_info }; let initialize_payment_feed_ix = InitializePaymentFeed { delegate_wallet: admin.pubkey(), delegate_account: find_delegate_pda(admin.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::SwitchboardUsdcUsd).0, payment_mint_account: usdc_mint, system_program: solana_program::system_program::id(), } .instruction(initialize_payment_feed_args); let transaction: Transaction = Transaction::new_signed_with_payer( &[initialize_payment_feed_ix], Some(&admin.pubkey()), &[&admin], context.last_blockhash, ); assert!(context .banks_client .process_transaction(transaction) .await .is_ok()); ExecuteTestContext { context, admin, bond_mint, } } #[tokio::test] async fn update_bond_payment_feed_as_delegate_pyth_usd_to_switchboard_usd() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::UsdcUsd, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::SwitchboardUsdcUsd).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); } #[tokio::test] async fn update_bond_payment_feed_as_delegate_pyth_mxn_to_switchboard_mxn() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::UsdcMxn, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::SwitchboardUsdcMxn).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); } #[tokio::test] async fn update_bond_payment_feed_as_delegate_stub_to_switchboard_usd() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::Stub, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::SwitchboardUsdcUsd).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); } #[tokio::test] async fn fail_update_bond_payment_feed_as_delegate_pyth_usd_to_switchboard_mxn() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::UsdcUsd, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::SwitchboardUsdcMxn).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_err()); } #[tokio::test] async fn fail_update_bond_payment_feed_as_delegate_pyth_mxn_to_switchboard_usd() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::UsdcMxn, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::SwitchboardUsdcUsd).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_err()); } #[tokio::test] async fn fail_update_bond_payment_feed_as_delegate_switchboard_usd_to_pyth_mxn() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::SwitchboardUsdcUsd, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::UsdcMxn).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_err()); } #[tokio::test] async fn fail_update_bond_payment_feed_as_delegate_switchboard_mxn_to_pyth_usd() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::SwitchboardUsdcMxn, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::UsdcUsd).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_err()); } #[tokio::test] async fn fail_update_bond_payment_feed_as_delegate_pyth_mxn_to_stub() { let mut et = setup().await; let initialize_bond_ix_args = InitializeBondInstructionArgs { name: "Test Mint".to_string(), symbol: "TM".to_string(), uri: "https://test.com".to_string(), payment_feed_type: PaymentFeedType::UsdcMxn, cutoff_in_seconds: 60 * 60 * 24, }; let initialize_bond_ix = InitializeBond { delegate_account: find_delegate_pda(et.admin.pubkey()).0, delegate_wallet: et.admin.pubkey(), bond_account: find_bond_pda(et.bond_mint.pubkey()).0, mint_account: et.bond_mint.pubkey(), metadata_account: Metadata::find_pda(&et.bond_mint.pubkey()).0, token2022_program: spl_token_2022::id(), metadata_program: mpl_token_metadata::ID, sysvar_instructions: solana_program::sysvar::instructions::id(), system_program: solana_program::system_program::id(), } .instruction(initialize_bond_ix_args); let transaction = Transaction::new_signed_with_payer( &[initialize_bond_ix], Some(&et.admin.pubkey()), &[&et.admin, &et.bond_mint], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_ok()); let update_bond_payment_feed_ix = UpdateBondPaymentFeed { delegate_wallet: et.admin.pubkey(), delegate_account: find_delegate_pda(et.admin.pubkey()).0, bond_account: find_bond_pda(et.bond_mint.pubkey()).0, payment_feed_account: find_payment_feed_pda(PaymentFeedType::Stub).0, system_program: solana_program::system_program::id(), } .instruction(); let transaction = Transaction::new_signed_with_payer( &[update_bond_payment_feed_ix], Some(&et.admin.pubkey()), &[&et.admin], et.context.last_blockhash, ); assert!(et .context .banks_client .process_transaction(transaction) .await .is_err()); }