#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 kMaxOffersToConsume{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::kZero) { ammLiquidity_.emplace( ctx.view, (*ammSle)[sfAccount], getTradingFee(ctx.view, *ammSle, ctx.ammContext.account()), in, out, ctx.ammContext, ctx.j); } } public: [[nodiscard]] Book const& book() const { return book_; } [[nodiscard]] std::optional cachedIn() const override { if (!cache_) return std::nullopt; return EitherAmount(cache_->in); } [[nodiscard]] std::optional cachedOut() const override { if (!cache_) return std::nullopt; return EitherAmount(cache_->out); } [[nodiscard]] DebtDirection debtDirection(ReadView const& sb, StrandDirection dir) const override { return ownerPaysTransferFee_ ? DebtDirection::Issues : DebtDirection::Redeems; } [[nodiscard]] std::optional bookStepBook() const override { return book_; } [[nodiscard]] std::pair, DebtDirection> qualityUpperBound(ReadView const& v, DebtDirection prevStepDir) const override; [[nodiscard]] std::pair, DebtDirection> getQualityFunc(ReadView const& v, DebtDirection prevStepDir) const override; [[nodiscard]] 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. [[nodiscard]] TER check(StrandContext const& ctx) const; [[nodiscard]] 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(); } [[nodiscard]] 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); } [[nodiscard]] 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