use std::collections::{HashMap, HashSet};
use gmsol_solana_utils::{AtomicGroup, IntoAtomicGroup, ParallelGroup};
use serde::{Deserialize, Serialize};
use tsify_next::Tsify;
use wasm_bindgen::prelude::*;
use crate::{
builders::{
glv_deposit::{CreateGlvDeposit, CreateGlvDepositHint},
token::{PrepareTokenAccounts, WrapNative},
user::PrepareUser,
StoreProgram,
},
js::instructions::BuildTransactionOptions,
serde::StringPubkey,
};
use super::{TransactionGroup, TransactionGroupOptions};
#[derive(Debug, Serialize, Deserialize, Tsify, Clone)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct CreateGlvDepositParamsJs {
pub glv_token: StringPubkey,
pub market_token: StringPubkey,
#[serde(default)]
pub receiver: Option<StringPubkey>,
#[serde(default)]
pub long_pay_token: Option<StringPubkey>,
#[serde(default)]
pub short_pay_token: Option<StringPubkey>,
#[serde(default)]
pub long_swap_path: Option<Vec<StringPubkey>>,
#[serde(default)]
pub short_swap_path: Option<Vec<StringPubkey>>,
#[serde(default)]
pub long_pay_amount: Option<u128>,
#[serde(default)]
pub short_pay_amount: Option<u128>,
#[serde(default)]
pub market_token_amount: Option<u128>,
#[serde(default)]
pub min_market_token_amount: Option<u128>,
#[serde(default)]
pub min_receive_amount: Option<u128>,
#[serde(default)]
pub skip_unwrap_native_on_receive: Option<bool>,
#[serde(default)]
pub skip_glv_token_ata_creation: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize, Tsify)]
#[tsify(into_wasm_abi, from_wasm_abi)]
pub struct CreateGlvDepositOptions {
pub recent_blockhash: String,
pub payer: StringPubkey,
#[serde(default)]
pub program: Option<StoreProgram>,
#[serde(default)]
pub compute_unit_price_micro_lamports: Option<u64>,
#[serde(default)]
pub compute_unit_min_priority_lamports: Option<u64>,
pub hints: HashMap<StringPubkey, CreateGlvDepositHint>,
#[serde(default)]
pub transaction_group: TransactionGroupOptions,
#[serde(default)]
skip_wrap_native_on_pay: Option<bool>,
}
#[wasm_bindgen]
pub struct CreateGlvDepositsBuilder {
payer: StringPubkey,
tokens: HashSet<StringPubkey>,
groups: Vec<AtomicGroup>,
transaction_group: TransactionGroupOptions,
build: BuildTransactionOptions,
}
#[wasm_bindgen]
pub fn create_glv_deposits_builder(
deposits: Vec<CreateGlvDepositParamsJs>,
options: CreateGlvDepositOptions,
) -> crate::Result<CreateGlvDepositsBuilder> {
let mut tokens = HashSet::default();
let mut groups: Vec<AtomicGroup> = Vec::with_capacity(deposits.len());
let should_wrap_native = !options.skip_wrap_native_on_pay.unwrap_or_default();
let mut wrap_native = false;
for params in deposits.into_iter() {
let glv_token = params.glv_token;
let hint = options.hints.get(&glv_token).ok_or_else(|| {
crate::Error::custom(format!("hint for {} is not provided", glv_token.0))
})?;
let mut wrap_amount = 0;
if let Some(t) = params.long_pay_token.as_ref() {
if should_wrap_native && **t == WrapNative::NATIVE_MINT {
wrap_amount += params.long_pay_amount.unwrap_or_default();
}
}
if let Some(t) = params.short_pay_token.as_ref() {
if should_wrap_native && **t == WrapNative::NATIVE_MINT {
wrap_amount += params.short_pay_amount.unwrap_or_default();
}
}
let program = options.program.clone().unwrap_or_default();
let builder = CreateGlvDeposit::builder()
.program(program)
.payer(options.payer)
.glv_token(params.glv_token)
.market_token(params.market_token)
.long_pay_token(params.long_pay_token)
.short_pay_token(params.short_pay_token)
.long_swap_path(params.long_swap_path.unwrap_or_default())
.short_swap_path(params.short_swap_path.unwrap_or_default())
.long_pay_amount(params.long_pay_amount.unwrap_or_default().try_into()?)
.short_pay_amount(params.short_pay_amount.unwrap_or_default().try_into()?)
.market_token_amount(params.market_token_amount.unwrap_or_default().try_into()?)
.min_market_token_amount(
params
.min_market_token_amount
.unwrap_or_default()
.try_into()?,
)
.min_receive_amount(params.min_receive_amount.unwrap_or_default().try_into()?)
.unwrap_native_on_receive(!params.skip_unwrap_native_on_receive.unwrap_or_default())
.skip_glv_token_ata_creation(params.skip_glv_token_ata_creation.unwrap_or_default());
let built = if let Some(r) = params.receiver {
builder.receiver(r).build()
} else {
builder.build()
}
.into_atomic_group(hint)?;
let ag = if wrap_amount == 0 {
built
} else {
wrap_native = true;
let mut wrap = WrapNative::builder()
.owner(options.payer)
.lamports(wrap_amount.try_into().map_err(crate::Error::custom)?)
.build()
.into_atomic_group(&true)?;
wrap.merge(built);
wrap
};
groups.push(ag);
}
if wrap_native {
tokens.insert(WrapNative::NATIVE_MINT.into());
}
Ok(CreateGlvDepositsBuilder {
payer: options.payer,
tokens,
groups,
transaction_group: options.transaction_group,
build: BuildTransactionOptions {
recent_blockhash: options.recent_blockhash,
compute_unit_price_micro_lamports: options.compute_unit_price_micro_lamports,
compute_unit_min_priority_lamports: options.compute_unit_min_priority_lamports,
},
})
}
#[wasm_bindgen]
impl CreateGlvDepositsBuilder {
pub fn build_with_options(
self,
transaction_group: Option<TransactionGroupOptions>,
build: Option<BuildTransactionOptions>,
) -> crate::Result<TransactionGroup> {
let mut group = transaction_group.unwrap_or(self.transaction_group).build();
let prepare_user = PrepareUser::builder()
.payer(self.payer)
.build()
.into_atomic_group(&())?;
let prepare_tokens = PrepareTokenAccounts::builder()
.owner(self.payer)
.payer(self.payer)
.tokens(self.tokens)
.build()
.into_atomic_group(&())?;
let build = build.unwrap_or(self.build);
TransactionGroup::new(
group
.add(prepare_user)?
.add(prepare_tokens)?
.add(self.groups.into_iter().collect::<ParallelGroup>())?
.optimize(false),
&build.recent_blockhash,
build.compute_unit_price_micro_lamports,
build.compute_unit_min_priority_lamports,
)
}
}
#[wasm_bindgen]
pub fn create_glv_deposits(
deposits: Vec<CreateGlvDepositParamsJs>,
options: CreateGlvDepositOptions,
) -> crate::Result<TransactionGroup> {
create_glv_deposits_builder(deposits, options)?.build_with_options(None, None)
}