/** Calculate the amount particular user could get through an offer. @param amount the maximum flow that is available to the taker. @param offer the offer to flow through. @param taker the person taking the offer. @return the maximum amount that can flow through this offer. */ Amounts Taker::flow (Amounts amount, Offer const& offer, Account const& taker) { // Limit taker's input by available funds less fees Amount const taker_funds (view ().accountFunds ( taker, amount.in, fhZERO_IF_FROZEN)); // Get fee rate paid by taker std::uint32_t const taker_charge_rate (rippleTransferRate (view (), taker, offer.account (), amount.in.getIssuer())); // Skip some math when there's no fee if (taker_charge_rate == QUALITY_ONE) { amount = offer.quality ().ceil_in (amount, taker_funds); } else { Amount const taker_charge (amountFromRate (taker_charge_rate)); amount = offer.quality ().ceil_in (amount, divide (taker_funds, taker_charge, taker_funds.issue ())); } // Best flow the owner can get. // Start out assuming entire offer will flow. Amounts owner_amount (amount); // Limit owner's output by available funds less fees Amount const owner_funds (view ().accountFunds ( offer.account (), owner_amount.out, fhZERO_IF_FROZEN)); // Get fee rate paid by owner std::uint32_t const owner_charge_rate (rippleTransferRate (view (), offer.account (), taker, amount.out.getIssuer())); if (owner_charge_rate == QUALITY_ONE) { // Skip some math when there's no fee owner_amount = offer.quality ().ceil_out (owner_amount, owner_funds); } else { Amount const owner_charge (amountFromRate (owner_charge_rate)); owner_amount = offer.quality ().ceil_out (owner_amount, divide (owner_funds, owner_charge, owner_funds.issue ())); } // Calculate the amount that will flow through the offer // This does not include the fees. return (owner_amount.in < amount.in) ? owner_amount : amount; }
// Fill a direct offer. // @param offer the offer we are going to use. // @param amount the amount to flow through the offer. // @returns: tesSUCCESS if successful, or an error code otherwise. TER Taker::fill (Offer const& offer, Amounts const& amount) { consume (offer, amount); // Pay the taker, then the owner TER result = view ().accountSend (offer.account(), account(), amount.out); if (result == tesSUCCESS) result = view ().accountSend (account(), offer.account(), amount.in); return result; }
// Fill a direct offer. // @param offer the offer we are going to use. // @param amount the amount to flow through the offer. // @returns: tesSUCCESS if successful, or an error code otherwise. TER Taker::fill (Offer const& offer, Amounts const& amount) { TER result (tesSUCCESS); Amounts const remain ( offer.entry ()->getFieldAmount (sfTakerPays) - amount.in, offer.entry ()->getFieldAmount (sfTakerGets) - amount.out); offer.entry ()->setFieldAmount (sfTakerPays, remain.in); offer.entry ()->setFieldAmount (sfTakerGets, remain.out); view ().entryModify (offer.entry()); // Pay the taker, then the owner result = view ().accountSend (offer.account(), account(), amount.out); if (result == tesSUCCESS) result = view ().accountSend (account(), offer.account(), amount.in); return result; }
// Fill a bridged offer. // @param leg1 the first leg we are going to use. // @param amount1 the amount to flow through the first leg of the offer. // @param leg2 the second leg we are going to use. // @param amount2 the amount to flow through the second leg of the offer. // @return tesSUCCESS if successful, or an error code otherwise. TER Taker::fill ( Offer const& leg1, Amounts const& amount1, Offer const& leg2, Amounts const& amount2) { assert (amount1.out == amount2.in); TER result (tesSUCCESS); Amounts const remain1 ( leg1.entry ()->getFieldAmount (sfTakerPays) - amount1.in, leg1.entry ()->getFieldAmount (sfTakerGets) - amount1.out); leg1.entry ()->setFieldAmount (sfTakerPays, remain1.in); leg1.entry ()->setFieldAmount (sfTakerGets, remain1.out); view ().entryModify (leg1.entry()); Amounts const remain2 ( leg2.entry ()->getFieldAmount (sfTakerPays) - amount2.in, leg2.entry ()->getFieldAmount (sfTakerGets) - amount2.out); view ().entryModify (leg2.entry ()); // Execute the payments in order: // Taker pays Leg1 (amount1.in - A currency) // Leg1 pays Leg2 (amount1.out == amount2.in, XRP) // Leg2 pays Taker (amount2.out - B currency) result = view ().accountSend (m_account, leg1.account (), amount1.in); if (result == tesSUCCESS) result = view ().accountSend (leg1.account (), leg2.account (), amount1.out); if (result == tesSUCCESS) result = view ().accountSend (leg2.account (), m_account, amount2.out); return result; }
// Fill a bridged offer. // @param leg1 the first leg we are going to use. // @param amount1 the amount to flow through the first leg of the offer. // @param leg2 the second leg we are going to use. // @param amount2 the amount to flow through the second leg of the offer. // @return tesSUCCESS if successful, or an error code otherwise. TER Taker::fill ( Offer const& leg1, Amounts const& amount1, Offer const& leg2, Amounts const& amount2) { assert (amount1.out == amount2.in); consume (leg1, amount1); consume (leg2, amount2); /* It is possible that m_account is the same as leg1.account, leg2.account * or both. This could happen when bridging over one's own offer. In that * case, accountSend won't actually do a send, which is what we want. */ TER result = view ().accountSend (m_account, leg1.account (), amount1.in); if (result == tesSUCCESS) result = view ().accountSend (leg1.account (), leg2.account (), amount1.out); if (result == tesSUCCESS) result = view ().accountSend (leg2.account (), m_account, amount2.out); return result; }