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

SparseSwap #159

Merged
merged 25 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
38a58d1
draft for swap ix
yugure-orca Jun 26, 2024
197d76f
use vec
yugure-orca Jun 26, 2024
dc268e9
add SparseSwapTickSequenceBuilder
yugure-orca Jun 28, 2024
ec4efb7
use ref
yugure-orca Jun 28, 2024
65ff33b
v2 support, add errors
yugure-orca Jun 28, 2024
d60e221
refactor: use named field
yugure-orca Jun 28, 2024
9a12165
update two_hop_swap v1/v2 to support ss
yugure-orca Jun 28, 2024
98375a1
add cargo test test cases
yugure-orca Jul 1, 2024
a00469a
add integration test (partial)
yugure-orca Jul 2, 2024
80a3e32
add ProxiedTickArray to reduce heap usage
yugure-orca Jul 2, 2024
6566271
fix testcases
yugure-orca Jul 2, 2024
3c32a22
refactor s/additional/supplemental
yugure-orca Jul 2, 2024
2290612
add twoHopSwap test b to a
yugure-orca Jul 3, 2024
fa5a892
add twoHopSwap test a to b
yugure-orca Jul 3, 2024
b377176
update client-side remaining accounts handling
yugure-orca Jul 3, 2024
67b81d5
add supplemental tick arrays test cases
yugure-orca Jul 3, 2024
a462f70
add supplemental tick array test cases (2)
yugure-orca Jul 3, 2024
dc03ca3
fix existing test cases
yugure-orca Jul 4, 2024
94df44d
address review comment (add comments, small refactor)
yugure-orca Jul 5, 2024
d7afcb3
address review comments (refactor with TickArrayType trait)
yugure-orca Jul 5, 2024
98e8406
fix testcases on swap
yugure-orca Jul 5, 2024
af3aede
Merge branch 'main' into yugure/sparse-swap
yugure-orca Jul 11, 2024
3f30966
adjust error code (merge introduce 1 new error code)
yugure-orca Jul 11, 2024
bcc061a
SparseSwap (SDK) (#162)
yugure-orca Aug 1, 2024
a8cd8fe
Merge branch 'main' into yugure/sparse-swap
yugure-orca Aug 1, 2024
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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions programs/whirlpool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ solana-program = "1.17"
thiserror = "1.0"
uint = {version = "0.9.1", default-features = false}
borsh09 = {package = "borsh", version = "0.9.1"}
arrayref = "0.3.7"
bytemuck = "1.14.3"
solana-security-txt = { version = "=1.1.1" }

[dev-dependencies]
Expand Down
5 changes: 5 additions & 0 deletions programs/whirlpool/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ pub enum ErrorCode {

#[msg("Same accounts type is provided more than once")]
RemainingAccountsDuplicatedAccountsType, // 0x17a5 (6053)

#[msg("Too many supplemental tick arrays provided")]
TooManySupplementalTickArrays, // 0x17a6 (6054)
#[msg("TickArray account for different whirlpool provided")]
DifferentWhirlpoolTickArrayAccount, // 0x17a7 (6055)
}

impl From<TryFromIntError> for ErrorCode {
Expand Down
36 changes: 23 additions & 13 deletions programs/whirlpool/src/instructions/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use anchor_spl::token::{self, Token, TokenAccount};
use crate::{
errors::ErrorCode,
manager::swap_manager::*,
state::{TickArray, Whirlpool},
util::{to_timestamp_u64, update_and_swap_whirlpool, SwapTickSequence},
state::Whirlpool,
util::{to_timestamp_u64, update_and_swap_whirlpool, SparseSwapTickSequenceBuilder},
};

#[derive(Accounts)]
Expand All @@ -28,14 +28,17 @@ pub struct Swap<'info> {
#[account(mut, address = whirlpool.token_vault_b)]
pub token_vault_b: Box<Account<'info, TokenAccount>>,

#[account(mut, has_one = whirlpool)]
pub tick_array_0: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_0: UncheckedAccount<'info>,

#[account(mut, has_one = whirlpool)]
pub tick_array_1: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_1: UncheckedAccount<'info>,

#[account(mut, has_one = whirlpool)]
pub tick_array_2: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_2: UncheckedAccount<'info>,

#[account(seeds = [b"oracle", whirlpool.key().as_ref()],bump)]
/// CHECK: Oracle is currently unused and will be enabled on subsequent updates
Expand All @@ -54,12 +57,19 @@ pub fn handler(
let clock = Clock::get()?;
// Update the global reward growth which increases as a function of time.
let timestamp = to_timestamp_u64(clock.unix_timestamp)?;
let mut swap_tick_sequence = SwapTickSequence::new(
ctx.accounts.tick_array_0.load_mut().unwrap(),
ctx.accounts.tick_array_1.load_mut().ok(),
ctx.accounts.tick_array_2.load_mut().ok(),
);

let builder = SparseSwapTickSequenceBuilder::try_from(
whirlpool,
a_to_b,
vec![
ctx.accounts.tick_array_0.to_account_info(),
ctx.accounts.tick_array_1.to_account_info(),
ctx.accounts.tick_array_2.to_account_info(),
],
None,
)?;
let mut swap_tick_sequence = builder.build()?;

let swap_update = swap(
&whirlpool,
&mut swap_tick_sequence,
Expand Down
68 changes: 43 additions & 25 deletions programs/whirlpool/src/instructions/two_hop_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use anchor_spl::token::{self, Token, TokenAccount};
use crate::{
errors::ErrorCode,
manager::swap_manager::*,
state::{TickArray, Whirlpool},
util::{to_timestamp_u64, update_and_swap_whirlpool, SwapTickSequence},
state::Whirlpool,
util::{to_timestamp_u64, update_and_swap_whirlpool, SparseSwapTickSequenceBuilder},
};

#[derive(Accounts)]
Expand Down Expand Up @@ -41,23 +41,29 @@ pub struct TwoHopSwap<'info> {
#[account(mut, address = whirlpool_two.token_vault_b)]
pub token_vault_two_b: Box<Account<'info, TokenAccount>>,

#[account(mut, constraint = tick_array_one_0.load()?.whirlpool == whirlpool_one.key())]
pub tick_array_one_0: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_one_0: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_one_1.load()?.whirlpool == whirlpool_one.key())]
pub tick_array_one_1: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_one_1: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_one_2.load()?.whirlpool == whirlpool_one.key())]
pub tick_array_one_2: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_one_2: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_two_0.load()?.whirlpool == whirlpool_two.key())]
pub tick_array_two_0: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_two_0: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_two_1.load()?.whirlpool == whirlpool_two.key())]
pub tick_array_two_1: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_two_1: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_two_2.load()?.whirlpool == whirlpool_two.key())]
pub tick_array_two_2: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_two_2: UncheckedAccount<'info>,

