anchor/spl/src/dex/proxy.rs

178 lines
6.2 KiB
Rust

use crate::dex;
use crate::dex::middleware::{Context, ErrorCode, MarketMiddleware};
use anchor_lang::prelude::*;
use anchor_lang::solana_program::program;
use anchor_lang::solana_program::pubkey::Pubkey;
use serum_dex::instruction::*;
/// MarketProxy provides an abstraction for implementing proxy programs to the
/// Serum orderbook, allowing one to implement a middleware for the purposes
/// of intercepting and modifying requests before being relayed to the
/// orderbook.
///
/// The only requirement for a middleware is that, when all are done processing,
/// a valid DEX instruction--accounts and instruction data--must be left to
/// forward to the orderbook program.
#[derive(Default)]
pub struct MarketProxy<'a> {
middlewares: Vec<&'a mut dyn MarketMiddleware>,
}
impl<'a> MarketProxy<'a> {
/// Constructs a new `MarketProxy`.
pub fn new() -> Self {
Self {
middlewares: Vec::new(),
}
}
/// Builder method for adding a middleware to the proxy.
pub fn middleware(mut self, mw: &'a mut dyn MarketMiddleware) -> Self {
self.middlewares.push(mw);
self
}
/// Entrypoint to the program.
pub fn run(
mut self,
program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let mut ix_data = data;
// First account is the Serum DEX executable--used for CPI.
let dex = &accounts[0];
require!(dex.key == &dex::ID, ErrorCode::InvalidTargetProgram);
let acc_infos = (&accounts[1..]).to_vec();
// Process the instruction data.
for mw in &mut self.middlewares {
mw.instruction(&mut ix_data)?;
}
// Request context.
let mut ctx = Context::new(program_id, dex.key, acc_infos);
// Decode instruction.
let ix = MarketInstruction::unpack(ix_data);
// Method dispatch.
match ix {
Some(MarketInstruction::InitOpenOrders) => {
require!(ctx.accounts.len() >= 4, ErrorCode::NotEnoughAccounts);
for mw in &self.middlewares {
mw.init_open_orders(&mut ctx)?;
}
}
Some(MarketInstruction::NewOrderV3(ix)) => {
require!(ctx.accounts.len() >= 12, ErrorCode::NotEnoughAccounts);
for mw in &self.middlewares {
mw.new_order_v3(&mut ctx, &ix)?;
}
}
Some(MarketInstruction::CancelOrderV2(ix)) => {
require!(ctx.accounts.len() >= 6, ErrorCode::NotEnoughAccounts);
for mw in &self.middlewares {
mw.cancel_order_v2(&mut ctx, &ix)?;
}
}
Some(MarketInstruction::CancelOrderByClientIdV2(ix)) => {
require!(ctx.accounts.len() >= 6, ErrorCode::NotEnoughAccounts);
for mw in &self.middlewares {
mw.cancel_order_by_client_id_v2(&mut ctx, ix)?;
}
}
Some(MarketInstruction::SettleFunds) => {
require!(ctx.accounts.len() >= 10, ErrorCode::NotEnoughAccounts);
for mw in &self.middlewares {
mw.settle_funds(&mut ctx)?;
}
}
Some(MarketInstruction::CloseOpenOrders) => {
require!(ctx.accounts.len() >= 4, ErrorCode::NotEnoughAccounts);
for mw in &self.middlewares {
mw.close_open_orders(&mut ctx)?;
}
}
Some(MarketInstruction::Prune(limit)) => {
require!(ctx.accounts.len() >= 7, ErrorCode::NotEnoughAccounts);
for mw in &self.middlewares {
mw.prune(&mut ctx, limit)?;
}
}
_ => {
for mw in &self.middlewares {
mw.fallback(&mut ctx)?;
}
return Ok(());
}
};
// Extract the middleware adjusted context.
let Context {
seeds,
accounts,
pre_instructions,
post_instructions,
..
} = ctx;
// Execute pre instructions.
for (ix, acc_infos, seeds) in pre_instructions {
let tmp_signers: Vec<Vec<&[u8]>> = seeds
.iter()
.map(|seeds| {
let seeds: Vec<&[u8]> = seeds.iter().map(|seed| &seed[..]).collect();
seeds
})
.collect();
let signers: Vec<&[&[u8]]> = tmp_signers.iter().map(|seeds| &seeds[..]).collect();
program::invoke_signed(&ix, &acc_infos, &signers)?;
}
// Execute the main dex relay.
{
let tmp_signers: Vec<Vec<&[u8]>> = seeds
.iter()
.map(|seeds| {
let seeds: Vec<&[u8]> = seeds.iter().map(|seed| &seed[..]).collect();
seeds
})
.collect();
let signers: Vec<&[&[u8]]> = tmp_signers.iter().map(|seeds| &seeds[..]).collect();
// CPI to the DEX.
let dex_accounts = accounts
.iter()
.map(|acc| AccountMeta {
pubkey: *acc.key,
is_signer: acc.is_signer,
is_writable: acc.is_writable,
})
.collect();
let ix = anchor_lang::solana_program::instruction::Instruction {
data: ix_data.to_vec(),
accounts: dex_accounts,
program_id: dex::ID,
};
program::invoke_signed(&ix, &accounts, &signers)?;
}
// Execute post instructions.
for (ix, acc_infos, seeds) in post_instructions {
let tmp_signers: Vec<Vec<&[u8]>> = seeds
.iter()
.map(|seeds| {
let seeds: Vec<&[u8]> = seeds.iter().map(|seed| &seed[..]).collect();
seeds
})
.collect();
let signers: Vec<&[&[u8]]> = tmp_signers.iter().map(|seeds| &seeds[..]).collect();
program::invoke_signed(&ix, &acc_infos, &signers)?;
}
Ok(())
}
}