#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { template class BookStep : public StepImp> { protected: enum class OfferType { AMM, CLOB }; static constexpr uint32_t MaxOffersToConsume{1000}; Book book_; AccountID strandSrc_; AccountID strandDst_; // Charge transfer fees when the prev step redeems Step const* const prevStep_ = nullptr; bool const ownerPaysTransferFee_; // Mark as inactive (dry) if too many offers are consumed bool inactive_ = false; /** Number of offers consumed or partially consumed the last time the step ran, including expired and unfunded offers. N.B. This is not the total number offers consumed by this step for the entire payment, it is only the number the last time it ran. Offers may be partially consumed multiple times during a payment. */ std::uint32_t offersUsed_ = 0; // If set, AMM liquidity might be available // if AMM offer quality is better than CLOB offer // quality or there is no CLOB offer. std::optional> ammLiquidity_; beast::Journal const j_; Asset const strandDeliver_; struct Cache { TIn in; TOut out; Cache(TIn const& in_, TOut const& out_) : in(in_), out(out_) { } }; std::optional cache_; private: BookStep(StrandContext const& ctx, Asset const& in, Asset const& out) : book_(in, out, ctx.domainID) , strandSrc_(ctx.strandSrc) , strandDst_(ctx.strandDst) , prevStep_(ctx.prevStep) , ownerPaysTransferFee_(ctx.ownerPaysTransferFee) , j_(ctx.j) , strandDeliver_(ctx.strandDeliver) { if (auto const ammSle = ctx.view.read(keylet::amm(in, out)); ammSle && ammSle->getFieldAmount(sfLPTokenBalance) != beast::zero) { ammLiquidity_.emplace( ctx.view, (*ammSle)[sfAccount], getTradingFee(ctx.view, *ammSle, ctx.ammContext.account()), in, out, ctx.ammContext, ctx.j); } } public: Book const& book() const { return book_; } std::optional cachedIn() const override { if (!cache_) return std::nullopt; return EitherAmount(cache_->in); } std::optional cachedOut() const override { if (!cache_) return std::nullopt; return EitherAmount(cache_->out); } DebtDirection debtDirection(ReadView const& sb, StrandDirection dir) const override { return ownerPaysTransferFee_ ? DebtDirection::issues : DebtDirection::redeems; } std::optional bookStepBook() const override { return book_; } std::pair, DebtDirection> qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const override; std::pair, DebtDirection> getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const override; std::uint32_t offersUsed() const override; std::pair revImp( PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set& ofrsToRm, TOut const& out); std::pair fwdImp( PaymentSandbox& sb, ApplyView& afView, boost::container::flat_set& ofrsToRm, TIn const& in); std::pair validFwd(PaymentSandbox& sb, ApplyView& afView, EitherAmount const& in) override; // Check for errors frozen constraints. TER check(StrandContext const& ctx) const; bool inactive() const override { return inactive_; } protected: std::string logStringImpl(char const* name) const { std::ostringstream ostr; ostr << name << ": " << "\ninIss: " << book_.in.getIssuer() << "\noutIss: " << book_.out.getIssuer() << "\ninCur: " << to_string(book_.in) << "\noutCur: " << to_string(book_.out); return ostr.str(); } Rate rate(ReadView const& view, Asset const& asset, AccountID const& dstAccount) const; private: friend bool operator==(BookStep const& lhs, BookStep const& rhs) { return lhs.book_ == rhs.book_; } friend bool operator!=(BookStep const& lhs, BookStep const& rhs) { return !(lhs == rhs); } bool equal(Step const& rhs) const override; // Iterate through the offers at the best quality in a book. // Unfunded offers and bad offers are skipped (and returned). // callback is called with the offer SLE, taker pays, taker gets. // If callback returns false, don't process any more offers. // Return the unfunded, bad offers and the number of offers consumed. template std::pair, std::uint32_t> forEachOffer( PaymentSandbox& sb, ApplyView& afView, DebtDirection prevStepDebtDir, Callback& callback) const; // Offer is either TOffer or AMMOffer template