#[account(seeds = [b"oracle", whirlpool_one.key().as_ref()],bump)]
/// CHECK: Oracle is currently unused and will be enabled on subsequent updates
Expand Down Expand Up @@ -105,17 +111,29 @@ pub fn handler(
return Err(ErrorCode::InvalidIntermediaryMint.into());
}

let mut swap_tick_sequence_one = SwapTickSequence::new(
ctx.accounts.tick_array_one_0.load_mut().unwrap(),
ctx.accounts.tick_array_one_1.load_mut().ok(),
ctx.accounts.tick_array_one_2.load_mut().ok(),
);

let mut swap_tick_sequence_two = SwapTickSequence::new(
ctx.accounts.tick_array_two_0.load_mut().unwrap(),
ctx.accounts.tick_array_two_1.load_mut().ok(),
ctx.accounts.tick_array_two_2.load_mut().ok(),
);
let builder_one = SparseSwapTickSequenceBuilder::try_from(
whirlpool_one,
a_to_b_one,
vec![
ctx.accounts.tick_array_one_0.to_account_info(),
ctx.accounts.tick_array_one_1.to_account_info(),
ctx.accounts.tick_array_one_2.to_account_info(),
],
None,
)?;
let mut swap_tick_sequence_one = builder_one.build()?;

