Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ List of supported programs:
- [x] BisonFi
- [x] HumidiFi
- [x] Obric
- [x] Scorch
- [x] SolFi
- [x] Tessera
- [x] ZeroFi
Expand All @@ -52,6 +53,7 @@ CU benchmarks between Mona, Jupiter and direct (non-router) program hit:
| goonfi | — | — | 87,703 |
| humidifi | 41,194 | 47,342 | 52,723 |
| obric | 54,406 | 56,813 | 61,111 |
| scorch | TODO | TODO | TODO |
| solfi | 82,616 | 90,086 | 99,717 |
| tessera | — | 63,198 | 79,376 |
| zerofi | 34,734 | 37,238 | 41,910 |
Expand Down
1 change: 1 addition & 0 deletions src/adapters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod aquifer_v1;
pub mod bisonfi_v1;
pub mod humidifi_v1;
pub mod obric_v2;
pub mod scorch_v1;
pub mod solfi_v2;
pub mod tessera_v1;
pub mod zerofi_v1;
107 changes: 107 additions & 0 deletions src/adapters/scorch_v1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use crate::cons::scorch::{ACCS_LEN, ARGS_LEN, SWAP_SELECTOR};
use pinocchio::cpi::{invoke_unchecked, CpiAccount};
use pinocchio::instruction::{InstructionAccount, InstructionView};
use pinocchio::AccountView;

// Scorch V1 swap args: selector(1) + param(16) + padding (1) + amount_in(8) + min_out(8) = 34 bytes.
#[repr(C, packed)]
pub struct SwapArgs {
pub selector: [u8; 1],
pub scorch_param: [u8; 16],
pub flag: u8,
pub amount_in: [u8; 8],
pub min_out: [u8; 8],
}

impl SwapArgs {
pub fn new(amount_in: u64) -> Self {
Self {
selector: *SWAP_SELECTOR,
scorch_param: [0u8; 16], // patched by caller from scorch_param key
flag: 0,
amount_in: amount_in.to_le_bytes(),
min_out: 1u64.to_le_bytes(),
}
}

pub fn as_bytes(&self) -> &[u8; ARGS_LEN] {
unsafe { &*(self as *const Self as *const [u8; ARGS_LEN]) }
}
}

/// Remaining layout (18 accounts):
/// 0 program (readonly)
/// 1 market (readonly)
/// 2 user_ata_a (writable)
/// 3 user_ata_b (writable)
/// 4 market_ta_a (writable)
/// 5 market_ta_b (writable)
/// 6 mint_a (readonly)
/// 7 mint_b (readonly)
/// 8 token_prog (readonly)
/// 9 token_prog (readonly)
/// 10 memo_prog (readonly)
/// 11 core_prog (readonly)
/// 12 acc1 (readonly)
/// 13 state_a (writable)
/// 14 state_b (writable)
/// 15 state_c (writable)
/// 16 sysvar_ixs (readonly)
/// 17 scorch_param (readonly) - encoded in first 16 bytes of key
///
/// CPI to Scorch (18 accounts): payer injected at position 1.
pub fn swap_v1(payer: &AccountView, rem: &[AccountView], amount_in: u64, _a_to_b: bool) {
let mut args = SwapArgs::new(amount_in);

// extract params from the scorch_param account key (first 16 bytes)
args.scorch_param
.copy_from_slice(&rem[17].address().as_ref()[..16]);

let ix_accs = [
InstructionAccount::readonly(rem[1].address()), // market
InstructionAccount::writable_signer(payer.address()), // payer
InstructionAccount::writable(rem[2].address()), // user_ata_a
InstructionAccount::writable(rem[3].address()), // user_ata_b
InstructionAccount::writable(rem[4].address()), // market_ta_a
InstructionAccount::writable(rem[5].address()), // market_ta_b
InstructionAccount::readonly(rem[6].address()), // mint_a
InstructionAccount::readonly(rem[7].address()), // mint_b
InstructionAccount::readonly(rem[8].address()), // token_prog
InstructionAccount::readonly(rem[9].address()), // token_prog
InstructionAccount::readonly(rem[10].address()), // memo_prog
InstructionAccount::readonly(rem[11].address()), // core_prog
InstructionAccount::readonly(rem[12].address()), // acc1
InstructionAccount::writable(rem[13].address()), // state_a
InstructionAccount::writable(rem[14].address()), // state_b
InstructionAccount::writable(rem[15].address()), // state_c
InstructionAccount::readonly(rem[16].address()), // sysvar_ixs
];

let ix = InstructionView {
program_id: rem[0].address(),
data: args.as_bytes(),
accounts: &ix_accs,
};

let cpi: [CpiAccount; ACCS_LEN - 1] = [
CpiAccount::from(&rem[1]),
CpiAccount::from(payer),
CpiAccount::from(&rem[2]),
CpiAccount::from(&rem[3]),
CpiAccount::from(&rem[4]),
CpiAccount::from(&rem[5]),
CpiAccount::from(&rem[6]),
CpiAccount::from(&rem[7]),
CpiAccount::from(&rem[8]),
CpiAccount::from(&rem[9]),
CpiAccount::from(&rem[10]),
CpiAccount::from(&rem[11]),
CpiAccount::from(&rem[12]),
CpiAccount::from(&rem[13]),
CpiAccount::from(&rem[14]),
CpiAccount::from(&rem[15]),
CpiAccount::from(&rem[16]),
];

unsafe { invoke_unchecked(&ix, &cpi) }
}
45 changes: 34 additions & 11 deletions src/cons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use five8_const::decode_32_const as d;

