1#include <xrpl/ledger/View.h>
2#include <xrpl/ledger/helpers/TokenHelpers.h>
3#include <xrpl/protocol/Feature.h>
4#include <xrpl/protocol/Rate.h>
5#include <xrpl/protocol/TxFlags.h>
6#include <xrpl/tx/transactors/nft/NFTokenAcceptOffer.h>
7#include <xrpl/tx/transactors/nft/NFTokenUtils.h>
14 auto const bo = ctx.
tx[~sfNFTokenBuyOffer];
15 auto const so = ctx.
tx[~sfNFTokenSellOffer];
23 if (
auto const bf = ctx.
tx[~sfNFTokenBrokerFee])
28 if (*bf <= beast::zero)
38 auto const checkOffer =
63 if ((*offerSLE)[sfAmount].negative())
71 auto const [bo, err1] = checkOffer(ctx.
tx[~sfNFTokenBuyOffer]);
74 auto const [so, err2] = checkOffer(ctx.
tx[~sfNFTokenSellOffer]);
82 if ((*bo)[sfNFTokenID] != (*so)[sfNFTokenID])
86 if ((*bo)[sfAmount].issue() != (*so)[sfAmount].issue())
91 if (((*bo)[sfOwner] == (*so)[sfOwner]))
96 if ((*so)[sfAmount] > (*bo)[sfAmount])
101 if (
auto const dest = bo->at(~sfDestination); dest && *dest != ctx.
tx[sfAccount])
108 if (
auto const dest = so->at(~sfDestination); dest && *dest != ctx.
tx[sfAccount])
117 if (
auto const brokerFee = ctx.
tx[~sfNFTokenBrokerFee])
119 if (brokerFee->issue() != (*bo)[sfAmount].issue())
122 if (brokerFee >= (*bo)[sfAmount])
125 if ((*so)[sfAmount] > (*bo)[sfAmount] - *brokerFee)
129 if (!brokerFee->native() && ctx.
view.
rules().
enabled(fixEnforceNFTokenTrustlineV2))
132 ctx.
view, ctx.
tx[sfAccount], ctx.
j, brokerFee->asset().get<
Issue>());
137 ctx.
view, ctx.
tx[sfAccount], ctx.
j, brokerFee->asset().get<
Issue>());
146 if (((*bo)[sfFlags] & lsfSellNFToken) == lsfSellNFToken)
150 if ((*bo)[sfOwner] == ctx.
tx[sfAccount])
162 if (
auto const dest = bo->at(~sfDestination);
163 dest.has_value() && *dest != ctx.
tx[sfAccount])
171 auto const needed = bo->at(sfAmount);
180 if (ctx.
view.
rules().
enabled(fixEnforceNFTokenTrustlineV2) && !needed.native())
183 ctx.
view, bo->at(sfOwner), ctx.
j, needed.asset().get<
Issue>());
190 ctx.
view, ctx.
tx[sfAccount], ctx.
j, needed.asset().get<
Issue>());
195 ctx.
view, ctx.
tx[sfAccount], ctx.
j, needed.asset().get<
Issue>());
204 if (((*so)[sfFlags] & lsfSellNFToken) != lsfSellNFToken)
208 if ((*so)[sfOwner] == ctx.
tx[sfAccount])
220 if (
auto const dest = so->at(~sfDestination);
221 dest.has_value() && *dest != ctx.
tx[sfAccount])
226 auto const needed = so->at(sfAmount);
246 if (!needed.native())
251 ctx.
view, (*so)[sfOwner], ctx.
j, needed.asset().get<
Issue>());
258 ctx.
view, ctx.
tx[sfAccount], ctx.
j, needed.asset().get<
Issue>());
265 ctx.
view, (*so)[sfOwner], ctx.
j, needed.asset().get<
Issue>());
273 auto const& offer = bo ? bo : so;
280 auto const& tokenID = offer->at(sfNFTokenID);
281 auto const& amount = offer->at(sfAmount);
291 nftMinter != amount.getIssuer() &&
299 ctx.
view, nftMinter, ctx.
j, amount.asset().get<
Issue>());
304 ctx.
view, nftMinter, ctx.
j, amount.asset().get<
Issue>());
317 if (amount < beast::zero)
355 std::uint32_t const buyerOwnerCountBefore = sleBuyer->getFieldU32(sfOwnerCount);
365 if (
view().rules().enabled(fixNFTokenReserve))
372 auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance);
374 auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
375 if (buyerOwnerCountAfter > buyerOwnerCountBefore)
377 if (
auto const reserve =
view().fees().accountReserve(buyerOwnerCountAfter);
378 buyerBalance < reserve)
389 bool const isSell = offer->isFlag(lsfSellNFToken);
390 AccountID const owner = (*offer)[sfOwner];
394 auto const nftokenID = (*offer)[sfNFTokenID];
396 if (
auto amount = offer->getFieldAmount(sfAmount); amount != beast::zero)
404 cut != beast::zero && seller != issuer && buyer != issuer)
431 auto bo = loadToken(
ctx_.
tx[~sfNFTokenBuyOffer]);
432 auto so = loadToken(
ctx_.
tx[~sfNFTokenSellOffer]);
437 if (
view().rules().enabled(fixExpiredNFTokenOfferRemoval))
439 bool foundExpired =
false;
441 auto const deleteOfferIfExpired =
445 JLOG(
j_.
trace()) <<
"Offer is expired, deleting: " << offer->
key();
450 <<
"Unable to delete expired offer '" << offer->
key() <<
"': ignoring";
454 JLOG(
j_.
trace()) <<
"Deleted offer " << offer->
key();
460 if (
auto const r = deleteOfferIfExpired(bo); !
isTesSuccess(r))
462 if (
auto const r = deleteOfferIfExpired(so); !
isTesSuccess(r))
472 JLOG(
j_.
fatal()) <<
"Unable to delete buy offer '" <<
to_string(bo->key()) <<
"': ignoring";
492 auto const nftokenID = (*so)[sfNFTokenID];
511 if (
auto const cut =
ctx_.
tx[~sfNFTokenBrokerFee]; cut && cut.value() != beast::zero)
516 amount -= cut.value();
524 if (
auto const issuer =
nft::getIssuer(nftokenID); seller != issuer && buyer != issuer)
534 if (amount > beast::zero)
Stream trace() const
Severity stream access functions.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
TER acceptOffer(std::shared_ptr< SLE > const &offer)
TER transferNFToken(AccountID const &buyer, AccountID const &seller, uint256 const &nfTokenID)
TER pay(AccountID const &from, AccountID const &to, STAmount const &amount)
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Keylet line(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
Keylet account(AccountID const &id) noexcept
AccountID root.
TER insertToken(ApplyView &view, AccountID owner, STObject &&nft)
Insert the token in the owner's token directory.
constexpr std::uint16_t const flagCreateTrustLines
TER removeToken(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
Remove the token from the owner's token directory.
TER checkTrustlineDeepFrozen(ReadView const &view, AccountID const id, beast::Journal const j, Issue const &issue)
TER checkTrustlineAuthorized(ReadView const &view, AccountID const id, beast::Journal const j, Issue const &issue)
std::optional< STObject > findToken(ReadView const &view, AccountID const &owner, uint256 const &nftokenID)
Finds the specified token in the owner's token directory.
AccountID getIssuer(uint256 const &id)
std::uint16_t getTransferFee(uint256 const &id)
std::optional< TokenAndPage > findTokenAndPage(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
Rate transferFeeAsRate(std::uint16_t fee)
Given a transfer fee (in basis points) convert it to a transfer rate.
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
std::uint16_t getFlags(uint256 const &id)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::string to_string(base_uint< Bits, Tag > const &a)
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
STAmount multiply(STAmount const &amount, Rate const &rate)
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee=WaiveTransferFee::No)
Calls static accountSendIOU if saAmount represents Issue.
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
bool isTesSuccess(TER x) noexcept
@ tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
@ tecNFTOKEN_BUY_SELL_MISMATCH
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
@ tecINSUFFICIENT_RESERVE
@ tecINSUFFICIENT_PAYMENT
TERSubset< CanCvtToNotTEC > NotTEC
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.