// TODO: fees; limit-order to match existing orders; storage cost for limit orders use near_contract_standards::fungible_token::core_impl::ext_fungible_token; use near_contract_standards::fungible_token::receiver::FungibleTokenReceiver; use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use near_sdk::collections::LookupMap; use near_sdk::json_types::{ValidAccountId, U128}; use near_sdk::serde::Serialize; use near_sdk::{ env, ext_contract, near_bindgen, AccountId, Balance, Promise, PromiseOrValue, PromiseResult, PublicKey, }; use crate::pair::DirectedPair; use crate::price_point::OrderOrdinal; use crate::rate::Rate; mod heap; mod pair; mod price_point; mod rate; mod web4; near_sdk::setup_alloc!(); const NUM_TOKENS_ON_ACCOUNT: usize = 20; /// The cost of storing an account or an access key on the contract. 0.01N const BASE_COST: Balance = 10_000_000_000_000_000_000_000; /// The cost of registering a new token. 0.1N /// The idea here is that TokenId is 4 bytes, and thus only 4B tokens can be registered. /// With 0.1N per token that means 400M NEAR spent on tokens registration to saturate TokenIds. /// Possible, but not very plausible. const TOKEN_REGISTRATION_COST: Balance = 100_000_000_000_000_000_000_000; /// The cost of registering a pair. const PAIR_REGISTRATION_COST: Balance = 200_000_000_000_000_000_000_000; const DEFAULT_LIMIT_ORDERS_ALLOWANCE: u32 = 10; /// The methods that can be called with an access key added to the contract const EXCHANGE_METHODS: &[u8] = b"limit_order,market_order,cancel_order"; type TokenId = u32; #[derive(BorshDeserialize, BorshSerialize, Serialize, Copy, Clone, Debug, Eq, PartialEq)] #[serde(crate = "near_sdk::serde")] pub enum ExchangeError { TooManyTokens, InsufficientFunds, NoSuchAccount, NoSuchPair, NoSuchOrder, NoSuchToken, } #[derive(BorshDeserialize, BorshSerialize, Default, Copy, Clone)] struct TokenOnAccount { token_id: TokenId, balance: Balance, } #[derive(BorshDeserialize, BorshSerialize, Default, Clone)] struct Account { access_key: Option, tokens: [TokenOnAccount; NUM_TOKENS_ON_ACCOUNT], store_orders: bool, limit_orders_allowance: u32, } #[derive(BorshDeserialize, BorshSerialize, Serialize, Default, Clone, Eq, PartialEq, Debug)] #[serde(crate = "near_sdk::serde")] struct Order { account_id: AccountId, want_token_id: TokenId, provide_token_id: TokenId, want_amount: Balance, provide_amount: Balance, order_ord: OrderOrdinal, } #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize, Default, Copy, Clone)] pub struct Exchange { next_token_id: TokenId, } #[ext_contract(ext_self)] pub trait ExchangeCallbacks { fn internal_finish_withdrawal( &mut self, account_id: AccountId, token_id: TokenId, amount: U128, ); } #[ext_contract(ext_storage_mgmt)] pub trait StorageManagement { fn storage_deposit( &mut self, account_id: Option, registration_only: Option, ) -> StorageBalance; } impl Account { fn deposit(&mut self, token_id: TokenId, amount: Balance) -> Result<(), ExchangeError> { if amount == 0 { return Ok(()); } for i in 0..NUM_TOKENS_ON_ACCOUNT { if self.tokens[i].token_id == 0 || self.tokens[i].token_id == token_id { self.tokens[i].token_id = token_id; self.tokens[i].balance += amount; return Ok(()); } } Err(ExchangeError::TooManyTokens) } fn start_withdrawal( &mut self, token_id: TokenId, amount: Balance, ) -> Result<(), ExchangeError> { for i in 0..NUM_TOKENS_ON_ACCOUNT { if self.tokens[i].token_id == token_id { if self.tokens[i].balance < amount { return Err(ExchangeError::InsufficientFunds); } self.tokens[i].balance -= amount; return Ok(()); } } Err(ExchangeError::InsufficientFunds) } fn finish_withdrawal(&mut self, token_id: TokenId) { for i in 0..NUM_TOKENS_ON_ACCOUNT { if self.tokens[i].token_id == token_id && self.tokens[i].balance == 0 { self.tokens[i].token_id = 0; } } } fn withdraw(&mut self, token_id: TokenId, amount: Balance) -> Result<(), ExchangeError> { self.start_withdrawal(token_id, amount)?; self.finish_withdrawal(token_id); Ok(()) } fn get_balance(&self, token_id: TokenId) -> Balance { for i in 0..NUM_TOKENS_ON_ACCOUNT { if self.tokens[i].token_id == token_id { return self.tokens[i].balance; } } 0 } } // Orderbook endpoints #[near_bindgen] impl Exchange { pub fn limit_order( account_id: AccountId, want_token_id: TokenId, want_amount: U128, provide_token_id: TokenId, provide_amount: U128, max_orders_to_match: u32, ) -> Result { let mut accounts_map = Self::get_accounts_map(); let mut pairs_map = Self::get_pairs_map(); let pair_id = (want_token_id, provide_token_id); let want_amount = want_amount.into(); let provide_amount = provide_amount.into(); let max_orders_to_match = max_orders_to_match as usize; let mut account = accounts_map .get(&account_id) .ok_or(ExchangeError::NoSuchAccount)?; let mut pair = pairs_map.get(&pair_id).ok_or(ExchangeError::NoSuchPair)?; assert!( env::predecessor_account_id() == account_id || Some(env::signer_account_pk()) == account.access_key ); account.withdraw(provide_token_id, provide_amount)?; if max_orders_to_match != 0 { // TODO: market-match here } let order_ord = pair.place_order(want_amount, provide_amount); if account.store_orders { let mut first_orders = Self::get_first_orders_map(); let mut next_orders = Self::get_next_orders_map(); let mut prev_orders = Self::get_prev_orders_map(); let cur_order = Order { account_id: account_id.clone(), provide_token_id, want_token_id, provide_amount, want_amount, order_ord, }; let first_order = first_orders.insert(&account_id, &cur_order); if let Some(first_order) = first_order { next_orders.insert(&cur_order, &first_order); prev_orders.insert(&first_order, &cur_order); } } pairs_map.insert(&pair_id, &pair); accounts_map.insert(&account_id, &account); Ok(U128(order_ord)) } /// Returns the amount of `want_token_id` received, and the amount of `provide_token_id` spent. pub fn market_order( account_id: AccountId, want_token_id: TokenId, want_amount: U128, provide_token_id: TokenId, provide_max_amount: U128, max_orders_to_match: u32, ) -> Result<(U128, U128), ExchangeError> { let mut accounts_map = Self::get_accounts_map(); let mut pairs_map = Self::get_pairs_map(); let pair_id = (provide_token_id, want_token_id); let want_amount = want_amount.into(); let provide_max_amount = provide_max_amount.into(); let max_orders_to_match = max_orders_to_match as usize; let max_rate = Rate::new(provide_max_amount, want_amount); let mut account = accounts_map .get(&account_id) .ok_or(ExchangeError::NoSuchAccount)?; let mut pair = pairs_map.get(&pair_id).ok_or(ExchangeError::NoSuchPair)?; assert!( env::predecessor_account_id() == account_id || Some(env::signer_account_pk()) == account.access_key ); let (remaining_want_amount, spent_provided_amount) = pair.match_orders(want_amount, max_rate, max_orders_to_match); account.withdraw(provide_token_id, spent_provided_amount)?; account.deposit(want_token_id, want_amount - remaining_want_amount)?; pairs_map.insert(&pair_id, &pair); accounts_map.insert(&account_id, &account); Ok(( (want_amount - remaining_want_amount).into(), spent_provided_amount.into(), )) } pub fn cancel_order( account_id: AccountId, wanted_token_id: TokenId, wanted_amount: U128, provided_token_id: TokenId, provided_amount: U128, order_ord: U128, ) -> Result<(U128, U128), ExchangeError> { let mut accounts_map = Self::get_accounts_map(); let mut pairs_map = Self::get_pairs_map(); let pair_id = (wanted_token_id, provided_token_id); let wanted_amount = wanted_amount.into(); let provided_amount = provided_amount.into(); let order_ord = order_ord.into(); let mut account = accounts_map .get(&account_id) .ok_or(ExchangeError::NoSuchAccount)?; let mut pair = pairs_map.get(&pair_id).ok_or(ExchangeError::NoSuchPair)?; assert!( env::predecessor_account_id() == account_id || Some(env::signer_account_pk()) == account.access_key ); let (remaining_provided_amount, received_wanted_amount) = pair .remove_order(wanted_amount, provided_amount, order_ord) .ok_or(ExchangeError::NoSuchOrder)?; account.deposit(provided_token_id, remaining_provided_amount)?; account.deposit(wanted_token_id, received_wanted_amount)?; pairs_map.insert(&pair_id, &pair); accounts_map.insert(&account_id, &account); // Manage the linked list if account.store_orders { let mut first_orders = Self::get_first_orders_map(); let mut next_orders = Self::get_next_orders_map(); let mut prev_orders = Self::get_prev_orders_map(); let cur_order = Order { account_id: account_id.clone(), provide_token_id: provided_token_id, want_token_id: wanted_token_id, provide_amount: provided_amount, want_amount: wanted_amount, order_ord, }; let next_order = next_orders.remove(&cur_order); let prev_order = prev_orders.remove(&cur_order); let first_order = first_orders.get(&account_id); match (&next_order, &prev_order) { (Some(next_order), Some(prev_order)) => { next_orders.insert(prev_order, next_order); prev_orders.insert(next_order, prev_order); } (Some(next_order), None) => { prev_orders.remove(next_order); } (None, Some(prev_order)) => { next_orders.remove(prev_order); } (None, None) => {} }; if first_order == Some(cur_order.clone()) { match next_order { None => first_orders.remove(&account_id), Some(next_order) => first_orders.insert(&account_id, &next_order), }; } else { // If the first order is not the current order, the current must have the previous. // This assert is necessary. Otherwise if one calls `cancel_order` with // `provide_amount` and `want_amount` that result in the same bucket and rate, but // are not equal to the amounts provided, the order would get cancelled, but will // not be removed from the linked list (and thus would continue occupying space). assert!(prev_order.is_some()); } } Ok(( remaining_provided_amount.into(), received_wanted_amount.into(), )) } } // Accounts and tokens management endpoints #[near_bindgen] impl Exchange { pub fn is_account_registered(&self, account_id: AccountId) -> bool { Self::get_accounts_map().get(&account_id).is_some() } pub fn get_account_token_balance( &self, account_id: AccountId, token_address: AccountId, ) -> Option { let token_id = Self::get_token_address_to_token_id_map().get(&token_address)?; Some( Self::get_accounts_map() .get(&account_id)? .get_balance(token_id) .into(), ) } pub fn get_all_account_balances(&self, account_id: AccountId) -> Vec<(AccountId, U128)> { let token_map = Self::get_token_id_to_token_address_map(); match Self::get_accounts_map().get(&account_id) { None => vec![], Some(account) => account .tokens .iter() .filter_map(|x| { token_map .get(&x.token_id) .map(|token| (token.clone(), x.balance.into())) }) .collect(), } } pub fn get_account_orders( &self, account_id: AccountId, num_orders: usize, ) -> Vec<(AccountId, AccountId, U128, U128, U128)> { let tokens_map = Self::get_token_id_to_token_address_map(); let next_orders_map = Self::get_next_orders_map(); let mut maybe_order = Self::get_first_orders_map().get(&account_id); let mut ret = vec![]; for _ in 0..num_orders { if let Some(order) = maybe_order { ret.push(( tokens_map.get(&order.want_token_id).unwrap(), tokens_map.get(&order.provide_token_id).unwrap(), order.want_amount.into(), order.provide_amount.into(), order.order_ord.into(), )); maybe_order = next_orders_map.get(&order); } else { break; } } ret } /// Registers a new account. /// Expects 0.01N to be attached for the account itself, and additional 0.01N if the account /// wants to register an access key on the contract. /// The allowance on the access key will be the excess of the attached deposit. /// /// Is expected to be initiated from the account of the user. #[payable] pub fn register_account(access_key: Option, store_orders: bool) { let cost = if access_key.is_some() { BASE_COST * 2 } else { BASE_COST }; assert!(env::attached_deposit() >= cost); let account = Account { access_key: access_key.clone(), store_orders, limit_orders_allowance: DEFAULT_LIMIT_ORDERS_ALLOWANCE, ..Default::default() }; let old_value = Self::get_accounts_map().insert(&env::predecessor_account_id(), &account); assert!(old_value.is_none()); if let Some(access_key) = access_key { Promise::new(env::current_account_id()).add_access_key( access_key, env::attached_deposit() - BASE_COST * 2, env::current_account_id(), EXCHANGE_METHODS.to_vec(), ); } } /// Removes the access key from the account, and reimburses the 0.01N paid to register it. /// Is expected to be initiated from the exchange contract using the access key itself. pub fn remove_access_key(account_id: AccountId) { let mut account = Self::get_accounts_map().get(&account_id).unwrap(); assert_eq!(account.access_key, Some(env::signer_account_pk())); account.access_key = None; Self::get_accounts_map().insert(&account_id, &account); Promise::new(env::current_account_id()).delete_key(env::signer_account_pk()); Promise::new(account_id).transfer(BASE_COST); } #[payable] pub fn update_access_key(new_access_key: PublicKey) { let mut account = Self::get_accounts_map() .get(&env::predecessor_account_id()) .unwrap(); assert!(env::attached_deposit() >= BASE_COST); account.access_key = Some(new_access_key.clone()); Promise::new(env::current_account_id()).add_access_key( new_access_key, env::attached_deposit() - BASE_COST * 2, env::current_account_id(), EXCHANGE_METHODS.to_vec(), ); Self::get_accounts_map().insert(&env::predecessor_account_id(), &account); } pub fn unregister_account() { let account = Self::get_accounts_map() .remove(&env::predecessor_account_id()) .unwrap(); for i in 0..NUM_TOKENS_ON_ACCOUNT { assert_eq!(account.tokens[i].balance, 0, "Account has non-zero balance"); } Promise::new(env::predecessor_account_id()).transfer(BASE_COST); } #[payable] pub fn register_token(&mut self, token_address: AccountId) { assert_eq!(env::attached_deposit(), TOKEN_REGISTRATION_COST); let mut token_id_token_address_map = Self::get_token_id_to_token_address_map(); let mut token_address_token_id_map = Self::get_token_address_to_token_id_map(); let token_id = self.next_token_id; self.next_token_id += 1; let old_value = token_address_token_id_map.insert(&token_address, &token_id); assert!(old_value.is_none()); token_id_token_address_map.insert(&token_id, &token_address); ext_storage_mgmt::storage_deposit( None, None, &token_address, BASE_COST, 50_000_000_000_000, ); } #[payable] pub fn register_pair(&mut self, token_id1: TokenId, token_id2: TokenId) { assert_eq!(env::attached_deposit(), PAIR_REGISTRATION_COST); let mut pairs_map = Self::get_pairs_map(); let pair1 = DirectedPair::new((1u128 << 64) + ((token_id1 as u128) << 32) + (token_id2 as u128)); let pair2 = DirectedPair::new((1u128 << 64) + ((token_id2 as u128) << 32) + (token_id1 as u128)); let r1 = pairs_map.insert(&(token_id1, token_id2), &pair1); let r2 = pairs_map.insert(&(token_id2, token_id1), &pair2); assert!(r1.is_none() && r2.is_none()); } pub fn does_pair_exist(&self, token_address1: AccountId, token_address2: AccountId) -> bool { let token_id1 = Self::get_token_address_to_token_id_map() .get(&token_address1) .expect("One of the two tokens is not registered on Orderly"); let token_id2 = Self::get_token_address_to_token_id_map() .get(&token_address2) .expect("One of the two tokens is not registered on Orderly"); Self::get_pairs_map().get(&(token_id1, token_id2)).is_some() } pub fn get_token_id(&self, token_address: AccountId) -> TokenId { Self::get_token_address_to_token_id_map() .get(&token_address) .unwrap_or(0) } /// Returns 1M * rate pub fn get_min_rate( &self, token_address1: AccountId, token_address2: AccountId, ) -> Option { let token_id1 = Self::get_token_address_to_token_id_map().get(&token_address1)?; let token_id2 = Self::get_token_address_to_token_id_map().get(&token_address2)?; let pair = Self::get_pairs_map().get(&(token_id1, token_id2))?; let rate = pair.get_min_rate(); if rate.is_none() { None } else { Some(rate.mul(1000000, false).into()) } } } // Tokens sending and receiving #[near_bindgen] impl Exchange { pub fn withdraw( &mut self, account_id: AccountId, token_address: AccountId, amount: U128, ) -> Promise { let token_id = Self::get_token_address_to_token_id_map() .get(&token_address) .unwrap(); let mut account = Self::get_accounts_map().get(&account_id).unwrap(); account.start_withdrawal(token_id, amount.into()).unwrap(); Self::get_accounts_map().insert(&account_id, &account); ext_fungible_token::ft_transfer( account_id.clone(), amount, None, &token_address, 1, 50_000_000_000_000, ) .then(ext_self::internal_finish_withdrawal( account_id, token_id, amount, &env::current_account_id(), 0, 50_000_000_000_000, )) } fn internal_receive_tokens( account_id: AccountId, token_address: AccountId, amount: Balance, ) -> Result<(), ExchangeError> { let token_id = Self::get_token_address_to_token_id_map() .get(&token_address) .ok_or(ExchangeError::NoSuchToken)?; let mut account = Self::get_accounts_map() .get(&account_id) .ok_or(ExchangeError::NoSuchAccount)?; account.deposit(token_id, amount)?; Self::get_accounts_map().insert(&account_id, &account); Ok(()) } #[private] pub fn internal_finish_withdrawal( &mut self, account_id: AccountId, token_id: TokenId, amount: U128, ) { assert_eq!(env::promise_results_count(), 1); let mut account = Self::get_accounts_map().get(&account_id).unwrap(); match env::promise_result(0) { PromiseResult::NotReady => unreachable!(), PromiseResult::Successful(_) => { account.finish_withdrawal(token_id); } PromiseResult::Failed => { account.deposit(token_id, amount.into()).unwrap(); } }; Self::get_accounts_map().insert(&account_id, &account); } } // Lookup maps impl Exchange { fn get_accounts_map() -> LookupMap { LookupMap::new(vec![b'_', b'A']) } fn get_pairs_map() -> LookupMap<(TokenId, TokenId), DirectedPair> { LookupMap::new(vec![b'_', b'P']) } fn get_first_orders_map() -> LookupMap { LookupMap::new(vec![b'_', b'!', b'f']) } fn get_next_orders_map() -> LookupMap { LookupMap::new(vec![b'_', b'!', b'n']) } fn get_prev_orders_map() -> LookupMap { LookupMap::new(vec![b'_', b'!', b'p']) } fn get_token_address_to_token_id_map() -> LookupMap { LookupMap::new(vec![b'_', b't']) } fn get_token_id_to_token_address_map() -> LookupMap { LookupMap::new(vec![b'_', b'T']) } } #[near_bindgen] impl FungibleTokenReceiver for Exchange { #[allow(unused)] fn ft_on_transfer( &mut self, sender_id: ValidAccountId, amount: U128, msg: String, ) -> PromiseOrValue { let token_address = env::predecessor_account_id(); if let Err(_) = Self::internal_receive_tokens( sender_id.clone().into(), token_address.clone(), amount.into(), ) { // TODO: more specific error env::panic("Failed to receive tokens".as_ref()); } PromiseOrValue::Value(U128(0)) } } #[cfg(not(target_arch = "wasm32"))] #[cfg(test)] mod tests { use super::*; use near_sdk::test_utils::test_env; #[test] fn test_exchange_basic() { test_env::setup_free(); let alice: AccountId = "alice.near".to_string(); let bob: AccountId = "bob.near".to_string(); let mut map = Exchange::get_accounts_map(); let mut alice_account = Account { access_key: Some(env::signer_account_pk()), store_orders: true, ..Default::default() }; let mut bob_account = Account { access_key: Some(env::signer_account_pk()), store_orders: true, ..Default::default() }; alice_account.deposit(1, 1000).unwrap(); bob_account.deposit(2, 1000).unwrap(); map.insert(&alice, &alice_account); map.insert(&bob, &bob_account); let mut pairs_map = Exchange::get_pairs_map(); let pair1 = DirectedPair::new((1u128 << 64) + ((1 as u128) << 32) + (2 as u128)); let pair2 = DirectedPair::new((1u128 << 64) + ((2 as u128) << 32) + (1 as u128)); pairs_map.insert(&(1, 2), &pair1); pairs_map.insert(&(2, 1), &pair2); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(1), 1000 ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(2), 0 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(1), 0 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(2), 1000 ); assert_eq!( Exchange::limit_order(alice.clone(), 1, U128(200), 2, U128(100), 0), Err(ExchangeError::InsufficientFunds) ); assert!(Exchange::get_first_orders_map().get(&alice).is_none()); let order_ord = Exchange::limit_order(alice.clone(), 2, U128(400), 1, U128(200), 0).unwrap(); assert!(Exchange::get_first_orders_map().get(&bob).is_none()); let order1 = Exchange::get_first_orders_map() .get(&alice) .unwrap() .clone(); assert_eq!(order1.order_ord, order_ord.into()); assert_eq!(order1.want_token_id, 2); assert_eq!(order1.provide_token_id, 1); assert!(Exchange::get_next_orders_map().get(&order1).is_none()); assert!(Exchange::get_prev_orders_map().get(&order1).is_none()); // The rate is too low to match Alice's order assert_eq!( Exchange::market_order(bob.clone(), 1, U128(50), 2, U128(99), 100), Ok((U128(0), U128(0))) ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(1), 800 ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(2), 0 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(1), 0 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(2), 1000 ); assert_eq!( Exchange::market_order(bob.clone(), 1, U128(25), 2, U128(100), 100), Ok((U128(25), U128(50))) ); assert_eq!( Exchange::market_order(bob.clone(), 1, U128(25), 2, U128(150), 100), Ok((U128(25), U128(50))) ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(1), 800 ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(2), 0 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(1), 50 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(2), 900 ); assert_eq!( Exchange::cancel_order(alice.clone(), 2, U128(400), 1, U128(200), order_ord), Ok((U128(150), U128(100))) ); assert!(Exchange::get_first_orders_map().get(&alice).is_none()); assert!(Exchange::get_next_orders_map().get(&order1).is_none()); assert!(Exchange::get_prev_orders_map().get(&order1).is_none()); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(1), 950 ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(2), 100 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(1), 50 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(2), 900 ); assert_eq!( Exchange::cancel_order(alice.clone(), 2, U128(200), 1, U128(100), order_ord), Err(ExchangeError::NoSuchOrder) ); let order_ord1 = Exchange::limit_order(alice.clone(), 2, U128(200), 1, U128(100), 0).unwrap(); let order_ord2 = Exchange::limit_order(alice.clone(), 2, U128(200), 1, U128(100), 0).unwrap(); let order_ord3 = Exchange::limit_order(alice.clone(), 2, U128(800), 1, U128(200), 0).unwrap(); let order3 = Exchange::get_first_orders_map() .get(&alice) .unwrap() .clone(); let order2 = Exchange::get_next_orders_map() .get(&order3) .unwrap() .clone(); let order1 = Exchange::get_next_orders_map() .get(&order2) .unwrap() .clone(); assert_eq!(order1.order_ord, order_ord1.into()); assert_eq!(order1.want_amount, 200); assert_eq!(order2.order_ord, order_ord2.into()); assert_eq!(order2.want_amount, 200); assert_eq!(order3.order_ord, order_ord3.into()); assert_eq!(order3.want_amount, 800); assert_eq!( Exchange::get_prev_orders_map().get(&order1).unwrap(), order2 ); assert_eq!( Exchange::get_prev_orders_map().get(&order2).unwrap(), order3 ); assert!(Exchange::get_prev_orders_map().get(&order3).is_none()); assert!(Exchange::get_next_orders_map().get(&order1).is_none()); // Buys 200 for 400, and 100 more for 400 assert_eq!( Exchange::market_order(bob.clone(), 1, U128(300), 2, U128(1200), 100), Ok((U128(300), U128(800))) ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(1), 550 ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(2), 100 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(1), 350 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(2), 100 ); assert_eq!( Exchange::cancel_order(alice.clone(), 2, U128(200), 1, U128(100), order_ord2), Ok((U128(0), U128(200))) ); assert_eq!( Exchange::get_first_orders_map().get(&alice).unwrap(), order3 ); assert_eq!( Exchange::get_next_orders_map().get(&order3).unwrap(), order1 ); assert_eq!( Exchange::get_prev_orders_map().get(&order1).unwrap(), order3 ); assert_eq!( Exchange::cancel_order(alice.clone(), 2, U128(800), 1, U128(200), order_ord3), Ok((U128(100), U128(400))) ); assert_eq!( Exchange::get_first_orders_map().get(&alice).unwrap(), order1 ); assert!(Exchange::get_next_orders_map().get(&order3).is_none()); assert!(Exchange::get_prev_orders_map().get(&order3).is_none()); assert!(Exchange::get_next_orders_map().get(&order2).is_none()); assert!(Exchange::get_next_orders_map().get(&order2).is_none()); assert!(Exchange::get_prev_orders_map().get(&order1).is_none()); assert!(Exchange::get_prev_orders_map().get(&order1).is_none()); assert_eq!( Exchange::cancel_order(alice.clone(), 2, U128(200), 1, U128(100), order_ord1), Ok((U128(0), U128(200))) ); assert!(Exchange::get_first_orders_map().get(&alice).is_none()); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(1), 650 ); assert_eq!( Exchange::get_accounts_map() .get(&alice) .unwrap() .get_balance(2), 900 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(1), 350 ); assert_eq!( Exchange::get_accounts_map() .get(&bob) .unwrap() .get_balance(2), 100 ); } }