pub const MAX_HOPS: usize = 30;

const OBF_CPI_KEY_SEED: [u8; 32] =
[58, 255, 47, 255, 226, 186, 235, 195, 123, 131, 245, 8, 11, 233, 132, 219, 225, 40, 79, 119, 169, 121, 169, 58, 197, 1, 122, 9, 216, 164, 149, 97];
const OBF_CPI_KEY_SEED: [u8; 32] = [
58, 255, 47, 255, 226, 186, 235, 195, 123, 131, 245, 8, 11, 233, 132, 219, 225, 40, 79, 119,
169, 121, 169, 58, 197, 1, 122, 9, 216, 164, 149, 97,
];
pub const OBF_CPI_KEY: u64 = u64::from_le_bytes([
OBF_CPI_KEY_SEED[0],
OBF_CPI_KEY_SEED[1],
Expand Down Expand Up @@ -74,6 +76,13 @@ pub mod alphaq {
pub const ARGS_LEN: usize = 18;
}

pub mod scorch {
pub const ID: [u8; 32] = super::d("SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn");
pub const SWAP_SELECTOR: &[u8; 1] = &[0x02];
pub const ACCS_LEN: usize = 18;
pub const ARGS_LEN: usize = 34;
}

/// DEX discriminant;
/// each variant maps to a specific adapter.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
Expand All @@ -85,13 +94,25 @@ pub enum Dex {
HumidifiV2 = 3,
HumidifiV3 = 4,
Obric = 5,
Solfi = 6,
Tessera = 7,
Zerofi = 8,
Scorch = 6,
Solfi = 7,
Tessera = 8,
Zerofi = 9,
}

impl Dex {
pub const ALL: [Dex; 9] = [Dex::Alphaq, Dex::Aquifer, Dex::Bisonfi, Dex::HumidifiV2, Dex::HumidifiV3, Dex::Obric, Dex::Solfi, Dex::Tessera, Dex::Zerofi];
pub const ALL: [Dex; 10] = [
Dex::Alphaq,
Dex::Aquifer,
Dex::Bisonfi,
Dex::HumidifiV2,
Dex::HumidifiV3,
Dex::Obric,
Dex::Scorch,
Dex::Solfi,
Dex::Tessera,
Dex::Zerofi,
];

/// Number of remaining accounts per hop for swap_v1 (excludes shared payer).
#[inline(always)]
Expand All @@ -108,6 +129,7 @@ impl Dex {
Dex::Bisonfi => if a_to_b { 5 } else { 4 },
Dex::HumidifiV2 | Dex::HumidifiV3 => if a_to_b { 5 } else { 4 },
Dex::Obric => if a_to_b { 7 } else { 6 },
Dex::Scorch => 3,
Dex::Solfi => if a_to_b { 7 } else { 6 },
Dex::Tessera => if a_to_b { 6 } else { 5 },
Dex::Zerofi => 7,
Expand All @@ -117,22 +139,23 @@ impl Dex {
/// Map byte to Dex variant.
#[inline(always)]
pub fn from_u8(v: u8) -> Option<Self> {
if v <= 8 {
if v <= 9 {
Some(Self::ALL[v as usize])
} else {
None
}
}
}

const REM_ACCS_LEN_V1: [usize; 9] = [
const REM_ACCS_LEN_V1: [usize; 10] = [
alphaq::ACCS_LEN, // 0 Alphaq
aquifer::ACCS_LEN, // 1 Aquifer
bisonfi::ACCS_LEN, // 2 Bisonfi
humidifi::ACCS_LEN_V2V3, // 3 HumidifiV2
humidifi::ACCS_LEN_V2V3, // 4 HumidifiV3
obric::ACCS_LEN, // 5 Obric
solfi::ACCS_LEN, // 6 Solfi
tessera::ACCS_LEN, // 7 Tessera
zerofi::ACCS_LEN, // 8 Zerofi
scorch::ACCS_LEN, // 6 Scorch
solfi::ACCS_LEN, // 7 Solfi
tessera::ACCS_LEN, // 8 Tessera
zerofi::ACCS_LEN, // 9 Zerofi
];
1 change: 1 addition & 0 deletions src/ixs/swap_v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ fn dispatch(payer: &AccountView, rem: &[AccountView], amount_in: u64, a_to_b: bo
Dex::Bisonfi => adapters::bisonfi_v1::swap_v1,
Dex::HumidifiV2 | Dex::HumidifiV3 => adapters::humidifi_v1::swap_v3,
Dex::Obric => adapters::obric_v2::swap_v1,
Dex::Scorch => adapters::scorch_v1::swap_v1,
Dex::Solfi => adapters::solfi_v2::swap_v1,
Dex::Tessera => adapters::tessera_v1::swap_v1,
Dex::Zerofi => adapters::zerofi_v1::swap_v1,
Expand Down
Loading