Exemplo n.º 1
0
      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
Exemplo n.º 2
0
      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) ) }
Exemplo n.º 3
0
      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 );
      }
Exemplo n.º 4
0
 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) ) }
Exemplo n.º 5
0
   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) ) }
Exemplo n.º 6
0
   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(...)
Exemplo n.º 8
0
      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, "" ) }