Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions crates/lib/src/oracle/jupiter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ impl JupiterPriceOracle {
#[cfg(test)]
mod tests {
use super::*;
use crate::constant::SOL_MINT;
use mockito::{Matcher, Server};

#[tokio::test]
Expand Down Expand Up @@ -264,13 +265,18 @@ mod tests {
}
}"#;
let mut server = Server::new_async().await;

let expected_query =
format!("ids={},{}", SOL_MINT, "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN");

let _m = server
.mock("GET", "/price/v3")
.match_query(Matcher::Any)
.match_query(Matcher::Exact(expected_query))
.with_status(200)
.with_header("content-type", "application/json")
.with_body(mock_response)
.create();
.create()
.expect(1);

let client = Client::new();
// Test without API key - should use lite API
Expand All @@ -283,6 +289,9 @@ mod tests {
assert_eq!(
result.err(),
Some(KoraError::RpcError("No price data from Jupiter".to_string()))
)
);

// Verify the mock was called
_m.assert();
}
}
4 changes: 1 addition & 3 deletions crates/lib/src/transaction/versioned_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,7 @@ impl VersionedTransactionResolved {
.map_err(|e| KoraError::RpcError(format!("Failed to simulate transaction: {e}")))?;

if let Some(err) = simulation_result.value.err {
log::warn!(
"Transaction simulation failed: {err}, continuing without inner instructions",
);
log::warn!("Transaction simulation failed: {err}");
return Err(KoraError::InvalidTransaction(
"Transaction inner instructions fetching failed.".to_string(),
));
Expand Down
7 changes: 7 additions & 0 deletions makefiles/RUST_TESTS.makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ test-regular:
$(call run_integration_phase,1,Regular Tests,$(REGULAR_CONFIG),,--test rpc,)
@$(call stop_solana_validator)

test-token:
$(call print_header,TOKEN TESTS)
@$(call start_solana_validator)
@cargo run -p tests --bin setup_test_env $(QUIET_OUTPUT)
$(call run_integration_phase,1,Tokens Tests,$(REGULAR_CONFIG),,--test tokens,)
@$(call stop_solana_validator)

test-auth:
$(call print_header,AUTHENTICATION TESTS)
@$(call start_solana_validator)
Expand Down
8 changes: 8 additions & 0 deletions tests/src/common/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ pub const SENDER_KEYPAIR_PATH: &str =
pub const USDC_MINT_KEYPAIR_PATH: &str =
concat!(env!("CARGO_MANIFEST_DIR"), "/src/common/local-keys/usdc-mint-local.json");

/// USDC mint 2022 keypair path (local testing only)
pub const USDC_MINT_2022_KEYPAIR_PATH: &str =
concat!(env!("CARGO_MANIFEST_DIR"), "/src/common/local-keys/usdc-mint-2022-local.json");

/// Interest bearing mint keypair path (local testing only)
pub const INTEREST_BEARING_MINT_KEYPAIR_PATH: &str =
concat!(env!("CARGO_MANIFEST_DIR"), "/src/common/local-keys/mint-2022-interest-bearing.json");

/// Second signer keypair path (for multi-signer tests)
pub const SIGNER2_KEYPAIR_PATH: &str =
concat!(env!("CARGO_MANIFEST_DIR"), "/src/common/local-keys/signer2-local.json");
Expand Down
168 changes: 168 additions & 0 deletions tests/src/common/extension_helpers.rs
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you want to add additional tests for other methods than signTransactionIfPaid?

Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use anyhow::Result;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::{
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
};
use spl_token_2022::{
extension::{interest_bearing_mint::instruction::initialize, ExtensionType},
instruction as token_2022_instruction,
state::{Account as Token2022Account, Mint as Token2022Mint},
};
use std::sync::Arc;

use crate::common::USDCMintTestHelper;

/// Helper functions for creating Token 2022 accounts with specific extensions for testing
pub struct ExtensionHelpers;

impl ExtensionHelpers {
/// Create a mint with InterestBearingConfig extension
pub async fn create_mint_with_interest_bearing(
rpc_client: &Arc<RpcClient>,
payer: &Keypair,
mint_keypair: &Keypair,
) -> Result<()> {
if (rpc_client.get_account(&mint_keypair.pubkey()).await).is_ok() {
return Ok(());
}

let decimals = USDCMintTestHelper::get_test_usdc_mint_decimals();

let space = ExtensionType::try_calculate_account_len::<Token2022Mint>(&[
ExtensionType::InterestBearingConfig,
])?;

let rent = rpc_client.get_minimum_balance_for_rent_exemption(space).await?;

let create_account_instruction = solana_sdk::system_instruction::create_account(
&payer.pubkey(),
&mint_keypair.pubkey(),
rent,
space as u64,
&spl_token_2022::id(),
);

let initialize_interest_bearing_instruction =
initialize(&spl_token_2022::id(), &mint_keypair.pubkey(), Some(payer.pubkey()), 10)?;

let initialize_mint_instruction = token_2022_instruction::initialize_mint2(
&spl_token_2022::id(),
&mint_keypair.pubkey(),
&payer.pubkey(),
Some(&payer.pubkey()),
decimals,
)?;

let recent_blockhash = rpc_client.get_latest_blockhash().await?;

let transaction = Transaction::new_signed_with_payer(
&[
create_account_instruction,
initialize_interest_bearing_instruction,
initialize_mint_instruction,
],
Some(&payer.pubkey()),
&[payer, mint_keypair],
recent_blockhash,
);

rpc_client.send_and_confirm_transaction(&transaction).await?;
Ok(())
}

/// Create a manual token account with MemoTransfer extension
pub async fn create_token_account_with_memo_transfer(
rpc_client: &Arc<RpcClient>,
payer: &Keypair,
token_account_keypair: &Keypair,
mint: &Pubkey,
owner: &Keypair,
) -> Result<()> {
if (rpc_client.get_account(&token_account_keypair.pubkey()).await).is_ok() {
return Ok(());
}

// Calculate space for token accounts with MemoTransfer extension
// Also include TransferFeeAmount if the mint has TransferFeeConfig
// (The USDC mint 2022 has TransferFeeConfig, so we need to account for it)
let account_space = ExtensionType::try_calculate_account_len::<Token2022Account>(&[
ExtensionType::MemoTransfer,
ExtensionType::TransferFeeAmount,
])?;
let rent = rpc_client.get_minimum_balance_for_rent_exemption(account_space).await?;

let create_account_instruction = solana_sdk::system_instruction::create_account(
&payer.pubkey(),
&token_account_keypair.pubkey(),
rent,
account_space as u64,
&spl_token_2022::id(),
);

// Initialize MemoTransfer account extension (requires memo for transfers)
let initialize_memo_transfer_instruction =
spl_token_2022::extension::memo_transfer::instruction::enable_required_transfer_memos(
&spl_token_2022::id(),
&token_account_keypair.pubkey(),
&owner.pubkey(),
&[&owner.pubkey()],
)?;

let initialize_account_instruction = token_2022_instruction::initialize_account3(
&spl_token_2022::id(),
&token_account_keypair.pubkey(),
mint,
&owner.pubkey(),
)?;

let recent_blockhash = rpc_client.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[
create_account_instruction,
initialize_account_instruction,
initialize_memo_transfer_instruction,
],
Some(&payer.pubkey()),
&[payer, token_account_keypair, owner],
recent_blockhash,
);

rpc_client.send_and_confirm_transaction(&transaction).await?;
Ok(())
}

pub async fn mint_tokens_to_account(
rpc_client: &Arc<RpcClient>,
payer: &Keypair,
mint: &Pubkey,
token_account: &Pubkey,
mint_authority: &Keypair,
amount: Option<u64>,
) -> Result<()> {
let amount = amount.unwrap_or_else(|| {
1_000_000 * 10_u64.pow(USDCMintTestHelper::get_test_usdc_mint_decimals() as u32)
});

let instruction = token_2022_instruction::mint_to(
&spl_token_2022::id(),
mint,
token_account,
&mint_authority.pubkey(),
&[],
amount,
)?;

let recent_blockhash = rpc_client.get_latest_blockhash().await?;
let transaction = Transaction::new_signed_with_payer(
&[instruction],
Some(&payer.pubkey()),
&[payer, mint_authority],
recent_blockhash,
);

rpc_client.send_and_confirm_transaction(&transaction).await?;
Ok(())
}
}
13 changes: 13 additions & 0 deletions tests/src/common/fixtures/kora-test.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,28 @@ allowed_programs = [
]
allowed_tokens = [
"9BgeTKqmFsPVnfYscfM6NvsgmZxei7XfdciShQ6D3bxJ", # Test USDC mint for local testing
"95kSi2m5MDiKAs8bucgzengMTP5M5FiQnJps9duYcmfG", # Test USDC mint 2022 for local testing
"AtCGtK6HPgdpk2c2LcpZimbH8dtHXYmJdoKsawWNCh2m", # Test Interest Bearing mint 2022 for local testing
]
allowed_spl_paid_tokens = [
"9BgeTKqmFsPVnfYscfM6NvsgmZxei7XfdciShQ6D3bxJ", # Test USDC mint for local testing
"95kSi2m5MDiKAs8bucgzengMTP5M5FiQnJps9duYcmfG", # Test USDC mint 2022 for local testing
"AtCGtK6HPgdpk2c2LcpZimbH8dtHXYmJdoKsawWNCh2m", # Test Interest Bearing mint 2022 for local testing
]

disallowed_accounts = [
"hndXZGK45hCxfBYvxejAXzCfCujoqkNf7rk4sTB8pek", # Test disallowed account for lookup table
]

# Block specific extensions for testing (only affects extension test accounts)
[validation.token_2022]
blocked_mint_extensions = [
"interest_bearing_config", # Block mints with interest bearing config for extension testing
]
blocked_account_extensions = [
"memo_transfer", # Block token accounts with MemoTransfer extension for extension testing
]

[validation.fee_payer_policy]
allow_sol_transfers = true
allow_spl_transfers = true
Expand Down
39 changes: 39 additions & 0 deletions tests/src/common/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ pub struct TestAccountInfo {
pub sender_token_account: Pubkey,
pub recipient_token_account: Pubkey,
pub fee_payer_token_account: Pubkey,
// Token 2022 fields
pub usdc_mint_2022_pubkey: Pubkey,
pub sender_token_2022_account: Pubkey,
pub recipient_token_2022_account: Pubkey,
pub fee_payer_token_2022_account: Pubkey,
}

/// Helper function to parse a private key string in multiple formats.
Expand Down Expand Up @@ -93,3 +98,37 @@ impl USDCMintTestHelper {
.unwrap_or(TEST_USDC_MINT_DECIMALS)
}
}

