Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion Cargo.lock

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

4 changes: 2 additions & 2 deletions src/program-rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "onesol-protocol"
version = "0.4.2"
version = "0.5.0"
authors = ["croath <croathliu@gmail.com>"]
edition = "2018"
edition = "2021"
exclude = ["js/**"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
6 changes: 6 additions & 0 deletions src/program-rust/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ pub enum ProtocolError {
#[error("invalid crema swap account data")]
InvalidCremaSwapAccountData,

#[error("invalid whirlpool info account data")]
InvalidWhirlpoolInfoAccountData,

#[error("overflow")]
Overflow,
}
Expand Down Expand Up @@ -332,6 +335,9 @@ impl PrintProgramError for ProtocolError {
ProtocolError::InvalidPoolMint => {
msg!("Error: InvalidPoolMint")
}
ProtocolError::InvalidWhirlpoolInfoAccountData => {
msg!("Error: InvalidWhirlpoolInfoAccountData")
}
ProtocolError::Overflow => {
msg!("Error: Overflow")
}
Expand Down
1 change: 1 addition & 0 deletions src/program-rust/src/exchanger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod raydium;
pub mod serum_dex;
pub mod spl_token_swap;
pub mod stable_swap;
pub mod whirlpool;
226 changes: 226 additions & 0 deletions src/program-rust/src/exchanger/whirlpool/instruction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
//! Instruction types

#![allow(clippy::too_many_arguments)]
use solana_program::{
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
};

/// Swap instruction data
/// [
/// {
/// "name": "amount",
/// "type": "u64"
/// },
/// {
/// "name": "otherAmountThreshold",
/// "type": "u64"
/// },
/// {
/// "name": "sqrtPriceLimit",
/// "type": "u128"
/// },
/// {
/// "name": "exactInput",
/// "type": "bool"
/// },
/// {
/// "name": "aToB",
/// "type": "bool"
/// }
/// ]
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Swap {
pub amount: u64,
pub other_amount_threshold: u64,
pub sqrt_price_limit: u128,
pub exact_input: bool,
pub a_to_b: bool,
}

/// Instructions supported by the SwapInfo program.
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum SwapInstruction {
Swap(Swap),
}

impl SwapInstruction {
pub fn pack(&self) -> Vec<u8> {
let mut buf = Vec::new();
match self {
SwapInstruction::Swap(Swap {
amount,
other_amount_threshold,
sqrt_price_limit,
exact_input,
a_to_b,
}) => {
// swap
buf.extend_from_slice(&[248, 198, 158, 145, 225, 117, 135, 200]);
buf.extend_from_slice(&amount.to_le_bytes());
buf.extend_from_slice(&other_amount_threshold.to_le_bytes());
buf.extend_from_slice(&sqrt_price_limit.to_le_bytes());
buf.push(if *exact_input { 1 } else { 0 });
buf.push(if *a_to_b { 1 } else { 0 });
}
}
buf
}
}

///! {
///! "name": "swap",
///! "accounts": [
///! {
///! "name": "tokenProgram",
///! "isMut": false,
///! "isSigner": false
///! },
///! {
///! "name": "tokenAuthority",
///! "isMut": false,
///! "isSigner": true
///! },
///! {
///! "name": "whirlpool",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "tokenOwnerAccountA",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "tokenVaultA",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "tokenOwnerAccountB",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "tokenVaultB",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "tickArray0",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "tickArray1",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "tickArray2",
///! "isMut": true,
///! "isSigner": false
///! },
///! {
///! "name": "oracle",
///! "isMut": false,
///! "isSigner": false
///! }
///! ],
///! "args": [
///! {
///! "name": "amount",
///! "type": "u64"
///! },
///! {
///! "name": "otherAmountThreshold",
///! "type": "u64"
///! },
///! {
///! "name": "sqrtPriceLimit",
///! "type": "u128"
///! },
///! {
///! "name": "exactInput",
///! "type": "bool"
///! },
///! {
///! "name": "aToB",
///! "type": "bool"
///! }
///! ]
///! },
///! A demo: https://solscan.io/tx/hFJW8skZkzdr8jeBLk2xxGbmtNAsYL2ktyApM3X31zZFjwdPRwHdGY1MKBaxEppoC3EUeDvVzmkR46GaV5iJfVT
#[allow(dead_code)]
pub fn swap(
program_id: &Pubkey,
token_program_id: &Pubkey,
user_authority_key: &Pubkey,
whirlpool: &Pubkey,
token_owner_account_a: &Pubkey,
token_vault_a: &Pubkey,
token_owner_account_b: &Pubkey,
token_vault_b: &Pubkey,
tick_array_0: &Pubkey,
tick_array_1: &Pubkey,
tick_array_2: &Pubkey,
oracle: &Pubkey,
amount: u64,
other_amount_threshold: u64,
sqrt_price_limit: u128,
exact_input: bool,
a_to_b: bool,
) -> Instruction {
let data = SwapInstruction::Swap(Swap {
amount,
other_amount_threshold,
sqrt_price_limit,
exact_input,
a_to_b,
})
.pack();

let accounts = vec![
AccountMeta::new_readonly(*token_program_id, false),
AccountMeta::new_readonly(*user_authority_key, true),
AccountMeta::new(*whirlpool, false),
AccountMeta::new(*token_owner_account_a, false),
AccountMeta::new(*token_vault_a, false),
AccountMeta::new(*token_owner_account_b, false),
AccountMeta::new(*token_vault_b, false),
AccountMeta::new(*tick_array_0, false),
AccountMeta::new(*tick_array_1, false),
AccountMeta::new(*tick_array_2, false),
AccountMeta::new_readonly(*oracle, false),
];
Instruction {
program_id: *program_id,
accounts,
data,
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_pack_swap_instruction() {
let a = "59p8WydnSZtRpoLy1jH2AXMMgQqWZivjtLP24DhngRF8TmzpmKzziurcyV";
let data1 = bs58::decode(a).into_vec().unwrap();
let b = [248, 198, 158, 145, 225, 117, 135, 200];
let (a1, _a2) = data1.split_at(8);
assert!(b.eq(a1), "instruction not same");
let data2 = SwapInstruction::Swap(Swap {
amount: 2000000000,
other_amount_threshold: 0,
sqrt_price_limit: 79226673515401279992447579055,
exact_input: true,
a_to_b: false,
})
.pack();
assert_eq!(data1, data2)
}
}
1 change: 1 addition & 0 deletions src/program-rust/src/exchanger/whirlpool/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod instruction;
Loading