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