use anyhow::Error;
use anyhow::{Context, Result};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use solana_account_decoder::{UiAccount, UiAccountEncoding};
use std::{collections::HashMap, convert::TryFrom, str::FromStr};
mod swap;
pub use swap::{Side, Swap};
use solana_sdk::{account::Account, instruction::AccountMeta, pubkey::Pubkey};
#[derive(Debug)]
pub struct QuoteParams {
pub in_amount: u64,
pub input_mint: Pubkey,
pub output_mint: Pubkey,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Quote {
pub not_enough_liquidity: bool,
pub min_in_amount: Option<u64>,
pub min_out_amount: Option<u64>,
pub in_amount: u64,
pub out_amount: u64,
pub fee_amount: u64,
pub fee_mint: Pubkey,
pub fee_pct: Decimal,
}
pub type QuoteMintToReferrer = HashMap<Pubkey, Pubkey>;
pub struct SwapParams<'a, 'b> {
pub in_amount: u64,
pub source_mint: Pubkey,
pub destination_mint: Pubkey,
pub user_source_token_account: Pubkey,
pub user_destination_token_account: Pubkey,
pub user_transfer_authority: Pubkey,
pub open_order_address: Option<Pubkey>,
pub quote_mint_to_referrer: Option<&'a QuoteMintToReferrer>,
pub jupiter_program_id: &'b Pubkey,
}
impl<'a, 'b> SwapParams<'a, 'b> {
pub fn placeholder_account_meta(&self) -> AccountMeta {
AccountMeta::new_readonly(*self.jupiter_program_id, false)
}
}
pub struct SwapAndAccountMetas {
pub swap: Swap,
pub account_metas: Vec<AccountMeta>,
}
#[derive(Clone)]
pub enum AmmUserSetup {
SerumDexOpenOrdersSetup { market: Pubkey, program_id: Pubkey },
}
pub type AccountMap = HashMap<Pubkey, Account>;
pub fn try_get_account_data<'a>(account_map: &'a AccountMap, address: &Pubkey) -> Result<&'a [u8]> {
account_map
.get(address)
.map(|partial_account| partial_account.data.as_slice())
.context(format!("Could not find address: {address}"))
}
pub trait Amm {
fn from_keyed_account(keyed_account: &KeyedAccount) -> Result<Self>
where
Self: Sized;
fn label(&self) -> String;
fn program_id(&self) -> Pubkey;
fn key(&self) -> Pubkey;
fn get_reserve_mints(&self) -> Vec<Pubkey>;
fn get_accounts_to_update(&self) -> Vec<Pubkey>;
fn update(&mut self, account_map: &AccountMap) -> Result<()>;
fn quote(&self, quote_params: &QuoteParams) -> Result<Quote>;
fn get_swap_and_account_metas(&self, swap_params: &SwapParams) -> Result<SwapAndAccountMetas>;
fn has_dynamic_accounts(&self) -> bool {
false
}
fn get_user_setup(&self) -> Option<AmmUserSetup> {
None
}
fn clone_amm(&self) -> Box<dyn Amm + Send + Sync>;
fn unidirectional(&self) -> bool {
false
}
fn program_dependencies(&self) -> Vec<(Pubkey, String)> {
vec![]
}
fn get_accounts_len(&self) -> usize {
32 }
}
impl Clone for Box<dyn Amm + Send + Sync> {
fn clone(&self) -> Box<dyn Amm + Send + Sync> {
self.clone_amm()
}
}
#[derive(Clone, Deserialize, Serialize)]
pub struct KeyedAccount {
pub key: Pubkey,
pub account: Account,
pub params: Option<Value>,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct KeyedUiAccount {
pub pubkey: String,
#[serde(flatten)]
pub ui_account: UiAccount,
pub params: Option<Value>,
}
impl From<KeyedAccount> for KeyedUiAccount {
fn from(keyed_account: KeyedAccount) -> Self {
let KeyedAccount {
key,
account,
params,
} = keyed_account;
let ui_account = UiAccount::encode(&key, &account, UiAccountEncoding::Base64, None, None);
KeyedUiAccount {
pubkey: key.to_string(),
ui_account,
params,
}
}
}
impl TryFrom<KeyedUiAccount> for KeyedAccount {
type Error = Error;
fn try_from(keyed_ui_account: KeyedUiAccount) -> Result<Self, Self::Error> {
let KeyedUiAccount {
pubkey,
ui_account,
params,
} = keyed_ui_account;
let account = ui_account
.decode()
.expect(&format!("Failed to decode ui_account for {}", pubkey));
Ok(KeyedAccount {
key: Pubkey::from_str(&pubkey)?,
account,
params,
})
}
}