use core::fmt;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
pub struct RemainingAccountsParams {
pub writable_spot_market_indexes: Vec<u16>,
pub readable_spot_market_indexes: Vec<u16>,
pub oracle_pubkeys: Vec<(u16, Pubkey)>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum RemainingAccountsError {
MissingOracle(u16),
}
impl fmt::Display for RemainingAccountsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingOracle(idx) => write!(f, "missing oracle for market index {idx}"),
}
}
}
pub fn get_remaining_accounts(
params: &RemainingAccountsParams,
) -> Result<Vec<AccountMeta>, RemainingAccountsError> {
let mut accounts = Vec::new();
let mut seen_indexes: Vec<u16> = Vec::new();
for &idx in ¶ms.writable_spot_market_indexes {
if !seen_indexes.contains(&idx) {
seen_indexes.push(idx);
}
}
for &idx in ¶ms.readable_spot_market_indexes {
if !seen_indexes.contains(&idx) {
seen_indexes.push(idx);
}
}
for &idx in &seen_indexes {
let (_, oracle) = params
.oracle_pubkeys
.iter()
.find(|(mi, _)| *mi == idx)
.ok_or(RemainingAccountsError::MissingOracle(idx))?;
accounts.push(AccountMeta {
pubkey: *oracle,
is_signer: false,
is_writable: false,
});
}
for &idx in &seen_indexes {
let is_writable = params.writable_spot_market_indexes.contains(&idx);
accounts.push(AccountMeta {
pubkey: pyra_accounts::get_drift_spot_market(idx),
is_signer: false,
is_writable,
});
}
for &idx in &seen_indexes {
if !params.writable_spot_market_indexes.contains(&idx) {
continue;
}
if let Some(token) = pyra_tokens::Token::find_by_drift_market_index(idx) {
if token.token_program == pyra_tokens::TOKEN_2022_PROGRAM_ID {
accounts.push(AccountMeta {
pubkey: token.mint,
is_signer: false,
is_writable: false,
});
}
}
}
Ok(accounts)
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
use solana_pubkey::pubkey;
#[test]
fn test_single_writable_market_with_quote() {
let sol_oracle = pubkey!("BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF");
let usdc_oracle = pubkey!("En8hkHLkRe9d9DraYmBTrus518BvmVH448YcvmrFM6Ce");
let result = get_remaining_accounts(&RemainingAccountsParams {
writable_spot_market_indexes: vec![1],
readable_spot_market_indexes: vec![0],
oracle_pubkeys: vec![(1, sol_oracle), (0, usdc_oracle)],
})
.expect("should succeed");
assert_eq!(result.len(), 4);
assert_eq!(result[0].pubkey, sol_oracle);
assert!(!result[0].is_writable);
assert_eq!(result[1].pubkey, usdc_oracle);
assert!(!result[1].is_writable);
let sol_spot_market = pyra_accounts::get_drift_spot_market(1);
let usdc_spot_market = pyra_accounts::get_drift_spot_market(0);
assert_eq!(result[2].pubkey, sol_spot_market);
assert!(result[2].is_writable);
assert_eq!(result[3].pubkey, usdc_spot_market);
assert!(!result[3].is_writable);
}
#[test]
fn test_token_2022_appends_mint() {
let pyusd_oracle = pubkey!("BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF");
let usdc_oracle = pubkey!("En8hkHLkRe9d9DraYmBTrus518BvmVH448YcvmrFM6Ce");
let result = get_remaining_accounts(&RemainingAccountsParams {
writable_spot_market_indexes: vec![22],
readable_spot_market_indexes: vec![0],
oracle_pubkeys: vec![(22, pyusd_oracle), (0, usdc_oracle)],
})
.expect("should succeed");
assert_eq!(result.len(), 5);
let pyusd = pyra_tokens::Token::find_by_drift_market_index(22)
.expect("PYUSD should be a supported token");
assert_eq!(result[4].pubkey, pyusd.mint);
assert!(!result[4].is_writable);
assert!(!result[4].is_signer);
}
#[test]
fn test_deduplicates_overlapping_indexes() {
let usdc_oracle = pubkey!("En8hkHLkRe9d9DraYmBTrus518BvmVH448YcvmrFM6Ce");
let result = get_remaining_accounts(&RemainingAccountsParams {
writable_spot_market_indexes: vec![0],
readable_spot_market_indexes: vec![0],
oracle_pubkeys: vec![(0, usdc_oracle)],
})
.expect("should succeed");
assert_eq!(result.len(), 2);
assert!(result[1].is_writable); }
#[test]
fn test_spl_token_market_no_mint_appended() {
let sol_oracle = pubkey!("BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF");
let result = get_remaining_accounts(&RemainingAccountsParams {
writable_spot_market_indexes: vec![1],
readable_spot_market_indexes: vec![],
oracle_pubkeys: vec![(1, sol_oracle)],
})
.expect("should succeed");
assert_eq!(result.len(), 2);
}
#[test]
fn test_empty_params() {
let result = get_remaining_accounts(&RemainingAccountsParams {
writable_spot_market_indexes: vec![],
readable_spot_market_indexes: vec![],
oracle_pubkeys: vec![],
})
.expect("should succeed");
assert!(result.is_empty());
}
#[test]
fn test_missing_oracle_returns_error() {
let result = get_remaining_accounts(&RemainingAccountsParams {
writable_spot_market_indexes: vec![1],
readable_spot_market_indexes: vec![0],
oracle_pubkeys: vec![], });
assert_eq!(result, Err(RemainingAccountsError::MissingOracle(1)));
}
}