use anchor_lang::prelude::*;
use gmsol_utils::InitSpace;
use crate::{events::DepositRemoved, states::MarketConfigKey, CoreError};
use super::{
common::{
action::{Action, ActionHeader, Closable},
swap::SwapActionParams,
token::TokenAndAccount,
},
Market, Seed,
};
#[account(zero_copy)]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Deposit {
pub(crate) header: ActionHeader,
pub(crate) tokens: DepositTokenAccounts,
pub(crate) params: DepositActionParams,
pub(crate) swap: SwapActionParams,
#[cfg_attr(feature = "debug", debug(skip))]
padding_0: [u8; 4],
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
#[cfg_attr(feature = "debug", debug(skip))]
reserved: [u8; 128],
}
pub fn find_first_deposit_receiver_pda(store_program_id: &Pubkey) -> (Pubkey, u8) {
Pubkey::find_program_address(&[Deposit::FIRST_DEPOSIT_RECEIVER_SEED], store_program_id)
}
impl InitSpace for Deposit {
const INIT_SPACE: usize = std::mem::size_of::<Self>();
}
impl Closable for Deposit {
type ClosedEvent = DepositRemoved;
fn to_closed_event(&self, address: &Pubkey, reason: &str) -> Result<Self::ClosedEvent> {
DepositRemoved::new(
self.header.id,
self.header.store,
*address,
self.tokens.market_token(),
self.header.owner,
self.header.action_state()?,
reason,
)
}
}
impl Deposit {
pub const FIRST_DEPOSIT_RECEIVER_SEED: &'static [u8] = b"first_deposit_receiver";
pub fn first_deposit_receiver() -> Pubkey {
find_first_deposit_receiver_pda(&crate::ID).0
}
pub fn tokens(&self) -> &DepositTokenAccounts {
&self.tokens
}
pub fn swap(&self) -> &SwapActionParams {
&self.swap
}
pub(crate) fn validate_first_deposit(
receiver: &Pubkey,
min_amount: u64,
market: &Market,
) -> Result<()> {
let min_tokens_for_first_deposit = market
.get_config_by_key(MarketConfigKey::MinTokensForFirstDeposit)
.ok_or_else(|| error!(CoreError::Unimplemented))?;
if *min_tokens_for_first_deposit == 0 {
return Ok(());
}
require_keys_eq!(
*receiver,
Self::first_deposit_receiver(),
CoreError::InvalidReceiverForFirstDeposit
);
require_gte!(
min_amount as u128,
*min_tokens_for_first_deposit,
CoreError::NotEnoughMarketTokenAmountForFirstDeposit
);
Ok(())
}
}
impl Seed for Deposit {
const SEED: &'static [u8] = b"deposit";
}
impl Action for Deposit {
const MIN_EXECUTION_LAMPORTS: u64 = 200_000;
fn header(&self) -> &ActionHeader {
&self.header
}
}
#[zero_copy]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DepositTokenAccounts {
pub initial_long_token: TokenAndAccount,
pub initial_short_token: TokenAndAccount,
pub(crate) market_token: TokenAndAccount,
#[cfg_attr(feature = "debug", debug(skip))]
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
reserved: [u8; 128],
}
impl DepositTokenAccounts {
pub fn market_token(&self) -> Pubkey {
self.market_token.token().expect("must exist")
}
pub fn market_token_account(&self) -> Pubkey {
self.market_token.account().expect("must exist")
}
}
#[zero_copy]
#[cfg_attr(feature = "debug", derive(derive_more::Debug))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct DepositActionParams {
pub(crate) initial_long_token_amount: u64,
pub(crate) initial_short_token_amount: u64,
pub(crate) min_market_token_amount: u64,
#[cfg_attr(feature = "serde", serde(with = "serde_bytes"))]
#[cfg_attr(feature = "debug", debug(skip))]
reserved: [u8; 64],
}
impl Default for DepositActionParams {
fn default() -> Self {
Self {
initial_long_token_amount: 0,
initial_short_token_amount: 0,
min_market_token_amount: 0,
reserved: [0; 64],
}
}
}
impl DepositActionParams {
pub(crate) fn validate_market_token_amount(&self, minted: u64) -> Result<()> {
require_gte!(
minted,
self.min_market_token_amount,
CoreError::InsufficientOutputAmount
);
Ok(())
}
}