Compare commits

...

5 Commits

Author SHA1 Message Date
Mayukha Vadari
2def8be3e6 Include Keylet.h in the SLEBase test
(cherry picked from commit 3cca9d3266)
2026-10-05 14:50:31 +01:00
Mayukha Vadari
1e1882978c test: Drop extern template declarations from SLEBase test
An explicit instantiation declaration stops clang from emitting the
inline constructors of the header-only entries at -O0, so the debug
builds fail to link xrpl_tests (undefined AccountRootEntry constructors).
Naming each class in a static_assert already instantiates its definition,
which is all the exhaustiveness check needs.

(cherry picked from commit 611415e1f9)
2026-10-05 14:50:31 +01:00
Mayukha Vadari
957171240b refactor: Move ammHolds onto AMMEntry
Replace the free ammHolds(view, ammSle, ..., j) with the const member
AMMEntry::holds(optAsset1, optAsset2, freezeHandling, authHandling).
The member reads the pool balances through the entry's own view and
logs through the entry's journal, so the preclaim call sites now pass
ctx.j to the AMMEntryR constructor.

The body moves to the new src/libxrpl/ledger/entries/AMMEntry.cpp,
which explicitly instantiates AMMEntry for ReadView and ApplyView.
2026-10-05 13:55:18 +01:00
Mayukha Vadari
c7e03bd83e test: Declare, not define, entry instantiations in SLEBase test
The exhaustive per-entry-type check in the SLEBase gtest used explicit
instantiation definitions. Once an entry class gets out-of-line member
bodies in its own .cpp (with the explicit instantiation definition
there), the test would instantiate the class a second time, and MSVC
rejects it with C4661 because those member bodies are not visible in
the test translation unit. Explicit instantiation declarations still
instantiate the class definition, which is all the check needs.

(cherry picked from commit 8b07abc3cd)
2026-10-05 13:55:17 +01:00
Mayukha Vadari
f0c32fad3c refactor: Use AMMEntry everywhere
Replace raw AMM SLE pointers with AMMEntryR/AMMEntryW in the AMM
transactors, AMMHelpers, the payment path steps, TrustSet, and the
LPToken checks in View.cpp and TokenHelpers.cpp. Helpers that take an
AMM SLE (ammHolds, ammLPHolds, getTradingFee, initializeFeeAuctionVote,
verifyAndAdjustLPTokenBalance, and the AMMWithdraw/AMMClawback private
members) now take AMMEntryR const& or AMMEntryW& and stay where they
are.

AMMInfo and the jtx AMM helper wrap their existing SLE to match the new
ammLPHolds signature. TheoreticalQuality_test no longer uses a const
PaymentSandbox, because read-only entries peek() through an ApplyView.

No behavior change.
2026-10-05 13:40:46 +01:00
22 changed files with 249 additions and 207 deletions

View File

@@ -5,9 +5,16 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/SLEBase.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
#include <expected>
#include <optional>
#include <tuple>
namespace xrpl {
@@ -37,6 +44,16 @@ public:
: Base(keylet::amm(ammID), view, j)
{
}
// Get the AMM pool and LP token balances. If both optAsset1 and optAsset2
// are set, they are used as the AMM token pair assets. Otherwise the
// missing assets are read from this AMM.
[[nodiscard]] std::expected<std::tuple<STAmount, STAmount, STAmount>, TER>
holds(
std::optional<Asset> const& optAsset1,
std::optional<Asset> const& optAsset2,
FreezeHandling freezeHandling,
AuthHandling authHandling) const;
};
using AMMEntryR = AMMEntry<ReadView>;

View File

