1#include <xrpl/tx/transactors/nft/NFTokenAcceptOffer.h>
3#include <xrpl/basics/Log.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/beast/utility/Zero.h>
6#include <xrpl/core/ServiceRegistry.h>
7#include <xrpl/ledger/View.h>
8#include <xrpl/ledger/helpers/AccountRootHelpers.h>
9#include <xrpl/ledger/helpers/NFTokenHelpers.h>
10#include <xrpl/ledger/helpers/TokenHelpers.h>
11#include <xrpl/protocol/Asset.h>
12#include <xrpl/protocol/Feature.h>
13#include <xrpl/protocol/Indexes.h>
14#include <xrpl/protocol/Issue.h>
15#include <xrpl/protocol/LedgerFormats.h>
16#include <xrpl/protocol/Rate.h>
17#include <xrpl/protocol/SField.h>
18#include <xrpl/protocol/STAmount.h>
19#include <xrpl/protocol/STLedgerEntry.h>
20#include <xrpl/protocol/STTx.h>
21#include <xrpl/protocol/TER.h>
22#include <xrpl/protocol/XRPAmount.h>
23#include <xrpl/protocol/nft.h>
24#include <xrpl/tx/Transactor.h>
35 auto const bo = ctx.
tx[~sfNFTokenBuyOffer];
36 auto const so = ctx.
tx[~sfNFTokenSellOffer];
44 if (
auto const bf = ctx.
tx[~sfNFTokenBrokerFee])
66 auto const checkOffer =
88 if ((*offerSLE)[sfAmount].negative())
96 auto const [bo, err1] = checkOffer(ctx.
tx[~sfNFTokenBuyOffer]);
99 auto const [so, err2] = checkOffer(ctx.
tx[~sfNFTokenSellOffer]);
107 if ((*bo)[sfNFTokenID] != (*so)[sfNFTokenID])
111 if ((*bo)[sfAmount].asset() != (*so)[sfAmount].asset())
116 if (((*bo)[sfOwner] == (*so)[sfOwner]))
121 if ((*so)[sfAmount] > (*bo)[sfAmount])
126 if (
auto const dest = bo->at(~sfDestination); dest && *dest != ctx.
tx[sfAccount])
133 if (
auto const dest = so->at(~sfDestination); dest && *dest != ctx.
tx[sfAccount])
142 if (
auto const brokerFee = ctx.
tx[~sfNFTokenBrokerFee])
144 if (brokerFee->asset() != (*bo)[sfAmount].asset())
147 if (brokerFee >= (*bo)[sfAmount])
150 if ((*so)[sfAmount] > (*bo)[sfAmount] - *brokerFee)
154 if (!brokerFee->native() && ctx.
view.
rules().
enabled(fixEnforceNFTokenTrustlineV2))
157 ctx.
view, ctx.
tx[sfAccount], ctx.
j, brokerFee->asset().get<
Issue>());
162 ctx.
view, ctx.
tx[sfAccount], ctx.
j, brokerFee->asset().get<
Issue>());
171 if (bo->isFlag(lsfSellNFToken))
175 if ((*bo)[sfOwner] == ctx.
tx[sfAccount])
187 if (
auto const dest = bo->at(~sfDestination);
188 dest.has_value() && *dest != ctx.
tx[sfAccount])
196 auto const needed = bo->at(sfAmount);
206 if (ctx.
view.
rules().
enabled(fixEnforceNFTokenTrustlineV2) && !needed.native())
209 ctx.
view, bo->at(sfOwner), ctx.
j, needed.asset().get<
Issue>());
216 ctx.
view, ctx.
tx[sfAccount], ctx.
j, needed.asset().get<
Issue>());
221 ctx.
view, ctx.
tx[sfAccount], ctx.
j, needed.asset().get<
Issue>());
230 if (!so->isFlag(lsfSellNFToken))
234 if ((*so)[sfOwner] == ctx.
tx[sfAccount])
246 if (
auto const dest = so->at(~sfDestination);
247 dest.has_value() && *dest != ctx.
tx[sfAccount])
252 auto const needed = so->at(sfAmount);
274 if (!needed.native())
279 ctx.
view, (*so)[sfOwner], ctx.
j, needed.asset().get<
Issue>());
286 ctx.
view, ctx.
tx[sfAccount], ctx.
j, needed.asset().get<
Issue>());
293 ctx.
view, (*so)[sfOwner], ctx.
j, needed.asset().get<
Issue>());
301 auto const& offer = bo ? bo : so;
308 auto const& tokenID = offer->at(sfNFTokenID);
309 auto const& amount = offer->at(sfAmount);
319 nftMinter != amount.getIssuer() &&
327 ctx.
view, nftMinter, ctx.
j, amount.asset().get<
Issue>());
332 ctx.
view, nftMinter, ctx.
j, amount.asset().get<
Issue>());
383 std::uint32_t const buyerOwnerCountBefore = sleBuyer->getFieldU32(sfOwnerCount);
395 auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance);
397 auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
398 if (buyerOwnerCountAfter > buyerOwnerCountBefore)
410 bool const isSell = offer->isFlag(lsfSellNFToken);
411 AccountID const owner = (*offer)[sfOwner];
415 auto const nftokenID = (*offer)[sfNFTokenID];
417 if (
auto amount = offer->getFieldAmount(sfAmount); amount !=
beast::kZero)
425 cut !=
beast::kZero && seller != issuer && buyer != issuer)
452 auto bo = loadToken(
ctx_.tx[~sfNFTokenBuyOffer]);
453 auto so = loadToken(
ctx_.tx[~sfNFTokenSellOffer]);
457 if (
view().rules().enabled(fixCleanup3_1_3))
459 bool foundExpired =
false;
461 auto const deleteOfferIfExpired = [
this, &foundExpired](
SLE::Ref offer) ->
TER {
464 JLOG(
j_.trace()) <<
"Offer is expired, deleting: " << offer->
key();
469 <<
"Unable to delete expired offer '" << offer->
key() <<
"': ignoring";
473 JLOG(
j_.trace()) <<
"Deleted offer " << offer->
key();
479 if (
auto const r = deleteOfferIfExpired(bo); !
isTesSuccess(r))
481 if (
auto const r = deleteOfferIfExpired(so); !
isTesSuccess(r))
491 JLOG(
j_.fatal()) <<
"Unable to delete buy offer '" <<
to_string(bo->key()) <<
"': ignoring";
499 JLOG(
j_.fatal()) <<
"Unable to delete sell offer '" <<
to_string(so->key())
511 auto const nftokenID = (*so)[sfNFTokenID];
530 if (
auto const cut =
ctx_.tx[~sfNFTokenBrokerFee]; cut && cut.value() !=
beast::kZero)
535 amount -= cut.
value();
543 if (
auto const issuer =
nft::getIssuer(nftokenID); seller != issuer && buyer != issuer)
A generic endpoint for log messages.
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
bool finalizeInvariants(STTx const &tx, TER result, XRPAmount fee, ReadView const &view, beast::Journal const &j) override
Check transaction-specific post-conditions after all entries have been visited.
void visitInvariantEntry(bool isDelete, SLE::ConstRef before, SLE::ConstRef after) override
Inspect a single ledger entry modified by this transaction.
TER pay(AccountID const &from, AccountID const &to, STAmount const &amount)
static NotTEC preflight(PreflightContext const &ctx)
static TER preclaim(PreclaimContext const &ctx)
TER transferNFToken(AccountID const &buyer, AccountID const &seller, UInt256 const &nfTokenID)
TER acceptOffer(SLE::Ref offer)
virtual Rules const & rules() const =0
Returns the tx processing rules.
virtual SLE::const_pointer 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.
STAmount const & value() const noexcept
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & ConstRef
std::shared_ptr< STLedgerEntry > const & Ref
AccountID const accountID_
Keylet nftokenOffer(AccountID const &owner, SeqProxy const &seq)
An offer from an account to buy or sell an NFT.
Keylet account(AccountID const &id) noexcept
AccountID root.
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const ¤cy) noexcept
The index of a trust line for a given currency.
TER insertToken(ApplyView &view, AccountID owner, STObject &&nft)
Insert the token in the owner's token directory.
constexpr std::uint16_t const kFlagCreateTrustLines
std::optional< TokenAndPage > findTokenAndPage(ApplyView &view, AccountID const &owner, UInt256 const &nftokenID)
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)
std::optional< STObject > findToken(ReadView const &view, AccountID const &owner, UInt256 const &nftokenID)
Finds the specified token in the owner's token directory.
TER checkTrustlineAuthorized(ReadView const &view, AccountID const id, beast::Journal const j, Issue const &issue)
std::uint16_t getFlags(UInt256 const &id)
AccountID getIssuer(UInt256 const &id)
std::uint16_t getTransferFee(UInt256 const &id)
bool deleteTokenOffer(ApplyView &view, SLE::Ref offer)
Deletes the given token offer.
Rate transferFeeAsRate(std::uint16_t fee)
Given a transfer fee (in basis points) convert it to a transfer rate.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp, ExpiryComparison comparison=ExpiryComparison::Inclusive)
Determines whether the given expiration time has passed.
std::string to_string(BaseUInt< Bits, Tag > const &a)
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
TERSubset< CanCvtToNotTEC > NotTEC
TER accountSend(ApplyView &view, AccountID const &from, AccountID const &to, STAmount const &saAmount, beast::Journal j, SLE::Ref sponsorSle={}, WaiveTransferFee waiveFee=WaiveTransferFee::No, AllowMPTOverflow allowOverflow=AllowMPTOverflow::No)
Calls static accountSendIOU if saAmount represents Issue.
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
bool isTesSuccess(TER x) noexcept
TERSubset< CanCvtToTER > TER
XRPAmount accountReserve(ReadView const &view, SLE::ConstRef sle, beast::Journal j, Adjustment adj={})
Returns the account reserve, in drops.
@ tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
@ tecNFTOKEN_BUY_SELL_MISMATCH
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
@ tecINSUFFICIENT_RESERVE
@ tecINSUFFICIENT_PAYMENT
STAmount multiply(STAmount const &amount, Number const &frac, Number::RoundingMode rm)
BadAsset const & badAsset()
State information when determining if a tx is likely to claim a fee.
State information when preflighting a tx.