let builder_two = SparseSwapTickSequenceBuilder::try_from(
whirlpool_two,
a_to_b_two,
vec![
ctx.accounts.tick_array_two_0.to_account_info(),
ctx.accounts.tick_array_two_1.to_account_info(),
ctx.accounts.tick_array_two_2.to_account_info(),
],
None,
)?;
let mut swap_tick_sequence_two = builder_two.build()?;

// TODO: WLOG, we could extend this to N-swaps, but the account inputs to the instruction would
// need to be jankier and we may need to programatically map/verify rather than using anchor constraints
Expand Down
37 changes: 24 additions & 13 deletions programs/whirlpool/src/instructions/v2/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::util::{calculate_transfer_fee_excluded_amount, calculate_transfer_fee
use crate::{
errors::ErrorCode,
manager::swap_manager::*,
state::{TickArray, Whirlpool},
util::{to_timestamp_u64, v2::update_and_swap_whirlpool_v2, SwapTickSequence},
state::Whirlpool,
util::{to_timestamp_u64, v2::update_and_swap_whirlpool_v2, SwapTickSequence, SparseSwapTickSequenceBuilder},
constants::transfer_memo,
};

Expand Down Expand Up @@ -40,14 +40,17 @@ pub struct SwapV2<'info> {
#[account(mut, address = whirlpool.token_vault_b)]
pub token_vault_b: Box<InterfaceAccount<'info, TokenAccount>>,

#[account(mut, has_one = whirlpool)]
pub tick_array_0: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_0: UncheckedAccount<'info>,

#[account(mut, has_one = whirlpool)]
pub tick_array_1: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_1: UncheckedAccount<'info>,

#[account(mut, has_one = whirlpool)]
pub tick_array_2: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_2: UncheckedAccount<'info>,

#[account(mut, seeds = [b"oracle", whirlpool.key().as_ref()], bump)]
/// CHECK: Oracle is currently unused and will be enabled on subsequent updates
Expand All @@ -56,6 +59,7 @@ pub struct SwapV2<'info> {
// remaining accounts
// - accounts for transfer hook program of token_mint_a
// - accounts for transfer hook program of token_mint_b
// - supplemental TickArray accounts
}

pub fn handler<'a, 'b, 'c, 'info>(
Expand All @@ -79,14 +83,21 @@ pub fn handler<'a, 'b, 'c, 'info>(
&[
AccountsType::TransferHookA,
AccountsType::TransferHookB,
AccountsType::SupplementalTickArrays,
],
)?;

let mut swap_tick_sequence = SwapTickSequence::new(
ctx.accounts.tick_array_0.load_mut().unwrap(),
ctx.accounts.tick_array_1.load_mut().ok(),
ctx.accounts.tick_array_2.load_mut().ok(),
);
let builder = SparseSwapTickSequenceBuilder::try_from(
whirlpool,
a_to_b,
vec![
ctx.accounts.tick_array_0.to_account_info(),
ctx.accounts.tick_array_1.to_account_info(),
ctx.accounts.tick_array_2.to_account_info(),
],
remaining_accounts.supplemental_tick_arrays,
)?;
let mut swap_tick_sequence = builder.build()?;

let swap_update = swap_with_transfer_fee_extension(
&whirlpool,
Expand Down
72 changes: 47 additions & 25 deletions programs/whirlpool/src/instructions/v2/two_hop_swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::swap_with_transfer_fee_extension;
use crate::util::{calculate_transfer_fee_excluded_amount, parse_remaining_accounts, update_and_two_hop_swap_whirlpool_v2, AccountsType, RemainingAccountsInfo};
use crate::{
errors::ErrorCode,
state::{TickArray, Whirlpool},
util::{to_timestamp_u64, SwapTickSequence},
state::Whirlpool,
util::{to_timestamp_u64, SparseSwapTickSequenceBuilder},
constants::transfer_memo,
};

Expand Down Expand Up @@ -55,23 +55,29 @@ pub struct TwoHopSwapV2<'info> {

pub token_authority: Signer<'info>,

#[account(mut, constraint = tick_array_one_0.load()?.whirlpool == whirlpool_one.key())]
pub tick_array_one_0: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_one_0: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_one_1.load()?.whirlpool == whirlpool_one.key())]
pub tick_array_one_1: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_one_1: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_one_2.load()?.whirlpool == whirlpool_one.key())]
pub tick_array_one_2: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_one_2: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_two_0.load()?.whirlpool == whirlpool_two.key())]
pub tick_array_two_0: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_two_0: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_two_1.load()?.whirlpool == whirlpool_two.key())]
pub tick_array_two_1: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_two_1: UncheckedAccount<'info>,