@@ -8,6 +8,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AMMCore.h>
#include <xrpl/protocol/AccountID.h>
@@ -812,21 +813,6 @@ checkAMMPrecisionLoss(
STAmount const& newLPTokenBalance,
beast::Journal const j);
/**
* Get AMM pool and LP token balances. If both optIssue are
* provided then they are used as the AMM token pair issues.
* Otherwise the missing issues are fetched from ammSle.
*/
std::expected<std::tuple<STAmount, STAmount, STAmount>, TER>
ammHolds(
ReadView const& view,
SLE const& ammSle,
std::optional<Asset> const& optAsset1,
std::optional<Asset> const& optAsset2,
FreezeHandling freezeHandling,
AuthHandling authHandling,
beast::Journal const j);
/**
* Get the balance of LP tokens.
*/
@@ -842,7 +828,7 @@ ammLPHolds(
STAmount
ammLPHolds(
ReadView const& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& lpAccount,
beast::Journal const j);
@@ -852,7 +838,7 @@ ammLPHolds(
* accounts.
*/
std::uint16_t
getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account);
getTradingFee(ReadView const& view, AMMEntryR const& ammSle, AccountID const& account);
/**
* Returns total amount held by AMM for the given token.
@@ -873,7 +859,7 @@ deleteAMMAccount(Sandbox& view, Asset const& asset, Asset const& asset2, beast::
void
initializeFeeAuctionVote(
ApplyView& view,
SLE::pointer& ammSle,
AMMEntryW& ammSle,
AccountID const& account,
Asset const& lptAsset,
std::uint16_t tfee);
@@ -895,7 +881,7 @@ std::expected<bool, TER>
verifyAndAdjustLPTokenBalance(
Sandbox& sb,
STAmount const& lpTokens,
SLE::pointer& ammSle,
AMMEntryW& ammSle,
AccountID const& account);
} // namespace xrpl

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -52,7 +53,7 @@ checkFreeze(
if (auto const sleDst = view.read(keylet::account(dst));
sleDst && sleDst->isFieldPresent(sfAMMID))
{
auto const sleAmm = view.read(keylet::amm((*sleDst)[sfAMMID]));
AMMEntryR const sleAmm((*sleDst)[sfAMMID], view);
if (!sleAmm)
return tecINTERNAL; // LCOV_EXCL_LINE

View File

@@ -3,6 +3,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STTx.h>
@@ -71,7 +72,7 @@ private:
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
equalWithdrawMatchingOneAmount(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& holder,
AccountID const& ammAccount,
STAmount const& amountBalance,

View File

@@ -3,6 +3,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Asset.h>
@@ -121,7 +122,7 @@ public:
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
equalWithdrawTokens(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const account,
std::optional<AccountID> const& clawbackIssuer,
AccountID const& ammAccount,
@@ -168,7 +169,7 @@ public:
static std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
withdraw(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
std::optional<AccountID> const& clawbackIssuer,
AccountID const& account,
@@ -188,7 +189,7 @@ public:
static std::pair<TER, bool>
deleteAMMAccountIfEmpty(
Sandbox& sb,
SLE::pointer const ammSle,
AMMEntryW& ammSle,
STAmount const& lpTokenBalance,
Asset const& asset1,
Asset const& asset2,
@@ -221,7 +222,7 @@ private:
std::pair<TER, STAmount>
withdraw(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& amountWithdraw,
@@ -247,7 +248,7 @@ private:
std::pair<TER, STAmount>
equalWithdrawTokens(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& amount2Balance,
@@ -273,7 +274,7 @@ private:
std::pair<TER, STAmount>
equalWithdrawLimit(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& amount2Balance,
@@ -296,7 +297,7 @@ private:
std::pair<TER, STAmount>
singleWithdraw(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& lptAMMBalance,
@@ -318,7 +319,7 @@ private:
std::pair<TER, STAmount>
singleWithdrawTokens(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& lptAMMBalance,
@@ -341,7 +342,7 @@ private:
std::pair<TER, STAmount>
singleWithdrawEPrice(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& lptAMMBalance,

View File

@@ -9,6 +9,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
@@ -193,7 +194,7 @@ canTransferLPToken(
if (!sleIssuer || !sleIssuer->isFieldPresent(sfAMMID))
return tesSUCCESS;
auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID]));
AMMEntryR const sleAmm((*sleIssuer)[sfAMMID], view);
if (!sleAmm)
return tecINTERNAL; // LCOV_EXCL_LINE

View File

@@ -0,0 +1,91 @@
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/basics/Log.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AMMCore.h>
#include <xrpl/protocol/Asset.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
#include <expected>
#include <optional>
#include <tuple>
#include <utility>
namespace xrpl {
template <typename ViewT>
std::expected<std::tuple<STAmount, STAmount, STAmount>, TER>
AMMEntry<ViewT>::holds(
std::optional<Asset> const& optAsset1,
std::optional<Asset> const& optAsset2,
FreezeHandling freezeHandling,
AuthHandling authHandling) const
{
auto const j = this->journal();
auto const assets = [&]() -> std::optional<std::pair<Asset, Asset>> {
auto const asset1 = Base::operator*()[sfAsset];
auto const asset2 = Base::operator*()[sfAsset2];
if (optAsset1 && optAsset2)
{
if (invalidAMMAssetPair(
*optAsset1, *optAsset2, std::make_optional(std::make_pair(asset1, asset2))))
{
// This error can only be hit if the AMM is corrupted
// LCOV_EXCL_START
JLOG(j.debug()) << "ammHolds: Invalid optAsset1 or optAsset2 " << *optAsset1 << " "
<< *optAsset2;
return std::nullopt;
// LCOV_EXCL_STOP
}
return std::make_optional(std::make_pair(*optAsset1, *optAsset2));
}
auto const singleAsset = [&asset1, &asset2, &j](
Asset checkIssue,
char const* label) -> std::optional<std::pair<Asset, Asset>> {
if (checkIssue == asset1)
{
return std::make_optional(std::make_pair(asset1, asset2));
}
if (checkIssue == asset2)
{
return std::make_optional(std::make_pair(asset2, asset1));
}
// Unreachable unless AMM corrupted.
// LCOV_EXCL_START
JLOG(j.debug()) << "ammHolds: Invalid " << label << " " << checkIssue;
return std::nullopt;
// LCOV_EXCL_STOP
};
if (optAsset1)
{
return singleAsset(*optAsset1, "optAsset1");
}
if (optAsset2)
{
// Cannot have Amount2 without Amount.
return singleAsset(*optAsset2, "optAsset2"); // LCOV_EXCL_LINE
}
return std::make_optional(std::make_pair(asset1, asset2));
}();
if (!assets)
return std::unexpected(tecAMM_INVALID_TOKENS);
auto const [amount1, amount2] = ammPoolHolds(
this->readView(),
Base::operator->()->getAccountID(sfAccount),
assets->first,
assets->second,
freezeHandling,
authHandling,
j);
return std::make_tuple(amount1, amount2, Base::operator*()[sfLPTokenBalance]);
}
template class AMMEntry<ReadView>;
template class AMMEntry<ApplyView>;
} // namespace xrpl

View File

@@ -11,6 +11,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -471,74 +472,6 @@ checkAMMPrecisionLoss(
return checkAMMPrecisionLoss(root2(amount * amount2), newLPTokenBalance);
}
std::expected<std::tuple<STAmount, STAmount, STAmount>, TER>
ammHolds(
ReadView const& view,
SLE const& ammSle,
std::optional<Asset> const& optAsset1,
std::optional<Asset> const& optAsset2,
FreezeHandling freezeHandling,
AuthHandling authHandling,
beast::Journal const j)
{
auto const assets = [&]() -> std::optional<std::pair<Asset, Asset>> {
auto const asset1 = ammSle[sfAsset];
auto const asset2 = ammSle[sfAsset2];
if (optAsset1 && optAsset2)
{
if (invalidAMMAssetPair(
*optAsset1, *optAsset2, std::make_optional(std::make_pair(asset1, asset2))))
{
// This error can only be hit if the AMM is corrupted
// LCOV_EXCL_START
JLOG(j.debug()) << "ammHolds: Invalid optAsset1 or optAsset2 " << *optAsset1 << " "
<< *optAsset2;
return std::nullopt;
// LCOV_EXCL_STOP
}
return std::make_optional(std::make_pair(*optAsset1, *optAsset2));
}
auto const singleAsset = [&asset1, &asset2, &j](
Asset checkIssue,
char const* label) -> std::optional<std::pair<Asset, Asset>> {
if (checkIssue == asset1)
{
return std::make_optional(std::make_pair(asset1, asset2));
}
if (checkIssue == asset2)
{
return std::make_optional(std::make_pair(asset2, asset1));
}
// Unreachable unless AMM corrupted.
// LCOV_EXCL_START
JLOG(j.debug()) << "ammHolds: Invalid " << label << " " << checkIssue;
return std::nullopt;
// LCOV_EXCL_STOP
};
if (optAsset1)
{
return singleAsset(*optAsset1, "optAsset1");
}
if (optAsset2)
{
// Cannot have Amount2 without Amount.
return singleAsset(*optAsset2, "optAsset2"); // LCOV_EXCL_LINE
}
return std::make_optional(std::make_pair(asset1, asset2));
}();
if (!assets)
return std::unexpected(tecAMM_INVALID_TOKENS);
auto const [amount1, amount2] = ammPoolHolds(
view,
ammSle.getAccountID(sfAccount),
assets->first,
assets->second,
freezeHandling,
authHandling,
j);
return std::make_tuple(amount1, amount2, ammSle[sfLPTokenBalance]);
}
STAmount
ammLPHolds(
ReadView const& view,
@@ -592,21 +525,22 @@ ammLPHolds(
STAmount
ammLPHolds(
ReadView const& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& lpAccount,
beast::Journal const j)
{
return ammLPHolds(view, ammSle[sfAsset], ammSle[sfAsset2], ammSle[sfAccount], lpAccount, j);
return ammLPHolds(
view, (*ammSle)[sfAsset], (*ammSle)[sfAsset2], (*ammSle)[sfAccount], lpAccount, j);
}
std::uint16_t
getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
getTradingFee(ReadView const& view, AMMEntryR const& ammSle, AccountID const& account)
{
using namespace std::chrono;
XRPL_ASSERT(ammSle.isFieldPresent(sfAuctionSlot), "xrpl::getTradingFee : auction present");
if (ammSle.isFieldPresent(sfAuctionSlot))
XRPL_ASSERT(ammSle->isFieldPresent(sfAuctionSlot), "xrpl::getTradingFee : auction present");
if (ammSle->isFieldPresent(sfAuctionSlot))
{
auto const& auctionSlot = safeDowncast<STObject const&>(ammSle.peekAtField(sfAuctionSlot));
auto const& auctionSlot = safeDowncast<STObject const&>(ammSle->peekAtField(sfAuctionSlot));
// Not expired
if (auto const expiration = auctionSlot[~sfExpiration];
duration_cast<seconds>(view.header().parentCloseTime.time_since_epoch()).count() <
@@ -624,7 +558,7 @@ getTradingFee(ReadView const& view, SLE const& ammSle, AccountID const& account)
}
}
}
return ammSle[sfTradingFee];
return (*ammSle)[sfTradingFee];
}
STAmount
@@ -747,7 +681,7 @@ deleteAMMMPTokens(Sandbox& sb, AccountID const& ammAccountID, beast::Journal j)
TER
deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Journal j)
{
auto ammSle = sb.peek(keylet::amm(asset, asset2));
AMMEntryW ammSle(asset, asset2, sb, j);
if (!ammSle)
{
// LCOV_EXCL_START
@@ -794,7 +728,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
// LCOV_EXCL_STOP
}
sb.erase(ammSle);
ammSle.erase();
sb.erase(sleAMMRoot);
return tesSUCCESS;
@@ -803,7 +737,7 @@ deleteAMMAccount(Sandbox& sb, Asset const& asset, Asset const& asset2, beast::Jo
void
initializeFeeAuctionVote(
ApplyView& view,
SLE::pointer& ammSle,
AMMEntryW& ammSle,
AccountID const& account,
Asset const& lptAsset,
std::uint16_t tfee)
@@ -961,7 +895,7 @@ std::expected<bool, TER>
verifyAndAdjustLPTokenBalance(
Sandbox& sb,
STAmount const& lpTokens,
SLE::pointer& ammSle,
AMMEntryW& ammSle,
AccountID const& account)
{
auto const res = isOnlyLiquidityProvider(sb, lpTokens.get<Issue>(), account);
@@ -976,7 +910,7 @@ verifyAndAdjustLPTokenBalance(
lpTokens, ammSle->getFieldAmount(sfLPTokenBalance), Number{1, -3}))
{
ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
sb.update(ammSle);
ammSle.update();
}
else
{

View File

@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -300,7 +301,7 @@ getLineIfUsable(
}
if (sleIssuer->isFieldPresent(sfAMMID))
{
auto const sleAmm = view.read(keylet::amm((*sleIssuer)[sfAMMID]));
AMMEntryR const sleAmm((*sleIssuer)[sfAMMID], view);
if (!sleAmm ||
isLPTokenFrozen(view, account, (*sleAmm)[sfAsset], (*sleAmm)[sfAsset2]))

View File

@@ -7,6 +7,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/PaymentSandbox.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -106,13 +107,13 @@ private:
, j_(ctx.j)
, strandDeliver_(ctx.strandDeliver)
{
if (auto const ammSle = ctx.view.read(keylet::amm(in, out));
if (AMMEntryR const ammSle(in, out, ctx.view);
ammSle && ammSle->getFieldAmount(sfLPTokenBalance) != beast::kZero)
{
ammLiquidity_.emplace(
ctx.view,
(*ammSle)[sfAccount],
getTradingFee(ctx.view, *ammSle, ctx.ammContext.account()),
getTradingFee(ctx.view, ammSle, ctx.ammContext.account()),
in,
out,
ctx.ammContext,

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -104,7 +105,7 @@ AMMBid::preflight(PreflightContext const& ctx)
TER
AMMBid::preclaim(PreclaimContext const& ctx)
{
auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
AMMEntryR const ammSle(ctx.tx[sfAsset], ctx.tx[sfAsset2], ctx.view);
if (!ammSle)
{
JLOG(ctx.j.debug()) << "AMM Bid: Invalid asset pair.";
@@ -127,7 +128,7 @@ AMMBid::preclaim(PreclaimContext const& ctx)
}
}
auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
auto const lpTokens = ammLPHolds(ctx.view, ammSle, ctx.tx[sfAccount], ctx.j);
// Not LP
if (lpTokens == beast::kZero)
{
@@ -179,11 +180,11 @@ static std::pair<TER, bool>
applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journal j)
{
using namespace std::chrono;
auto const ammSle = sb.peek(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
AMMEntryW ammSle(ctx.tx[sfAsset], ctx.tx[sfAsset2], sb, j);
if (!ammSle)
return {tecINTERNAL, false};
STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance];
auto const lpTokens = ammLPHolds(sb, *ammSle, account, ctx.journal);
auto const lpTokens = ammLPHolds(sb, ammSle, account, ctx.journal);
XRPL_ASSERT(ammSle->isFieldPresent(sfAuctionSlot), "xrpl::applyBid : has auction slot");
if (!ammSle->isFieldPresent(sfAuctionSlot))
@@ -250,7 +251,7 @@ applyBid(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Journa
return res;
}
ammSle->setFieldAmount(sfLPTokenBalance, lptAMMBalance - saBurn);
sb.update(ammSle);
ammSle.update();
return tesSUCCESS;
};

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AccountID.h>
@@ -109,7 +110,7 @@ AMMClawback::preclaim(PreclaimContext const& ctx)
if (!ctx.view.read(keylet::account(ctx.tx[sfHolder])))
return terNO_ACCOUNT;
auto const ammSle = ctx.view.read(keylet::amm(asset, asset2));
AMMEntryR const ammSle(asset, asset2, ctx.view);
if (!ammSle)
{
JLOG(ctx.j.debug()) << "AMM Clawback: Invalid asset pair.";
@@ -173,7 +174,7 @@ AMMClawback::applyGuts(Sandbox& sb)
Asset const asset = ctx_.tx[sfAsset];
Asset const asset2 = ctx_.tx[sfAsset2];
auto ammSle = sb.peek(keylet::amm(asset, asset2));
AMMEntryW ammSle(asset, asset2, sb, j_);
if (!ammSle)
return tecINTERNAL; // LCOV_EXCL_LINE
@@ -185,7 +186,7 @@ AMMClawback::applyGuts(Sandbox& sb)
if (sb.rules().enabled(fixAMMClawbackRounding))
{
// retrieve LP token balance inside the amendment gate to avoid inconsistent error behavior
auto const lpTokenBalance = ammLPHolds(sb, *ammSle, holder, j_);
auto const lpTokenBalance = ammLPHolds(sb, ammSle, holder, j_);
if (lpTokenBalance == beast::kZero)
return tecAMM_BALANCE;
@@ -194,14 +195,8 @@ AMMClawback::applyGuts(Sandbox& sb)
return res.error(); // LCOV_EXCL_LINE
}
auto const expected = ammHolds(
sb,
*ammSle,
asset,
asset2,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
ctx_.journal);
auto const expected =
ammSle.holds(asset, asset2, FreezeHandling::IgnoreFreeze, AuthHandling::IgnoreAuth);
if (!expected)
return expected.error(); // LCOV_EXCL_LINE
@@ -214,7 +209,7 @@ AMMClawback::applyGuts(Sandbox& sb)
// calling a second time on purpose since `verifyAndAdjustLPTokenBalance` rounds and may adjust
// the balance
auto const holdLPtokens = ammLPHolds(sb, *ammSle, holder, j_);
auto const holdLPtokens = ammLPHolds(sb, ammSle, holder, j_);
if (holdLPtokens == beast::kZero)
return tecAMM_BALANCE;
@@ -225,7 +220,7 @@ AMMClawback::applyGuts(Sandbox& sb)
std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
AMMWithdraw::equalWithdrawTokens(
sb,
*ammSle,
ammSle,
holder,
issuer,
ammAccount,
@@ -247,7 +242,7 @@ AMMClawback::applyGuts(Sandbox& sb)
std::tie(result, newLPTokenBalance, amountWithdraw, amount2Withdraw) =
equalWithdrawMatchingOneAmount(
sb,
*ammSle,
ammSle,
holder,
ammAccount,
amountBalance,
@@ -304,7 +299,7 @@ AMMClawback::applyGuts(Sandbox& sb)
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
AMMClawback::equalWithdrawMatchingOneAmount(
Sandbox& sb,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& holder,
AccountID const& ammAccount,
STAmount const& amountBalance,

View File

@@ -5,6 +5,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -98,7 +99,7 @@ AMMCreate::preclaim(PreclaimContext const& ctx)
// Check if AMM already exists for the token pair
if (auto const ammKeylet = keylet::amm(amount.asset(), amount2.asset());
ctx.view.read(ammKeylet))
AMMEntryR(ammKeylet, ctx.view))
{
JLOG(ctx.j.debug()) << "AMM Instance: ltAMM already exists.";
return tecDUPLICATE;
@@ -291,7 +292,8 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
auto const lpTokens = ammLPTokens(amount, amount2, lptIss);
// Create ltAMM
auto ammSle = std::make_shared<SLE>(ammKeylet);
AMMEntryW ammSle(ammKeylet, sb, j);
ammSle.newSLE();
ammSle->setAccountID(sfAccount, accountId);
ammSle->setFieldAmount(sfLPTokenBalance, lpTokens);
auto const& [asset1, asset2] = std::minmax(amount.asset(), amount2.asset());
@@ -301,12 +303,14 @@ applyCreate(ApplyContext& ctx, Sandbox& sb, AccountID const& account, beast::Jou
initializeFeeAuctionVote(ctx.view(), ammSle, account, lptIss, ctx.tx[sfTradingFee]);
// Add owner directory to link the root account and AMM object.
if (auto ter = dirLink(sb, accountId, ammSle); ter)
// dirLink takes a non-const pointer reference; a copy shares the same SLE.
auto ammRawSle = ammSle.mutableRawSle();
if (auto ter = dirLink(sb, accountId, ammRawSle); ter)
{
JLOG(j.debug()) << "AMM Instance: failed to insert owner dir";
return {ter, false};
}
sb.insert(ammSle);
ammSle.insert();
// Send LPT to LP.
auto res = accountSend(sb, accountId, account, lpTokens, ctx.journal);

View File

@@ -2,11 +2,12 @@
#include <xrpl/basics/Log.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/protocol/AMMCore.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -36,7 +37,7 @@ AMMDelete::preflight(PreflightContext const& ctx)
TER
AMMDelete::preclaim(PreclaimContext const& ctx)
{
auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
AMMEntryR const ammSle(ctx.tx[sfAsset], ctx.tx[sfAsset2], ctx.view);
if (!ammSle)
{
JLOG(ctx.j.debug()) << "AMM Delete: Invalid asset pair.";

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -179,21 +180,15 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
{
auto const accountID = ctx.tx[sfAccount];
auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
AMMEntryR const ammSle(ctx.tx[sfAsset], ctx.tx[sfAsset2], ctx.view, ctx.j);
if (!ammSle)
{
JLOG(ctx.j.debug()) << "AMM Deposit: Invalid asset pair.";
return terNO_AMM;
}
auto const expected = ammHolds(
ctx.view,
*ammSle,
std::nullopt,
std::nullopt,
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
ctx.j);
auto const expected = ammSle.holds(
std::nullopt, std::nullopt, FreezeHandling::IgnoreFreeze, AuthHandling::IgnoreAuth);
if (!expected)
return expected.error(); // LCOV_EXCL_LINE
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
@@ -388,7 +383,7 @@ AMMDeposit::preclaim(PreclaimContext const& ctx)
// Check the reserve for LPToken trustline if not LP.
// We checked above but need to check again if depositing IOU only.
if (ammLPHolds(ctx.view, *ammSle, accountID, ctx.j) == beast::kZero)
if (ammLPHolds(ctx.view, ammSle, accountID, ctx.j) == beast::kZero)
{
STAmount const xrpBalance = xrpLiquid(ctx.view, accountID, 1, ctx.j);
// Insufficient reserve
@@ -416,25 +411,22 @@ AMMDeposit::applyGuts(Sandbox& sb)
auto const amount2 = ctx_.tx[~sfAmount2];
auto const ePrice = ctx_.tx[~sfEPrice];
auto const lpTokensDeposit = ctx_.tx[~sfLPTokenOut];
auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
AMMEntryW ammSle(ctx_.tx[sfAsset], ctx_.tx[sfAsset2], sb, j_);
if (!ammSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const ammAccountID = (*ammSle)[sfAccount];
auto const expected = ammHolds(
sb,
*ammSle,
auto const expected = ammSle.holds(
amount ? amount->asset() : std::optional<Asset>{},
amount2 ? amount2->asset() : std::optional<Asset>{},
FreezeHandling::ZeroIfFrozen,
AuthHandling::ZeroIfUnauthorized,
ctx_.journal);
AuthHandling::ZeroIfUnauthorized);
if (!expected)
return {expected.error(), false}; // LCOV_EXCL_LINE
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
auto const tfee = (lptAMMBalance == beast::kZero)
? ctx_.tx[~sfTradingFee].value_or(0)
: getTradingFee(ctx_.view(), *ammSle, accountID_);
: getTradingFee(ctx_.view(), ammSle, accountID_);
auto const subTxType = ctx_.tx.getFlags() & tfDepositSubTx;
@@ -541,7 +533,7 @@ AMMDeposit::applyGuts(Sandbox& sb)
if (lptAMMBalance == beast::kZero)
initializeFeeAuctionVote(sb, ammSle, accountID_, lptAMMBalance.asset(), tfee);
sb.update(ammSle);
ammSle.update();
}
return {result, isTesSuccess(result)};

View File

@@ -4,12 +4,13 @@
#include <xrpl/basics/Number.h>
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/protocol/AMMCore.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/MPTIssue.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STArray.h>
@@ -58,7 +59,7 @@ AMMVote::preflight(PreflightContext const& ctx)
TER
AMMVote::preclaim(PreclaimContext const& ctx)
{
auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
AMMEntryR const ammSle(ctx.tx[sfAsset], ctx.tx[sfAsset2], ctx.view);
if (!ammSle)
{
JLOG(ctx.j.debug()) << "AMM Vote: Invalid asset pair.";
@@ -68,7 +69,7 @@ AMMVote::preclaim(PreclaimContext const& ctx)
{
return tecAMM_EMPTY;
}
if (auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
if (auto const lpTokensNew = ammLPHolds(ctx.view, ammSle, ctx.tx[sfAccount], ctx.j);
lpTokensNew == beast::kZero)
{
JLOG(ctx.j.debug()) << "AMM Vote: account is not LP.";
@@ -82,11 +83,11 @@ static std::pair<TER, bool>
applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& accountID, beast::Journal j)
{
auto const feeNew = ctx.tx[sfTradingFee];
auto ammSle = sb.peek(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
AMMEntryW ammSle(ctx.tx[sfAsset], ctx.tx[sfAsset2], sb, j);
if (!ammSle)
return {tecINTERNAL, false};
STAmount const lptAMMBalance = (*ammSle)[sfLPTokenBalance];
auto const lpTokensNew = ammLPHolds(sb, *ammSle, accountID, ctx.journal);
auto const lpTokensNew = ammLPHolds(sb, ammSle, accountID, ctx.journal);
std::optional<STAmount> minTokens;
std::size_t minPos{0};
AccountID minAccount{0};
@@ -104,7 +105,7 @@ applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& accountID, beast::Jou
for (auto const& entry : ammSle->getFieldArray(sfVoteSlots))
{
auto const entryAccount = entry[sfAccount];
auto lpTokens = ammLPHolds(sb, *ammSle, entryAccount, ctx.journal);
auto lpTokens = ammLPHolds(sb, ammSle, entryAccount, ctx.journal);
if (lpTokens == beast::kZero)
{
JLOG(j.debug()) << "AMMVote::applyVote, accountID " << entryAccount << " is not LP";
@@ -226,7 +227,7 @@ applyVote(ApplyContext& ctx, Sandbox& sb, AccountID const& accountID, beast::Jou
auctionSlot.makeFieldAbsent(sfDiscountedFee);
}
}
sb.update(ammSle);
ammSle.update();
return {tesSUCCESS, true};
}

View File

@@ -6,6 +6,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/Sandbox.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/MPTokenHelpers.h>
@@ -185,7 +186,7 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx)
{
auto const accountID = ctx.tx[sfAccount];
auto const ammSle = ctx.view.read(keylet::amm(ctx.tx[sfAsset], ctx.tx[sfAsset2]));
AMMEntryR const ammSle(ctx.tx[sfAsset], ctx.tx[sfAsset2], ctx.view, ctx.j);
if (!ammSle)
{
JLOG(ctx.j.debug()) << "AMM Withdraw: Invalid asset pair.";
@@ -195,14 +196,11 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx)
auto const amount = ctx.tx[~sfAmount];
auto const amount2 = ctx.tx[~sfAmount2];
auto const expected = ammHolds(
ctx.view,
*ammSle,
auto const expected = ammSle.holds(
amount ? amount->asset() : std::optional<Asset>{},
amount2 ? amount2->asset() : std::optional<Asset>{},
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
ctx.j);
AuthHandling::IgnoreAuth);
if (!expected)
return expected.error();
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
@@ -277,7 +275,7 @@ AMMWithdraw::preclaim(PreclaimContext const& ctx)
if (auto const ter = checkAmount(amount2, amount2Balance))
return ter;
auto const lpTokens = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
auto const lpTokens = ammLPHolds(ctx.view, ammSle, ctx.tx[sfAccount], ctx.j);
auto const lpTokensWithdraw = tokensWithdraw(lpTokens, ctx.tx[~sfLPTokenIn], ctx.tx.getFlags());
if (lpTokens <= beast::kZero)
@@ -320,7 +318,7 @@ AMMWithdraw::issuerFreezeHandling() const
{
// When the withdrawer is the issuer of a pool asset, the issuer can
// always receive their own token — even when the pool is frozen.
// Use IgnoreFreeze so ammHolds returns real balances instead of zero.
// Use IgnoreFreeze so AMMEntry::holds() returns real balances instead of zero.
if (!ctx_.view().rules().enabled(fixCleanup3_3_0))
return FreezeHandling::ZeroIfFrozen;
@@ -340,14 +338,14 @@ AMMWithdraw::applyGuts(Sandbox& sb)
auto const amount = ctx_.tx[~sfAmount];
auto const amount2 = ctx_.tx[~sfAmount2];
auto const ePrice = ctx_.tx[~sfEPrice];
auto ammSle = sb.peek(keylet::amm(ctx_.tx[sfAsset], ctx_.tx[sfAsset2]));
AMMEntryW ammSle(ctx_.tx[sfAsset], ctx_.tx[sfAsset2], sb, j_);
if (!ammSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const ammAccountID = (*ammSle)[sfAccount];
auto const accountSle = sb.read(keylet::account(ammAccountID));
if (!accountSle)
return {tecINTERNAL, false}; // LCOV_EXCL_LINE
auto const lpTokens = ammLPHolds(ctx_.view(), *ammSle, ctx_.tx[sfAccount], ctx_.journal);
auto const lpTokens = ammLPHolds(ctx_.view(), ammSle, ctx_.tx[sfAccount], ctx_.journal);
auto const lpTokensWithdraw =
tokensWithdraw(lpTokens, ctx_.tx[~sfLPTokenIn], ctx_.tx.getFlags());
@@ -359,18 +357,15 @@ AMMWithdraw::applyGuts(Sandbox& sb)
return {res.error(), false};
}
auto const tfee = getTradingFee(ctx_.view(), *ammSle, accountID_);
auto const tfee = getTradingFee(ctx_.view(), ammSle, accountID_);
auto const freezeHandling = issuerFreezeHandling();
auto const expected = ammHolds(
sb,
*ammSle,
auto const expected = ammSle.holds(
amount ? amount->asset() : std::optional<Asset>{},
amount2 ? amount2->asset() : std::optional<Asset>{},
freezeHandling,
AuthHandling::ZeroIfUnauthorized,
ctx_.journal);
AuthHandling::ZeroIfUnauthorized);
if (!expected)
return {expected.error(), false};
auto const [amountBalance, amount2Balance, lptAMMBalance] = *expected;
@@ -384,7 +379,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
{
return equalWithdrawLimit(
sb,
*ammSle,
ammSle,
ammAccountID,
amountBalance,
amount2Balance,
@@ -397,7 +392,7 @@ AMMWithdraw::applyGuts(Sandbox& sb)
{
return singleWithdrawTokens(
sb,
*ammSle,
ammSle,
ammAccountID,
amountBalance,
lptAMMBalance,
@@ -408,18 +403,18 @@ AMMWithdraw::applyGuts(Sandbox& sb)
if (subTxType & tfLimitLPToken)
{
return singleWithdrawEPrice(
sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, *ePrice, tfee);
sb, ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, *ePrice, tfee);
}
if (subTxType & tfSingleAsset)
{
return singleWithdraw(
sb, *ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
sb, ammSle, ammAccountID, amountBalance, lptAMMBalance, *amount, tfee);
}
if (subTxType & tfLPToken || subTxType & tfWithdrawAll)
{
return equalWithdrawTokens(
sb,
*ammSle,
ammSle,
ammAccountID,
amountBalance,
amount2Balance,
@@ -502,7 +497,7 @@ AMMWithdraw::doApply()
std::pair<TER, STAmount>
AMMWithdraw::withdraw(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& amountWithdraw,
@@ -537,7 +532,7 @@ AMMWithdraw::withdraw(
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
AMMWithdraw::withdraw(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
std::optional<AccountID> const& clawbackIssuer,
AccountID const& account,
@@ -555,8 +550,8 @@ AMMWithdraw::withdraw(
beast::Journal const& journal)
{
auto const lpTokens = ammLPHolds(view, ammSle, account, journal);
auto const expected = ammHolds(
view, ammSle, amountWithdraw.asset(), std::nullopt, freezeHandling, authHandling, journal);
auto const expected =
ammSle.holds(amountWithdraw.asset(), std::nullopt, freezeHandling, authHandling);
// LCOV_EXCL_START
if (!expected)
return {expected.error(), STAmount{}, STAmount{}, STAmount{}};
@@ -835,7 +830,7 @@ adjustLPTokensIn(
std::pair<TER, STAmount>
AMMWithdraw::equalWithdrawTokens(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& amount2Balance,
@@ -870,7 +865,7 @@ AMMWithdraw::equalWithdrawTokens(
std::pair<TER, bool>
AMMWithdraw::deleteAMMAccountIfEmpty(
Sandbox& sb,
SLE::pointer const ammSle,
AMMEntryW& ammSle,
STAmount const& lpTokenBalance,
Asset const& asset1,
Asset const& asset2,
@@ -890,7 +885,7 @@ AMMWithdraw::deleteAMMAccountIfEmpty(
if (updateBalance)
{
ammSle->setFieldAmount(sfLPTokenBalance, lpTokenBalance);
sb.update(ammSle);
ammSle.update();
}
return {ter, true};
@@ -902,7 +897,7 @@ AMMWithdraw::deleteAMMAccountIfEmpty(
std::tuple<TER, STAmount, STAmount, std::optional<STAmount>>
AMMWithdraw::equalWithdrawTokens(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const account,
std::optional<AccountID> const& clawbackIssuer,
AccountID const& ammAccount,
@@ -1018,7 +1013,7 @@ AMMWithdraw::equalWithdrawTokens(
std::pair<TER, STAmount>
AMMWithdraw::equalWithdrawLimit(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& amount2Balance,
@@ -1089,7 +1084,7 @@ AMMWithdraw::equalWithdrawLimit(
std::pair<TER, STAmount>
AMMWithdraw::singleWithdraw(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& lptAMMBalance,
@@ -1141,7 +1136,7 @@ AMMWithdraw::singleWithdraw(
std::pair<TER, STAmount>
AMMWithdraw::singleWithdrawTokens(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& lptAMMBalance,
@@ -1195,7 +1190,7 @@ AMMWithdraw::singleWithdrawTokens(
std::pair<TER, STAmount>
AMMWithdraw::singleWithdrawEPrice(
Sandbox& view,
SLE const& ammSle,
AMMEntryR const& ammSle,
AccountID const& ammAccount,
STAmount const& amountBalance,
STAmount const& lptAMMBalance,

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
#include <xrpl/ledger/helpers/SponsorHelpers.h>
@@ -205,7 +206,7 @@ TrustSet::preclaim(PreclaimContext const& ctx)
{
// pass
}
else if (auto const ammSle = ctx.view.read({ltAMM, sleDst->getFieldH256(sfAMMID)}))
else if (auto const ammSle = AMMEntryR(sleDst->getFieldH256(sfAMMID), ctx.view))
{
auto const lpTokens = ammSle->getFieldAmount(sfLPTokenBalance);
if (lpTokens == beast::kZero)

View File

@@ -252,7 +252,10 @@ class TheoreticalQuality_test : public beast::unit_test::Suite
std::shared_ptr<ReadView const> closed,
std::optional<Quality> const& expectedQ = {})
{
PaymentSandbox const sb(closed.get(), TapNone);
// Not const: read-only ledger entries peek() through an ApplyView, which
// is undefined behavior on a const object (see SLEBase.h).
// NOLINTNEXTLINE(misc-const-correctness)
PaymentSandbox sb(closed.get(), TapNone);
AMMContext ammContext(rcp.srcAccount, false);
auto const sendMaxIssue = [&rcp]() -> std::optional<Asset> {

View File

@@ -11,6 +11,7 @@
#include <xrpl/basics/safe_cast.h>
#include <xrpl/json/json_value.h>
#include <xrpl/json/to_string.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AMMCore.h>
@@ -258,7 +259,7 @@ AMM::balances(Asset const& asset1, Asset const& asset2, std::optional<AccountID>
AuthHandling::IgnoreAuth,
env_.journal);
auto const lptAMMBalance = account
? ammLPHolds(*env_.current(), *amm, *account, env_.journal)
? ammLPHolds(*env_.current(), AMMEntryR(amm, *env_.current()), *account, env_.journal)
: amm->getFieldAmount(sfLPTokenBalance);
return {asset1Balance, asset2Balance, lptAMMBalance};
}
@@ -297,7 +298,8 @@ AMM::expectLPTokens(AccountID const& account, IOUAmount const& expTokens) const
{
if (auto const amm = env_.current()->read(keylet::amm(asset1_.asset(), asset2_.asset())))
{
auto const lptAMMBalance = ammLPHolds(*env_.current(), *amm, account, env_.journal);
auto const lptAMMBalance =
ammLPHolds(*env_.current(), AMMEntryR(amm, *env_.current()), account, env_.journal);
return lptAMMBalance == STAmount{expTokens, lptIssue_};
}
return false;

View File

@@ -38,6 +38,7 @@
#include <xrpl/ledger/entries/XChainOwnedClaimIDEntry.h> // IWYU pragma: keep
#include <xrpl/ledger/entries/XChainOwnedCreateAccountClaimIDEntry.h> // IWYU pragma: keep
#include <xrpl/protocol/Indexes.h>
#include <xrpl/protocol/Keylet.h>
#include <xrpl/protocol/LedgerFormats.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STLedgerEntry.h>
@@ -76,12 +77,22 @@ template class SLEBase<ApplyView>;
#pragma push_macro("LEDGER_ENTRY")
#undef LEDGER_ENTRY
// No explicit instantiation here, neither a definition nor a declaration.
// A definition would instantiate an entry a second time once its member
// bodies live in its own .cpp (MSVC rejects that with C4661 under /WX), and a
// declaration (`extern template`) stops clang from emitting the inline
// constructors of the header-only entries at -O0, which leaves them undefined
// at link time in the debug builds. Naming the class in a static_assert is
// enough to instantiate its definition, which is all this exhaustiveness
// check needs; the per-type suites exercise the members.
#define LEDGER_ENTRY(tag, value, name, ...) \
template class name##Entry<ReadView>; \
template class name##Entry<ApplyView>; \
static_assert( \
name##Entry<ReadView>::kEntryType == tag && name##Entry<ApplyView>::kEntryType == tag, \
#name "Entry must be bound to " #tag);
#name "Entry must be bound to " #tag); \
static_assert( \
std::is_constructible_v<name##Entry<ReadView>, Keylet const&, ReadView const&> && \
std::is_constructible_v<name##Entry<ApplyView>, Keylet const&, ApplyView&>, \
#name "Entry must be constructible from a Keylet and a view");
#include <xrpl/protocol/detail/ledger_entries.macro>

View File

@@ -11,6 +11,7 @@
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/json/json_value.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/AMMEntry.h>
#include <xrpl/ledger/helpers/AMMHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
#include <xrpl/protocol/AMMCore.h>
@@ -191,8 +192,9 @@ doAMMInfo(rpc::JsonContext& context)
FreezeHandling::IgnoreFreeze,
AuthHandling::IgnoreAuth,
context.j);
auto const lptAMMBalance =
accountID ? ammLPHolds(*ledger, *amm, *accountID, context.j) : (*amm)[sfLPTokenBalance];
auto const lptAMMBalance = accountID
? ammLPHolds(*ledger, AMMEntryR(amm, *ledger), *accountID, context.j)
: (*amm)[sfLPTokenBalance];
json::Value ammResult;
asset1Balance.setJson(ammResult[jss::amount]);