use anchor_lang::prelude::*;
use anchor_spl::token::{Mint, Token, TokenAccount};
use crate::{
constants,
events::EventEmitter,
ops::{
deposit::ExecuteDepositOperation,
execution_fee::PayExecutionFeeOperation,
market::{MarketTransferInOperation, MarketTransferOutOperation},
},
states::{
common::{
action::{ActionExt, ActionSigner},
swap::SwapActionParamsExt,
},
feature::{ActionDisabledFlag, DomainDisabledFlag},
Chainlink, Deposit, Market, Oracle, Seed, Store, TokenMapHeader, TokenMapLoader,
},
utils::internal,
CoreError,
};
#[event_cpi]
#[derive(Accounts)]
pub struct ExecuteDeposit<'info> {
pub authority: Signer<'info>,
#[account(has_one = token_map)]
pub store: AccountLoader<'info, Store>,
#[account(has_one = store)]
pub token_map: AccountLoader<'info, TokenMapHeader>,
#[account(mut, has_one = store)]
pub oracle: AccountLoader<'info, Oracle>,
#[account(mut, has_one = store)]
pub market: AccountLoader<'info, Market>,
#[account(
mut,
constraint = deposit.load()?.header.market == market.key() @ CoreError::MarketMismatched,
constraint = deposit.load()?.header.store == store.key() @ CoreError::StoreMismatched,
constraint = deposit.load()?.tokens.market_token.account().expect("must exist") == market_token_escrow.key() @ CoreError::MarketTokenAccountMismatched,
constraint = deposit.load()?.tokens.initial_long_token.account() == initial_long_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
constraint = deposit.load()?.tokens.initial_short_token.account() == initial_short_token_escrow.as_ref().map(|a| a.key()) @ CoreError::TokenAccountMismatched,
seeds = [Deposit::SEED, store.key().as_ref(), deposit.load()?.header.owner.as_ref(), &deposit.load()?.header.nonce],
bump = deposit.load()?.header.bump,
)]
pub deposit: AccountLoader<'info, Deposit>,
#[account(mut, constraint = market.load()?.meta().market_token_mint == market_token.key() @ CoreError::MarketTokenMintMismatched)]
pub market_token: Box<Account<'info, Mint>>,
#[account(
constraint = deposit.load()?.tokens.initial_long_token.token().map(|token| initial_long_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
)]
pub initial_long_token: Option<Box<Account<'info, Mint>>>,
#[account(
constraint = deposit.load()?.tokens.initial_short_token.token().map(|token| initial_short_token.key() == token).unwrap_or(true) @ CoreError::TokenMintMismatched
)]
pub initial_short_token: Option<Box<Account<'info, Mint>>>,
#[account(
mut,
associated_token::mint = market_token,
associated_token::authority = deposit,
)]
pub market_token_escrow: Box<Account<'info, TokenAccount>>,
#[account(
mut,
associated_token::mint = initial_long_token,
associated_token::authority = deposit,
)]
pub initial_long_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
#[account(
mut,
associated_token::mint = initial_short_token,
associated_token::authority = deposit,
)]
pub initial_short_token_escrow: Option<Box<Account<'info, TokenAccount>>>,
#[account(
mut,
token::mint = initial_long_token,
token::authority = store,
seeds = [
constants::MARKET_VAULT_SEED,
store.key().as_ref(),
initial_long_token_vault.mint.as_ref(),
],
bump,
)]
pub initial_long_token_vault: Option<Box<Account<'info, TokenAccount>>>,
#[account(
mut,
token::mint = initial_short_token,
token::authority = store,
seeds = [
constants::MARKET_VAULT_SEED,
store.key().as_ref(),
initial_short_token_vault.mint.as_ref(),
],
bump,
)]
pub initial_short_token_vault: Option<Box<Account<'info, TokenAccount>>>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
pub chainlink_program: Option<Program<'info, Chainlink>>,
}
#[inline(never)]
pub(crate) fn unchecked_execute_deposit<'info>(
ctx: Context<'_, '_, 'info, 'info, ExecuteDeposit<'info>>,
execution_fee: u64,
throw_on_execution_error: bool,
) -> Result<()> {
let accounts = ctx.accounts;
let remaining_accounts = ctx.remaining_accounts;
accounts
.store
.load()?
.validate_feature_enabled(DomainDisabledFlag::Deposit, ActionDisabledFlag::Execute)?;
let signer = accounts.deposit.load()?.signer();
let event_authority = accounts.event_authority.clone();
let event_emitter = EventEmitter::new(&event_authority, ctx.bumps.event_authority);
accounts.transfer_tokens_in(&signer, remaining_accounts, &event_emitter)?;
let executed =
accounts.perform_execution(remaining_accounts, throw_on_execution_error, &event_emitter)?;
if executed {
accounts.deposit.load_mut()?.header.completed()?;
} else {
accounts.deposit.load_mut()?.header.cancelled()?;
accounts.transfer_tokens_out(remaining_accounts, &event_emitter)?;
}
accounts.pay_execution_fee(execution_fee)?;
Ok(())
}
impl<'info> internal::Authentication<'info> for ExecuteDeposit<'info> {
fn authority(&self) -> &Signer<'info> {
&self.authority
}
fn store(&self) -> &AccountLoader<'info, Store> {
&self.store
}
}
impl<'info> ExecuteDeposit<'info> {
#[inline(never)]
fn pay_execution_fee(&self, execution_fee: u64) -> Result<()> {
let execution_lamports = self.deposit.load()?.execution_lamports(execution_fee);
PayExecutionFeeOperation::builder()
.payer(self.deposit.to_account_info())
.receiver(self.authority.to_account_info())
.execution_lamports(execution_lamports)
.build()
.execute()?;
Ok(())
}
#[inline(never)]
fn transfer_tokens_in(
&self,
signer: &ActionSigner,
remaining_accounts: &'info [AccountInfo<'info>],
event_emitter: &EventEmitter<'_, 'info>,
) -> Result<()> {
let seeds = signer.as_seeds();
let builder = MarketTransferInOperation::builder()
.store(&self.store)
.from_authority(self.deposit.to_account_info())
.token_program(self.token_program.to_account_info())
.signer_seeds(&seeds)
.event_emitter(*event_emitter);
let store = &self.store.key();
if let Some(escrow) = self.initial_long_token_escrow.as_ref() {
let market = self
.deposit
.load()?
.swap
.find_and_unpack_first_market(store, true, remaining_accounts)?
.unwrap_or(self.market.clone());
let vault = self
.initial_long_token_vault
.as_ref()
.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
builder
.clone()
.market(&market)
.from(escrow.to_account_info())
.vault(vault)
.amount(self.deposit.load()?.params.initial_long_token_amount)
.build()
.execute()?;
}
if let Some(escrow) = self.initial_short_token_escrow.as_ref() {
let market = self
.deposit
.load()?
.swap
.find_and_unpack_first_market(store, false, remaining_accounts)?
.unwrap_or(self.market.clone());
let vault = self
.initial_short_token_vault
.as_ref()
.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
builder
.clone()
.market(&market)
.from(escrow.to_account_info())
.vault(vault)
.amount(self.deposit.load()?.params.initial_short_token_amount)
.build()
.execute()?;
}
Ok(())
}
#[inline(never)]
fn transfer_tokens_out(
&self,
remaining_accounts: &'info [AccountInfo<'info>],
event_emitter: &EventEmitter<'_, 'info>,
) -> Result<()> {
let builder = MarketTransferOutOperation::builder()
.store(&self.store)
.token_program(self.token_program.to_account_info())
.event_emitter(*event_emitter);
let store = &self.store.key();
if let Some(escrow) = self.initial_long_token_escrow.as_ref() {
let market = self
.deposit
.load()?
.swap
.find_and_unpack_first_market(store, true, remaining_accounts)?
.unwrap_or(self.market.clone());
let vault = self
.initial_long_token_vault
.as_ref()
.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
let token = self
.initial_long_token
.as_ref()
.ok_or_else(|| error!(CoreError::TokenMintNotProvided))?;
builder
.clone()
.market(&market)
.to(escrow.to_account_info())
.vault(vault.to_account_info())
.amount(self.deposit.load()?.params.initial_long_token_amount)
.decimals(token.decimals)
.token_mint(token.to_account_info())
.build()
.execute()?;
}
if let Some(escrow) = self.initial_short_token_escrow.as_ref() {
let market = self
.deposit
.load()?
.swap
.find_and_unpack_first_market(store, false, remaining_accounts)?
.unwrap_or(self.market.clone());
let vault = self
.initial_short_token_vault
.as_ref()
.ok_or_else(|| error!(CoreError::TokenAccountNotProvided))?;
let token = self
.initial_short_token
.as_ref()
.ok_or_else(|| error!(CoreError::TokenMintNotProvided))?;
builder
.market(&market)
.to(escrow.to_account_info())
.vault(vault.to_account_info())
.amount(self.deposit.load()?.params.initial_short_token_amount)
.decimals(token.decimals)
.token_mint(token.to_account_info())
.build()
.execute()?;
}
Ok(())
}
#[inline(never)]
fn perform_execution(
&mut self,
remaining_accounts: &'info [AccountInfo<'info>],
throw_on_execution_error: bool,
event_emitter: &EventEmitter<'_, 'info>,
) -> Result<bool> {
let feeds = self
.deposit
.load()?
.swap()
.to_feeds(&self.token_map.load_token_map()?)
.map_err(CoreError::from)?;
let ops = ExecuteDepositOperation::builder()
.store(&self.store)
.market(&self.market)
.deposit(&self.deposit)
.market_token_mint(&mut self.market_token)
.market_token_receiver(self.market_token_escrow.to_account_info())
.token_program(self.token_program.to_account_info())
.throw_on_execution_error(throw_on_execution_error)
.event_emitter(*event_emitter);
let executed = self.oracle.load_mut()?.with_prices(
&self.store,
&self.token_map,
&feeds.tokens,
remaining_accounts,
self.chainlink_program.as_ref(),
|oracle, remaining_accounts| {
ops.oracle(oracle)
.remaining_accounts(remaining_accounts)
.build()
.execute()
},
)?;
Ok(executed)
}
}