Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rust-sdk position test #601

Merged
merged 17 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from 15 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
98 changes: 98 additions & 0 deletions rust-sdk/whirlpool/src/position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use orca_whirlpools_core::POSITION_BUNDLE_SIZE;
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signer::Signer;

use crate::{get_token_accounts_for_owner, ParsedTokenAccount};

Expand Down Expand Up @@ -295,3 +296,100 @@ pub async fn fetch_positions_in_whirlpool(
let filters = vec![PositionFilter::Whirlpool(whirlpool)];
fetch_all_position_with_filter(rpc, filters).await
}

#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{
setup_ata_with_amount, setup_mint_with_decimals, setup_position, setup_position_bundle,
setup_te_position, setup_whirlpool, RpcContext,
};
use serial_test::serial;
use solana_program_test::tokio;
use std::error::Error;

const DEFAULT_TICK_RANGE: (i32, i32) = (-100, 100);

#[tokio::test]
#[serial]
#[ignore = "Skipped until solana-bankrun supports gpa"]
async fn test_fetch_positions_for_owner_no_positions() -> Result<(), Box<dyn Error>> {
let ctx = RpcContext::new().await;
let owner = ctx.signer.pubkey();
let positions = fetch_positions_for_owner(&ctx.rpc, owner).await?;
assert!(
positions.is_empty(),
"No positions should exist for a new owner"
);
Ok(())
}

#[tokio::test]
#[serial]
#[ignore = "Skipped until solana-bankrun supports gpa"]
async fn test_fetch_positions_for_owner_with_position() -> Result<(), Box<dyn Error>> {
let ctx = RpcContext::new().await;
let mint_a = setup_mint_with_decimals(&ctx, 9).await?;
let mint_b = setup_mint_with_decimals(&ctx, 9).await?;
setup_ata_with_amount(&ctx, mint_a, 1_000_000_000).await?;
setup_ata_with_amount(&ctx, mint_b, 1_000_000_000).await?;

let whirlpool = setup_whirlpool(&ctx, mint_a, mint_b, 64).await?;
let normal_position_pubkey = setup_position(whirlpool).await?;

// 1) Add a te_position (uses token-2022)
let te_position_pubkey = setup_te_position(whirlpool).await?;

// 2) Add a position bundle, optionally with multiple bundled positions
let position_bundle_pubkey = setup_position_bundle(whirlpool, Some(vec![(), ()])).await?;

let owner = ctx.signer.pubkey();
let positions = fetch_positions_for_owner(&ctx.rpc, owner).await?;

// Expect at least 3: normal, te_position, and a bundle
assert!(
positions.len() >= 3,
"Did not find all positions for the owner (expected normal, te_position, bundle)"
);

// Existing checks remain...
match &positions[0] {
PositionOrBundle::Position(pos) => {
assert_eq!(pos.address, normal_position_pubkey);
}
_ => panic!("Expected a single position, but found a bundle!"),
}

Ok(())
}

#[tokio::test]
#[serial]
#[ignore = "Skipped until solana-bankrun supports gpa"]
async fn test_fetch_positions_in_whirlpool() -> Result<(), Box<dyn Error>> {
let ctx = RpcContext::new().await;
let mint_a = setup_mint_with_decimals(&ctx, 9).await?;
let mint_b = setup_mint_with_decimals(&ctx, 9).await?;
setup_ata_with_amount(&ctx, mint_a, 1_000_000_000).await?;
setup_ata_with_amount(&ctx, mint_b, 1_000_000_000).await?;

let whirlpool = setup_whirlpool(&ctx, mint_a, mint_b, 64).await?;
let _normal_position_pubkey = setup_position(whirlpool).await?;

// 1) te_position
let _te_position_pubkey = setup_te_position(whirlpool).await?;

// 2) position bundle
let _position_bundle_pubkey = setup_position_bundle(whirlpool, Some(vec![(), ()])).await?;

let positions = fetch_positions_in_whirlpool(&ctx.rpc, whirlpool).await?;

// Expect at least 3: normal + te_position + bundle
assert!(
positions.len() >= 3,
"Should find multiple positions in this whirlpool, including te_position & bundle"
);

Ok(())
}
}
154 changes: 143 additions & 11 deletions rust-sdk/whirlpool/src/tests/program.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
use solana_sdk::{pubkey::Pubkey, signer::Signer, system_program};
use std::error::Error;

use orca_whirlpools_client::{
get_fee_tier_address, get_token_badge_address, get_whirlpool_address, InitializePoolV2,
InitializePoolV2InstructionArgs,
get_bundled_position_address, get_fee_tier_address, get_position_address,
get_position_bundle_address, get_token_badge_address, get_whirlpool_address, InitializePoolV2,
InitializePoolV2InstructionArgs, InitializePositionBundle, OpenBundledPosition,
OpenBundledPositionInstructionArgs, OpenPosition, OpenPositionInstructionArgs,
};
use orca_whirlpools_core::tick_index_to_sqrt_price;
use solana_program::program_pack::Pack;
use solana_program::sysvar::rent::ID as RENT_PROGRAM_ID;
use solana_sdk::{
pubkey::Pubkey,
signer::{keypair::Keypair, Signer},
system_instruction, system_program,
};
use spl_associated_token_account::{
get_associated_token_address, get_associated_token_address_with_program_id,
instruction::create_associated_token_account,
};
use spl_token::{state::Mint, ID as TOKEN_PROGRAM_ID};
use spl_token_2022::{state::Mint as Token2022Mint, ID as TOKEN_2022_PROGRAM_ID};
use std::error::Error;

