rippled
Loading...
Searching...
No Matches
NFTokenAcceptOffer.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2021 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/tx/detail/NFTokenAcceptOffer.h>
21#include <xrpld/app/tx/detail/NFTokenUtils.h>
22
23#include <xrpl/ledger/View.h>
24#include <xrpl/protocol/Feature.h>
25#include <xrpl/protocol/Rate.h>
26#include <xrpl/protocol/TxFlags.h>
27
28namespace ripple {
29
32{
33 if (!ctx.rules.enabled(featureNonFungibleTokensV1))
34 return temDISABLED;
35
36 if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
37 return ret;
38
40 return temINVALID_FLAG;
41
42 auto const bo = ctx.tx[~sfNFTokenBuyOffer];
43 auto const so = ctx.tx[~sfNFTokenSellOffer];
44
45 // At least one of these MUST be specified
46 if (!bo && !so)
47 return temMALFORMED;
48
49 // The `BrokerFee` field must not be present in direct mode but may be
50 // present and greater than zero in brokered mode.
51 if (auto const bf = ctx.tx[~sfNFTokenBrokerFee])
52 {
53 if (!bo || !so)
54 return temMALFORMED;
55
56 if (*bf <= beast::zero)
57 return temMALFORMED;
58 }
59
60 return preflight2(ctx);
61}
62
63TER
65{
66 auto const checkOffer = [&ctx](std::optional<uint256> id)
68 if (id)
69 {
70 if (id->isZero())
71 return {nullptr, tecOBJECT_NOT_FOUND};
72
73 auto offerSLE = ctx.view.read(keylet::nftoffer(*id));
74
75 if (!offerSLE)
76 return {nullptr, tecOBJECT_NOT_FOUND};
77
78 if (hasExpired(ctx.view, (*offerSLE)[~sfExpiration]))
79 return {nullptr, tecEXPIRED};
80
81 // The initial implementation had a bug that allowed a negative
82 // amount. The fixNFTokenNegOffer amendment fixes that.
83 if ((*offerSLE)[sfAmount].negative() &&
84 ctx.view.rules().enabled(fixNFTokenNegOffer))
85 return {nullptr, temBAD_OFFER};
86
87 return {std::move(offerSLE), tesSUCCESS};
88 }
89 return {nullptr, tesSUCCESS};
90 };
91
92 auto const [bo, err1] = checkOffer(ctx.tx[~sfNFTokenBuyOffer]);
93 if (!isTesSuccess(err1))
94 return err1;
95 auto const [so, err2] = checkOffer(ctx.tx[~sfNFTokenSellOffer]);
96 if (!isTesSuccess(err2))
97 return err2;
98
99 if (bo && so)
100 {
101 // Brokered mode:
102 // The two offers being brokered must be for the same token:
103 if ((*bo)[sfNFTokenID] != (*so)[sfNFTokenID])
105
106 // The two offers being brokered must be for the same asset:
107 if ((*bo)[sfAmount].issue() != (*so)[sfAmount].issue())
109
110 // The two offers may not form a loop. A broker may not sell the
111 // token to the current owner of the token.
112 if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2) &&
113 ((*bo)[sfOwner] == (*so)[sfOwner]))
115
116 // Ensure that the buyer is willing to pay at least as much as the
117 // seller is requesting:
118 if ((*so)[sfAmount] > (*bo)[sfAmount])
120
121 // If the buyer specified a destination
122 if (auto const dest = bo->at(~sfDestination))
123 {
124 // Before this fix the destination could be either the seller or
125 // a broker. After, it must be whoever is submitting the tx.
126 if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2))
127 {
128 if (*dest != ctx.tx[sfAccount])
129 return tecNO_PERMISSION;
130 }
131 else if (*dest != so->at(sfOwner) && *dest != ctx.tx[sfAccount])
133 }
134
135 // If the seller specified a destination
136 if (auto const dest = so->at(~sfDestination))
137 {
138 // Before this fix the destination could be either the seller or
139 // a broker. After, it must be whoever is submitting the tx.
140 if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2))
141 {
142 if (*dest != ctx.tx[sfAccount])
143 return tecNO_PERMISSION;
144 }
145 else if (*dest != bo->at(sfOwner) && *dest != ctx.tx[sfAccount])
147 }
148
149 // The broker can specify an amount that represents their cut; if they
150 // have, ensure that the seller will get at least as much as they want
151 // to get *after* this fee is accounted for (but before the issuer's
152 // cut, if any).
153 if (auto const brokerFee = ctx.tx[~sfNFTokenBrokerFee])
154 {
155 if (brokerFee->issue() != (*bo)[sfAmount].issue())
157
158 if (brokerFee >= (*bo)[sfAmount])
160
161 if ((*so)[sfAmount] > (*bo)[sfAmount] - *brokerFee)
163
164 // Check if broker is allowed to receive the fee with these IOUs.
165 if (!brokerFee->native() &&
166 ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2))
167 {
169 ctx.view,
170 ctx.tx[sfAccount],
171 ctx.j,
172 brokerFee->asset().get<Issue>());
173 if (res != tesSUCCESS)
174 return res;
175
177 ctx.view,
178 ctx.tx[sfAccount],
179 ctx.j,
180 brokerFee->asset().get<Issue>());
181 if (res != tesSUCCESS)
182 return res;
183 }
184 }
185 }
186
187 if (bo)
188 {
189 if (((*bo)[sfFlags] & lsfSellNFToken) == lsfSellNFToken)
191
192 // An account can't accept an offer it placed:
193 if ((*bo)[sfOwner] == ctx.tx[sfAccount])
195
196 // If not in bridged mode, the account must own the token:
197 if (!so &&
198 !nft::findToken(ctx.view, ctx.tx[sfAccount], (*bo)[sfNFTokenID]))
199 return tecNO_PERMISSION;
200
201 // If not in bridged mode...
202 if (!so)
203 {
204 // If the offer has a Destination field, the acceptor must be the
205 // Destination.
206 if (auto const dest = bo->at(~sfDestination);
207 dest.has_value() && *dest != ctx.tx[sfAccount])
208 return tecNO_PERMISSION;
209 }
210
211 // The account offering to buy must have funds:
212 //
213 // After this amendment, we allow an IOU issuer to buy an NFT with their
214 // own currency
215 auto const needed = bo->at(sfAmount);
216 if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2))
217 {
218 if (accountFunds(
219 ctx.view, (*bo)[sfOwner], needed, fhZERO_IF_FROZEN, ctx.j) <
220 needed)
222 }
223 else if (
225 ctx.view,
226 (*bo)[sfOwner],
227 needed.getCurrency(),
228 needed.getIssuer(),
230 ctx.j) < needed)
232
233 // Check that the account accepting the buy offer (he's selling the NFT)
234 // is allowed to receive IOUs. Also check that this offer's creator is
235 // authorized. But we need to exclude the case when the transaction is
236 // created by the broker.
237 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2) &&
238 !needed.native())
239 {
241 ctx.view, bo->at(sfOwner), ctx.j, needed.asset().get<Issue>());
242 if (res != tesSUCCESS)
243 return res;
244
245 if (!so)
246 {
248 ctx.view,
249 ctx.tx[sfAccount],
250 ctx.j,
251 needed.asset().get<Issue>());
252 if (res != tesSUCCESS)
253 return res;
254
256 ctx.view,
257 ctx.tx[sfAccount],
258 ctx.j,
259 needed.asset().get<Issue>());
260 if (res != tesSUCCESS)
261 return res;
262 }
263 }
264 }
265
266 if (so)
267 {
268 if (((*so)[sfFlags] & lsfSellNFToken) != lsfSellNFToken)
270
271 // An account can't accept an offer it placed:
272 if ((*so)[sfOwner] == ctx.tx[sfAccount])
274
275 // The seller must own the token.
276 if (!nft::findToken(ctx.view, (*so)[sfOwner], (*so)[sfNFTokenID]))
277 return tecNO_PERMISSION;
278
279 // If not in bridged mode...
280 if (!bo)
281 {
282 // If the offer has a Destination field, the acceptor must be the
283 // Destination.
284 if (auto const dest = so->at(~sfDestination);
285 dest.has_value() && *dest != ctx.tx[sfAccount])
286 return tecNO_PERMISSION;
287 }
288
289 // The account offering to buy must have funds:
290 auto const needed = so->at(sfAmount);
291 if (!ctx.view.rules().enabled(fixNonFungibleTokensV1_2))
292 {
293 if (accountHolds(
294 ctx.view,
295 ctx.tx[sfAccount],
296 needed.getCurrency(),
297 needed.getIssuer(),
299 ctx.j) < needed)
301 }
302 else if (!bo)
303 {
304 // After this amendment, we allow buyers to buy with their own
305 // issued currency.
306 //
307 // In the case of brokered mode, this check is essentially
308 // redundant, since we have already confirmed that buy offer is >
309 // than the sell offer, and that the buyer can cover the buy
310 // offer.
311 //
312 // We also _must not_ check the tx submitter in brokered
313 // mode, because then we are confirming that the broker can
314 // cover what the buyer will pay, which doesn't make sense, causes
315 // an unnecessary tec, and is also resolved with this amendment.
316 if (accountFunds(
317 ctx.view,
318 ctx.tx[sfAccount],
319 needed,
321 ctx.j) < needed)
323 }
324
325 // Make sure that we are allowed to hold what the taker will pay us.
326 if (!needed.native())
327 {
328 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2))
329 {
331 ctx.view,
332 (*so)[sfOwner],
333 ctx.j,
334 needed.asset().get<Issue>());
335 if (res != tesSUCCESS)
336 return res;
337
338 if (!bo)
339 {
341 ctx.view,
342 ctx.tx[sfAccount],
343 ctx.j,
344 needed.asset().get<Issue>());
345 if (res != tesSUCCESS)
346 return res;
347 }
348 }
349
350 auto const res = nft::checkTrustlineDeepFrozen(
351 ctx.view, (*so)[sfOwner], ctx.j, needed.asset().get<Issue>());
352 if (res != tesSUCCESS)
353 return res;
354 }
355 }
356
357 // Additional checks are required in case a minter set a transfer fee for
358 // this nftoken
359 auto const& offer = bo ? bo : so;
360 if (!offer)
361 // Purely defensive, should be caught in preflight.
362 return tecINTERNAL;
363
364 auto const& tokenID = offer->at(sfNFTokenID);
365 auto const& amount = offer->at(sfAmount);
366 auto const nftMinter = nft::getIssuer(tokenID);
367
368 if (nft::getTransferFee(tokenID) != 0 && !amount.native())
369 {
370 // Fix a bug where the transfer of an NFToken with a transfer fee could
371 // give the NFToken issuer an undesired trust line.
372 // Issuer doesn't need a trust line to accept their own currency.
373 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustline) &&
374 (nft::getFlags(tokenID) & nft::flagCreateTrustLines) == 0 &&
375 nftMinter != amount.getIssuer() &&
376 !ctx.view.read(keylet::line(nftMinter, amount.issue())))
377 return tecNO_LINE;
378
379 // Check that the issuer is allowed to receive IOUs.
380 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustlineV2))
381 {
383 ctx.view, nftMinter, ctx.j, amount.asset().get<Issue>());
384 if (res != tesSUCCESS)
385 return res;
386
388 ctx.view, nftMinter, ctx.j, amount.asset().get<Issue>());
389 if (res != tesSUCCESS)
390 return res;
391 }
392 }
393
394 return tesSUCCESS;
395}
396
397TER
399 AccountID const& from,
400 AccountID const& to,
401 STAmount const& amount)
402{
403 // This should never happen, but it's easy and quick to check.
404 if (amount < beast::zero)
405 return tecINTERNAL;
406
407 auto const result = accountSend(view(), from, to, amount, j_);
408
409 // After this amendment, if any payment would cause a non-IOU-issuer to
410 // have a negative balance, or an IOU-issuer to have a positive balance in
411 // their own currency, we know that something went wrong. This was
412 // originally found in the context of IOU transfer fees. Since there are
413 // several payouts in this tx, just confirm that the end state is OK.
414 if (!view().rules().enabled(fixNonFungibleTokensV1_2))
415 return result;
416 if (result != tesSUCCESS)
417 return result;
418 if (accountFunds(view(), from, amount, fhZERO_IF_FROZEN, j_).signum() < 0)
420 if (accountFunds(view(), to, amount, fhZERO_IF_FROZEN, j_).signum() < 0)
422 return tesSUCCESS;
423}
424
425TER
427 AccountID const& buyer,
428 AccountID const& seller,
429 uint256 const& nftokenID)
430{
431 auto tokenAndPage = nft::findTokenAndPage(view(), seller, nftokenID);
432
433 if (!tokenAndPage)
434 return tecINTERNAL;
435
436 if (auto const ret = nft::removeToken(
437 view(), seller, nftokenID, std::move(tokenAndPage->page));
438 !isTesSuccess(ret))
439 return ret;
440
441 auto const sleBuyer = view().read(keylet::account(buyer));
442 if (!sleBuyer)
443 return tecINTERNAL;
444
445 std::uint32_t const buyerOwnerCountBefore =
446 sleBuyer->getFieldU32(sfOwnerCount);
447
448 auto const insertRet =
449 nft::insertToken(view(), buyer, std::move(tokenAndPage->token));
450
451 // if fixNFTokenReserve is enabled, check if the buyer has sufficient
452 // reserve to own a new object, if their OwnerCount changed.
453 //
454 // There was an issue where the buyer accepts a sell offer, the ledger
455 // didn't check if the buyer has enough reserve, meaning that buyer can get
456 // NFTs free of reserve.
457 if (view().rules().enabled(fixNFTokenReserve))
458 {
459 // To check if there is sufficient reserve, we cannot use mPriorBalance
460 // because NFT is sold for a price. So we must use the balance after
461 // the deduction of the potential offer price. A small caveat here is
462 // that the balance has already deducted the transaction fee, meaning
463 // that the reserve requirement is a few drops higher.
464 auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance);
465
466 auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
467 if (buyerOwnerCountAfter > buyerOwnerCountBefore)
468 {
469 if (auto const reserve =
470 view().fees().accountReserve(buyerOwnerCountAfter);
471 buyerBalance < reserve)
473 }
474 }
475
476 return insertRet;
477}
478
479TER
481{
482 bool const isSell = offer->isFlag(lsfSellNFToken);
483 AccountID const owner = (*offer)[sfOwner];
484 AccountID const& seller = isSell ? owner : account_;
485 AccountID const& buyer = isSell ? account_ : owner;
486
487 auto const nftokenID = (*offer)[sfNFTokenID];
488
489 if (auto amount = offer->getFieldAmount(sfAmount); amount != beast::zero)
490 {
491 // Calculate the issuer's cut from this sale, if any:
492 if (auto const fee = nft::getTransferFee(nftokenID); fee != 0)
493 {
494 auto const cut = multiply(amount, nft::transferFeeAsRate(fee));
495
496 if (auto const issuer = nft::getIssuer(nftokenID);
497 cut != beast::zero && seller != issuer && buyer != issuer)
498 {
499 if (auto const r = pay(buyer, issuer, cut); !isTesSuccess(r))
500 return r;
501 amount -= cut;
502 }
503 }
504
505 // Send the remaining funds to the seller of the NFT
506 if (auto const r = pay(buyer, seller, amount); !isTesSuccess(r))
507 return r;
508 }
509
510 // Now transfer the NFT:
511 return transferNFToken(buyer, seller, nftokenID);
512}
513
514TER
516{
517 auto const loadToken = [this](std::optional<uint256> const& id) {
519 if (id)
520 sle = view().peek(keylet::nftoffer(*id));
521 return sle;
522 };
523
524 auto bo = loadToken(ctx_.tx[~sfNFTokenBuyOffer]);
525 auto so = loadToken(ctx_.tx[~sfNFTokenSellOffer]);
526
527 if (bo && !nft::deleteTokenOffer(view(), bo))
528 {
529 JLOG(j_.fatal()) << "Unable to delete buy offer '"
530 << to_string(bo->key()) << "': ignoring";
531 return tecINTERNAL;
532 }
533
534 if (so && !nft::deleteTokenOffer(view(), so))
535 {
536 JLOG(j_.fatal()) << "Unable to delete sell offer '"
537 << to_string(so->key()) << "': ignoring";
538 return tecINTERNAL;
539 }
540
541 // Bridging two different offers
542 if (bo && so)
543 {
544 AccountID const buyer = (*bo)[sfOwner];
545 AccountID const seller = (*so)[sfOwner];
546
547 auto const nftokenID = (*so)[sfNFTokenID];
548
549 // The amount is what the buyer of the NFT pays:
550 STAmount amount = (*bo)[sfAmount];
551
552 // Three different folks may be paid. The order of operations is
553 // important.
554 //
555 // o The broker is paid the cut they requested.
556 // o The issuer's cut is calculated from what remains after the
557 // broker is paid. The issuer can take up to 50% of the remainder.
558 // o Finally, the seller gets whatever is left.
559 //
560 // It is important that the issuer's cut be calculated after the
561 // broker's portion is already removed. Calculating the issuer's
562 // cut before the broker's cut is removed can result in more money
563 // being paid out than the seller authorized. That would be bad!
564
565 // Send the broker the amount they requested.
566 if (auto const cut = ctx_.tx[~sfNFTokenBrokerFee];
567 cut && cut.value() != beast::zero)
568 {
569 if (auto const r = pay(buyer, account_, cut.value());
570 !isTesSuccess(r))
571 return r;
572
573 amount -= cut.value();
574 }
575
576 // Calculate the issuer's cut, if any.
577 if (auto const fee = nft::getTransferFee(nftokenID);
578 amount != beast::zero && fee != 0)
579 {
580 auto cut = multiply(amount, nft::transferFeeAsRate(fee));
581
582 if (auto const issuer = nft::getIssuer(nftokenID);
583 seller != issuer && buyer != issuer)
584 {
585 if (auto const r = pay(buyer, issuer, cut); !isTesSuccess(r))
586 return r;
587
588 amount -= cut;
589 }
590 }
591
592 // And send whatever remains to the seller.
593 if (amount > beast::zero)
594 {
595 if (auto const r = pay(buyer, seller, amount); !isTesSuccess(r))
596 return r;
597 }
598
599 // Now transfer the NFT:
600 return transferNFToken(buyer, seller, nftokenID);
601 }
602
603 if (bo)
604 return acceptOffer(bo);
605
606 if (so)
607 return acceptOffer(so);
608
609 return tecINTERNAL;
610}
611
612} // namespace ripple
Stream fatal() const
Definition Journal.h:352
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
A currency issued by an account.
Definition Issue.h:33
TER acceptOffer(std::shared_ptr< SLE > const &offer)
static TER preclaim(PreclaimContext const &ctx)
TER pay(AccountID const &from, AccountID const &to, STAmount const &amount)
TER transferNFToken(AccountID const &buyer, AccountID const &seller, uint256 const &nfTokenID)
static NotTEC preflight(PreflightContext const &ctx)
virtual std::shared_ptr< SLE const > read(Keylet const &k) const =0
Return the state item associated with a key.
virtual Rules const & rules() const =0
Returns the tx processing rules.
bool enabled(uint256 const &feature) const
Returns true if a feature is enabled.
Definition Rules.cpp:130
STAmount const & value() const noexcept
Definition STAmount.h:594
std::uint32_t getFlags() const
Definition STObject.cpp:537
AccountID const account_
Definition Transactor.h:145
ApplyView & view()
Definition Transactor.h:161
beast::Journal const j_
Definition Transactor.h:143
ApplyContext & ctx_
Definition Transactor.h:141
Keylet line(AccountID const &id0, AccountID const &id1, Currency const &currency) noexcept
The index of a trust line for a given currency.
Definition Indexes.cpp:244
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition Indexes.cpp:184
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition Indexes.cpp:427
std::uint16_t getTransferFee(uint256 const &id)
Definition nft.h:68
std::uint16_t getFlags(uint256 const &id)
Definition nft.h:60
TER removeToken(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
Remove the token from the owner's token directory.
AccountID getIssuer(uint256 const &id)
Definition nft.h:120
TER checkTrustlineDeepFrozen(ReadView const &view, AccountID const id, beast::Journal const j, Issue const &issue)
bool deleteTokenOffer(ApplyView &view, std::shared_ptr< SLE > const &offer)
Deletes the given token offer.
std::optional< TokenAndPage > findTokenAndPage(ApplyView &view, AccountID const &owner, uint256 const &nftokenID)
TER insertToken(ApplyView &view, AccountID owner, STObject &&nft)
Insert the token in the owner's token directory.
std::optional< STObject > findToken(ReadView const &view, AccountID const &owner, uint256 const &nftokenID)
Finds the specified token in the owner's token directory.
constexpr std::uint16_t const flagCreateTrustLines
Definition nft.h:55
TER checkTrustlineAuthorized(ReadView const &view, AccountID const id, beast::Journal const j, Issue const &issue)
Rate transferFeeAsRate(std::uint16_t fee)
Given a transfer fee (in basis points) convert it to a transfer rate.
Definition Rate2.cpp:45
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition View.cpp:551
@ fhZERO_IF_FROZEN
Definition View.h:77
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition Rate2.cpp:53
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
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.
Definition View.cpp:2174
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition View.cpp:173
@ tecOBJECT_NOT_FOUND
Definition TER.h:326
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
Definition TER.h:323
@ tecINSUFFICIENT_FUNDS
Definition TER.h:325
@ tecNFTOKEN_BUY_SELL_MISMATCH
Definition TER.h:322
@ tecINTERNAL
Definition TER.h:310
@ tecNO_PERMISSION
Definition TER.h:305
@ tecNO_LINE
Definition TER.h:301
@ tecINSUFFICIENT_PAYMENT
Definition TER.h:327
@ tecINSUFFICIENT_RESERVE
Definition TER.h:307
@ tecCANT_ACCEPT_OWN_NFTOKEN_OFFER
Definition TER.h:324
@ tecEXPIRED
Definition TER.h:314
@ tesSUCCESS
Definition TER.h:244
STAmount accountHolds(ReadView const &view, AccountID const &account, Currency const &currency, AccountID const &issuer, FreezeHandling zeroIfFrozen, beast::Journal j)
Definition View.cpp:384
bool isTesSuccess(TER x) noexcept
Definition TER.h:674
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:630
constexpr std::uint32_t const tfNFTokenAcceptOfferMask
Definition TxFlags.h:238
TERSubset< CanCvtToNotTEC > NotTEC
Definition TER.h:605
@ temMALFORMED
Definition TER.h:87
@ temBAD_OFFER
Definition TER.h:95
@ temINVALID_FLAG
Definition TER.h:111
@ temDISABLED
Definition TER.h:114
State information when determining if a tx is likely to claim a fee.
Definition Transactor.h:80
ReadView const & view
Definition Transactor.h:83
beast::Journal const j
Definition Transactor.h:88
State information when preflighting a tx.
Definition Transactor.h:35