pub struct USDCMint2022TestHelper;

impl USDCMint2022TestHelper {
pub fn get_test_usdc_mint_2022_keypair() -> Keypair {
dotenv::dotenv().ok();
let mint_keypair = match std::env::var("TEST_USDC_MINT_2022_KEYPAIR") {
Ok(key) => key,
Err(_) => std::fs::read_to_string(USDC_MINT_2022_KEYPAIR_PATH)
.expect("Failed to read USDC mint 2022 private key file"),
};
parse_private_key_string(&mint_keypair)
.expect("Failed to parse test USDC mint 2022 private key")
}

pub fn get_test_usdc_mint_2022_pubkey() -> Pubkey {
Self::get_test_usdc_mint_2022_keypair().pubkey()
}

pub fn get_test_interest_bearing_mint_keypair() -> Keypair {
dotenv::dotenv().ok();
let mint_keypair = match std::env::var("TEST_INTEREST_BEARING_MINT_KEYPAIR") {
Ok(key) => key,
Err(_) => std::fs::read_to_string(INTEREST_BEARING_MINT_KEYPAIR_PATH)
.expect("Failed to read interest bearing mint private key file"),
};
parse_private_key_string(&mint_keypair)
.expect("Failed to parse test interest bearing mint private key")
}

pub fn get_test_interest_bearing_mint_pubkey() -> Pubkey {
Self::get_test_interest_bearing_mint_keypair().pubkey()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[78,63,83,18,2,4,230,26,94,142,178,78,179,23,4,195,116,211,25,214,9,31,129,226,122,194,246,64,215,104,16,86,146,214,141,14,252,8,190,24,86,19,255,157,82,235,44,165,148,6,30,152,175,62,180,176,51,161,19,28,22,83,141,46]
1 change: 1 addition & 0 deletions tests/src/common/local-keys/usdc-mint-2022-local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[88,238,182,173,120,156,180,249,148,252,218,221,159,84,55,159,191,243,121,129,173,14,168,89,104,182,108,47,177,47,225,222,120,20,240,168,32,222,220,197,136,143,185,82,29,124,70,158,138,58,140,15,125,198,38,156,255,86,143,167,1,165,96,195]
4 changes: 3 additions & 1 deletion tests/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod assertions;
pub mod auth_helpers;
pub mod client;
pub mod constants;
pub mod extension_helpers;
pub mod helpers;
pub mod lookup_tables;
pub mod setup;
Expand All @@ -11,6 +12,7 @@ pub mod transaction;
// Re-export commonly used items for convenience
pub use assertions::{JsonRpcErrorCodes, RpcAssertions, TransactionAssertions};
pub use client::{TestClient, TestContext};
pub use extension_helpers::ExtensionHelpers;
pub use transaction::{TransactionBuilder, TransactionVersion};

// Re-export auth helpers (excluding constants that are in constants.rs)
Expand All @@ -21,7 +23,7 @@ pub use auth_helpers::{
// Re-export helpers (excluding constants that are in constants.rs)
pub use helpers::{
parse_private_key_string, FeePayerTestHelper, RecipientTestHelper, SenderTestHelper,
TestAccountInfo, USDCMintTestHelper,
TestAccountInfo, USDCMint2022TestHelper, USDCMintTestHelper,
};
pub use lookup_tables::{LookupTableHelper, LookupTablesAddresses};

Expand Down
Loading
Loading