use crate::WHIRLPOOLS_CONFIG_ADDRESS;

use super::rpc::RpcContext;

use crate::tests::token::{setup_ata, setup_mint_with_decimals};
use crate::tests::token_extensions::setup_mint_te;

pub async fn setup_whirlpool(
ctx: &RpcContext,
token_a: Pubkey,
Expand Down Expand Up @@ -60,14 +75,131 @@ pub async fn setup_whirlpool(
Ok(whirlpool)
}

pub async fn setup_position(_whirlpool: Pubkey) -> Result<Pubkey, Box<dyn Error>> {
todo!()
pub async fn setup_position(whirlpool: Pubkey) -> Result<Pubkey, Box<dyn Error>> {
let ctx = RpcContext::new().await;

let position_mint = ctx.get_next_keypair();

let (position_pubkey, position_bump) = get_position_address(&position_mint.pubkey())?;

let position_token_account = get_associated_token_address_with_program_id(
&ctx.signer.pubkey(),
&position_mint.pubkey(),
&TOKEN_PROGRAM_ID,
);

let open_position_ix = OpenPosition {
funder: ctx.signer.pubkey(),
owner: ctx.signer.pubkey(),
position: position_pubkey,
position_mint: position_mint.pubkey(),
position_token_account,
whirlpool,
token_program: TOKEN_PROGRAM_ID,
system_program: system_program::id(),
associated_token_program: spl_associated_token_account::id(),
rent: RENT_PROGRAM_ID,
}
.instruction(OpenPositionInstructionArgs {
tick_lower_index: -128,
tick_upper_index: 128,
position_bump,
});

ctx.send_transaction_with_signers(vec![open_position_ix], vec![&position_mint])
.await?;

Ok(position_pubkey)
}

pub async fn setup_te_position(_whirlpool: Pubkey) -> Result<Pubkey, Box<dyn Error>> {
todo!()
pub async fn setup_te_position(whirlpool: Pubkey) -> Result<Pubkey, Box<dyn Error>> {
let ctx = RpcContext::new().await;

let te_position_mint = ctx.get_next_keypair();

let (position_pubkey, position_bump) = get_position_address(&te_position_mint.pubkey())?;

let position_token_account = get_associated_token_address_with_program_id(
&ctx.signer.pubkey(),
Copy link
Member

Choose a reason for hiding this comment

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

There is one more for position bundle

Copy link
Contributor Author

Choose a reason for hiding this comment

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

my bad, updated

&te_position_mint.pubkey(),
&TOKEN_2022_PROGRAM_ID,
);

let open_position_ix = OpenPosition {
funder: ctx.signer.pubkey(),
owner: ctx.signer.pubkey(),
position: position_pubkey,
position_mint: te_position_mint.pubkey(),
position_token_account: Pubkey::default(),
whirlpool,
token_program: TOKEN_2022_PROGRAM_ID,
system_program: system_program::id(),
associated_token_program: spl_associated_token_account::id(),
rent: RENT_PROGRAM_ID,
}
.instruction(OpenPositionInstructionArgs {
tick_lower_index: -128,
tick_upper_index: 128,
position_bump,
});

ctx.send_transaction_with_signers(vec![open_position_ix], vec![&te_position_mint])
.await?;

Ok(position_pubkey)
}

pub async fn setup_position_bundle(_whirlpool: Pubkey) -> Result<Pubkey, Box<dyn Error>> {
todo!()
pub async fn setup_position_bundle(
whirlpool: Pubkey,
bundle_positions: Option<Vec<()>>,
) -> Result<Pubkey, Box<dyn Error>> {
let ctx = RpcContext::new().await;

let position_bundle_mint = ctx.get_next_keypair();
let (position_bundle_address, _bundle_bump) =
get_position_bundle_address(&position_bundle_mint.pubkey())?;

let open_bundle_ix = InitializePositionBundle {
funder: ctx.signer.pubkey(),
position_bundle: position_bundle_address,
position_bundle_mint: position_bundle_mint.pubkey(),
position_bundle_token_account: Pubkey::default(),
position_bundle_owner: ctx.signer.pubkey(),
token_program: TOKEN_PROGRAM_ID,
system_program: system_program::id(),
associated_token_program: spl_associated_token_account::id(),
rent: RENT_PROGRAM_ID,
}
.instruction();

ctx.send_transaction_with_signers(vec![open_bundle_ix], vec![&position_bundle_mint])
.await?;

if let Some(positions) = bundle_positions {
for (i, _) in positions.iter().enumerate() {
let bundle_index = i as u16;
let (bundled_position_address, _) =
get_bundled_position_address(&position_bundle_mint.pubkey(), bundle_index as u8)?;

let open_bundled_ix = OpenBundledPosition {
funder: ctx.signer.pubkey(),
bundled_position: bundled_position_address,
position_bundle: position_bundle_address,
position_bundle_authority: ctx.signer.pubkey(),
position_bundle_token_account: Pubkey::default(),
whirlpool,
system_program: system_program::id(),
rent: RENT_PROGRAM_ID,
}
.instruction(OpenBundledPositionInstructionArgs {
tick_lower_index: -128,
tick_upper_index: 128,
bundle_index,
});

ctx.send_transaction(vec![open_bundled_ix]).await?;
}
}

Ok(position_bundle_address)
}
Loading