#[account(mut, constraint = tick_array_two_2.load()?.whirlpool == whirlpool_two.key())]
pub tick_array_two_2: AccountLoader<'info, TickArray>,
#[account(mut)]
/// CHECK: checked in the handler
pub tick_array_two_2: UncheckedAccount<'info>,

#[account(mut, seeds = [b"oracle", whirlpool_one.key().as_ref()], bump)]
/// CHECK: Oracle is currently unused and will be enabled on subsequent updates
Expand All @@ -87,6 +93,8 @@ pub struct TwoHopSwapV2<'info> {
// - accounts for transfer hook program of token_mint_input
// - accounts for transfer hook program of token_mint_intermediate
// - accounts for transfer hook program of token_mint_output
// - supplemental TickArray accounts for whirlpool_one
// - supplemental TickArray accounts for whirlpool_two
}

pub fn handler<'a, 'b, 'c, 'info>(
Expand Down Expand Up @@ -135,20 +143,34 @@ pub fn handler<'a, 'b, 'c, 'info>(
AccountsType::TransferHookInput,
AccountsType::TransferHookIntermediate,
AccountsType::TransferHookOutput,
AccountsType::SupplementalTickArraysOne,
AccountsType::SupplementalTickArraysTwo,
],
)?;

let mut swap_tick_sequence_one = SwapTickSequence::new(
ctx.accounts.tick_array_one_0.load_mut().unwrap(),
ctx.accounts.tick_array_one_1.load_mut().ok(),
ctx.accounts.tick_array_one_2.load_mut().ok(),
);

let mut swap_tick_sequence_two = SwapTickSequence::new(
ctx.accounts.tick_array_two_0.load_mut().unwrap(),
ctx.accounts.tick_array_two_1.load_mut().ok(),
ctx.accounts.tick_array_two_2.load_mut().ok(),
);
let builder_one = SparseSwapTickSequenceBuilder::try_from(
whirlpool_one,
a_to_b_one,
vec![
ctx.accounts.tick_array_one_0.to_account_info(),
ctx.accounts.tick_array_one_1.to_account_info(),
ctx.accounts.tick_array_one_2.to_account_info(),
],
remaining_accounts.supplemental_tick_arrays_one,
)?;
let mut swap_tick_sequence_one = builder_one.build()?;

let builder_two = SparseSwapTickSequenceBuilder::try_from(
whirlpool_two,
a_to_b_two,
vec![
ctx.accounts.tick_array_two_0.to_account_info(),
ctx.accounts.tick_array_two_1.to_account_info(),
ctx.accounts.tick_array_two_2.to_account_info(),
],
remaining_accounts.supplemental_tick_arrays_two,
)?;
let mut swap_tick_sequence_two = builder_two.build()?;

// TODO: WLOG, we could extend this to N-swaps, but the account inputs to the instruction would
// need to be jankier and we may need to programatically map/verify rather than using anchor constraints
Expand Down
Loading