TER Taker::redeemIOU ( AccountID const& account, STAmount const& amount, Issue const& issue) { if (isXRP (amount)) Throw<std::logic_error> ("Using redeemIOU with XRP"); if (account == issue.account) return tesSUCCESS; // Transferring zero is equivalent to not doing a transfer if (amount == zero) return tesSUCCESS; // If we are trying to redeem some amount, then the account // must have a credit balance. if (get_funds (account, amount) <= zero) Throw<std::logic_error> ("redeemIOU has no funds to redeem"); auto ret = ripple::redeemIOU (view_, account, amount, issue, journal_); if (get_funds (account, amount) < zero) Throw<std::logic_error> ("redeemIOU redeemed more funds than available"); return ret; }
Taker::Taker (CrossType cross_type, ApplyView& view, AccountID const& account, Amounts const& offer, std::uint32_t flags, beast::Journal journal) : BasicTaker (cross_type, account, offer, Quality(offer), flags, calculateRate(view, offer.in.getIssuer(), account), calculateRate(view, offer.out.getIssuer(), account), journal) , view_ (view) , xrp_flow_ (0) , direct_crossings_ (0) , bridge_crossings_ (0) { assert (issue_in () == offer.in.issue ()); assert (issue_out () == offer.out.issue ()); if (journal_.debug) { journal_.debug << "Crossing as: " << to_string (account); if (isXRP (issue_in ())) journal_.debug << " Offer in: " << format_amount (offer.in); else journal_.debug << " Offer in: " << format_amount (offer.in) << " (issuer: " << issue_in ().account << ")"; if (isXRP (issue_out ())) journal_.debug << " Offer out: " << format_amount (offer.out); else journal_.debug << " Offer out: " << format_amount (offer.out) << " (issuer: " << issue_out ().account << ")"; journal_.debug << " Balance: " << format_amount (get_funds (account, offer.in)); } }
bool BasicTaker::done () const { // We are done if we have consumed all the input currency if (remaining_.in <= zero) { journal_.debug << "Done: all the input currency has been consumed."; return true; } // We are done if using buy semantics and we received the // desired amount of output currency if (!sell_ && (remaining_.out <= zero)) { journal_.debug << "Done: the desired amount has been received."; return true; } // We are done if the taker is out of funds if (get_funds (account(), remaining_.in) <= zero) { journal_.debug << "Done: taker out of funds."; return true; } return false; }
// Calculates the direct flow through the specified offer BasicTaker::Flow BasicTaker::do_cross (Amounts offer, Quality quality, AccountID const& owner) { assert (!done ()); auto const owner_funds = get_funds (owner, offer.out); auto const taker_funds = get_funds (account (), offer.in); Flow result; if (cross_type_ == CrossType::XrpToIou) { result = flow_xrp_to_iou (offer, quality, owner_funds, taker_funds, out_rate (owner, account ())); } else if (cross_type_ == CrossType::IouToXrp) { result = flow_iou_to_xrp (offer, quality, owner_funds, taker_funds, in_rate (owner, account ())); } else { result = flow_iou_to_iou (offer, quality, owner_funds, taker_funds, in_rate (owner, account ()), out_rate (owner, account ())); } if (!result.sanity_check ()) Throw<std::logic_error> ("Computed flow fails sanity check."); remaining_.out -= result.order.out; remaining_.in -= result.order.in; assert (remaining_.in >= zero); return result; }
Amounts cross (Amounts offer, Quality quality) { if (reject (quality)) return Amounts (offer.in.zeroed (), offer.out.zeroed ()); // we need to emulate "unfunded offers" behavior if (get_funds (AccountID (0x4702), offer.out) == beast::zero) return Amounts (offer.in.zeroed (), offer.out.zeroed ()); if (done ()) return Amounts (offer.in.zeroed (), offer.out.zeroed ()); auto result = do_cross (offer, quality, AccountID (0x4702)); funds_ -= result.order.in; return result.order; }
// Calculates the bridged flow through the specified offers std::pair<BasicTaker::Flow, BasicTaker::Flow> BasicTaker::do_cross ( Amounts offer1, Quality quality1, AccountID const& owner1, Amounts offer2, Quality quality2, AccountID const& owner2) { assert (!done ()); assert (!offer1.in.native ()); assert (offer1.out.native ()); assert (offer2.in.native ()); assert (!offer2.out.native ()); // If the taker owns the first leg of the offer, then the taker's available // funds aren't the limiting factor for the input - the offer itself is. auto leg1_in_funds = get_funds (account (), offer1.in); if (account () == owner1) { journal_.trace << "The taker owns the first leg of a bridge."; leg1_in_funds = std::max (leg1_in_funds, offer1.in); } // If the taker owns the second leg of the offer, then the taker's available // funds are not the limiting factor for the output - the offer itself is. auto leg2_out_funds = get_funds (owner2, offer2.out); if (account () == owner2) { journal_.trace << "The taker owns the second leg of a bridge."; leg2_out_funds = std::max (leg2_out_funds, offer2.out); } // The amount available to flow via XRP is the amount that the owner of the // first leg of the bridge has, up to the first leg's output. // // But, when both legs of a bridge are owned by the same person, the amount // of XRP that can flow between the two legs is, essentially, infinite // since all the owner is doing is taking out XRP of his left pocket // and putting it in his right pocket. In that case, we set the available // XRP to the largest of the two offers. auto xrp_funds = get_funds (owner1, offer1.out); if (owner1 == owner2) { journal_.trace << "The bridge endpoints are owneb by the same account."; xrp_funds = std::max (offer1.out, offer2.in); } if (journal_.debug) { journal_.debug << "Available bridge funds:"; journal_.debug << " leg1 in: " << format_amount (leg1_in_funds); journal_.debug << " leg2 out: " << format_amount (leg2_out_funds); journal_.debug << " xrp: " << format_amount (xrp_funds); } auto const leg1_rate = in_rate (owner1, account ()); auto const leg2_rate = out_rate (owner2, account ()); // Attempt to determine the maximal flow that can be achieved across each // leg independent of the other. auto flow1 = flow_iou_to_xrp (offer1, quality1, xrp_funds, leg1_in_funds, leg1_rate); if (!flow1.sanity_check ()) Throw<std::logic_error> ("Computed flow1 fails sanity check."); auto flow2 = flow_xrp_to_iou (offer2, quality2, leg2_out_funds, xrp_funds, leg2_rate); if (!flow2.sanity_check ()) Throw<std::logic_error> ("Computed flow2 fails sanity check."); // We now have the maximal flows across each leg individually. We need to // equalize them, so that the amount of XRP that flows out of the first leg // is the same as the amount of XRP that flows into the second leg. We take // the side which is the limiting factor (if any) and adjust the other. if (flow1.order.out < flow2.order.in) { // Adjust the second leg of the offer down: flow2.order.in = flow1.order.out; flow2.order.out = qual_div (flow2.order.in, quality2, flow2.order.out); flow2.issuers.out = leg2_rate.multiply (flow2.order.out); log_flow ("Balancing: adjusted second leg down", flow2); } else if (flow1.order.out > flow2.order.in) { // Adjust the first leg of the offer down: flow1.order.out = flow2.order.in; flow1.order.in = qual_mul (flow1.order.out, quality1, flow1.order.in); flow1.issuers.in = leg1_rate.multiply (flow1.order.in); log_flow ("Balancing: adjusted first leg down", flow2); } if (flow1.order.out != flow2.order.in) Throw<std::logic_error> ("Bridged flow is out of balance."); remaining_.out -= flow2.order.out; remaining_.in -= flow1.order.in; return std::make_pair (flow1, flow2); }