xrpld
Loading...
Searching...
No Matches
NFTokenAcceptOffer.cpp
1#include <xrpl/tx/transactors/nft/NFTokenAcceptOffer.h>
2
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/Feature.h>
12#include <xrpl/protocol/Indexes.h>
13#include <xrpl/protocol/Issue.h>
14#include <xrpl/protocol/LedgerFormats.h>
15#include <xrpl/protocol/Rate.h>
16#include <xrpl/protocol/SField.h>
17#include <xrpl/protocol/STLedgerEntry.h>
18#include <xrpl/protocol/STTx.h>
19#include <xrpl/protocol/TER.h>
20#include <xrpl/protocol/XRPAmount.h>
21#include <xrpl/protocol/nft.h>
22#include <xrpl/tx/Transactor.h>
23
24#include <cstdint>
25#include <optional>
26#include <utility>
27
28namespace xrpl {
29
32{
33 auto const bo = ctx.tx[~sfNFTokenBuyOffer];
34 auto const so = ctx.tx[~sfNFTokenSellOffer];
35
36 // At least one of these MUST be specified
37 if (!bo && !so)
38 return temMALFORMED;
39
40 // The `BrokerFee` field must not be present in direct mode but may be
41 // present and greater than zero in brokered mode.
42 if (auto const bf = ctx.tx[~sfNFTokenBrokerFee])
43 {
44 if (!bo || !so)
45 return temMALFORMED;
46
47 if (*bf <= beast::kZero)
48 return temMALFORMED;
49 }
50
51 return tesSUCCESS;
52}
53
54TER
56{
57 auto const checkOffer =
59 if (id)
60 {
61 if (id->isZero())
62 return {nullptr, tecOBJECT_NOT_FOUND};
63
64 auto offerSLE = ctx.view.read(keylet::nftokenOffer(*id));
65
66 if (!offerSLE)
67 return {nullptr, tecOBJECT_NOT_FOUND};
68
69 if (hasExpired(ctx.view, (*offerSLE)[~sfExpiration]))
70 {
71 // Before fixCleanup3_1_3 amendment, expired offers caused tecEXPIRED in preclaim,
72 // leaving them on ledger forever. After the amendment, we allow expired offers to
73 // reach doApply() where they get deleted and tecEXPIRED is returned.
74 if (!ctx.view.rules().enabled(fixCleanup3_1_3))
75 return {nullptr, tecEXPIRED};
76 // Amendment enabled: return the expired offer to be handled in doApply.
77 }
78
79 if ((*offerSLE)[sfAmount].negative())
80 return {nullptr, temBAD_OFFER};
81
82 return {std::move(offerSLE), tesSUCCESS};
83 }
84 return {nullptr, tesSUCCESS};
85 };
86
87 auto const [bo, err1] = checkOffer(ctx.tx[~sfNFTokenBuyOffer]);
88 if (!isTesSuccess(err1))
89 return err1;
90 auto const [so, err2] = checkOffer(ctx.tx[~sfNFTokenSellOffer]);
91 if (!isTesSuccess(err2))
92 return err2;
93
94 if (bo && so)
95 {
96 // Brokered mode:
97 // The two offers being brokered must be for the same token:
98 if ((*bo)[sfNFTokenID] != (*so)[sfNFTokenID])
100
101 // The two offers being brokered must be for the same asset:
102 if ((*bo)[sfAmount].asset() != (*so)[sfAmount].asset())
104
105 // The two offers may not form a loop. A broker may not sell the
106 // token to the current owner of the token.
107 if (((*bo)[sfOwner] == (*so)[sfOwner]))
109
110 // Ensure that the buyer is willing to pay at least as much as the
111 // seller is requesting:
112 if ((*so)[sfAmount] > (*bo)[sfAmount])
114
115 // The destination must be whoever is submitting the tx if the buyer
116 // specified it
117 if (auto const dest = bo->at(~sfDestination); dest && *dest != ctx.tx[sfAccount])
118 {
119 return tecNO_PERMISSION;
120 }
121
122 // The destination must be whoever is submitting the tx if the seller
123 // specified it
124 if (auto const dest = so->at(~sfDestination); dest && *dest != ctx.tx[sfAccount])
125 {
126 return tecNO_PERMISSION;
127 }
128
129 // The broker can specify an amount that represents their cut; if they
130 // have, ensure that the seller will get at least as much as they want
131 // to get *after* this fee is accounted for (but before the issuer's
132 // cut, if any).
133 if (auto const brokerFee = ctx.tx[~sfNFTokenBrokerFee])
134 {
135 if (brokerFee->asset() != (*bo)[sfAmount].asset())
137
138 if (brokerFee >= (*bo)[sfAmount])
140
141 if ((*so)[sfAmount] > (*bo)[sfAmount] - *brokerFee)
143
144 // Check if broker is allowed to receive the fee with these IOUs.
145 if (!brokerFee->native() && ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2))
146 {
148 ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get<Issue>());
149 if (!isTesSuccess(res))
150 return res;
151
153 ctx.view, ctx.tx[sfAccount], ctx.j, brokerFee->asset().get<Issue>());
154 if (!isTesSuccess(res))
155 return res;
156 }
157 }
158 }
159
160 if (bo)
161 {
162 if (bo->isFlag(lsfSellNFToken))
164
165 // An account can't accept an offer it placed:
166 if ((*bo)[sfOwner] == ctx.tx[sfAccount])
168
169 // If not in bridged mode, the account must own the token:
170 if (!so && !nft::findToken(ctx.view, ctx.tx[sfAccount], (*bo)[sfNFTokenID]))
171 return tecNO_PERMISSION;
172
173 // If not in bridged mode...
174 if (!so)
175 {
176 // If the offer has a Destination field, the acceptor must be the
177 // Destination.
178 if (auto const dest = bo->at(~sfDestination);
179 dest.has_value() && *dest != ctx.tx[sfAccount])
180 return tecNO_PERMISSION;
181 }
182
183 // The account offering to buy must have funds:
184 //
185 // After this amendment, we allow an IOU issuer to buy an NFT with their
186 // own currency
187 auto const needed = bo->at(sfAmount);
188
189 if (accountFunds(ctx.view, (*bo)[sfOwner], needed, FreezeHandling::ZeroIfFrozen, ctx.j) <
190 needed)
192
193 // Check that the account accepting the buy offer (he's selling the NFT)
194 // is allowed to receive IOUs. Also check that this offer's creator is
195 // authorized. But we need to exclude the case when the transaction is
196 // created by the broker.
197 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2) && !needed.native())
198 {
200 ctx.view, bo->at(sfOwner), ctx.j, needed.asset().get<Issue>());
201 if (!isTesSuccess(res))
202 return res;
203
204 if (!so)
205 {
207 ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get<Issue>());
208 if (!isTesSuccess(res))
209 return res;
210
212 ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get<Issue>());
213 if (!isTesSuccess(res))
214 return res;
215 }
216 }
217 }
218
219 if (so)
220 {
221 if (!so->isFlag(lsfSellNFToken))
223
224 // An account can't accept an offer it placed:
225 if ((*so)[sfOwner] == ctx.tx[sfAccount])
227
228 // The seller must own the token.
229 if (!nft::findToken(ctx.view, (*so)[sfOwner], (*so)[sfNFTokenID]))
230 return tecNO_PERMISSION;
231
232 // If not in bridged mode...
233 if (!bo)
234 {
235 // If the offer has a Destination field, the acceptor must be the
236 // Destination.
237 if (auto const dest = so->at(~sfDestination);
238 dest.has_value() && *dest != ctx.tx[sfAccount])
239 return tecNO_PERMISSION;
240 }
241
242 // The account offering to buy must have funds:
243 auto const needed = so->at(sfAmount);
244 if (!bo)
245 {
246 // After this amendment, we allow buyers to buy with their own
247 // issued currency.
248 //
249 // In the case of brokered mode, this check is essentially
250 // redundant, since we have already confirmed that buy offer is >
251 // than the sell offer, and that the buyer can cover the buy
252 // offer.
253 //
254 // We also _must not_ check the tx submitter in brokered
255 // mode, because then we are confirming that the broker can
256 // cover what the buyer will pay, which doesn't make sense, causes
257 // an unnecessary tec, and is also resolved with this amendment.
258 if (accountFunds(
259 ctx.view, ctx.tx[sfAccount], needed, FreezeHandling::ZeroIfFrozen, ctx.j) <
260 needed)
262 }
263
264 // Make sure that we are allowed to hold what the taker will pay us.
265 if (!needed.native())
266 {
267 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2))
268 {
270 ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get<Issue>());
271 if (!isTesSuccess(res))
272 return res;
273
274 if (!bo)
275 {
277 ctx.view, ctx.tx[sfAccount], ctx.j, needed.asset().get<Issue>());
278 if (!isTesSuccess(res))
279 return res;
280 }
281 }
282
283 auto const res = nft::checkTrustlineDeepFrozen(
284 ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get<Issue>());
285 if (!isTesSuccess(res))
286 return res;
287 }
288 }
289
290 // Additional checks are required in case a minter set a transfer fee for
291 // this nftoken
292 auto const& offer = bo ? bo : so;
293 if (!offer)
294 {
295 // Purely defensive, should be caught in preflight.
296 return tecINTERNAL; // LCOV_EXCL_LINE
297 }
298
299 auto const& tokenID = offer->at(sfNFTokenID);
300 auto const& amount = offer->at(sfAmount);
301 auto const nftMinter = nft::getIssuer(tokenID);
302
303 if (nft::getTransferFee(tokenID) != 0 && !amount.native())
304 {
305 // Fix a bug where the transfer of an NFToken with a transfer fee could
306 // give the NFToken issuer an undesired trust line.
307 // Issuer doesn't need a trust line to accept their own currency.
308 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustline) &&
309 (nft::getFlags(tokenID) & nft::kFlagCreateTrustLines) == 0 &&
310 nftMinter != amount.getIssuer() &&
311 !ctx.view.read(keylet::trustLine(nftMinter, amount.get<Issue>())))
312 return tecNO_LINE;
313
314 // Check that the issuer is allowed to receive IOUs.
315 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2))
316 {
318 ctx.view, nftMinter, ctx.j, amount.asset().get<Issue>());
319 if (!isTesSuccess(res))
320 return res;
321
323 ctx.view, nftMinter, ctx.j, amount.asset().get<Issue>());
324 if (!isTesSuccess(res))
325 return res;
326 }
327 }
328
329 return tesSUCCESS;
330}
331
332TER
333NFTokenAcceptOffer::pay(AccountID const& from, AccountID const& to, STAmount const& amount)
334{
335 // This should never happen, but it's easy and quick to check.
336 if (amount < beast::kZero)
337 return tecINTERNAL; // LCOV_EXCL_LINE
338
339 auto const result = accountSend(view(), from, to, amount, j_);
340
341 // If any payment causes a non-IOU-issuer to have a negative balance,
342 // or an IOU-issuer to have a positive balance in their own currency,
343 // we know that something went wrong. This was originally found in the
344 // context of IOU transfer fees. Since there are several payouts in this tx,
345 // just confirm that the end state is OK.
346 if (!isTesSuccess(result))
347 return result;
348 if (accountFunds(view(), from, amount, FreezeHandling::ZeroIfFrozen, j_).signum() < 0)
350 if (accountFunds(view(), to, amount, FreezeHandling::ZeroIfFrozen, j_).signum() < 0)
352 return tesSUCCESS;
353}
354
355TER
357 AccountID const& buyer,
358 AccountID const& seller,
359 uint256 const& nftokenID)
360{
361 auto tokenAndPage = nft::findTokenAndPage(view(), seller, nftokenID);
362
363 if (!tokenAndPage)
364 return tecINTERNAL; // LCOV_EXCL_LINE
365
366 if (auto const ret = nft::removeToken(view(), seller, nftokenID, tokenAndPage->page);
367 !isTesSuccess(ret))
368 return ret;
369
370 auto const sleBuyer = view().read(keylet::account(buyer));
371 if (!sleBuyer)
372 return tecINTERNAL; // LCOV_EXCL_LINE
373
374 std::uint32_t const buyerOwnerCountBefore = sleBuyer->getFieldU32(sfOwnerCount);
375
376 auto const insertRet = nft::insertToken(view(), buyer, std::move(tokenAndPage->token));
377
378 // There was an issue where the buyer accepts a sell offer, the ledger
379 // didn't check if the buyer has enough reserve, meaning that buyer can get
380 // NFTs free of reserve.
381 // To check if there is sufficient reserve, we cannot use preFeeBalance_
382 // because NFT is sold for a price. So we must use the balance after
383 // the deduction of the potential offer price. A small caveat here is
384 // that the balance has already deducted the transaction fee, meaning
385 // that the reserve requirement is a few drops higher.
386 auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance);
387
388 auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
389 if (buyerOwnerCountAfter > buyerOwnerCountBefore)
390 {
391 if (buyerBalance < accountReserve(view(), sleBuyer, j_))
393 }
394
395 return insertRet;
396}
397
398TER
400{
401 bool const isSell = offer->isFlag(lsfSellNFToken);
402 AccountID const owner = (*offer)[sfOwner];
403 AccountID const& seller = isSell ? owner : accountID_;
404 AccountID const& buyer = isSell ? accountID_ : owner;
405
406 auto const nftokenID = (*offer)[sfNFTokenID];
407
408 if (auto amount = offer->getFieldAmount(sfAmount); amount != beast::kZero)
409 {
410 // Calculate the issuer's cut from this sale, if any:
411 if (auto const fee = nft::getTransferFee(nftokenID); fee != 0)
412 {
413 auto const cut = multiply(amount, nft::transferFeeAsRate(fee));
414
415 if (auto const issuer = nft::getIssuer(nftokenID);
416 cut != beast::kZero && seller != issuer && buyer != issuer)
417 {
418 if (auto const r = pay(buyer, issuer, cut); !isTesSuccess(r))
419 return r;
420 amount -= cut;
421 }
422 }
423
424 // Send the remaining funds to the seller of the NFT
425 if (auto const r = pay(buyer, seller, amount); !isTesSuccess(r))
426 return r;
427 }
428
429 // Now transfer the NFT:
430 return transferNFToken(buyer, seller, nftokenID);
431}
432
433TER
435{
436 auto const loadToken = [this](std::optional<uint256> const& id) {
437 SLE::pointer sle;
438 if (id)
439 sle = view().peek(keylet::nftokenOffer(*id));
440 return sle;
441 };
442
443 auto bo = loadToken(ctx_.tx[~sfNFTokenBuyOffer]);
444 auto so = loadToken(ctx_.tx[~sfNFTokenSellOffer]);
445
446 // With fixCleanup3_1_3 amendment, check for expired offers and delete them, returning
447 // tecEXPIRED. This ensures expired offers are properly cleaned up from the ledger.
448 if (view().rules().enabled(fixCleanup3_1_3))
449 {
450 bool foundExpired = false;
451
452 auto const deleteOfferIfExpired = [this, &foundExpired](SLE::ref offer) -> TER {
453 if (offer && hasExpired(view(), (*offer)[~sfExpiration]))
454 {
455 JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key();
456 if (!nft::deleteTokenOffer(view(), offer))
457 {
458 // LCOV_EXCL_START
459 JLOG(j_.fatal())
460 << "Unable to delete expired offer '" << offer->key() << "': ignoring";
461 return tecINTERNAL;
462 // LCOV_EXCL_STOP
463 }
464 JLOG(j_.trace()) << "Deleted offer " << offer->key();
465 foundExpired = true;
466 }
467 return tesSUCCESS;
468 };
469
470 if (auto const r = deleteOfferIfExpired(bo); !isTesSuccess(r))
471 return r;
472 if (auto const r = deleteOfferIfExpired(so); !isTesSuccess(r))
473 return r;
474
475 if (foundExpired)
476 return tecEXPIRED;
477 }
478
479 if (bo && !nft::deleteTokenOffer(view(), bo))
480 {
481 // LCOV_EXCL_START
482 JLOG(j_.fatal()) << "Unable to delete buy offer '" << to_string(bo->key()) << "': ignoring";
483 return tecINTERNAL;
484 // LCOV_EXCL_STOP
485 }
486
487 if (so && !nft::deleteTokenOffer(view(), so))
488 {
489 // LCOV_EXCL_START
490 JLOG(j_.fatal()) << "Unable to delete sell offer '" << to_string(so->key())
491 << "': ignoring";
492 return tecINTERNAL;
493 // LCOV_EXCL_STOP
494 }
495
496 // Bridging two different offers
497 if (bo && so)
498 {
499 AccountID const buyer = (*bo)[sfOwner];
500 AccountID const seller = (*so)[sfOwner];
501
502 auto const nftokenID = (*so)[sfNFTokenID];
503
504 // The amount is what the buyer of the NFT pays:
505 STAmount amount = (*bo)[sfAmount];
506
507 // Three different folks may be paid. The order of operations is
508 // important.
509 //
510 // o The broker is paid the cut they requested.
511 // o The issuer's cut is calculated from what remains after the
512 // broker is paid. The issuer can take up to 50% of the remainder.
513 // o Finally, the seller gets whatever is left.
514 //
515 // It is important that the issuer's cut be calculated after the
516 // broker's portion is already removed. Calculating the issuer's
517 // cut before the broker's cut is removed can result in more money
518 // being paid out than the seller authorized. That would be bad!
519
520 // Send the broker the amount they requested.
521 if (auto const cut = ctx_.tx[~sfNFTokenBrokerFee]; cut && cut.value() != beast::kZero)
522 {
523 if (auto const r = pay(buyer, accountID_, cut.value()); !isTesSuccess(r))
524 return r;
525
526 amount -= cut.value();
527 }
528
529 // Calculate the issuer's cut, if any.
530 if (auto const fee = nft::getTransferFee(nftokenID); amount != beast::kZero && fee != 0)
531 {
532 auto cut = multiply(amount, nft::transferFeeAsRate(fee));
533
534 if (auto const issuer = nft::getIssuer(nftokenID); seller != issuer && buyer != issuer)
535 {
536 if (auto const r = pay(buyer, issuer, cut); !isTesSuccess(r))
537 return r;
538
539 amount -= cut;
540 }
541 }
542
543 // And send whatever remains to the seller.
544 if (amount > beast::kZero)
545 {
546 if (auto const r = pay(buyer, seller, amount); !isTesSuccess(r))
547 return r;
548 }
549
550 // Now transfer the NFT:
551 return transferNFToken(buyer, seller, nftokenID);
552 }
553
554 if (bo)
555 return acceptOffer(bo);
556
557 if (so)
558 return acceptOffer(so);
559
560 return tecINTERNAL; // LCOV_EXCL_LINE
561}
562
563void
565{
566 // No transaction-specific invariants yet (future work).
567}
568
569bool
571 STTx const&,
572 TER,
573 XRPAmount,
574 ReadView const&,
575 beast::Journal const&)
576{
577 // No transaction-specific invariants yet (future work).
578 return true;
579}
580
581} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
virtual SLE::pointer peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
Definition Issue.h:18
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.
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)
void visitInvariantEntry(bool isDelete, SLE::const_ref before, SLE::const_ref after) override
Inspect a single ledger entry modified by this transaction.
TER acceptOffer(SLE::ref offer)
A view into a ledger.
Definition ReadView.h:41
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.
Definition Rules.cpp:180
STAmount const & value() const noexcept
Definition STAmount.h:610
std::shared_ptr< STLedgerEntry > const & ref
std::shared_ptr< STLedgerEntry > pointer
std::shared_ptr< STLedgerEntry const > const & const_ref
beast::Journal const j_
Definition Transactor.h:155
ApplyView & view()
Definition Transactor.h:175
AccountID const accountID_
Definition Transactor.h:157
ApplyContext & ctx_
Definition Transactor.h:153
constexpr Zero kZero
Definition Zero.h:30
Keylet nftokenOffer(AccountID const &owner, SeqProxy const &seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:423
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:198
Keylet trustLine(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:253
TER insertToken(ApplyView &view, AccountID owner, STObject &&nft)
Insert the token in the owner's token directory.
constexpr std::uint16_t const kFlagCreateTrustLines
Definition nft.h:34
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)
Definition nft.h:99
std::uint16_t getTransferFee(uint256 const &id)
Definition nft.h:47
bool deleteTokenOffer(ApplyView &view, SLE::ref offer)
Deletes the given token offer.
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.
Definition Rate2.cpp:26
std::uint16_t getFlags(uint256 const &id)
Definition nft.h:39
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp, ExpiryComparison comparison=ExpiryComparison::Inclusive)
Determines whether the given expiration time has passed.
Definition View.cpp:48
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.
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:607
BaseUInt< 160, detail::AccountIDTag > AccountID
A 160-bit unsigned that uniquely identifies an account.
Definition AccountID.h:34
@ temMALFORMED
Definition TER.h:75
@ temBAD_OFFER
Definition TER.h:83
bool isTesSuccess(TER x) noexcept
Definition TER.h:676
TERSubset< CanCvtToTER > TER
Definition TER.h:647
@ tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
Definition TER.h:327
@ tecOBJECT_NOT_FOUND
Definition TER.h:329
@ tecINTERNAL
Definition TER.h:313
@ tecNFTOKEN_BUY_SELL_MISMATCH
Definition TER.h:325
@ tecINSUFFICIENT_FUNDS
Definition TER.h:328
@ tecEXPIRED
Definition TER.h:317
@ tecNO_LINE
Definition TER.h:304
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
Definition TER.h:326
@ tecINSUFFICIENT_RESERVE
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:308
@ tecINSUFFICIENT_PAYMENT
Definition TER.h:330
STAmount multiply(STAmount const &amount, Number const &frac, Number::RoundingMode rm)
BaseUInt< 256 > uint256
Definition base_uint.h:580
XRPAmount accountReserve(ReadView const &view, SLE::const_ref sle, beast::Journal j, Adjustment adj={})
Returns the account reserve, in drops.
@ tesSUCCESS
Definition TER.h:245
uint256 key
Definition Keylet.h:21
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:83
ReadView const & view
Definition Transactor.h:86
beast::Journal const j
Definition Transactor.h:91
State information when preflighting a tx.
Definition Transactor.h:38