use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use color_eyre::{Result, eyre::eyre};
use lil_tabby::tabby;
use serde::{Deserialize, Serialize};
use solana_sdk::pubkey::Pubkey;
use crate::utils::SolanaLinks;
pub const ACCOUNT_DISCRIMINATOR_BONDING_CURVE: [u8; 8] = [23, 183, 248, 55, 96, 216, 172, 96];
pub const ACCOUNT_DISCRIMINATOR_GLOBAL: [u8; 8] = [167, 232, 232, 177, 200, 108, 114, 127];
pub const ACCOUNT_DISCRIMINATOR_LAST_WITHDRAW: [u8; 8] = [203, 18, 220, 103, 120, 145, 187, 2];
#[serde_with::serde_as]
#[derive(BorshSchema, BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug, Clone)]
pub struct BondingCurve {
pub virtual_token_reserves: u64,
pub virtual_sol_reserves: u64,
pub real_token_reserves: u64,
pub real_sol_reserves: u64,
pub token_total_supply: u64,
pub complete: bool,
#[serde_as(as = "serde_with::DisplayFromStr")]
pub creator: Pubkey,
}
#[serde_with::serde_as]
#[derive(BorshSchema, BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug, Clone)]
pub struct Global {
pub initialized: bool,
#[serde_as(as = "serde_with::DisplayFromStr")]
pub authority: Pubkey,
#[serde_as(as = "serde_with::DisplayFromStr")]
pub fee_recipient: Pubkey,
pub initial_virtual_token_reserves: u64,
pub initial_virtual_sol_reserves: u64,
pub initial_real_token_reserves: u64,
pub token_total_supply: u64,
pub fee_basis_points: u64,
#[serde_as(as = "serde_with::DisplayFromStr")]
pub withdraw_authority: Pubkey,
pub enable_migrate: bool,
pub pool_migration_fee: u64,
pub creator_fee: u64,
#[serde_as(as = "[serde_with::DisplayFromStr; 7]")]
pub fee_recipients: [Pubkey; 7],
#[serde_as(as = "serde_with::DisplayFromStr")]
pub set_creator_authority: Pubkey,
#[serde_as(as = "serde_with::DisplayFromStr")]
pub admin_set_creator_authority: Pubkey,
}
#[serde_with::serde_as]
#[derive(BorshSchema, BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug, Clone)]
pub struct LastWithdraw {
pub last_withdraw_timestamp: i64,
}
#[allow(clippy::large_enum_variant)]
#[serde_with::serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Account {
Global(Global),
BondingCurve(BondingCurve),
LastWithdraw(LastWithdraw),
}
pub fn decode_account(account_bytes: &[u8]) -> Result<Account> {
if account_bytes.len() < 8 {
return Err(eyre!(
"Event data too short to contain a discriminator.".to_string()
));
}
let discriminator: [u8; 8] = account_bytes[0..8]
.try_into()
.map_err(|_| eyre!("Failed to extract event discriminator slice"))?;
let rest = &account_bytes[8..];
match discriminator {
ACCOUNT_DISCRIMINATOR_BONDING_CURVE => {
let len = borsh::max_serialized_size::<BondingCurve>()
.map_err(|e| eyre!("Can't get max serialized size: {:#?}", e))?;
borsh::from_slice::<BondingCurve>(&rest[0..len])
.map(Account::BondingCurve)
.map_err(|e| eyre!("Can't deserialize BondingCurve: {:#?}", e))
}
ACCOUNT_DISCRIMINATOR_GLOBAL => {
let len = borsh::max_serialized_size::<Global>()
.map_err(|e| eyre!("Can't get max serialized size: {:#?}", e))?;
borsh::from_slice::<Global>(&rest[0..len])
.map(Account::Global)
.map_err(|e| eyre!("Can't deserialize Global: {:#?}", e))
}
ACCOUNT_DISCRIMINATOR_LAST_WITHDRAW => {
let len = borsh::max_serialized_size::<LastWithdraw>()
.map_err(|e| eyre!("Can't get max serialized size: {:#?}", e))?;
borsh::from_slice::<LastWithdraw>(&rest[0..len])
.map(Account::LastWithdraw)
.map_err(|e| eyre!("Can't deserialize LastWithdraw: {:#?}", e))
}
_ => Err(eyre!("Unknown account discriminator: {:?}", discriminator)),
}
}
impl std::fmt::Display for Account {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Account::BondingCurve(account) => {
writeln!(
f,
"{}",
tabby![
{ color: yellow, header: bright_green, labels: magenta }
[format!("BondingCurve")],
["virtual_token_reserves", &account.virtual_token_reserves.to_string()],
["virtual_sol_reserves", &account.virtual_sol_reserves.to_string()],
["real_token_reserves", &account.real_token_reserves.to_string()],
["real_sol_reserves", &account.real_sol_reserves.to_string()],
["token_total_supply", &account.token_total_supply.to_string()],
["complete", &account.complete.to_string()],
["creator", &account.creator.acc()]
]
)
}
Account::Global(account) => {
writeln!(
f,
"{}",
tabby![
{ color: yellow, header: bright_green, labels: magenta }
[format!("Global")],
["initialized", &account.initialized.to_string()],
["authority", &account.authority.acc()],
["fee_recipient", &account.fee_recipient.acc()],
["initial_virtual_token_reserves", &account.initial_virtual_token_reserves.to_string()],
["initial_virtual_sol_reserves", &account.initial_virtual_sol_reserves.to_string()],
["initial_real_token_reserves", &account.initial_real_token_reserves.to_string()],
["token_total_supply", &account.token_total_supply.to_string()],
["fee_basis_points", &account.fee_basis_points.to_string()],
["withdraw_authority", &account.withdraw_authority.acc()],
["enable_migrate", &account.enable_migrate.to_string()],
["pool_migration_fee", &account.pool_migration_fee.to_string()],
["creator_fee", &account.creator_fee.to_string()],
["fee_recipients", &account.fee_recipients.iter().map(|p| p.acc().to_string()).collect::<Vec<String>>().join("\n")]
]
)
}
Account::LastWithdraw(account) => {
writeln!(
f,
"{}",
tabby![
{ color: yellow, header: bright_green, labels: magenta }
[format!("LastWithdraw")],
["last_withdraw_timestamp", &account.last_withdraw_timestamp.to_string()]
]
)
}
}
}
}