void pay_current_ask( const market_transaction& mtrx, asset_record& base_asset ) { try { FC_ASSERT( _current_ask->type == ask_order ); FC_ASSERT( mtrx.ask_type == ask_order ); _current_ask->state.balance -= mtrx.ask_paid.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(mtrx.ask_owner), _quote_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( mtrx.ask_owner, asset(0,_quote_id), 0 ); ask_payout->balance += mtrx.ask_received.amount; ask_payout->last_update = _pending_state->now(); ask_payout->deposit_date = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); // if the balance is less than 1 XTS * PRICE < .001 USD XTS goes to fees if( (_current_ask->get_quantity() * _current_ask->get_price()).amount == 0 ) { base_asset.collected_fees += _current_ask->get_quantity().amount; _current_ask->state.balance = 0; } _pending_state->store_ask_record( _current_ask->market_index, _current_ask->state ); } FC_CAPTURE_AND_RETHROW( (mtrx) ) } // pay_current_ask
void pay_current_bid( const market_transaction& mtrx, asset_record& quote_asset ) { try { FC_ASSERT( _current_bid->type == bid_order ); FC_ASSERT( mtrx.bid_type == bid_order ); _current_bid->state.balance -= mtrx.bid_paid.amount; FC_ASSERT( _current_bid->state.balance >= 0 ); auto bid_payout = _pending_state->get_balance_record( withdraw_condition( withdraw_with_signature(mtrx.bid_owner), _base_id ).get_address() ); if( !bid_payout ) bid_payout = balance_record( mtrx.bid_owner, asset(0,_base_id), 0 ); bid_payout->balance += mtrx.bid_received.amount; bid_payout->last_update = _pending_state->now(); bid_payout->deposit_date = _pending_state->now(); _pending_state->store_balance_record( *bid_payout ); // if the balance is less than 1 XTS then it gets collected as fees. if( (_current_bid->get_quote_quantity() * _current_bid->get_price()).amount == 0 ) { quote_asset.collected_fees += _current_bid->get_quote_quantity().amount; _current_bid->state.balance = 0; } _pending_state->store_bid_record( _current_bid->market_index, _current_bid->state ); } FC_CAPTURE_AND_RETHROW( (mtrx) ) }
void cancel_current_short( market_transaction& mtrx, const asset_id_type& quote_asset_id ) { FC_ASSERT( _current_bid->type == short_order ); FC_ASSERT( mtrx.bid_type == short_order ); elog( "Canceling current short" ); edump( (mtrx) ); // Create automatic market cancel transaction mtrx.ask_paid = asset(); mtrx.ask_received = asset( 0, quote_asset_id ); mtrx.bid_received = _current_bid->get_balance(); mtrx.bid_paid = asset( 0, quote_asset_id ); mtrx.bid_collateral.reset(); // Fund refund balance record const balance_id_type id = withdraw_condition( withdraw_with_signature( mtrx.bid_owner ), 0 ).get_address(); obalance_record bid_payout = _pending_state->get_balance_record( id ); if( !bid_payout.valid() ) bid_payout = balance_record( mtrx.bid_owner, asset( 0, 0 ), 0 ); bid_payout->balance += mtrx.bid_received.amount; bid_payout->last_update = _pending_state->now(); bid_payout->deposit_date = _pending_state->now(); _pending_state->store_balance_record( *bid_payout ); // Remove short order _current_bid->state.balance = 0; _pending_state->store_short_record( _current_bid->market_index, _current_bid->state ); }
void ad_operation::evaluate( transaction_evaluation_state& eval_state )const { try { if( this->amount.amount <= 0 ) FC_CAPTURE_AND_THROW( negative_deposit, (amount) ); FC_ASSERT( !message.empty() ); FC_ASSERT( amount.asset_id == 0 ); const size_t message_kb = (message.size() / 1024) + 1; const share_type required_fee = message_kb * BTS_BLOCKCHAIN_MIN_AD_FEE; FC_ASSERT( amount.amount >= required_fee, "Message of size ${s} KiB requires at least ${a} satoshis to be pay!", ("s",message_kb)("a",required_fee) ); // half of the note fees goto collected fees(delegate pay), other go to ad owner eval_state.min_fees[amount.asset_id] += required_fee; FC_ASSERT( owner_account_id != 0 ); const oaccount_record owner_account_rec = eval_state.pending_state()->get_account_record( abs( this->owner_account_id ) ); FC_ASSERT( owner_account_rec.valid() ); auto owner_address = owner_account_rec->active_address(); auto ad_income_balance = eval_state.pending_state()->get_balance_record(withdraw_condition( withdraw_with_signature(owner_address), 0 ).get_address()); if( !ad_income_balance ) ad_income_balance = balance_record( owner_address, asset(0, 0), 0 ); auto ad_pay = amount.amount - required_fee; ad_income_balance->balance += ad_pay; ad_income_balance->last_update = eval_state.pending_state()->now(); ad_income_balance->deposit_date = eval_state.pending_state()->now(); eval_state.pending_state()->store_balance_record( *ad_income_balance ); eval_state.sub_balance( asset(ad_pay, amount.asset_id) ); // checking the signature of the publisher. FC_ASSERT( publisher_account_id != 0 ); const oaccount_record publisher_account_rec = eval_state.pending_state()->get_account_record( abs( this->publisher_account_id ) ); FC_ASSERT( publisher_account_rec.valid() ); eval_state.check_signature( publisher_account_rec->active_key() ); ad_record record; record.index.account_id = owner_account_id; record.index.transaction_id = eval_state.trx.id(); record.publisher_id = publisher_account_id; record.amount = amount; record.message = message; record.signer = message_signature; // the message must be signed by the claimed publisher account FC_ASSERT( publisher_account_rec->active_key() == record.signer_key() ); FC_ASSERT( !eval_state.pending_state()->get_ad_record( record.index ).valid() ); eval_state.pending_state()->store_ad_record( std::move( record ) ); } FC_CAPTURE_AND_RETHROW( (*this) ) }
void transaction_evaluation_state::evaluate_deposit( const deposit_operation& op ) { try { auto deposit_balance_id = op.balance_id(); auto delegate_record = _current_state->get_name_record( op.condition.delegate_id ); if( !delegate_record ) fail( BTS_INVALID_NAME_ID, fc::variant(op) ); if( !delegate_record->is_delegate() ) fail( BTS_INVALID_DELEGATE_ID, fc::variant(op) ); auto cur_record = _current_state->get_balance_record( deposit_balance_id ); if( !cur_record ) { cur_record = balance_record( op.condition ); } cur_record->last_update = _current_state->now(); cur_record->balance += op.amount; sub_balance( deposit_balance_id, asset(op.amount, cur_record->condition.asset_id) ); if( cur_record->condition.asset_id == 0 ) add_vote( cur_record->condition.delegate_id, op.amount ); _current_state->store_balance_record( *cur_record ); } FC_RETHROW_EXCEPTIONS( warn, "", ("op",op) ) }
void deposit_operation::evaluate( transaction_evaluation_state& eval_state )const { try { if( this->amount <= 0 ) FC_CAPTURE_AND_THROW( negative_deposit, (amount) ); switch( withdraw_condition_types( this->condition.type ) ) { case withdraw_signature_type: case withdraw_multisig_type: case withdraw_escrow_type: break; default: FC_CAPTURE_AND_THROW( invalid_withdraw_condition, (*this) ); } const balance_id_type deposit_balance_id = this->balance_id(); obalance_record cur_record = eval_state.pending_state()->get_balance_record( deposit_balance_id ); if( !cur_record.valid() ) { cur_record = balance_record( this->condition ); if( this->condition.type == withdraw_escrow_type ) cur_record->meta_data = variant_object("creating_transaction_id", eval_state.trx.id() ); } if( cur_record->balance == 0 ) { cur_record->deposit_date = eval_state.pending_state()->now(); } else { fc::uint128 old_sec_since_epoch( cur_record->deposit_date.sec_since_epoch() ); fc::uint128 new_sec_since_epoch( eval_state.pending_state()->now().sec_since_epoch() ); fc::uint128 avg = (old_sec_since_epoch * cur_record->balance) + (new_sec_since_epoch * this->amount); avg /= (cur_record->balance + this->amount); cur_record->deposit_date = time_point_sec( avg.to_integer() ); } cur_record->balance += this->amount; eval_state.sub_balance( asset( this->amount, cur_record->asset_id() ) ); if( cur_record->condition.asset_id == 0 && cur_record->condition.slate_id ) eval_state.adjust_vote( cur_record->condition.slate_id, this->amount ); cur_record->last_update = eval_state.pending_state()->now(); const oasset_record asset_rec = eval_state.pending_state()->get_asset_record( cur_record->condition.asset_id ); FC_ASSERT( asset_rec.valid() ); if( asset_rec->is_market_issued() ) { FC_ASSERT( cur_record->condition.slate_id == 0 ); } const auto& owners = cur_record->owners(); for( const address& owner : owners ) { FC_ASSERT( asset_rec->address_is_whitelisted( owner ) ); } eval_state.pending_state()->store_balance_record( *cur_record ); } FC_CAPTURE_AND_RETHROW( (*this) ) }
void execute( asset_id_type quote_id, asset_id_type base_id, const fc::time_point_sec& timestamp ) { try { _quote_id = quote_id; _base_id = base_id; auto quote_asset = _pending_state->get_asset_record( _quote_id ); // DISABLE MARKET ISSUED ASSETS if( quote_asset->is_market_issued() ) return; // don't execute anything. // the order book is soreted from low to high price, so to get the last item (highest bid), we need to go to the first item in the // next market class and then back up one auto next_pair = base_id+1 == quote_id ? price( 0, quote_id+1, 0) : price( 0, quote_id, base_id+1 ); _bid_itr = _db_impl._bid_db.lower_bound( market_index_key( next_pair ) ); _ask_itr = _db_impl._ask_db.lower_bound( market_index_key( price( 0, quote_id, base_id) ) ); _short_itr = _db_impl._short_db.lower_bound( market_index_key( next_pair ) ); _collateral_itr = _db_impl._collateral_db.lower_bound( market_index_key( next_pair ) ); if( !_ask_itr.valid() ) { wlog( "ask iter invalid..." ); _ask_itr = _db_impl._ask_db.begin(); } if( _short_itr.valid() ) --_short_itr; else _short_itr = _db_impl._short_db.last(); if( _bid_itr.valid() ) --_bid_itr; else _bid_itr = _db_impl._bid_db.last(); if( _collateral_itr.valid() ) --_collateral_itr; else _collateral_itr = _db_impl._collateral_db.last(); asset consumed_bid_depth(0,base_id); asset consumed_ask_depth(0,base_id); asset usd_fees_collected(0,quote_id); asset trading_volume(0, base_id); omarket_status market_stat = _pending_state->get_market_status( _quote_id, _base_id ); if( !market_stat.valid() ) { if( quote_asset->is_market_issued() ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); FC_ASSERT( market_stat.valid() ); } while( get_next_bid() && get_next_ask() ) { idump( (_current_bid)(_current_ask) ); price ask_price = _current_ask->get_price(); // this works for bids, asks, and shorts.... but in the case of a cover // the current ask can go lower than the call price in order to match // the bid.... if( _current_ask->type == cover_order ) { ask_price = std::min( _current_bid->get_price(), _current_ask->get_highest_cover_price() ); } if( _current_bid->get_price() < ask_price ) break; if( quote_asset->is_market_issued() ) { if( !market_stat || market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 || market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); } auto quantity = std::min( _current_bid->get_quantity(), _current_ask->get_quantity() ); auto usd_paid_by_bid = quantity * _current_bid->get_price(); auto usd_received_by_ask = quantity * _current_ask->get_price(); auto xts_paid_by_ask = quantity; auto xts_received_by_bid = quantity; consumed_bid_depth += quantity; consumed_ask_depth += quantity; if( _current_bid->type == short_order ) { usd_paid_by_bid = usd_received_by_ask; } if( _current_ask->type == cover_order ) { usd_received_by_ask = usd_paid_by_bid; } FC_ASSERT( usd_paid_by_bid.amount >= 0 ); FC_ASSERT( xts_paid_by_ask.amount >= 0 ); FC_ASSERT( usd_received_by_ask.amount >= 0 ); FC_ASSERT( xts_received_by_bid.amount >= 0 ); FC_ASSERT( usd_paid_by_bid >= usd_received_by_ask ); FC_ASSERT( xts_paid_by_ask >= xts_received_by_bid ); // sanity check to keep supply from growing without bound FC_ASSERT( usd_paid_by_bid < asset(quote_asset->maximum_share_supply,quote_id), "", ("usd_paid_by_bid",usd_paid_by_bid)("asset",quote_asset) ); usd_fees_collected += usd_paid_by_bid - usd_received_by_ask; idump( (usd_fees_collected)(xts_paid_by_ask)(xts_received_by_bid)(quantity) ); market_transaction mtrx; mtrx.bid_owner = _current_bid->get_owner(); mtrx.ask_owner = _current_ask->get_owner(); mtrx.bid_price = _current_bid->get_price(); mtrx.ask_price = ask_price; mtrx.bid_paid = usd_paid_by_bid; mtrx.bid_received = xts_received_by_bid; mtrx.ask_paid = xts_paid_by_ask; mtrx.ask_received = usd_received_by_ask; mtrx.bid_type = _current_bid->type; mtrx.fees_collected = xts_paid_by_ask - xts_received_by_bid; _market_transactions.push_back(mtrx); trading_volume += mtrx.bid_received; market_stat->ask_depth -= xts_paid_by_ask.amount; if( _current_ask->type == ask_order ) { /* rounding errors on price cause this not to go to 0 in some cases */ if( quantity == _current_ask->get_quantity() ) _current_ask->state.balance = 0; else _current_ask->state.balance -= xts_paid_by_ask.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), quote_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( _current_ask->get_owner(), asset(0,quote_id), 0 ); ask_payout->balance += usd_received_by_ask.amount; ask_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); _pending_state->store_ask_record( _current_ask->market_index, _current_ask->state ); } else if( _current_ask->type == cover_order ) { elog( "MATCHING COVER ORDER recv_usd: ${usd} paid_collat: ${c}", ("usd",usd_received_by_ask)("c",xts_paid_by_ask) ); wlog( "current ask: ${c}", ("c",_current_ask) ); // we are in the margin call range... _current_ask->state.balance -= usd_received_by_ask.amount; *(_current_ask->collateral) -= xts_paid_by_ask.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); FC_ASSERT( *_current_ask->collateral >= 0 ); if( _current_ask->state.balance == 0 ) // no more USD left { // send collateral home to mommy & daddy wlog( " collateral balance is now 0!" ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), base_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( _current_ask->get_owner(), asset(0,base_id), 0 ); ask_payout->balance += (*_current_ask->collateral); ask_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); _current_ask->collateral = 0; } wlog( "storing collateral ${c}", ("c",_current_ask) ); _pending_state->store_collateral_record( _current_ask->market_index, collateral_record( *_current_ask->collateral, _current_ask->state.balance ) ); } if( _current_bid->type == bid_order ) { _current_bid->state.balance -= usd_paid_by_bid.amount; FC_ASSERT( _current_bid->state.balance >= 0 ); auto bid_payout = _pending_state->get_balance_record( withdraw_condition( withdraw_with_signature(_current_bid->get_owner()), base_id ).get_address() ); if( !bid_payout ) bid_payout = balance_record( _current_bid->get_owner(), asset(0,base_id), 0 ); bid_payout->balance += xts_received_by_bid.amount; bid_payout->last_update = _pending_state->now(); _pending_state->store_balance_record( *bid_payout ); _pending_state->store_bid_record( _current_bid->market_index, _current_bid->state ); } else if( _current_bid->type == short_order ) { market_stat->bid_depth -= xts_received_by_bid.amount; // TODO: what if the amount paid is 0 for bid and ask due to rounding errors, // make sure this doesn't put us in an infinite loop. if( quantity == _current_bid->get_quantity() ) _current_bid->state.balance = 0; else _current_bid->state.balance -= xts_received_by_bid.amount; FC_ASSERT( _current_bid->state.balance >= 0 ); auto collateral = (xts_paid_by_ask + xts_received_by_bid).amount; auto cover_price = usd_received_by_ask / asset( (3*collateral)/4, base_id ); market_index_key cover_index( cover_price, _current_ask->get_owner() ); auto ocover_record = _pending_state->get_collateral_record( cover_index ); if( NOT ocover_record ) ocover_record = collateral_record(); ocover_record->collateral_balance += collateral; ocover_record->payoff_balance += usd_received_by_ask.amount; FC_ASSERT( ocover_record->payoff_balance >= 0 ); FC_ASSERT( ocover_record->collateral_balance >= 0 ); _pending_state->store_collateral_record( cover_index, *ocover_record ); _pending_state->store_short_record( _current_bid->market_index, _current_bid->state ); } } // while bid && ask if( quote_asset->is_market_issued() ) { if( !market_stat || market_stat->ask_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 || market_stat->bid_depth < BTS_BLOCKCHAIN_MARKET_DEPTH_REQUIREMENT/2 ) FC_CAPTURE_AND_THROW( insufficient_depth, (market_stat) ); } _pending_state->store_market_status( *market_stat ); if( trading_volume.amount > 0 && get_next_bid() && get_next_ask() ) { market_history_key key(quote_id, base_id, market_history_key::each_block, _db_impl._head_block_header.timestamp); market_history_record new_record(_current_bid->get_price(), _current_ask->get_price(), trading_volume.amount); //LevelDB iterators are dumb and don't support proper past-the-end semantics. auto last_key_itr = _db_impl._market_history_db.lower_bound(key); if( !last_key_itr.valid() ) last_key_itr = _db_impl._market_history_db.last(); else --last_key_itr; key.timestamp = timestamp; //Unless the previous record for this market is the same as ours... if( (!(last_key_itr.valid() && last_key_itr.key().quote_id == quote_id && last_key_itr.key().base_id == base_id && last_key_itr.key().granularity == market_history_key::each_block && last_key_itr.value() == new_record)) ) { //...add a new entry to the history table. _pending_state->market_history[key] = new_record; } fc::time_point_sec start_of_this_hour = timestamp - (timestamp.sec_since_epoch() % (60*60)); market_history_key old_key(quote_id, base_id, market_history_key::each_hour, start_of_this_hour); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; fc::time_point_sec start_of_this_day = timestamp - (timestamp.sec_since_epoch() % (60*60*24)); old_key = market_history_key(quote_id, base_id, market_history_key::each_day, start_of_this_day); if( auto opt = _db_impl._market_history_db.fetch_optional(old_key) ) { auto old_record = *opt; old_record.volume += new_record.volume; if( new_record.highest_bid > old_record.highest_bid || new_record.lowest_ask < old_record.lowest_ask ) { old_record.highest_bid = std::max(new_record.highest_bid, old_record.highest_bid); old_record.lowest_ask = std::min(new_record.lowest_ask, old_record.lowest_ask); _pending_state->market_history[old_key] = old_record; } } else _pending_state->market_history[old_key] = new_record; } auto market_state = _pending_state->get_market_status( quote_id, base_id ); if( !market_state ) market_state = market_status( quote_id, base_id, 0, 0 ); market_state->last_error.reset(); _pending_state->store_market_status( *market_state ); wlog( "done matching orders" ); _pending_state->apply_changes(); } catch( const fc::exception& e ) { wlog( "error executing market ${quote} / ${base}\n ${e}", ("quote",quote_id)("base",base_id)("e",e.to_detail_string()) ); auto market_state = _prior_state->get_market_status( quote_id, base_id ); if( !market_state ) market_state = market_status( quote_id, base_id, 0, 0 ); market_state->last_error = e; _prior_state->store_market_status( *market_state ); } } // execute(...)
void pay_current_cover( market_transaction& mtrx, asset_record& quote_asset ) { try { FC_ASSERT( _current_ask->type == cover_order ); FC_ASSERT( mtrx.ask_type == cover_order ); // we are in the margin call range... _current_ask->state.balance -= mtrx.bid_paid.amount; *(_current_ask->collateral) -= mtrx.ask_paid.amount; FC_ASSERT( _current_ask->state.balance >= 0 ); FC_ASSERT( *_current_ask->collateral >= 0, "", ("mtrx",mtrx)("_current_ask", _current_ask) ); quote_asset.current_share_supply -= mtrx.ask_received.amount; if( *_current_ask->collateral == 0 ) { quote_asset.collected_fees -= _current_ask->state.balance; _current_ask->state.balance = 0; } if( _current_ask->state.balance == 0 && *_current_ask->collateral > 0 ) // no more USD left { // send collateral home to mommy & daddy //wlog( " collateral balance is now 0!" ); auto ask_balance_address = withdraw_condition( withdraw_with_signature(_current_ask->get_owner()), _base_id ).get_address(); auto ask_payout = _pending_state->get_balance_record( ask_balance_address ); if( !ask_payout ) ask_payout = balance_record( _current_ask->get_owner(), asset(0,_base_id), 0 ); auto left_over_collateral = (*_current_ask->collateral); /** charge 5% fee for having a margin call */ auto fee = (left_over_collateral * 5000 )/100000; left_over_collateral -= fee; // when executing a cover order, it always takes the exact price of the // highest bid, so there should be no fees paid *except* this. FC_ASSERT( mtrx.fees_collected.amount == 0 ); // these go to the network... as dividends.. mtrx.fees_collected += asset( fee, _base_id ); ask_payout->balance += left_over_collateral; ask_payout->last_update = _pending_state->now(); ask_payout->deposit_date = _pending_state->now(); _pending_state->store_balance_record( *ask_payout ); _current_ask->collateral = 0; } //ulog( "storing collateral ${c}", ("c",_current_ask) ); // the collateral position is now worse than before, if we don't update the market index then // the index price will be "wrong"... ie: the call price should move up based upon the fact // that we consumed more collateral than USD... // // If we leave it as is, then chances are we will end up covering the entire amount this time, // but we cannot use the price on the call for anything other than a trigger. _pending_state->store_collateral_record( _current_ask->market_index, collateral_record( *_current_ask->collateral, _current_ask->state.balance ) ); } FC_CAPTURE_AND_RETHROW( (mtrx) ) }
/** * Process all fees and update the asset records. */ void transaction_evaluation_state::post_evaluate() { try { required_fees += asset(_current_state->calculate_data_fee(fc::raw::pack_size(trx)),0); balance[0]; // make sure we have something for this. for( auto fee : balance ) { if( fee.second < 0 ) FC_CAPTURE_AND_THROW( negative_fee, (fee) ); if( fee.second > 0 ) { if( fee.first == 0 ) continue; // check to see if there are any open bids to buy the asset } // lowest ask is someone with XTS offered at a price of USD / XTS, fee.first // is an amount of USD which can be converted to price*USD XTS provided we // send lowest_ask.index.owner the USD omarket_order lowest_ask = _current_state->get_lowest_ask_record( fee.first, 0 ); if( lowest_ask ) { // do we have enough funds in the ask to cover the fees? asset required_order_balance = asset( fee.second, fee.first ) * lowest_ask->market_index.order_price; if( required_order_balance.amount <= lowest_ask->state.balance ) { balance[0] += required_order_balance.amount; auto balance_id = withdraw_condition( withdraw_with_signature( lowest_ask->market_index.owner ), fee.first ).get_address(); auto balance_rec = _current_state->get_balance_record( balance_id ); if( balance_rec ) { balance_rec->balance += fee.second; } else { balance_rec = balance_record( lowest_ask->market_index.owner, asset( fee.second, fee.first ), 0 ); } _current_state->store_balance_record( *balance_rec ); lowest_ask->state.balance -= required_order_balance.amount; _current_state->store_ask_record( lowest_ask->market_index, lowest_ask->state ); } // trade fee.second } } for( auto fee : balance ) { if( fee.second < 0 ) FC_CAPTURE_AND_THROW( negative_fee, (fee) ); if( fee.second > 0 ) { if( fee.first == 0 && fee.second < required_fees.amount ) FC_CAPTURE_AND_THROW( insufficient_fee, (fee)(required_fees.amount) ); auto asset_record = _current_state->get_asset_record( fee.first ); if( !asset_record ) FC_CAPTURE_AND_THROW( unknown_asset_id, (fee.first) ); asset_record->collected_fees += fee.second; asset_record->current_share_supply -= fee.second; _current_state->store_asset_record( *asset_record ); } } for( auto required_deposit : required_deposits ) { auto provided_itr = provided_deposits.find( required_deposit.first ); if( provided_itr->second < required_deposit.second ) FC_CAPTURE_AND_THROW( missing_deposit, (required_deposit) ); } } FC_RETHROW_EXCEPTIONS( warn, "" ) }