Optimize exchange contract (#3926)
This commit is contained in:
@ -96,7 +96,7 @@ where
|
|||||||
let clients: Vec<_> = clients.into_iter().map(Arc::new).collect();
|
let clients: Vec<_> = clients.into_iter().map(Arc::new).collect();
|
||||||
let client = clients[0].as_ref();
|
let client = clients[0].as_ref();
|
||||||
|
|
||||||
let total_keys = accounts_in_groups as u64 * 5;
|
let total_keys = accounts_in_groups as u64 * 4;
|
||||||
info!("Generating {:?} keys", total_keys);
|
info!("Generating {:?} keys", total_keys);
|
||||||
let mut keypairs = generate_keypairs(total_keys);
|
let mut keypairs = generate_keypairs(total_keys);
|
||||||
let trader_signers: Vec<_> = keypairs
|
let trader_signers: Vec<_> = keypairs
|
||||||
@ -111,10 +111,6 @@ where
|
|||||||
.drain(0..accounts_in_groups)
|
.drain(0..accounts_in_groups)
|
||||||
.map(|keypair| keypair.pubkey())
|
.map(|keypair| keypair.pubkey())
|
||||||
.collect();
|
.collect();
|
||||||
let dst_pubkeys: Vec<_> = keypairs
|
|
||||||
.drain(0..accounts_in_groups)
|
|
||||||
.map(|keypair| keypair.pubkey())
|
|
||||||
.collect();
|
|
||||||
let profit_pubkeys: Vec<_> = keypairs
|
let profit_pubkeys: Vec<_> = keypairs
|
||||||
.drain(0..accounts_in_groups)
|
.drain(0..accounts_in_groups)
|
||||||
.map(|keypair| keypair.pubkey())
|
.map(|keypair| keypair.pubkey())
|
||||||
@ -127,8 +123,6 @@ where
|
|||||||
|
|
||||||
info!("Create {:?} source token accounts", src_pubkeys.len());
|
info!("Create {:?} source token accounts", src_pubkeys.len());
|
||||||
create_token_accounts(client, &trader_signers, &src_pubkeys);
|
create_token_accounts(client, &trader_signers, &src_pubkeys);
|
||||||
info!("Create {:?} destination token accounts", dst_pubkeys.len());
|
|
||||||
create_token_accounts(client, &trader_signers, &dst_pubkeys);
|
|
||||||
info!("Create {:?} profit token accounts", profit_pubkeys.len());
|
info!("Create {:?} profit token accounts", profit_pubkeys.len());
|
||||||
create_token_accounts(client, &swapper_signers, &profit_pubkeys);
|
create_token_accounts(client, &swapper_signers, &profit_pubkeys);
|
||||||
|
|
||||||
@ -216,7 +210,6 @@ where
|
|||||||
&shared_tx_active_thread_count,
|
&shared_tx_active_thread_count,
|
||||||
&trader_signers,
|
&trader_signers,
|
||||||
&src_pubkeys,
|
&src_pubkeys,
|
||||||
&dst_pubkeys,
|
|
||||||
trade_delay,
|
trade_delay,
|
||||||
batch_size,
|
batch_size,
|
||||||
account_groups,
|
account_groups,
|
||||||
@ -432,18 +425,16 @@ fn swapper<T>(
|
|||||||
stats.total += swaps_size as u64;
|
stats.total += swaps_size as u64;
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let swap_keys = generate_keypairs(swaps_size as u64);
|
|
||||||
|
|
||||||
let mut to_swap = vec![];
|
let mut to_swap = vec![];
|
||||||
let start = account_group * swaps_size as usize;
|
let start = account_group * swaps_size as usize;
|
||||||
let end = account_group * swaps_size as usize + batch_size as usize;
|
let end = account_group * swaps_size as usize + batch_size as usize;
|
||||||
for (signer, swap, swap_key, profit) in izip!(
|
for (signer, swap, profit) in izip!(
|
||||||
signers[start..end].iter(),
|
signers[start..end].iter(),
|
||||||
swaps,
|
swaps,
|
||||||
swap_keys,
|
|
||||||
profit_pubkeys[start..end].iter(),
|
profit_pubkeys[start..end].iter(),
|
||||||
) {
|
) {
|
||||||
to_swap.push((signer, swap_key, swap, profit));
|
to_swap.push((signer, swap, profit));
|
||||||
}
|
}
|
||||||
account_group = (account_group + 1) % account_groups as usize;
|
account_group = (account_group + 1) % account_groups as usize;
|
||||||
let duration = now.elapsed();
|
let duration = now.elapsed();
|
||||||
@ -471,30 +462,17 @@ fn swapper<T>(
|
|||||||
|
|
||||||
let to_swap_txs: Vec<_> = to_swap
|
let to_swap_txs: Vec<_> = to_swap
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|(signer, swap_key, swap, profit)| {
|
.map(|(signer, swap, profit)| {
|
||||||
let s: &Keypair = &signer;
|
let s: &Keypair = &signer;
|
||||||
let owner = &signer.pubkey();
|
let owner = &signer.pubkey();
|
||||||
let space = mem::size_of::<ExchangeState>() as u64;
|
|
||||||
Transaction::new_signed_instructions(
|
Transaction::new_signed_instructions(
|
||||||
&[s],
|
&[s],
|
||||||
vec![
|
vec![exchange_instruction::swap_request(
|
||||||
system_instruction::create_account(
|
|
||||||
owner,
|
owner,
|
||||||
&swap_key.pubkey(),
|
|
||||||
1,
|
|
||||||
space,
|
|
||||||
&id(),
|
|
||||||
),
|
|
||||||
exchange_instruction::swap_request(
|
|
||||||
owner,
|
|
||||||
&swap_key.pubkey(),
|
|
||||||
&swap.0.pubkey,
|
&swap.0.pubkey,
|
||||||
&swap.1.pubkey,
|
&swap.1.pubkey,
|
||||||
&swap.0.info.dst_account,
|
|
||||||
&swap.1.info.dst_account,
|
|
||||||
&profit,
|
&profit,
|
||||||
),
|
)],
|
||||||
],
|
|
||||||
blockhash,
|
blockhash,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -562,7 +540,6 @@ fn trader<T>(
|
|||||||
shared_tx_active_thread_count: &Arc<AtomicIsize>,
|
shared_tx_active_thread_count: &Arc<AtomicIsize>,
|
||||||
signers: &[Arc<Keypair>],
|
signers: &[Arc<Keypair>],
|
||||||
srcs: &[Pubkey],
|
srcs: &[Pubkey],
|
||||||
dsts: &[Pubkey],
|
|
||||||
delay: u64,
|
delay: u64,
|
||||||
batch_size: usize,
|
batch_size: usize,
|
||||||
account_groups: usize,
|
account_groups: usize,
|
||||||
@ -593,11 +570,10 @@ fn trader<T>(
|
|||||||
let start = account_group * batch_size as usize;
|
let start = account_group * batch_size as usize;
|
||||||
let end = account_group * batch_size as usize + batch_size as usize;
|
let end = account_group * batch_size as usize + batch_size as usize;
|
||||||
let mut direction = Direction::To;
|
let mut direction = Direction::To;
|
||||||
for (signer, trade, src, dst) in izip!(
|
for (signer, trade, src) in izip!(
|
||||||
signers[start..end].iter(),
|
signers[start..end].iter(),
|
||||||
trade_keys,
|
trade_keys,
|
||||||
srcs[start..end].iter(),
|
srcs[start..end].iter(),
|
||||||
dsts[start..end].iter()
|
|
||||||
) {
|
) {
|
||||||
direction = if direction == Direction::To {
|
direction = if direction == Direction::To {
|
||||||
Direction::From
|
Direction::From
|
||||||
@ -611,14 +587,13 @@ fn trader<T>(
|
|||||||
pair,
|
pair,
|
||||||
tokens,
|
tokens,
|
||||||
price,
|
price,
|
||||||
src_account: Pubkey::default(), // don't care
|
tokens_settled: 0,
|
||||||
dst_account: *dst,
|
|
||||||
};
|
};
|
||||||
trade_infos.push(TradeInfo {
|
trade_infos.push(TradeInfo {
|
||||||
trade_account: trade.pubkey(),
|
trade_account: trade.pubkey(),
|
||||||
order_info,
|
order_info,
|
||||||
});
|
});
|
||||||
trades.push((signer, trade.pubkey(), direction, src, dst));
|
trades.push((signer, trade.pubkey(), direction, src));
|
||||||
}
|
}
|
||||||
account_group = (account_group + 1) % account_groups as usize;
|
account_group = (account_group + 1) % account_groups as usize;
|
||||||
let duration = now.elapsed();
|
let duration = now.elapsed();
|
||||||
@ -647,7 +622,7 @@ fn trader<T>(
|
|||||||
|
|
||||||
let trades_txs: Vec<_> = chunk
|
let trades_txs: Vec<_> = chunk
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.map(|(signer, trade, direction, src, dst)| {
|
.map(|(signer, trade, direction, src)| {
|
||||||
let s: &Keypair = &signer;
|
let s: &Keypair = &signer;
|
||||||
let owner = &signer.pubkey();
|
let owner = &signer.pubkey();
|
||||||
let space = mem::size_of::<ExchangeState>() as u64;
|
let space = mem::size_of::<ExchangeState>() as u64;
|
||||||
@ -656,7 +631,7 @@ fn trader<T>(
|
|||||||
vec![
|
vec![
|
||||||
system_instruction::create_account(owner, trade, 1, space, &id()),
|
system_instruction::create_account(owner, trade, 1, space, &id()),
|
||||||
exchange_instruction::trade_request(
|
exchange_instruction::trade_request(
|
||||||
owner, trade, *direction, pair, tokens, price, src, dst,
|
owner, trade, *direction, pair, tokens, price, src,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
blockhash,
|
blockhash,
|
||||||
|
@ -20,9 +20,6 @@ pub struct TradeRequestInfo {
|
|||||||
/// The price ratio the primary price over the secondary price. The primary price is fixed
|
/// The price ratio the primary price over the secondary price. The primary price is fixed
|
||||||
/// and equal to the variable `SCALER`.
|
/// and equal to the variable `SCALER`.
|
||||||
pub price: u64,
|
pub price: u64,
|
||||||
|
|
||||||
/// Token account to deposit tokens on successful swap
|
|
||||||
pub dst_account: Pubkey,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
|
||||||
@ -40,8 +37,8 @@ pub enum ExchangeInstruction {
|
|||||||
|
|
||||||
/// Trade request
|
/// Trade request
|
||||||
/// key 0 - Signer
|
/// key 0 - Signer
|
||||||
/// key 1 - Account in which to record the swap
|
/// key 1 - Account in which to record the trade order
|
||||||
/// key 2 - Token account associated with this trade
|
/// key 2 - Token account to source tokens from
|
||||||
TradeRequest(TradeRequestInfo),
|
TradeRequest(TradeRequestInfo),
|
||||||
|
|
||||||
/// Trade cancellation
|
/// Trade cancellation
|
||||||
@ -51,11 +48,8 @@ pub enum ExchangeInstruction {
|
|||||||
|
|
||||||
/// Trade swap request
|
/// Trade swap request
|
||||||
/// key 0 - Signer
|
/// key 0 - Signer
|
||||||
/// key 1 - Account in which to record the swap
|
|
||||||
/// key 2 - 'To' trade order
|
/// key 2 - 'To' trade order
|
||||||
/// key 3 - `From` trade order
|
/// key 3 - `From` trade order
|
||||||
/// key 4 - Token account associated with the To Trade
|
|
||||||
/// key 5 - Token account associated with From trade
|
|
||||||
/// key 6 - Token account in which to deposit the brokers profit from the swap.
|
/// key 6 - Token account in which to deposit the brokers profit from the swap.
|
||||||
SwapRequest,
|
SwapRequest,
|
||||||
}
|
}
|
||||||
@ -95,7 +89,6 @@ pub fn trade_request(
|
|||||||
tokens: u64,
|
tokens: u64,
|
||||||
price: u64,
|
price: u64,
|
||||||
src_account: &Pubkey,
|
src_account: &Pubkey,
|
||||||
dst_account: &Pubkey,
|
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
AccountMeta::new(*owner, true),
|
AccountMeta::new(*owner, true),
|
||||||
@ -109,37 +102,29 @@ pub fn trade_request(
|
|||||||
pair,
|
pair,
|
||||||
tokens,
|
tokens,
|
||||||
price,
|
price,
|
||||||
dst_account: *dst_account,
|
|
||||||
}),
|
}),
|
||||||
account_metas,
|
account_metas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trade_cancellation(owner: &Pubkey, trade: &Pubkey, account: &Pubkey) -> Instruction {
|
pub fn trade_cancellation(owner: &Pubkey, trade: &Pubkey) -> Instruction {
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
AccountMeta::new(*owner, true),
|
AccountMeta::new(*owner, true),
|
||||||
AccountMeta::new(*trade, false),
|
AccountMeta::new(*trade, false),
|
||||||
AccountMeta::new(*account, false),
|
|
||||||
];
|
];
|
||||||
Instruction::new(id(), &ExchangeInstruction::TradeCancellation, account_metas)
|
Instruction::new(id(), &ExchangeInstruction::TradeCancellation, account_metas)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn swap_request(
|
pub fn swap_request(
|
||||||
owner: &Pubkey,
|
owner: &Pubkey,
|
||||||
swap: &Pubkey,
|
|
||||||
to_trade: &Pubkey,
|
to_trade: &Pubkey,
|
||||||
from_trade: &Pubkey,
|
from_trade: &Pubkey,
|
||||||
to_trade_account: &Pubkey,
|
|
||||||
from_trade_account: &Pubkey,
|
|
||||||
profit_account: &Pubkey,
|
profit_account: &Pubkey,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
AccountMeta::new(*owner, true),
|
AccountMeta::new(*owner, true),
|
||||||
AccountMeta::new(*swap, false),
|
|
||||||
AccountMeta::new(*to_trade, false),
|
AccountMeta::new(*to_trade, false),
|
||||||
AccountMeta::new(*from_trade, false),
|
AccountMeta::new(*from_trade, false),
|
||||||
AccountMeta::new(*to_trade_account, false),
|
|
||||||
AccountMeta::new(*from_trade_account, false),
|
|
||||||
AccountMeta::new(*profit_account, false),
|
AccountMeta::new(*profit_account, false),
|
||||||
];
|
];
|
||||||
Instruction::new(id(), &ExchangeInstruction::SwapRequest, account_metas)
|
Instruction::new(id(), &ExchangeInstruction::SwapRequest, account_metas)
|
||||||
|
@ -15,7 +15,7 @@ pub struct ExchangeProcessor {}
|
|||||||
impl ExchangeProcessor {
|
impl ExchangeProcessor {
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
fn map_to_invalid_arg(err: std::boxed::Box<bincode::ErrorKind>) -> InstructionError {
|
fn map_to_invalid_arg(err: std::boxed::Box<bincode::ErrorKind>) -> InstructionError {
|
||||||
warn!("Deserialze failed: {:?}", err);
|
warn!("Deserialize failed, not a valid state: {:?}", err);
|
||||||
InstructionError::InvalidArgument
|
InstructionError::InvalidArgument
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,13 +60,23 @@ impl ExchangeProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trade_to_token_account(trade: &TradeOrderInfo) -> TokenAccountInfo {
|
||||||
|
// Turn trade order into token account
|
||||||
|
|
||||||
|
let token = match trade.direction {
|
||||||
|
Direction::To => trade.pair.secondary(),
|
||||||
|
Direction::From => trade.pair.primary(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut account = TokenAccountInfo::default().owner(&trade.owner);
|
||||||
|
account.tokens[token] = trade.tokens_settled;
|
||||||
|
account
|
||||||
|
}
|
||||||
|
|
||||||
fn calculate_swap(
|
fn calculate_swap(
|
||||||
scaler: u64,
|
scaler: u64,
|
||||||
swap: &mut TradeSwapInfo,
|
|
||||||
to_trade: &mut TradeOrderInfo,
|
to_trade: &mut TradeOrderInfo,
|
||||||
from_trade: &mut TradeOrderInfo,
|
from_trade: &mut TradeOrderInfo,
|
||||||
to_trade_account: &mut TokenAccountInfo,
|
|
||||||
from_trade_account: &mut TokenAccountInfo,
|
|
||||||
profit_account: &mut TokenAccountInfo,
|
profit_account: &mut TokenAccountInfo,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
if to_trade.tokens == 0 || from_trade.tokens == 0 {
|
if to_trade.tokens == 0 || from_trade.tokens == 0 {
|
||||||
@ -124,7 +134,7 @@ impl ExchangeProcessor {
|
|||||||
let primary_token = to_trade.pair.primary();
|
let primary_token = to_trade.pair.primary();
|
||||||
let secondary_token = from_trade.pair.secondary();
|
let secondary_token = from_trade.pair.secondary();
|
||||||
|
|
||||||
// Update tokens/accounts
|
// Update tokens
|
||||||
|
|
||||||
if to_trade.tokens < primary_cost {
|
if to_trade.tokens < primary_cost {
|
||||||
error!("Not enough tokens in to account");
|
error!("Not enough tokens in to account");
|
||||||
@ -135,20 +145,13 @@ impl ExchangeProcessor {
|
|||||||
Err(InstructionError::InvalidArgument)?
|
Err(InstructionError::InvalidArgument)?
|
||||||
}
|
}
|
||||||
to_trade.tokens -= primary_cost;
|
to_trade.tokens -= primary_cost;
|
||||||
|
to_trade.tokens_settled += secondary_tokens;
|
||||||
from_trade.tokens -= secondary_cost;
|
from_trade.tokens -= secondary_cost;
|
||||||
|
from_trade.tokens_settled += primary_tokens;
|
||||||
to_trade_account.tokens[secondary_token] += secondary_tokens;
|
|
||||||
from_trade_account.tokens[primary_token] += primary_tokens;
|
|
||||||
|
|
||||||
profit_account.tokens[primary_token] += primary_profit;
|
profit_account.tokens[primary_token] += primary_profit;
|
||||||
profit_account.tokens[secondary_token] += secondary_profit;
|
profit_account.tokens[secondary_token] += secondary_profit;
|
||||||
|
|
||||||
swap.pair = to_trade.pair;
|
|
||||||
swap.primary_tokens = primary_cost;
|
|
||||||
swap.primary_price = to_trade.price;
|
|
||||||
swap.secondary_tokens = secondary_cost;
|
|
||||||
swap.secondary_price = from_trade.price;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,9 +195,11 @@ impl ExchangeProcessor {
|
|||||||
if &id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() {
|
if &id() == keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() {
|
||||||
to_account.tokens[token] += tokens;
|
to_account.tokens[token] += tokens;
|
||||||
} else {
|
} else {
|
||||||
let mut from_account =
|
let state: ExchangeState =
|
||||||
Self::deserialize_account(&keyed_accounts[FROM_ACCOUNT_INDEX].account.data)?;
|
bincode::deserialize(&keyed_accounts[FROM_ACCOUNT_INDEX].account.data)
|
||||||
|
.map_err(Self::map_to_invalid_arg)?;
|
||||||
|
match state {
|
||||||
|
ExchangeState::Account(mut from_account) => {
|
||||||
if &from_account.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
|
if &from_account.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
|
||||||
error!("Signer does not own from account");
|
error!("Signer does not own from account");
|
||||||
Err(InstructionError::GenericError)?
|
Err(InstructionError::GenericError)?
|
||||||
@ -213,6 +218,40 @@ impl ExchangeProcessor {
|
|||||||
&mut keyed_accounts[FROM_ACCOUNT_INDEX].account.data,
|
&mut keyed_accounts[FROM_ACCOUNT_INDEX].account.data,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
ExchangeState::Trade(mut from_trade) => {
|
||||||
|
if &from_trade.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
|
||||||
|
error!("Signer does not own from account");
|
||||||
|
Err(InstructionError::GenericError)?
|
||||||
|
}
|
||||||
|
|
||||||
|
let from_token = match from_trade.direction {
|
||||||
|
Direction::To => from_trade.pair.secondary(),
|
||||||
|
Direction::From => from_trade.pair.primary(),
|
||||||
|
};
|
||||||
|
if token != from_token {
|
||||||
|
error!("Trade to transfer from does not hold correct token");
|
||||||
|
Err(InstructionError::GenericError)?
|
||||||
|
}
|
||||||
|
|
||||||
|
if from_trade.tokens_settled < tokens {
|
||||||
|
error!("From trade balance too low");
|
||||||
|
Err(InstructionError::GenericError)?
|
||||||
|
}
|
||||||
|
|
||||||
|
from_trade.tokens_settled -= tokens;
|
||||||
|
to_account.tokens[token] += tokens;
|
||||||
|
|
||||||
|
Self::serialize(
|
||||||
|
&ExchangeState::Trade(from_trade),
|
||||||
|
&mut keyed_accounts[FROM_ACCOUNT_INDEX].account.data,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("Not a valid from account for transfer");
|
||||||
|
Err(InstructionError::InvalidArgument)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Self::serialize(
|
Self::serialize(
|
||||||
&ExchangeState::Account(to_account),
|
&ExchangeState::Account(to_account),
|
||||||
@ -266,8 +305,7 @@ impl ExchangeProcessor {
|
|||||||
pair: info.pair,
|
pair: info.pair,
|
||||||
tokens: info.tokens,
|
tokens: info.tokens,
|
||||||
price: info.price,
|
price: info.price,
|
||||||
src_account: *keyed_accounts[ACCOUNT_INDEX].unsigned_key(),
|
tokens_settled: 0,
|
||||||
dst_account: info.dst_account,
|
|
||||||
}),
|
}),
|
||||||
&mut keyed_accounts[TRADE_INDEX].account.data,
|
&mut keyed_accounts[TRADE_INDEX].account.data,
|
||||||
)?;
|
)?;
|
||||||
@ -280,78 +318,51 @@ impl ExchangeProcessor {
|
|||||||
fn do_trade_cancellation(keyed_accounts: &mut [KeyedAccount]) -> Result<(), InstructionError> {
|
fn do_trade_cancellation(keyed_accounts: &mut [KeyedAccount]) -> Result<(), InstructionError> {
|
||||||
const OWNER_INDEX: usize = 0;
|
const OWNER_INDEX: usize = 0;
|
||||||
const TRADE_INDEX: usize = 1;
|
const TRADE_INDEX: usize = 1;
|
||||||
const ACCOUNT_INDEX: usize = 2;
|
|
||||||
|
|
||||||
if keyed_accounts.len() < 3 {
|
if keyed_accounts.len() < 2 {
|
||||||
error!("Not enough accounts");
|
error!("Not enough accounts");
|
||||||
Err(InstructionError::InvalidArgument)?
|
Err(InstructionError::InvalidArgument)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut trade = Self::deserialize_trade(&keyed_accounts[TRADE_INDEX].account.data)?;
|
let trade = Self::deserialize_trade(&keyed_accounts[TRADE_INDEX].account.data)?;
|
||||||
let mut account = Self::deserialize_account(&keyed_accounts[ACCOUNT_INDEX].account.data)?;
|
|
||||||
|
|
||||||
if &trade.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
|
if &trade.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
|
||||||
error!("Signer does not own trade");
|
error!("Signer does not own trade");
|
||||||
Err(InstructionError::GenericError)?
|
Err(InstructionError::GenericError)?
|
||||||
}
|
}
|
||||||
|
|
||||||
if &account.owner != keyed_accounts[OWNER_INDEX].unsigned_key() {
|
|
||||||
error!("Signer does not own account");
|
|
||||||
Err(InstructionError::GenericError)?
|
|
||||||
}
|
|
||||||
|
|
||||||
let token = match trade.direction {
|
let token = match trade.direction {
|
||||||
Direction::To => trade.pair.primary(),
|
Direction::To => trade.pair.primary(),
|
||||||
Direction::From => trade.pair.secondary(),
|
Direction::From => trade.pair.secondary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Outstanding tokens transferred back to account
|
let mut account = TokenAccountInfo::default().owner(&trade.owner);
|
||||||
account.tokens[token] += trade.tokens;
|
account.tokens[token] = trade.tokens;
|
||||||
// Trade becomes invalid
|
account.tokens[token] += trade.tokens_settled;
|
||||||
trade.tokens = 0;
|
|
||||||
|
|
||||||
Self::serialize(
|
// Turn trade order into a token account
|
||||||
&ExchangeState::Trade(trade),
|
|
||||||
&mut keyed_accounts[TRADE_INDEX].account.data,
|
|
||||||
)?;
|
|
||||||
Self::serialize(
|
Self::serialize(
|
||||||
&ExchangeState::Account(account),
|
&ExchangeState::Account(account),
|
||||||
&mut keyed_accounts[ACCOUNT_INDEX].account.data,
|
&mut keyed_accounts[TRADE_INDEX].account.data,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_swap_request(keyed_accounts: &mut [KeyedAccount]) -> Result<(), InstructionError> {
|
fn do_swap_request(keyed_accounts: &mut [KeyedAccount]) -> Result<(), InstructionError> {
|
||||||
const SWAP_ACCOUNT_INDEX: usize = 1;
|
const TO_TRADE_INDEX: usize = 1;
|
||||||
const TO_TRADE_INDEX: usize = 2;
|
const FROM_TRADE_INDEX: usize = 2;
|
||||||
const FROM_TRADE_INDEX: usize = 3;
|
const PROFIT_ACCOUNT_INDEX: usize = 3;
|
||||||
const TO_ACCOUNT_INDEX: usize = 4;
|
|
||||||
const FROM_ACCOUNT_INDEX: usize = 5;
|
|
||||||
const PROFIT_ACCOUNT_INDEX: usize = 6;
|
|
||||||
|
|
||||||
if keyed_accounts.len() < 7 {
|
if keyed_accounts.len() < 4 {
|
||||||
error!("Not enough accounts");
|
error!("Not enough accounts");
|
||||||
Err(InstructionError::InvalidArgument)?
|
Err(InstructionError::InvalidArgument)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::is_account_unallocated(&keyed_accounts[SWAP_ACCOUNT_INDEX].account.data)?;
|
|
||||||
let mut to_trade = Self::deserialize_trade(&keyed_accounts[TO_TRADE_INDEX].account.data)?;
|
let mut to_trade = Self::deserialize_trade(&keyed_accounts[TO_TRADE_INDEX].account.data)?;
|
||||||
let mut from_trade =
|
let mut from_trade =
|
||||||
Self::deserialize_trade(&keyed_accounts[FROM_TRADE_INDEX].account.data)?;
|
Self::deserialize_trade(&keyed_accounts[FROM_TRADE_INDEX].account.data)?;
|
||||||
let mut to_trade_account =
|
|
||||||
Self::deserialize_account(&keyed_accounts[TO_ACCOUNT_INDEX].account.data)?;
|
|
||||||
let mut from_trade_account =
|
|
||||||
Self::deserialize_account(&keyed_accounts[FROM_ACCOUNT_INDEX].account.data)?;
|
|
||||||
let mut profit_account =
|
let mut profit_account =
|
||||||
Self::deserialize_account(&keyed_accounts[PROFIT_ACCOUNT_INDEX].account.data)?;
|
Self::deserialize_account(&keyed_accounts[PROFIT_ACCOUNT_INDEX].account.data)?;
|
||||||
|
|
||||||
if &to_trade.dst_account != keyed_accounts[TO_ACCOUNT_INDEX].unsigned_key() {
|
|
||||||
error!("To trade account and to account differ");
|
|
||||||
Err(InstructionError::InvalidArgument)?
|
|
||||||
}
|
|
||||||
if &from_trade.dst_account != keyed_accounts[FROM_ACCOUNT_INDEX].unsigned_key() {
|
|
||||||
error!("From trade account and from account differ");
|
|
||||||
Err(InstructionError::InvalidArgument)?
|
|
||||||
}
|
|
||||||
if to_trade.direction != Direction::To {
|
if to_trade.direction != Direction::To {
|
||||||
error!("To trade is not a To");
|
error!("To trade is not a To");
|
||||||
Err(InstructionError::InvalidArgument)?
|
Err(InstructionError::InvalidArgument)?
|
||||||
@ -369,19 +380,9 @@ impl ExchangeProcessor {
|
|||||||
Err(InstructionError::InvalidArgument)?
|
Err(InstructionError::InvalidArgument)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut swap = TradeSwapInfo::default();
|
if let Err(e) =
|
||||||
swap.to_trade_order = *keyed_accounts[TO_TRADE_INDEX].unsigned_key();
|
Self::calculate_swap(SCALER, &mut to_trade, &mut from_trade, &mut profit_account)
|
||||||
swap.from_trade_order = *keyed_accounts[FROM_TRADE_INDEX].unsigned_key();
|
{
|
||||||
|
|
||||||
if let Err(e) = Self::calculate_swap(
|
|
||||||
SCALER,
|
|
||||||
&mut swap,
|
|
||||||
&mut to_trade,
|
|
||||||
&mut from_trade,
|
|
||||||
&mut to_trade_account,
|
|
||||||
&mut from_trade_account,
|
|
||||||
&mut profit_account,
|
|
||||||
) {
|
|
||||||
error!(
|
error!(
|
||||||
"Swap calculation failed from {} for {} to {} for {}",
|
"Swap calculation failed from {} for {} to {} for {}",
|
||||||
from_trade.tokens, from_trade.price, to_trade.tokens, to_trade.price,
|
from_trade.tokens, from_trade.price, to_trade.tokens, to_trade.price,
|
||||||
@ -391,26 +392,32 @@ impl ExchangeProcessor {
|
|||||||
|
|
||||||
inc_new_counter_info!("exchange_processor-swap", 1);
|
inc_new_counter_info!("exchange_processor-swap", 1);
|
||||||
|
|
||||||
|
if to_trade.tokens == 0 {
|
||||||
|
// Turn into token account
|
||||||
Self::serialize(
|
Self::serialize(
|
||||||
&ExchangeState::Swap(swap),
|
&ExchangeState::Account(Self::trade_to_token_account(&from_trade)),
|
||||||
&mut keyed_accounts[SWAP_ACCOUNT_INDEX].account.data,
|
&mut keyed_accounts[TO_TRADE_INDEX].account.data,
|
||||||
)?;
|
)?;
|
||||||
|
} else {
|
||||||
Self::serialize(
|
Self::serialize(
|
||||||
&ExchangeState::Trade(to_trade),
|
&ExchangeState::Trade(to_trade),
|
||||||
&mut keyed_accounts[TO_TRADE_INDEX].account.data,
|
&mut keyed_accounts[TO_TRADE_INDEX].account.data,
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if from_trade.tokens == 0 {
|
||||||
|
// Turn into token account
|
||||||
|
Self::serialize(
|
||||||
|
&ExchangeState::Account(Self::trade_to_token_account(&from_trade)),
|
||||||
|
&mut keyed_accounts[FROM_TRADE_INDEX].account.data,
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
Self::serialize(
|
Self::serialize(
|
||||||
&ExchangeState::Trade(from_trade),
|
&ExchangeState::Trade(from_trade),
|
||||||
&mut keyed_accounts[FROM_TRADE_INDEX].account.data,
|
&mut keyed_accounts[FROM_TRADE_INDEX].account.data,
|
||||||
)?;
|
)?;
|
||||||
Self::serialize(
|
}
|
||||||
&ExchangeState::Account(to_trade_account),
|
|
||||||
&mut keyed_accounts[TO_ACCOUNT_INDEX].account.data,
|
|
||||||
)?;
|
|
||||||
Self::serialize(
|
|
||||||
&ExchangeState::Account(from_trade_account),
|
|
||||||
&mut keyed_accounts[FROM_ACCOUNT_INDEX].account.data,
|
|
||||||
)?;
|
|
||||||
Self::serialize(
|
Self::serialize(
|
||||||
&ExchangeState::Account(profit_account),
|
&ExchangeState::Account(profit_account),
|
||||||
&mut keyed_accounts[PROFIT_ACCOUNT_INDEX].account.data,
|
&mut keyed_accounts[PROFIT_ACCOUNT_INDEX].account.data,
|
||||||
@ -470,8 +477,8 @@ mod test {
|
|||||||
secondary_price: u64,
|
secondary_price: u64,
|
||||||
primary_tokens_expect: u64,
|
primary_tokens_expect: u64,
|
||||||
secondary_tokens_expect: u64,
|
secondary_tokens_expect: u64,
|
||||||
primary_account_tokens: Tokens,
|
primary_tokens_settled_expect: u64,
|
||||||
secondary_account_tokens: Tokens,
|
secondary_tokens_settled_expect: u64,
|
||||||
profit_account_tokens: Tokens,
|
profit_account_tokens: Tokens,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
trace!(
|
trace!(
|
||||||
@ -481,11 +488,8 @@ mod test {
|
|||||||
secondary_tokens,
|
secondary_tokens,
|
||||||
secondary_price,
|
secondary_price,
|
||||||
);
|
);
|
||||||
let mut swap = TradeSwapInfo::default();
|
|
||||||
let mut to_trade = TradeOrderInfo::default();
|
let mut to_trade = TradeOrderInfo::default();
|
||||||
let mut from_trade = TradeOrderInfo::default().direction(Direction::From);
|
let mut from_trade = TradeOrderInfo::default().direction(Direction::From);
|
||||||
let mut to_account = TokenAccountInfo::default();
|
|
||||||
let mut from_account = TokenAccountInfo::default();
|
|
||||||
let mut profit_account = TokenAccountInfo::default();
|
let mut profit_account = TokenAccountInfo::default();
|
||||||
|
|
||||||
to_trade.tokens = primary_tokens;
|
to_trade.tokens = primary_tokens;
|
||||||
@ -494,37 +498,28 @@ mod test {
|
|||||||
from_trade.price = secondary_price;
|
from_trade.price = secondary_price;
|
||||||
ExchangeProcessor::calculate_swap(
|
ExchangeProcessor::calculate_swap(
|
||||||
scaler,
|
scaler,
|
||||||
&mut swap,
|
|
||||||
&mut to_trade,
|
&mut to_trade,
|
||||||
&mut from_trade,
|
&mut from_trade,
|
||||||
&mut to_account,
|
|
||||||
&mut from_account,
|
|
||||||
&mut profit_account,
|
&mut profit_account,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"{:?} {:?} {:?} {:?}\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n{:?}",
|
"{:?} {:?} {:?} {:?}\n{:?}\n{:?}\n{:?}\n{:?}",
|
||||||
to_trade.tokens,
|
to_trade.tokens,
|
||||||
primary_tokens_expect,
|
primary_tokens_expect,
|
||||||
from_trade.tokens,
|
from_trade.tokens,
|
||||||
secondary_tokens_expect,
|
secondary_tokens_expect,
|
||||||
to_account.tokens,
|
primary_tokens_settled_expect,
|
||||||
primary_account_tokens,
|
secondary_tokens_settled_expect,
|
||||||
from_account.tokens,
|
|
||||||
secondary_account_tokens,
|
|
||||||
profit_account.tokens,
|
profit_account.tokens,
|
||||||
profit_account_tokens
|
profit_account_tokens
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(to_trade.tokens, primary_tokens_expect);
|
assert_eq!(to_trade.tokens, primary_tokens_expect);
|
||||||
assert_eq!(from_trade.tokens, secondary_tokens_expect);
|
assert_eq!(from_trade.tokens, secondary_tokens_expect);
|
||||||
assert_eq!(to_account.tokens, primary_account_tokens);
|
assert_eq!(to_trade.tokens_settled, primary_tokens_settled_expect);
|
||||||
assert_eq!(from_account.tokens, secondary_account_tokens);
|
assert_eq!(from_trade.tokens_settled, secondary_tokens_settled_expect);
|
||||||
assert_eq!(profit_account.tokens, profit_account_tokens);
|
assert_eq!(profit_account.tokens, profit_account_tokens);
|
||||||
assert_eq!(swap.primary_tokens, primary_tokens - to_trade.tokens);
|
|
||||||
assert_eq!(swap.primary_price, to_trade.price);
|
|
||||||
assert_eq!(swap.secondary_tokens, secondary_tokens - from_trade.tokens);
|
|
||||||
assert_eq!(swap.secondary_price, from_trade.price);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,22 +528,22 @@ mod test {
|
|||||||
fn test_calculate_swap() {
|
fn test_calculate_swap() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
||||||
try_calc(1, 50, 2, 50, 1, 0, 0, Tokens::new(0, 50, 0, 0), Tokens::new( 50, 0, 0, 0), Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
try_calc(1, 50, 2, 50, 1, 0, 0, 50, 50, Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
||||||
try_calc(1, 50, 1, 0, 1, 0, 0, Tokens::new(0, 50, 0, 0), Tokens::new( 50, 0, 0, 0), Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
try_calc(1, 50, 1, 0, 1, 0, 0, 50, 50, Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
||||||
try_calc(1, 0, 1, 50, 1, 0, 0, Tokens::new(0, 50, 0, 0), Tokens::new( 50, 0, 0, 0), Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
try_calc(1, 0, 1, 50, 1, 0, 0, 50, 50, Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
||||||
try_calc(1, 50, 1, 50, 0, 0, 0, Tokens::new(0, 50, 0, 0), Tokens::new( 50, 0, 0, 0), Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
try_calc(1, 50, 1, 50, 0, 0, 0, 50, 50, Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
||||||
try_calc(1, 50, 0, 50, 1, 0, 0, Tokens::new(0, 50, 0, 0), Tokens::new( 50, 0, 0, 0), Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
try_calc(1, 50, 0, 50, 1, 0, 0, 50, 50, Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
||||||
try_calc(1, 1, 2, 2, 3, 1, 2, Tokens::new(0, 0, 0, 0), Tokens::new( 0, 0, 0, 0), Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
try_calc(1, 1, 2, 2, 3, 1, 2, 0, 0, Tokens::new( 0, 0, 0, 0)).unwrap_err();
|
||||||
|
|
||||||
try_calc(1, 50, 1, 50, 1, 0, 0, Tokens::new(0, 50, 0, 0), Tokens::new( 50, 0, 0, 0), Tokens::new( 0, 0, 0, 0)).unwrap();
|
try_calc(1, 50, 1, 50, 1, 0, 0, 50, 50, Tokens::new( 0, 0, 0, 0)).unwrap();
|
||||||
try_calc(1, 1, 2, 3, 3, 0, 0, Tokens::new(0, 2, 0, 0), Tokens::new( 1, 0, 0, 0), Tokens::new( 0, 1, 0, 0)).unwrap();
|
try_calc(1, 1, 2, 3, 3, 0, 0, 2, 1, Tokens::new( 0, 1, 0, 0)).unwrap();
|
||||||
try_calc(1, 2, 2, 3, 3, 1, 0, Tokens::new(0, 2, 0, 0), Tokens::new( 1, 0, 0, 0), Tokens::new( 0, 1, 0, 0)).unwrap();
|
try_calc(1, 2, 2, 3, 3, 1, 0, 2, 1, Tokens::new( 0, 1, 0, 0)).unwrap();
|
||||||
try_calc(1, 3, 2, 3, 3, 2, 0, Tokens::new(0, 2, 0, 0), Tokens::new( 1, 0, 0, 0), Tokens::new( 0, 1, 0, 0)).unwrap();
|
try_calc(1, 3, 2, 3, 3, 2, 0, 2, 1, Tokens::new( 0, 1, 0, 0)).unwrap();
|
||||||
try_calc(1, 3, 2, 6, 3, 1, 0, Tokens::new(0, 4, 0, 0), Tokens::new( 2, 0, 0, 0), Tokens::new( 0, 2, 0, 0)).unwrap();
|
try_calc(1, 3, 2, 6, 3, 1, 0, 4, 2, Tokens::new( 0, 2, 0, 0)).unwrap();
|
||||||
try_calc(1000, 1, 2000, 3, 3000, 0, 0, Tokens::new(0, 2, 0, 0), Tokens::new( 1, 0, 0, 0), Tokens::new( 0, 1, 0, 0)).unwrap();
|
try_calc(1000, 1, 2000, 3, 3000, 0, 0, 2, 1, Tokens::new( 0, 1, 0, 0)).unwrap();
|
||||||
try_calc(1, 3, 2, 7, 3, 1, 1, Tokens::new(0, 4, 0, 0), Tokens::new( 2, 0, 0, 0), Tokens::new( 0, 2, 0, 0)).unwrap();
|
try_calc(1, 3, 2, 7, 3, 1, 1, 4, 2, Tokens::new( 0, 2, 0, 0)).unwrap();
|
||||||
try_calc(1000, 3000, 333, 1000, 500, 0, 1, Tokens::new(0, 999, 0, 0), Tokens::new(1998, 0, 0, 0), Tokens::new(1002, 0, 0, 0)).unwrap();
|
try_calc(1000, 3000, 333, 1000, 500, 0, 1,999, 1998, Tokens::new(1002, 0, 0, 0)).unwrap();
|
||||||
try_calc(1000, 50, 100, 50, 101, 0,45, Tokens::new(0, 5, 0, 0), Tokens::new( 49, 0, 0, 0), Tokens::new( 1, 0, 0, 0)).unwrap();
|
try_calc(1000, 50, 100, 50, 101, 0,45, 5, 49, Tokens::new( 1, 0, 0, 0)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_bank(lamports: u64) -> (Bank, Keypair) {
|
fn create_bank(lamports: u64) -> (Bank, Keypair) {
|
||||||
@ -619,10 +614,9 @@ mod test {
|
|||||||
src_tokens: u64,
|
src_tokens: u64,
|
||||||
trade_tokens: u64,
|
trade_tokens: u64,
|
||||||
price: u64,
|
price: u64,
|
||||||
) -> (Pubkey, Pubkey, Pubkey) {
|
) -> (Pubkey, Pubkey) {
|
||||||
let trade = create_account(&client, &owner);
|
let trade = create_account(&client, &owner);
|
||||||
let src = create_token_account(&client, &owner);
|
let src = create_token_account(&client, &owner);
|
||||||
let dst = create_token_account(&client, &owner);
|
|
||||||
transfer(&client, &owner, &src, from_token, src_tokens);
|
transfer(&client, &owner, &src, from_token, src_tokens);
|
||||||
|
|
||||||
let instruction = exchange_instruction::trade_request(
|
let instruction = exchange_instruction::trade_request(
|
||||||
@ -633,21 +627,11 @@ mod test {
|
|||||||
trade_tokens,
|
trade_tokens,
|
||||||
price,
|
price,
|
||||||
&src,
|
&src,
|
||||||
&dst,
|
|
||||||
);
|
);
|
||||||
client
|
client
|
||||||
.send_instruction(owner, instruction)
|
.send_instruction(owner, instruction)
|
||||||
.expect(&format!("{}:{}", line!(), file!()));
|
.expect(&format!("{}:{}", line!(), file!()));
|
||||||
(trade, src, dst)
|
(trade, src)
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_swap(data: &[u8]) -> TradeSwapInfo {
|
|
||||||
let state: ExchangeState =
|
|
||||||
bincode::deserialize(data).expect(&format!("{}:{}", line!(), file!()));
|
|
||||||
match state {
|
|
||||||
ExchangeState::Swap(info) => info,
|
|
||||||
_ => panic!("Not a valid swap"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -714,7 +698,7 @@ mod test {
|
|||||||
let (bank, mint_keypair) = create_bank(10_000);
|
let (bank, mint_keypair) = create_bank(10_000);
|
||||||
let (client, owner) = create_client(bank, mint_keypair);
|
let (client, owner) = create_client(bank, mint_keypair);
|
||||||
|
|
||||||
let (trade, src, dst) = trade(
|
let (trade, src) = trade(
|
||||||
&client,
|
&client,
|
||||||
&owner,
|
&owner,
|
||||||
Direction::To,
|
Direction::To,
|
||||||
@ -727,7 +711,6 @@ mod test {
|
|||||||
|
|
||||||
let trade_account_data = client.get_account_data(&trade).unwrap().unwrap();
|
let trade_account_data = client.get_account_data(&trade).unwrap().unwrap();
|
||||||
let src_account_data = client.get_account_data(&src).unwrap().unwrap();
|
let src_account_data = client.get_account_data(&src).unwrap().unwrap();
|
||||||
let dst_account_data = client.get_account_data(&dst).unwrap().unwrap();
|
|
||||||
|
|
||||||
// check results
|
// check results
|
||||||
|
|
||||||
@ -738,8 +721,7 @@ mod test {
|
|||||||
pair: TokenPair::AB,
|
pair: TokenPair::AB,
|
||||||
tokens: 2,
|
tokens: 2,
|
||||||
price: 1000,
|
price: 1000,
|
||||||
src_account: src,
|
tokens_settled: 0
|
||||||
dst_account: dst
|
|
||||||
},
|
},
|
||||||
ExchangeProcessor::deserialize_trade(&trade_account_data).unwrap()
|
ExchangeProcessor::deserialize_trade(&trade_account_data).unwrap()
|
||||||
);
|
);
|
||||||
@ -749,12 +731,6 @@ mod test {
|
|||||||
.tokens(100_040, 100_000, 100_000, 100_000),
|
.tokens(100_040, 100_000, 100_000, 100_000),
|
||||||
ExchangeProcessor::deserialize_account(&src_account_data).unwrap()
|
ExchangeProcessor::deserialize_account(&src_account_data).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
TokenAccountInfo::default()
|
|
||||||
.owner(&owner.pubkey())
|
|
||||||
.tokens(100_000, 100_000, 100_000, 100_000),
|
|
||||||
ExchangeProcessor::deserialize_account(&dst_account_data).unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -763,9 +739,8 @@ mod test {
|
|||||||
let (bank, mint_keypair) = create_bank(10_000);
|
let (bank, mint_keypair) = create_bank(10_000);
|
||||||
let (client, owner) = create_client(bank, mint_keypair);
|
let (client, owner) = create_client(bank, mint_keypair);
|
||||||
|
|
||||||
let swap = create_account(&client, &owner);
|
|
||||||
let profit = create_token_account(&client, &owner);
|
let profit = create_token_account(&client, &owner);
|
||||||
let (to_trade, to_src, to_dst) = trade(
|
let (to_trade, _) = trade(
|
||||||
&client,
|
&client,
|
||||||
&owner,
|
&owner,
|
||||||
Direction::To,
|
Direction::To,
|
||||||
@ -775,7 +750,7 @@ mod test {
|
|||||||
2,
|
2,
|
||||||
2000,
|
2000,
|
||||||
);
|
);
|
||||||
let (from_trade, from_src, from_dst) = trade(
|
let (from_trade, _) = trade(
|
||||||
&client,
|
&client,
|
||||||
&owner,
|
&owner,
|
||||||
Direction::From,
|
Direction::From,
|
||||||
@ -786,27 +761,15 @@ mod test {
|
|||||||
3000,
|
3000,
|
||||||
);
|
);
|
||||||
|
|
||||||
let instruction = exchange_instruction::swap_request(
|
let instruction =
|
||||||
&owner.pubkey(),
|
exchange_instruction::swap_request(&owner.pubkey(), &to_trade, &from_trade, &profit);
|
||||||
&swap,
|
|
||||||
&to_trade,
|
|
||||||
&from_trade,
|
|
||||||
&to_dst,
|
|
||||||
&from_dst,
|
|
||||||
&profit,
|
|
||||||
);
|
|
||||||
client
|
client
|
||||||
.send_instruction(&owner, instruction)
|
.send_instruction(&owner, instruction)
|
||||||
.expect(&format!("{}:{}", line!(), file!()));
|
.expect(&format!("{}:{}", line!(), file!()));
|
||||||
|
|
||||||
let to_trade_account_data = client.get_account_data(&to_trade).unwrap().unwrap();
|
let to_trade_account_data = client.get_account_data(&to_trade).unwrap().unwrap();
|
||||||
let to_src_account_data = client.get_account_data(&to_src).unwrap().unwrap();
|
|
||||||
let to_dst_account_data = client.get_account_data(&to_dst).unwrap().unwrap();
|
|
||||||
let from_trade_account_data = client.get_account_data(&from_trade).unwrap().unwrap();
|
let from_trade_account_data = client.get_account_data(&from_trade).unwrap().unwrap();
|
||||||
let from_src_account_data = client.get_account_data(&from_src).unwrap().unwrap();
|
|
||||||
let from_dst_account_data = client.get_account_data(&from_dst).unwrap().unwrap();
|
|
||||||
let profit_account_data = client.get_account_data(&profit).unwrap().unwrap();
|
let profit_account_data = client.get_account_data(&profit).unwrap().unwrap();
|
||||||
let swap_account_data = client.get_account_data(&swap).unwrap().unwrap();
|
|
||||||
|
|
||||||
// check results
|
// check results
|
||||||
|
|
||||||
@ -817,64 +780,83 @@ mod test {
|
|||||||
pair: TokenPair::AB,
|
pair: TokenPair::AB,
|
||||||
tokens: 1,
|
tokens: 1,
|
||||||
price: 2000,
|
price: 2000,
|
||||||
src_account: to_src,
|
tokens_settled: 2,
|
||||||
dst_account: to_dst
|
|
||||||
},
|
},
|
||||||
ExchangeProcessor::deserialize_trade(&to_trade_account_data).unwrap()
|
ExchangeProcessor::deserialize_trade(&to_trade_account_data).unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TokenAccountInfo::default()
|
TokenAccountInfo::default()
|
||||||
.owner(&owner.pubkey())
|
.owner(&owner.pubkey())
|
||||||
.tokens(100_000, 100_000, 100_000, 100_000),
|
.tokens(1, 0, 0, 0),
|
||||||
ExchangeProcessor::deserialize_account(&to_src_account_data).unwrap()
|
ExchangeProcessor::deserialize_account(&from_trade_account_data).unwrap()
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
TokenAccountInfo::default()
|
|
||||||
.owner(&owner.pubkey())
|
|
||||||
.tokens(100_000, 100_002, 100_000, 100_000),
|
|
||||||
ExchangeProcessor::deserialize_account(&to_dst_account_data).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
TradeOrderInfo {
|
|
||||||
owner: owner.pubkey(),
|
|
||||||
direction: Direction::From,
|
|
||||||
pair: TokenPair::AB,
|
|
||||||
tokens: 0,
|
|
||||||
price: 3000,
|
|
||||||
src_account: from_src,
|
|
||||||
dst_account: from_dst
|
|
||||||
},
|
|
||||||
ExchangeProcessor::deserialize_trade(&from_trade_account_data).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
TokenAccountInfo::default()
|
|
||||||
.owner(&owner.pubkey())
|
|
||||||
.tokens(100_000, 100_000, 100_000, 100_000),
|
|
||||||
ExchangeProcessor::deserialize_account(&from_src_account_data).unwrap()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
TokenAccountInfo::default()
|
|
||||||
.owner(&owner.pubkey())
|
|
||||||
.tokens(100_001, 100_000, 100_000, 100_000),
|
|
||||||
ExchangeProcessor::deserialize_account(&from_dst_account_data).unwrap()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TokenAccountInfo::default()
|
TokenAccountInfo::default()
|
||||||
.owner(&owner.pubkey())
|
.owner(&owner.pubkey())
|
||||||
.tokens(100_000, 100_001, 100_000, 100_000),
|
.tokens(100_000, 100_001, 100_000, 100_000),
|
||||||
ExchangeProcessor::deserialize_account(&profit_account_data).unwrap()
|
ExchangeProcessor::deserialize_account(&profit_account_data).unwrap()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_exchange_trade_to_token_account() {
|
||||||
|
solana_logger::setup();
|
||||||
|
let (bank, mint_keypair) = create_bank(10_000);
|
||||||
|
let (client, owner) = create_client(bank, mint_keypair);
|
||||||
|
|
||||||
|
let profit = create_token_account(&client, &owner);
|
||||||
|
let (to_trade, _) = trade(
|
||||||
|
&client,
|
||||||
|
&owner,
|
||||||
|
Direction::To,
|
||||||
|
TokenPair::AB,
|
||||||
|
Token::A,
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
2000,
|
||||||
|
);
|
||||||
|
let (from_trade, _) = trade(
|
||||||
|
&client,
|
||||||
|
&owner,
|
||||||
|
Direction::From,
|
||||||
|
TokenPair::AB,
|
||||||
|
Token::B,
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
3000,
|
||||||
|
);
|
||||||
|
|
||||||
|
let instruction =
|
||||||
|
exchange_instruction::swap_request(&owner.pubkey(), &to_trade, &from_trade, &profit);
|
||||||
|
client
|
||||||
|
.send_instruction(&owner, instruction)
|
||||||
|
.expect(&format!("{}:{}", line!(), file!()));
|
||||||
|
|
||||||
|
let new = create_token_account(&client, &owner);
|
||||||
|
|
||||||
|
let instruction =
|
||||||
|
exchange_instruction::transfer_request(&owner.pubkey(), &new, &to_trade, Token::B, 1);
|
||||||
|
client
|
||||||
|
.send_instruction(&owner, instruction)
|
||||||
|
.expect(&format!("{}:{}", line!(), file!()));
|
||||||
|
|
||||||
|
let instruction =
|
||||||
|
exchange_instruction::transfer_request(&owner.pubkey(), &new, &from_trade, Token::A, 1);
|
||||||
|
client
|
||||||
|
.send_instruction(&owner, instruction)
|
||||||
|
.expect(&format!("{}:{}", line!(), file!()));
|
||||||
|
|
||||||
|
let new_account_data = client.get_account_data(&new).unwrap().unwrap();
|
||||||
|
|
||||||
|
// Check results
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
TradeSwapInfo {
|
TokenAccountInfo::default()
|
||||||
pair: TokenPair::AB,
|
.owner(&owner.pubkey())
|
||||||
to_trade_order: to_trade,
|
.tokens(100_001, 100_001, 100_000, 100_000),
|
||||||
from_trade_order: from_trade,
|
ExchangeProcessor::deserialize_account(&new_account_data).unwrap()
|
||||||
primary_tokens: 1,
|
|
||||||
primary_price: 2000,
|
|
||||||
secondary_tokens: 3,
|
|
||||||
secondary_price: 3000,
|
|
||||||
},
|
|
||||||
deserialize_swap(&swap_account_data)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,16 +157,15 @@ pub struct TradeOrderInfo {
|
|||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
/// Token pair indicating two tokens to exchange, first is primary
|
/// Token pair indicating two tokens to exchange, first is primary
|
||||||
pub pair: TokenPair,
|
pub pair: TokenPair,
|
||||||
/// Number of tokens to exchange; primary or secondary depending on direction
|
/// Number of tokens to exchange; primary or secondary depending on direction. Once
|
||||||
|
/// this number goes to zero this trade order will be converted into a regular token account
|
||||||
pub tokens: u64,
|
pub tokens: u64,
|
||||||
/// Scaled price of the secondary token given the primary is equal to the scale value
|
/// Scaled price of the secondary token given the primary is equal to the scale value
|
||||||
/// If scale is 1 and price is 2 then ratio is 1:2 or 1 primary token for 2 secondary tokens
|
/// If scale is 1 and price is 2 then ratio is 1:2 or 1 primary token for 2 secondary tokens
|
||||||
pub price: u64,
|
pub price: u64,
|
||||||
/// account which the tokens were source from. The trade account holds the tokens in escrow
|
/// Number of tokens that have been settled so far. These nay be transferred to another
|
||||||
/// until either one or more part of a swap or the trade is cancelled.
|
/// token account by the owner.
|
||||||
pub src_account: Pubkey,
|
pub tokens_settled: u64,
|
||||||
/// account which the tokens the tokens will be deposited into on a successful trade
|
|
||||||
pub dst_account: Pubkey,
|
|
||||||
}
|
}
|
||||||
impl Default for TradeOrderInfo {
|
impl Default for TradeOrderInfo {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -176,8 +175,7 @@ impl Default for TradeOrderInfo {
|
|||||||
direction: Direction::To,
|
direction: Direction::To,
|
||||||
tokens: 0,
|
tokens: 0,
|
||||||
price: 0,
|
price: 0,
|
||||||
src_account: Pubkey::default(),
|
tokens_settled: 0,
|
||||||
dst_account: Pubkey::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,25 +220,6 @@ pub fn check_trade(direction: Direction, tokens: u64, price: u64) -> Result<(),
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Swap accounts are populated with this structure
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
|
||||||
pub struct TradeSwapInfo {
|
|
||||||
/// Pair swapped
|
|
||||||
pub pair: TokenPair,
|
|
||||||
/// `To` trade order
|
|
||||||
pub to_trade_order: Pubkey,
|
|
||||||
/// `From` trade order
|
|
||||||
pub from_trade_order: Pubkey,
|
|
||||||
/// Number of primary tokens exchanged
|
|
||||||
pub primary_tokens: u64,
|
|
||||||
/// Price the primary tokens were exchanged for
|
|
||||||
pub primary_price: u64,
|
|
||||||
/// Number of secondary tokens exchanged
|
|
||||||
pub secondary_tokens: u64,
|
|
||||||
/// Price the secondary tokens were exchanged for
|
|
||||||
pub secondary_price: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type of exchange account, account's user data is populated with this enum
|
/// Type of exchange account, account's user data is populated with this enum
|
||||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
pub enum ExchangeState {
|
pub enum ExchangeState {
|
||||||
@ -250,8 +229,6 @@ pub enum ExchangeState {
|
|||||||
Account(TokenAccountInfo),
|
Account(TokenAccountInfo),
|
||||||
// Trade order account
|
// Trade order account
|
||||||
Trade(TradeOrderInfo),
|
Trade(TradeOrderInfo),
|
||||||
// Swap account
|
|
||||||
Swap(TradeSwapInfo),
|
|
||||||
Invalid,
|
Invalid,
|
||||||
}
|
}
|
||||||
impl Default for ExchangeState {
|
impl Default for ExchangeState {
|
||||||
|
Reference in New Issue
Block a user