switchboard_on_demand/
utils.rs

1use solana_program::instruction::Instruction;
2
3use crate::anchor_traits::*;
4use crate::solana_compat::{hash, pubkey};
5use crate::{solana_program, Pubkey};
6
7/// Check if devnet environment is enabled via feature flag OR SB_ENV environment variable
8#[inline(always)]
9pub fn is_devnet() -> bool {
10    cfg!(feature = "devnet") || std::env::var("SB_ENV").unwrap_or_default() == "devnet"
11}
12
13/// Default devnet queue address
14pub const DEFAULT_DEVNET_QUEUE: Pubkey = pubkey!("EYiAmGSdsQTuCw413V5BzaruWuCCSDgTPtBGvLkXHbe7");
15/// Default mainnet queue address
16pub const DEFAULT_MAINNET_QUEUE: Pubkey = pubkey!("A43DyUGA7s8eXPxqEjJY6EBu1KKbNgfxF8h17VAHn13w");
17
18/// Returns the default queue address based on the environment (devnet or mainnet)
19#[inline(always)]
20pub fn default_queue() -> Pubkey {
21    if is_devnet() {
22        DEFAULT_DEVNET_QUEUE
23    } else {
24        DEFAULT_MAINNET_QUEUE
25    }
26}
27
28/// SPL Associated Token Account program ID
29pub const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: Pubkey =
30    pubkey!("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
31
32/// SPL Token program ID
33pub const SPL_TOKEN_PROGRAM_ID: Pubkey = pubkey!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
34
35pub const NATIVE_MINT: Pubkey = pubkey!("So11111111111111111111111111111111111111112");
36
37/// Address Lookup Table program ID
38pub const ADDRESS_LOOKUP_TABLE_PROGRAM_ID: Pubkey =
39    pubkey!("AddressLookupTab1e1111111111111111111111111");
40
41/// System program ID constant (same across all versions)
42pub const SYSTEM_PROGRAM_ID: Pubkey = pubkey!("11111111111111111111111111111111");
43
44/// Finds the associated token account address for a given owner and mint
45pub fn find_associated_token_address(owner: &Pubkey, mint: &Pubkey) -> Pubkey {
46    let (akey, _bump) = Pubkey::find_program_address(
47        &[owner.as_ref(), SPL_TOKEN_PROGRAM_ID.as_ref(), mint.as_ref()],
48        &SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
49    );
50    akey
51}
52
53/// Gets the instruction discriminator for a given instruction name
54pub fn get_ixn_discriminator(ixn_name: &str) -> [u8; 8] {
55    let preimage = format!("global:{}", ixn_name);
56    let mut sighash = [0u8; 8];
57    sighash.copy_from_slice(&hash::hash(preimage.as_bytes()).to_bytes()[..8]);
58    sighash
59}
60
61/// Gets the account discriminator for a given account name
62pub fn get_account_discriminator(account_name: &str) -> [u8; 8] {
63    let id = format!("account:{}", account_name);
64    hash::hash(id.as_bytes()).to_bytes()[..8]
65        .try_into()
66        .unwrap()
67}
68
69/// Reads a u64 value from a pointer at a given offset (unsafe)
70///
71/// # Safety
72/// The caller must ensure that:
73/// - `ptr` is a valid pointer
74/// - `ptr.add(offset)` is within bounds and valid
75/// - The memory at `ptr.add(offset)` contains a valid u64 value
76#[inline(always)]
77pub unsafe fn read_u64_at(ptr: *const u64, offset: usize) -> u64 {
78    core::ptr::read_unaligned(ptr.add(offset))
79}
80
81/// Reads a u64 value from a pointer (unsafe)
82///
83/// # Safety
84/// The caller must ensure that:
85/// - `ptr` is valid and properly aligned for u64 access
86/// - `ptr.add(offset)` is within bounds and valid
87/// - The memory at `ptr.add(offset)` contains a valid u64 value
88#[inline(always)]
89pub unsafe fn read(ptr: *const u64, offset: usize) -> u64 {
90    *ptr.add(offset)
91}
92
93/// Efficiently compares two Pubkeys for equality
94#[inline(always)]
95pub fn check_pubkey_eq<L: AsRef<[u8]>, R: AsRef<[u8]>>(lhs: L, rhs: R) -> bool {
96    let lhs_bytes = lhs.as_ref();
97    let rhs_bytes = rhs.as_ref();
98
99    unsafe {
100        let lhs_ptr = lhs_bytes.as_ptr() as *const u64;
101        let rhs_ptr = rhs_bytes.as_ptr() as *const u64;
102        core::ptr::read_unaligned(lhs_ptr) == core::ptr::read_unaligned(rhs_ptr)
103            && core::ptr::read_unaligned(lhs_ptr.add(1))
104                == core::ptr::read_unaligned(rhs_ptr.add(1))
105            && core::ptr::read_unaligned(lhs_ptr.add(2))
106                == core::ptr::read_unaligned(rhs_ptr.add(2))
107            && core::ptr::read_unaligned(lhs_ptr.add(3))
108                == core::ptr::read_unaligned(rhs_ptr.add(3))
109    }
110}
111
112/// Efficiently compares two 32-byte arrays via u64 pointers (unsafe)
113///
114/// # Safety
115/// The caller must ensure that:
116/// - Both `lhs_ptr` and `rhs_ptr` are valid pointers
117/// - Both pointers point to memory regions of at least 32 bytes (4 u64 values)
118/// - The memory regions are accessible for the duration of the function call
119#[inline(always)]
120pub unsafe fn check_p64_eq(lhs_ptr: *const u64, rhs_ptr: *const u64) -> bool {
121    core::ptr::read_unaligned(lhs_ptr) == core::ptr::read_unaligned(rhs_ptr)
122        && core::ptr::read_unaligned(lhs_ptr.add(1)) == core::ptr::read_unaligned(rhs_ptr.add(1))
123        && core::ptr::read_unaligned(lhs_ptr.add(2)) == core::ptr::read_unaligned(rhs_ptr.add(2))
124        && core::ptr::read_unaligned(lhs_ptr.add(3)) == core::ptr::read_unaligned(rhs_ptr.add(3))
125}
126
127/// Builds a Solana instruction from account metas and instruction data
128pub fn build_ix<A: ToAccountMetas, I: InstructionData + Discriminator + std::fmt::Debug>(
129    program_id: &Pubkey,
130    accounts: &A,
131    params: &I,
132) -> Instruction {
133    Instruction {
134        program_id: *program_id,
135        accounts: accounts.to_account_metas(None),
136        data: params.data(),
137    }
138}
139
140pub fn derive_lookup_table_address(
141    authority_address: &Pubkey,
142    recent_block_slot: u64,
143) -> (Pubkey, u8) {
144    Pubkey::find_program_address(
145        &[authority_address.as_ref(), &recent_block_slot.to_le_bytes()],
146        &crate::ADDRESS_LOOKUP_TABLE_PROGRAM_ID,
147    )
148}