From 715b121965c458a7b149974336a7b199d03cee75 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Fri, 2 Mar 2018 14:00:01 -0500 Subject: [PATCH] update exchange, implement find for secondary index --- contracts/CMakeLists.txt | 4 +- contracts/eosiolib/datastream.hpp | 13 ++ contracts/eosiolib/dispatcher.hpp | 4 +- contracts/eosiolib/multi_index.hpp | 19 +- contracts/eosiolib/table.hpp | 2 + contracts/eosiolib/token.hpp | 24 ++- contracts/exchange/exchange.cpp | 327 +---------------------------- contracts/exchange/exchange.hpp | 256 +++++++++++++++++----- 8 files changed, 267 insertions(+), 382 deletions(-) diff --git a/contracts/CMakeLists.txt b/contracts/CMakeLists.txt index 614a2df2a..33f091db2 100644 --- a/contracts/CMakeLists.txt +++ b/contracts/CMakeLists.txt @@ -8,15 +8,15 @@ add_subdirectory(eosiolib) add_subdirectory(musl) add_subdirectory(libc++) add_subdirectory(multi_index_test) - add_subdirectory(eosio.system) add_subdirectory(identity) add_subdirectory(currency) add_subdirectory(stltest) +add_subdirectory(exchange) + #add_subdirectory(bancor) #add_subdirectory(eosio.system) add_subdirectory(asserter) -#add_subdirectory(exchange) add_subdirectory(infinite) add_subdirectory(proxy) add_subdirectory(test_api) diff --git a/contracts/eosiolib/datastream.hpp b/contracts/eosiolib/datastream.hpp index 182b3225c..62368c39c 100644 --- a/contracts/eosiolib/datastream.hpp +++ b/contracts/eosiolib/datastream.hpp @@ -246,6 +246,19 @@ inline datastream& operator>>(datastream& ds, uint32_t& d) { return ds; } +template +inline datastream& operator<<(datastream& ds, const bool& d) { + return ds << uint8_t(d); +} +template +inline datastream& operator>>(datastream& ds, bool& d) { + uint8_t t; + ds >> t; + d = t; + return ds; +} + + /** * Serialize a int64_t into a stream * @brief Serialize a int64_t diff --git a/contracts/eosiolib/dispatcher.hpp b/contracts/eosiolib/dispatcher.hpp index f3a4479e0..d87c14aa9 100644 --- a/contracts/eosiolib/dispatcher.hpp +++ b/contracts/eosiolib/dispatcher.hpp @@ -6,7 +6,7 @@ namespace eosio { template bool dispatch( uint64_t code, uint64_t act ) { if( code == FirstAction::get_account() && FirstAction::get_name() == act ) { - Contract::on( unpack_action() ); + Contract().on( unpack_action() ); return true; } return false; @@ -26,7 +26,7 @@ namespace eosio { template bool dispatch( uint64_t code, uint64_t act ) { if( code == FirstAction::get_account() && FirstAction::get_name() == act ) { - Contract::on( unpack_action() ); + Contract().on( unpack_action() ); return true; } return eosio::dispatch( code, act ); diff --git a/contracts/eosiolib/multi_index.hpp b/contracts/eosiolib/multi_index.hpp index 9fa19e686..9f55d5b67 100644 --- a/contracts/eosiolib/multi_index.hpp +++ b/contracts/eosiolib/multi_index.hpp @@ -96,7 +96,8 @@ struct indexed_by { template struct index_by { - typedef Extractor extractor_secondary_type; + typedef Extractor extractor_secondary_type; + typedef Extractor secondary_extractor_type; typedef typename std::decay::type secondary_type; index_by(){} @@ -271,6 +272,7 @@ class multi_index typedef typename IndexType::secondary_type secondary_key_type; public: + typedef typename IndexType::secondary_extractor_type secondary_extractor_type; static constexpr uint64_t name() { return IndexType::name(); } struct const_iterator { @@ -367,6 +369,16 @@ class multi_index const_iterator begin()const { return lower_bound(typename IndexType::secondary_type()); } + const_iterator find( typename IndexType::secondary_type&& secondary )const { + auto lb = lower_bound( secondary ); + auto e = end(); + if( lb == e ) return e; + + if( secondary != typename IndexType::extractor_secondary_type()(*lb) ) + return e; + return lb; + } + const_iterator lower_bound( typename IndexType::secondary_type&& secondary )const { return lower_bound( secondary ); } @@ -471,7 +483,8 @@ class multi_index const_iterator end()const { return const_iterator( *this ); } const_iterator begin()const { return lower_bound(); } - const_iterator lower_bound( uint64_t primary = 0 )const { + + const_iterator lower_bound( uint64_t primary = 0 )const { auto itr = db_lowerbound_i64( _code, _scope, TableName, primary ); if( itr < 0 ) return end(); auto& obj = load_object_by_primary_iterator( itr ); @@ -604,7 +617,7 @@ class multi_index void remove( const T& obj ) { const auto& objitem = static_cast(obj); - auto& mutableitem = const_cast(objitem); + //auto& mutableitem = const_cast(objitem); // eosio_assert( &objitem.__idx == this, "invalid object" ); db_remove_i64( objitem.__primary_itr ); diff --git a/contracts/eosiolib/table.hpp b/contracts/eosiolib/table.hpp index 7f5f388e0..9c8421f64 100644 --- a/contracts/eosiolib/table.hpp +++ b/contracts/eosiolib/table.hpp @@ -1,5 +1,7 @@ #pragma once +#include #include +#include namespace eosio { diff --git a/contracts/eosiolib/token.hpp b/contracts/eosiolib/token.hpp index 6ecedc0f4..6fb998074 100644 --- a/contracts/eosiolib/token.hpp +++ b/contracts/eosiolib/token.hpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace eosio { @@ -20,6 +21,9 @@ namespace eosio { * @{ */ + template + struct price; + template< uint64_t Code, uint64_t Symbol, typename NumberType = uint64_t @@ -38,6 +42,10 @@ namespace eosio { */ token(){} + + template + friend price operator / ( const Base& b, const Quote& q ); + operator asset()const { return asset( quantity, Symbol ); } token( const asset& a ):quantity(a.amount) { @@ -190,6 +198,10 @@ namespace eosio { QuoteToken quote; }; + template + price operator / ( const Base& b, const Quote& q ) { + return price(b,q); + } /** @@ -250,6 +262,11 @@ namespace eosio { * @brief Default constructor. */ price():base_per_quote(1ul){} + explicit price( uint128_t b ):base_per_quote(b){} + price& operator=( uint128_t b ) { + base_per_quote = b; + return *this; + } /** * Construction for price given the base token and quote token. @@ -343,14 +360,19 @@ namespace eosio { */ friend bool operator != ( const price& a, const price& b ) { return a.base_per_quote != b.base_per_quote; } + + operator uint128_t()const { return base_per_quote; } + + EOSLIB_SERIALIZE( price, (base_per_quote) ) private: /** * Represents as number of base tokens to purchase 1 quote token. * @brief Represents number of base tokens to purchase 1 quote token. */ - eosio::uint128 base_per_quote; + uint128_t base_per_quote; }; + /// @} diff --git a/contracts/exchange/exchange.cpp b/contracts/exchange/exchange.cpp index 16253d283..d00ff251b 100644 --- a/contracts/exchange/exchange.cpp +++ b/contracts/exchange/exchange.cpp @@ -1,324 +1,11 @@ -/** - * @file exchange.cpp - * @copyright defined in eos/LICENSE.txt - * @brief defines an example exchange contract - * - * This exchange contract assumes the existence of two currency contracts - * located at @currencya and @currencyb. These currency contracts have - * provided an API header defined in currency.hpp which the exchange - * contract will use to process messages related to deposits and withdraws. - * - * The exchange contract knows that the currency contracts require_notice() - * of both the sender and receiver; therefore, the exchange contract can - * implement a message handler that will be called anytime funds are deposited - * to or withdrawn from the exchange. - * - * When tokens are sent to @exchange from another account the exchange will - * credit the user's balance of the proper currency. - * - * To withdraw from the exchange, the user simply reverses the "to" and "from" - * fields of the currency contract transfer message. The currency contract will - * require the "authority" of the exchange, but the exchange's init() function - * configured this permission to allow *anyone* to transfer from the exchange. - * - * To prevent people from stealing all the money from the exchange, the - * exchange's transfer handler requires both the authority of the receiver and - * asserts that the user has a sufficient balance on the exchange. Lacking - * both of these the exchange will kill the transfer. - * - * The exchange and one of the currency contracts are forced to execute in the same - * thread anytime there is a deposit or withdraw. The transaction containing - * the transfer are already required to include the exchange in the scope by - * the currency contract. - * - * creating, canceling, and filling orders do not require blocking either currency - * contract. Users can only deposit or withdraw to their own currency account. - */ -#include /// defines transfer struct -#include - -using namespace exchange; -using namespace eosio; - -namespace exchange { -inline void save( const account& a ) { - if( a.is_empty() ) { - print("remove"); - accounts::remove(a); - } - else { - print("store"); - accounts::store(a); - } -} - -template -inline void modify_account( account_name a, Lambda&& modify ) { - auto acnt = get_account( a ); - modify( acnt ); - save( acnt ); -} - -/** - * This method is called after the "transfer" action of code - * "currencya" is called and "exchange" is listed in the notifiers. - */ -void apply_currency_transfer( const currency::transfer& transfer ) { - if( transfer.to == N(exchange) ) { - modify_account( transfer.from, [&]( account& mod_account ){ - mod_account.currency_balance += transfer.quantity; - }); - } else if( transfer.from == N(exchange) ) { - require_auth( transfer.to ); /// require the receiver of funds (account owner) to authorize this transfer - - modify_account( transfer.to, [&]( account& mod_account ){ - mod_account.currency_balance -= transfer.quantity; - }); - } else { - eosio_assert( false, "notified on transfer that is not relevant to this exchange" ); - } -} - -/** - * This method is called after the "transfer" action of code - * "currencya" is called and "exchange" is listed in the notifiers. - */ -void apply_eos_transfer( const eosio::transfer& transfer ) { - if( transfer.to == N(exchange) ) { - modify_account( transfer.from, [&]( account& mod_account ){ - mod_account.eos_balance += transfer.quantity; - }); - } else if( transfer.from == N(exchange) ) { - require_auth( transfer.to ); /// require the receiver of funds (account owner) to authorize this transfer - - modify_account( transfer.to, [&]( account& mod_account ){ - mod_account.eos_balance -= transfer.quantity; - }); - } else { - eosio_assert( false, "notified on transfer that is not relevant to this exchange" ); - } -} - -void match( bid& bid_to_match, account& buyer, ask& ask_to_match, account& seller ) { - print( "match bid: ", bid_to_match, "\nmatch ask: ", ask_to_match, "\n"); - - eosio::tokens ask_eos = ask_to_match.quantity * ask_to_match.at_price; - - eos_tokens fill_amount_eos = min( ask_eos, bid_to_match.quantity ); - currency_tokens fill_amount_currency; - - if( fill_amount_eos == ask_eos ) { /// complete fill of ask_to_match - fill_amount_currency = ask_to_match.quantity; - } else { /// complete fill of buy - fill_amount_currency = fill_amount_eos / ask_to_match.at_price; - } - - print( "\n\nmatch bid: ", name(bid_to_match.buyer.name), ":", bid_to_match.buyer.number, - "match ask: ", name(ask_to_match.seller.name), ":", ask_to_match.seller.number, "\n\n" ); - - - bid_to_match.quantity -= fill_amount_eos; - seller.eos_balance += fill_amount_eos; - - ask_to_match.quantity -= fill_amount_currency; - buyer.currency_balance += fill_amount_currency; -} - -/** - * - * - */ -void apply_exchange_buy( buy_order order ) { - bid& exchange_bid = order; - require_auth( exchange_bid.buyer.name ); - - eosio_assert( exchange_bid.quantity > eosio::tokens(0), "invalid quantity" ); - eosio_assert( exchange_bid.expiration > now(), "order expired" ); - - print( name(exchange_bid.buyer.name), " created bid for ", order.quantity, " currency at price: ", order.at_price, "\n" ); - - bid existing_bid; - eosio_assert( !bids_by_id::get( exchange_bid.buyer, existing_bid ), "order with this id already exists" ); - print( __FILE__, __LINE__, "\n" ); - - auto buyer_account = get_account( exchange_bid.buyer.name ); - buyer_account.eos_balance -= exchange_bid.quantity; - - ask lowest_ask; - if( !asks_by_price::front( lowest_ask ) ) { - print( "\n No asks found, saving buyer account and storing bid\n" ); - eosio_assert( !order.fill_or_kill, "order not completely filled" ); - bids::store( exchange_bid ); - buyer_account.open_orders++; - save( buyer_account ); - return; - } - - print( "ask: ", lowest_ask, "\n" ); - print( "bid: ", exchange_bid, "\n" ); - - auto seller_account = get_account( lowest_ask.seller.name ); - - while( lowest_ask.at_price <= exchange_bid.at_price ) { - print( "lowest ask <= exchange_bid.at_price\n" ); - match( exchange_bid, buyer_account, lowest_ask, seller_account ); - - if( lowest_ask.quantity == currency_tokens(0) ) { - seller_account.open_orders--; - save( seller_account ); - save( buyer_account ); - asks::remove( lowest_ask ); - if( !asks_by_price::front( lowest_ask ) ) { - break; - } - seller_account = get_account( lowest_ask.seller.name ); - } else { - break; // buyer's bid should be filled - } - } - print( "lowest_ask >= exchange_bid.at_price or buyer's bid has been filled\n" ); - - if( exchange_bid.quantity && !order.fill_or_kill ) buyer_account.open_orders++; - save( buyer_account ); - print( "saving buyer's account\n" ); - if( exchange_bid.quantity ) { - print( exchange_bid.quantity, " eos left over" ); - eosio_assert( !order.fill_or_kill, "order not completely filled" ); - bids::store( exchange_bid ); - return; - } - print( "bid filled\n" ); - -} - -void apply_exchange_sell( sell_order order ) { - ask& exchange_ask = order; - require_auth( exchange_ask.seller.name ); - - eosio_assert( exchange_ask.quantity > currency_tokens(0), "invalid quantity" ); - eosio_assert( exchange_ask.expiration > now(), "order expired" ); - - print( "\n\n", name(exchange_ask.seller.name), " created sell for ", order.quantity, - " currency at price: ", order.at_price, "\n"); - - ask existing_ask; - eosio_assert( !asks_by_id::get( exchange_ask.seller, existing_ask ), "order with this id already exists" ); - - auto seller_account = get_account( exchange_ask.seller.name ); - seller_account.currency_balance -= exchange_ask.quantity; - - - bid highest_bid; - if( !bids_by_price::back( highest_bid ) ) { - eosio_assert( !order.fill_or_kill, "order not completely filled" ); - print( "\n No bids found, saving seller account and storing ask\n" ); - asks::store( exchange_ask ); - seller_account.open_orders++; - save( seller_account ); - return; - } - - print( "\n bids found, lets see what matches\n" ); - auto buyer_account = get_account( highest_bid.buyer.name ); - - while( highest_bid.at_price >= exchange_ask.at_price ) { - match( highest_bid, buyer_account, exchange_ask, seller_account ); - - if( highest_bid.quantity == eos_tokens(0) ) { - buyer_account.open_orders--; - save( seller_account ); - save( buyer_account ); - bids::remove( highest_bid ); - if( !bids_by_price::back( highest_bid ) ) { - break; - } - buyer_account = get_account( highest_bid.buyer.name ); - } else { - break; // buyer's bid should be filled - } - } - - if( exchange_ask.quantity && !order.fill_or_kill ) seller_account.open_orders++; - save( seller_account ); - if( exchange_ask.quantity ) { - eosio_assert( !order.fill_or_kill, "order not completely filled" ); - print( "saving ask\n" ); - asks::store( exchange_ask ); - return; - } - - print( "ask filled\n" ); -} - -void apply_exchange_cancel_buy( order_id order ) { - require_auth( order.name ); - - bid bid_to_cancel; - eosio_assert( bids_by_id::get( order, bid_to_cancel ), "bid with this id does not exists" ); - - auto buyer_account = get_account( order.name ); - buyer_account.eos_balance += bid_to_cancel.quantity; - buyer_account.open_orders--; - - bids::remove( bid_to_cancel ); - save( buyer_account ); - - print( "bid removed\n" ); -} - -void apply_exchange_cancel_sell( order_id order ) { - require_auth( order.name ); - - ask ask_to_cancel; - eosio_assert( asks_by_id::get( order, ask_to_cancel ), "ask with this id does not exists" ); - - auto seller_account = get_account( order.name ); - seller_account.currency_balance += ask_to_cancel.quantity; - seller_account.open_orders--; - - asks::remove( ask_to_cancel ); - save( seller_account ); - - print( "ask removed\n" ); -} - -} // namespace exchange +#include "exchange.hpp" extern "C" { + /// The apply method implements the dispatch of events to this contract + void apply( uint64_t code, uint64_t act ) { + typedef eosio::generic_currency< eosio::token > eos; + typedef eosio::generic_currency< eosio::token > cur; -// void validate( uint64_t code, uint64_t action ) { } -// void precondition( uint64_t code, uint64_t action ) { } - /** - * The apply method implements the dispatch of events to this contract - */ - void apply( uint64_t code, uint64_t action ) { - if( code == N(exchange) ) { - switch( action ) { - case N(buy): - apply_exchange_buy( current_action() ); - break; - case N(sell): - apply_exchange_sell( current_action() ); - break; - case N(cancelbuy): - apply_exchange_cancel_buy( current_action() ); - break; - case N(cancelsell): - apply_exchange_cancel_sell( current_action() ); - break; - default: - eosio_assert( false, "unknown action" ); - } - } - else if( code == N(currency) ) { - if( action == N(transfer) ) - apply_currency_transfer( current_action() ); - } - else if( code == N(eos) ) { - if( action == N(transfer) ) - apply_eos_transfer( current_action() ); - } - else { - } - } + exchange::apply( code, act ); + } } diff --git a/contracts/exchange/exchange.hpp b/contracts/exchange/exchange.hpp index 4ad70c383..4dfd85bab 100644 --- a/contracts/exchange/exchange.hpp +++ b/contracts/exchange/exchange.hpp @@ -2,78 +2,226 @@ * @file * @copyright defined in eos/LICENSE.txt */ -#include +#include +#include -namespace exchange { +using eosio::asset; +using eosio::symbol_name; +using eosio::indexed_by; +using eosio::const_mem_fun; +using eosio::price_ratio; +using eosio::price; - using currency::currency_tokens; - using eos_tokens = eosio::tokens; +template +class exchange { + public: + typedef eosio::generic_currency< eosio::token > exchange_currency; - //@abi action cancelbuy cancelsell - struct order_id { - account_name name = 0; - uint64_t number = 0; - }; + typedef typename BaseCurrency::token_type base_token_type; + typedef typename QuoteCurrency::token_type quote_token_type; + typedef typename exchange_currency::token_type ex_token_type; - typedef eosio::price price; + struct account { + account_name owner; + base_token_type base_balance; + quote_token_type quote_balance; - //@abi table - struct PACKED( bid ) { - order_id buyer; - price at_price; - eosio::tokens quantity; - time expiration; + uint64_t primary_key()const { return owner; } - void print() { - eosio::print( "{ quantity: ", quantity, ", price: ", at_price, " }" ); + EOSLIB_SERIALIZE( account, (owner)(base_balance)(quote_balance) ) + }; + typedef eosio::multi_index< N(accounts), account> account_index_type; + + template + struct limit_order { + typedef eosio::price price_type; + static const uint64_t precision = (1000ll * 1000ll * 1000ll * 1000ll); + + uint64_t primary; + account_name owner; + uint32_t id; + uint32_t expiration; + BaseTokenType for_sale; + price_type sell_price; + + uint64_t primary_key()const { return primary; } + + uint128_t by_owner_id()const { return get_owner_id( owner, id ); } + uint64_t by_expiration()const { return expiration; } + uint128_t by_price()const { return sell_price; } + + static uint128_t get_price( BaseTokenType base, QuoteTokenType quote ) { + return (uint128_t( precision ) * base.quantity ) / quote.quantity; + } + + static uint128_t get_owner_id( account_name owner, uint32_t id ) { return (uint128_t( owner ) << 64) | id; } + + EOSLIB_SERIALIZE( limit_order, (primary)(owner)(id)(expiration)(for_sale)(sell_price) ) + }; + + typedef limit_order limit_base_quote; + typedef eosio::multi_index< N(sellbq), limit_base_quote, + indexed_by< N(price), const_mem_fun >, + indexed_by< N(ownerid), const_mem_fun >, + indexed_by< N(expire), const_mem_fun > + > limit_base_quote_index; + + typedef limit_order limit_quote_base; + typedef eosio::multi_index< N(sellqb), limit_quote_base, + indexed_by< N(price), const_mem_fun >, + indexed_by< N(ownerid), const_mem_fun >, + indexed_by< N(expire), const_mem_fun > + > limit_quote_base_index; + + + + + + account_index_type _accounts; + limit_base_quote_index _base_quote_orders; + limit_base_quote_index _quote_base_orders; + + exchange() + :_accounts( ExchangeAccount, ExchangeAccount ), + _base_quote_orders( ExchangeAccount, ExchangeAccount ), + _quote_base_orders( ExchangeAccount, ExchangeAccount ) + { } - }; - static_assert( sizeof(bid) == 32+12, "unexpected padding" ); - - //@abi table - struct PACKED( ask ) { - order_id seller; - price at_price; - currency_tokens quantity; - time expiration; - - void print() { - eosio::print( "{ quantity: ", quantity, ", price: ", at_price, " }" ); + + ACTION( ExchangeAccount, deposit ) { + account_name from; + asset amount; + + EOSLIB_SERIALIZE( deposit, (from)(amount) ) + }; + + void on( const deposit& d ) { + require_auth( d.from ); + + const account* owner = _accounts.find( d.from ); + if( !owner ) { + owner = &_accounts.emplace( d.from, [&]( auto& a ) { + a.owner = d.from; + }); + } + + switch( d.amount.symbol ) { + case base_token_type::symbol: + BaseCurrency::inline_transfer( d.from, ExchangeAccount, base_token_type(d.amount.amount) ); + _accounts.update( *owner, 0, [&]( auto& a ) { + a.base_balance += base_token_type(d.amount); + }); + break; + case quote_token_type::symbol: + QuoteCurrency::inline_transfer( d.from, ExchangeAccount, quote_token_type(d.amount.amount) ); + _accounts.update( *owner, 0, [&]( auto& a ) { + a.quote_balance += quote_token_type(d.amount); + }); + break; + default: + eosio_assert( false, "invalid symbol" ); + } } - }; - static_assert( sizeof(ask) == 32+12, "unexpected padding" ); - //@abi table i64 - struct PACKED( account ) { - account( account_name o = account_name() ):owner(o){} + ACTION( ExchangeAccount, withdraw ) { + account_name to; + asset amount; - account_name owner; - eos_tokens eos_balance; - currency_tokens currency_balance; - uint32_t open_orders = 0; + EOSLIB_SERIALIZE( withdraw, (to)(amount) ) + }; - bool is_empty()const { return ! ( bool(eos_balance) | bool(currency_balance) | open_orders); } - }; + void on( const withdraw& w ) { + require_auth( w.to ); - using accounts = eosio::table; + const account* owner = _accounts.find( w.to ); + eosio_assert( owner != nullptr, "unknown exchange account" ); - TABLE2(bids,exchange,exchange,bids,bid,bids_by_id,order_id,bids_by_price,price); - TABLE2(asks,exchange,exchange,asks,ask,asks_by_id,order_id,asks_by_price,price); + switch( w.amount.symbol ) { + case base_token_type::symbol: + eosio_assert( owner->base_balance >= base_token_type(w.amount), "insufficient balance" ); + _accounts.update( *owner, 0, [&]( auto& a ) { + a.base_balance -= base_token_type(w.amount); + }); + BaseCurrency::inline_transfer( ExchangeAccount, w.to, base_token_type(w.amount.amount) ); + break; + case quote_token_type::symbol: + eosio_assert( owner->quote_balance >= quote_token_type(w.amount), "insufficient balance" ); - //@abi action buy - struct buy_order : public bid { uint8_t fill_or_kill = false; }; + _accounts.update( *owner, 0, [&]( auto& a ) { + a.quote_balance -= quote_token_type(w.amount); + }); - //@abi action sell - struct sell_order : public ask { uint8_t fill_or_kill = false; }; + QuoteCurrency::inline_transfer( ExchangeAccount, w.to, quote_token_type(w.amount.amount) ); + break; + default: + eosio_assert( false, "invalid symbol" ); + } + } + ACTION( ExchangeAccount, neworder ) { + account_name owner; + uint32_t id; + asset amount_to_sell; + bool fill_or_kill; + uint128_t sell_price; + uint32_t expiration; + + EOSLIB_SERIALIZE( neworder, (owner)(id)(amount_to_sell)(fill_or_kill)(sell_price)(expiration) ) + }; + + void on( const neworder& order ) { + require_auth( order.owner ); + + if( order.amount_to_sell.symbol == base_token_type::symbol ) { + _base_quote_orders.emplace( order.owner, [&]( auto& o ) { + o.primary = 0;// _base_quote_orders.next_available_id() + o.owner = order.owner; + o.id = order.id; + o.expiration = order.expiration; + o.for_sale = order.amount_to_sell; + o.sell_price = order.sell_price; + }); + } + else if( order.amount_to_sell.symbol == quote_token_type::symbol ) { + _quote_base_orders.emplace( order.owner, [&]( auto& o ) { + o.primary = 0;// _base_quote_orders.next_available_id() + o.owner = order.owner; + o.id = order.id; + o.expiration = order.expiration; + o.for_sale = order.amount_to_sell; + o.sell_price = order.sell_price; + }); + } + } - inline account get_account( account_name owner ) { - account owned_account(owner); - accounts::get( owned_account ); - return owned_account; + ACTION( ExchangeAccount, cancelorder ) { + account_name owner; + uint32_t id; + + EOSLIB_SERIALIZE( cancelorder, (owner)(id) ) + }; + + void on( const cancelorder& order ) { + require_auth( order.owner ); + + auto idx = _base_quote_orders.template get_index(); + auto itr = idx.find( limit_base_quote::get_owner_id( order.owner, order.id ) ); + if( itr != idx.end() ) { + _base_quote_orders.remove(*itr); + } + } - } -} + + static void apply( uint64_t code, uint64_t act ) { + if( !eosio::dispatch( code, act ) ) { + exchange::exchange_currency::apply( code, act ); + } + } +}; -- GitLab