Compare commits

...

1 Commits

Author SHA1 Message Date
Mayukha Vadari
007d178711 refactor: Use NFTokenOfferEntry everywhere
Replace raw NFTokenOffer SLE pointers with NFTokenOfferEntryR and
NFTokenOfferEntryW in the NFToken helpers, the NFTokenOffer
transactors, the expired-offer cleanup in Transactor, and the
AccountDelete deleter.

deleteTokenOffer() and NFTokenAcceptOffer::acceptOffer() now take the
wrapper instead of an SLE. They stay free/private functions; moving
helpers onto the wrapper comes in a later PR.

NFTokenAcceptOffer::doApply() reads the offers after it deletes them.
Because NFTokenOfferEntryW::erase() drops its SLE, doApply() keeps
read-only handles to the same SLEs for those reads.

No behavior change.
2026-10-05 13:50:25 +01:00
7 changed files with 59 additions and 39 deletions

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/NFTokenOfferEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/Issue.h>
#include <xrpl/protocol/Keylet.h>
@@ -81,7 +82,7 @@ removeToken(ApplyView& view, AccountID const& owner, UInt256 const& nftokenID, S
* The offer also consumes one incremental reserve.
*/
bool
deleteTokenOffer(ApplyView& view, SLE::Ref offer);
deleteTokenOffer(ApplyView& view, NFTokenOfferEntryW& offer);
/**
* Repairs the links in an NFTokenPage directory.

View File

@@ -4,6 +4,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/NFTokenOfferEntry.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/STTx.h>
@@ -21,10 +22,10 @@ private:
pay(AccountID const& from, AccountID const& to, STAmount const& amount);
TER
acceptOffer(SLE::Ref offer);
acceptOffer(NFTokenOfferEntryR const& offer);
TER
bridgeOffers(SLE::Ref buy, SLE::Ref sell);
bridgeOffers(NFTokenOfferEntryR const& buy, NFTokenOfferEntryR const& sell);
TER
transferNFToken(AccountID const& buyer, AccountID const& seller, UInt256 const& nfTokenID);

View File

@@ -7,6 +7,7 @@
#include <xrpl/beast/utility/instrumentation.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/NFTokenOfferEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/RippleStateHelpers.h>
@@ -591,7 +592,7 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
// deleting during iteration.
for (int i = offerIndexes.size() - 1; i >= 0; --i)
{
if (auto const offer = view.peek(keylet::nftokenOffer(offerIndexes[i])))
if (NFTokenOfferEntryW offer(offerIndexes[i], view); offer)
{
if (deleteTokenOffer(view, offer))
{
@@ -613,7 +614,7 @@ removeTokenOffersWithLimit(ApplyView& view, Keylet const& directory, std::size_t
}
bool
deleteTokenOffer(ApplyView& view, SLE::Ref offer)
deleteTokenOffer(ApplyView& view, NFTokenOfferEntryW& offer)
{
if (offer->getType() != ltNFTOKEN_OFFER)
return false;
@@ -635,7 +636,7 @@ deleteTokenOffer(ApplyView& view, SLE::Ref offer)
decreaseOwnerCount(view, owner, {}, 1, beast::Journal{beast::Journal::getNullSink()});
view.erase(offer);
offer.erase();
return true;
}
@@ -942,7 +943,8 @@ tokenOfferCreateApply(
priorBalance < accountReserve(view, acct, j, {.ownerCountDelta = 1}))
return tecINSUFFICIENT_RESERVE;
auto const offerID = keylet::nftokenOffer(acctID, seqProxy);
NFTokenOfferEntryW offer(acctID, seqProxy, view, j);
Keylet const offerID = offer.keylet();
// Create the offer:
{
@@ -973,7 +975,7 @@ tokenOfferCreateApply(
if (isSellOffer)
sleFlags |= lsfSellNFToken;
auto offer = std::make_shared<SLE>(offerID);
offer.newSLE();
(*offer)[sfOwner] = acctID;
(*offer)[sfNFTokenID] = nftokenID;
(*offer)[sfAmount] = amount;
@@ -987,7 +989,7 @@ tokenOfferCreateApply(
if (dest)
(*offer)[sfDestination] = *dest;
view.insert(offer);
offer.insert();
}
// Update owner count.

View File

@@ -11,6 +11,7 @@
#include <xrpl/json/to_string.h> // IWYU pragma: keep
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/entries/NFTokenOfferEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DelegateHelpers.h>
@@ -1236,7 +1237,7 @@ removeExpiredNFTokenOffers(
for (auto const& index : offers)
{
if (auto const offer = view.peek(keylet::nftokenOffer(index)))
if (NFTokenOfferEntryW offer(index, view, viewJ); offer)
{
nft::deleteTokenOffer(view, offer);
if (++removed == kExpiredOfferRemoveLimit)

View File

@@ -9,6 +9,7 @@
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/ledger/ReadView.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/NFTokenOfferEntry.h>
#include <xrpl/ledger/helpers/CredentialHelpers.h>
#include <xrpl/ledger/helpers/DirectoryHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
@@ -128,10 +129,11 @@ removeNFTokenOfferFromLedger(
ApplyView& view,
AccountID const& account,
UInt256 const& delIndex,
SLE::Ref sleDel,
beast::Journal)
SLE::Ref,
beast::Journal j)
{
if (!nft::deleteTokenOffer(view, sleDel))
NFTokenOfferEntryW offer(Keylet(ltNFTOKEN_OFFER, delIndex), view, j);
if (!nft::deleteTokenOffer(view, offer))
return tefBAD_LEDGER; // LCOV_EXCL_LINE
return tesSUCCESS;

View File

@@ -5,6 +5,7 @@
#include <xrpl/beast/utility/Zero.h>
#include <xrpl/core/ServiceRegistry.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/NFTokenOfferEntry.h>
#include <xrpl/ledger/helpers/AccountRootHelpers.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/ledger/helpers/TokenHelpers.h>
@@ -64,16 +65,17 @@ TER
NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
{
auto const checkOffer =
[&ctx](std::optional<UInt256> id) -> std::pair<SLE::const_pointer, TER> {
[&ctx](std::optional<UInt256> id) -> std::pair<NFTokenOfferEntryR, TER> {
NFTokenOfferEntryR const none(SLE::const_pointer{}, ctx.view);
if (id)
{
if (id->isZero())
return {nullptr, tecOBJECT_NOT_FOUND};
return {none, tecOBJECT_NOT_FOUND};
auto offerSLE = ctx.view.read(keylet::nftokenOffer(*id));
NFTokenOfferEntryR offerSLE(*id, ctx.view);
if (!offerSLE)
return {nullptr, tecOBJECT_NOT_FOUND};
return {none, tecOBJECT_NOT_FOUND};
if (hasExpired(ctx.view, (*offerSLE)[~sfExpiration]))
{
@@ -81,16 +83,16 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
// leaving them on ledger forever. After the amendment, we allow expired offers to
// reach doApply() where they get deleted and tecEXPIRED is returned.
if (!ctx.view.rules().enabled(fixCleanup3_1_3))
return {nullptr, tecEXPIRED};
return {none, tecEXPIRED};
// Amendment enabled: return the expired offer to be handled in doApply.
}
if ((*offerSLE)[sfAmount].negative())
return {nullptr, temBAD_OFFER};
return {none, temBAD_OFFER};
return {std::move(offerSLE), tesSUCCESS};
}
return {nullptr, tesSUCCESS};
return {none, tesSUCCESS};
};
auto const [bo, err1] = checkOffer(ctx.tx[~sfNFTokenBuyOffer]);
@@ -405,7 +407,7 @@ NFTokenAcceptOffer::transferNFToken(
}
TER
NFTokenAcceptOffer::acceptOffer(SLE::Ref offer)
NFTokenAcceptOffer::acceptOffer(NFTokenOfferEntryR const& offer)
{
bool const isSell = offer->isFlag(lsfSellNFToken);
AccountID const owner = (*offer)[sfOwner];
@@ -443,10 +445,14 @@ TER
NFTokenAcceptOffer::doApply()
{
auto const loadToken = [this](std::optional<UInt256> const& id) {
SLE::pointer sle;
std::optional<NFTokenOfferEntryW> offer;
if (id)
sle = view().peek(keylet::nftokenOffer(*id));
return sle;
{
offer.emplace(*id, view(), j_);
if (!*offer)
offer.reset();
}
return offer;
};
auto bo = loadToken(ctx_.tx[~sfNFTokenBuyOffer]);
@@ -458,11 +464,12 @@ NFTokenAcceptOffer::doApply()
{
bool foundExpired = false;
auto const deleteOfferIfExpired = [this, &foundExpired](SLE::Ref offer) -> TER {
if (offer && hasExpired(view(), (*offer)[~sfExpiration]))
auto const deleteOfferIfExpired =
[this, &foundExpired](std::optional<NFTokenOfferEntryW>& offer) -> TER {
if (offer && hasExpired(view(), (*offer)->at(~sfExpiration)))
{
JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key();
if (!nft::deleteTokenOffer(view(), offer))
if (!nft::deleteTokenOffer(view(), *offer))
{
// LCOV_EXCL_START
JLOG(j_.fatal())
@@ -485,7 +492,12 @@ NFTokenAcceptOffer::doApply()
return tecEXPIRED;
}
if (bo && !nft::deleteTokenOffer(view(), bo))
// deleteTokenOffer() erases the writable entries, but the offers' fields
// are still read below: keep read-only handles to the same SLEs.
std::optional<NFTokenOfferEntryR> const buyOffer(bo);
std::optional<NFTokenOfferEntryR> const sellOffer(so);
if (bo && !nft::deleteTokenOffer(view(), *bo))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete buy offer '" << to_string(bo->key()) << "': ignoring";
@@ -493,7 +505,7 @@ NFTokenAcceptOffer::doApply()
// LCOV_EXCL_STOP
}
if (so && !nft::deleteTokenOffer(view(), so))
if (so && !nft::deleteTokenOffer(view(), *so))
{
// LCOV_EXCL_START
JLOG(j_.fatal()) << "Unable to delete sell offer '" << to_string(so->key())
@@ -503,15 +515,15 @@ NFTokenAcceptOffer::doApply()
}
// Bridging two different offers
if (bo && so)
if (buyOffer && sellOffer)
{
AccountID const buyer = (*bo)[sfOwner];
AccountID const seller = (*so)[sfOwner];
AccountID const buyer = (*buyOffer)->at(sfOwner);
AccountID const seller = (*sellOffer)->at(sfOwner);
auto const nftokenID = (*so)[sfNFTokenID];
auto const nftokenID = (*sellOffer)->at(sfNFTokenID);
// The amount is what the buyer of the NFT pays:
STAmount amount = (*bo)[sfAmount];
STAmount amount = (*buyOffer)->at(sfAmount);
// Three different folks may be paid. The order of operations is
// important.
@@ -560,11 +572,11 @@ NFTokenAcceptOffer::doApply()
return transferNFToken(buyer, seller, nftokenID);
}
if (bo)
return acceptOffer(bo);
if (buyOffer)
return acceptOffer(*buyOffer);
if (so)
return acceptOffer(so);
if (sellOffer)
return acceptOffer(*sellOffer);
return tecINTERNAL; // LCOV_EXCL_LINE
}

View File

@@ -3,6 +3,7 @@
#include <xrpl/basics/Log.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/View.h>
#include <xrpl/ledger/entries/NFTokenOfferEntry.h>
#include <xrpl/ledger/helpers/NFTokenHelpers.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/Indexes.h>
@@ -88,7 +89,7 @@ NFTokenCancelOffer::doApply()
{
for (auto const& id : ctx_.tx[sfNFTokenOffers])
{
if (auto offer = view().peek(keylet::nftokenOffer(id));
if (NFTokenOfferEntryW offer(id, view(), j_);
offer && !nft::deleteTokenOffer(view(), offer))
{
// LCOV_EXCL_START