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#include <xrpld/ledger/View.h>
23
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 }
165
166 if (bo)
167 {
168 if (((*bo)[sfFlags] & lsfSellNFToken) == lsfSellNFToken)
170
171 // An account can't accept an offer it placed:
172 if ((*bo)[sfOwner] == ctx.tx[sfAccount])
174
175 // If not in bridged mode, the account must own the token:
176 if (!so &&
177 !nft::findToken(ctx.view, ctx.tx[sfAccount], (*bo)[sfNFTokenID]))
178 return tecNO_PERMISSION;
179
180 // If not in bridged mode...
181 if (!so)
182 {
183 // If the offer has a Destination field, the acceptor must be the
184 // Destination.
185 if (auto const dest = bo->at(~sfDestination);
186 dest.has_value() && *dest != ctx.tx[sfAccount])
187 return tecNO_PERMISSION;
188 }
189
190 // The account offering to buy must have funds:
191 //
192 // After this amendment, we allow an IOU issuer to buy an NFT with their
193 // own currency
194 auto const needed = bo->at(sfAmount);
195 if (ctx.view.rules().enabled(fixNonFungibleTokensV1_2))
196 {
197 if (accountFunds(
198 ctx.view, (*bo)[sfOwner], needed, fhZERO_IF_FROZEN, ctx.j) <
199 needed)
201 }
202 else if (
204 ctx.view,
205 (*bo)[sfOwner],
206 needed.getCurrency(),
207 needed.getIssuer(),
209 ctx.j) < needed)
211 }
212
213 if (so)
214 {
215 if (((*so)[sfFlags] & lsfSellNFToken) != lsfSellNFToken)
217
218 // An account can't accept an offer it placed:
219 if ((*so)[sfOwner] == ctx.tx[sfAccount])
221
222 // The seller must own the token.
223 if (!nft::findToken(ctx.view, (*so)[sfOwner], (*so)[sfNFTokenID]))
224 return tecNO_PERMISSION;
225
226 // If not in bridged mode...
227 if (!bo)
228 {
229 // If the offer has a Destination field, the acceptor must be the
230 // Destination.
231 if (auto const dest = so->at(~sfDestination);
232 dest.has_value() && *dest != ctx.tx[sfAccount])
233 return tecNO_PERMISSION;
234 }
235
236 // The account offering to buy must have funds:
237 auto const needed = so->at(sfAmount);
238 if (!ctx.view.rules().enabled(fixNonFungibleTokensV1_2))
239 {
240 if (accountHolds(
241 ctx.view,
242 ctx.tx[sfAccount],
243 needed.getCurrency(),
244 needed.getIssuer(),
246 ctx.j) < needed)
248 }
249 else if (!bo)
250 {
251 // After this amendment, we allow buyers to buy with their own
252 // issued currency.
253 //
254 // In the case of brokered mode, this check is essentially
255 // redundant, since we have already confirmed that buy offer is >
256 // than the sell offer, and that the buyer can cover the buy
257 // offer.
258 //
259 // We also _must not_ check the tx submitter in brokered
260 // mode, because then we are confirming that the broker can
261 // cover what the buyer will pay, which doesn't make sense, causes
262 // an unnecessary tec, and is also resolved with this amendment.
263 if (accountFunds(
264 ctx.view,
265 ctx.tx[sfAccount],
266 needed,
268 ctx.j) < needed)
270 }
271
272 // Make sure that we are allowed to hold what the taker will pay us.
273 // This is a similar approach taken by usual offers.
274 if (!needed.native())
275 {
276 auto const result = checkAcceptAsset(
277 ctx.view,
278 ctx.flags,
279 (*so)[sfOwner],
280 ctx.j,
281 needed.asset().get<Issue>());
282 if (result != tesSUCCESS)
283 return result;
284 }
285 }
286
287 // Fix a bug where the transfer of an NFToken with a transfer fee could
288 // give the NFToken issuer an undesired trust line.
289 if (ctx.view.rules().enabled(fixEnforceNFTokenTrustline))
290 {
291 std::shared_ptr<SLE const> const& offer = bo ? bo : so;
292 if (!offer)
293 // Should be caught in preflight.
294 return tecINTERNAL;
295
296 uint256 const& tokenID = offer->at(sfNFTokenID);
297 STAmount const& amount = offer->at(sfAmount);
298 if (nft::getTransferFee(tokenID) != 0 &&
299 (nft::getFlags(tokenID) & nft::flagCreateTrustLines) == 0 &&
300 !amount.native())
301 {
302 auto const issuer = nft::getIssuer(tokenID);
303 // Issuer doesn't need a trust line to accept their own currency.
304 if (issuer != amount.getIssuer() &&
305 !ctx.view.read(keylet::line(issuer, amount.issue())))
306 return tecNO_LINE;
307 }
308 }
309 return tesSUCCESS;
310}
311
312TER
314 AccountID const& from,
315 AccountID const& to,
316 STAmount const& amount)
317{
318 // This should never happen, but it's easy and quick to check.
319 if (amount < beast::zero)
320 return tecINTERNAL;
321
322 auto const result = accountSend(view(), from, to, amount, j_);
323
324 // After this amendment, if any payment would cause a non-IOU-issuer to
325 // have a negative balance, or an IOU-issuer to have a positive balance in
326 // their own currency, we know that something went wrong. This was
327 // originally found in the context of IOU transfer fees. Since there are
328 // several payouts in this tx, just confirm that the end state is OK.
329 if (!view().rules().enabled(fixNonFungibleTokensV1_2))
330 return result;
331 if (result != tesSUCCESS)
332 return result;
333 if (accountFunds(view(), from, amount, fhZERO_IF_FROZEN, j_).signum() < 0)
335 if (accountFunds(view(), to, amount, fhZERO_IF_FROZEN, j_).signum() < 0)
337 return tesSUCCESS;
338}
339
340TER
342 AccountID const& buyer,
343 AccountID const& seller,
344 uint256 const& nftokenID)
345{
346 auto tokenAndPage = nft::findTokenAndPage(view(), seller, nftokenID);
347
348 if (!tokenAndPage)
349 return tecINTERNAL;
350
351 if (auto const ret = nft::removeToken(
352 view(), seller, nftokenID, std::move(tokenAndPage->page));
353 !isTesSuccess(ret))
354 return ret;
355
356 auto const sleBuyer = view().read(keylet::account(buyer));
357 if (!sleBuyer)
358 return tecINTERNAL;
359
360 std::uint32_t const buyerOwnerCountBefore =
361 sleBuyer->getFieldU32(sfOwnerCount);
362
363 auto const insertRet =
364 nft::insertToken(view(), buyer, std::move(tokenAndPage->token));
365
366 // if fixNFTokenReserve is enabled, check if the buyer has sufficient
367 // reserve to own a new object, if their OwnerCount changed.
368 //
369 // There was an issue where the buyer accepts a sell offer, the ledger
370 // didn't check if the buyer has enough reserve, meaning that buyer can get
371 // NFTs free of reserve.
372 if (view().rules().enabled(fixNFTokenReserve))
373 {
374 // To check if there is sufficient reserve, we cannot use mPriorBalance
375 // because NFT is sold for a price. So we must use the balance after
376 // the deduction of the potential offer price. A small caveat here is
377 // that the balance has already deducted the transaction fee, meaning
378 // that the reserve requirement is a few drops higher.
379 auto const buyerBalance = sleBuyer->getFieldAmount(sfBalance);
380
381 auto const buyerOwnerCountAfter = sleBuyer->getFieldU32(sfOwnerCount);
382 if (buyerOwnerCountAfter > buyerOwnerCountBefore)
383 {
384 if (auto const reserve =
385 view().fees().accountReserve(buyerOwnerCountAfter);
386 buyerBalance < reserve)
388 }
389 }
390
391 return insertRet;
392}
393
394TER
396{
397 bool const isSell = offer->isFlag(lsfSellNFToken);
398 AccountID const owner = (*offer)[sfOwner];
399 AccountID const& seller = isSell ? owner : account_;
400 AccountID const& buyer = isSell ? account_ : owner;
401
402 auto const nftokenID = (*offer)[sfNFTokenID];
403
404 if (auto amount = offer->getFieldAmount(sfAmount); amount != beast::zero)
405 {
406 // Calculate the issuer's cut from this sale, if any:
407 if (auto const fee = nft::getTransferFee(nftokenID); fee != 0)
408 {
409 auto const cut = multiply(amount, nft::transferFeeAsRate(fee));
410
411 if (auto const issuer = nft::getIssuer(nftokenID);
412 cut != beast::zero && seller != issuer && buyer != issuer)
413 {
414 if (auto const r = pay(buyer, issuer, cut); !isTesSuccess(r))
415 return r;
416 amount -= cut;
417 }
418 }
419
420 // Send the remaining funds to the seller of the NFT
421 if (auto const r = pay(buyer, seller, amount); !isTesSuccess(r))
422 return r;
423 }
424
425 // Now transfer the NFT:
426 return transferNFToken(buyer, seller, nftokenID);
427}
428
429TER
431{
432 auto const loadToken = [this](std::optional<uint256> const& id) {
434 if (id)
435 sle = view().peek(keylet::nftoffer(*id));
436 return sle;
437 };
438
439 auto bo = loadToken(ctx_.tx[~sfNFTokenBuyOffer]);
440 auto so = loadToken(ctx_.tx[~sfNFTokenSellOffer]);
441
442 if (bo && !nft::deleteTokenOffer(view(), bo))
443 {
444 JLOG(j_.fatal()) << "Unable to delete buy offer '"
445 << to_string(bo->key()) << "': ignoring";
446 return tecINTERNAL;
447 }
448
449 if (so && !nft::deleteTokenOffer(view(), so))
450 {
451 JLOG(j_.fatal()) << "Unable to delete sell offer '"
452 << to_string(so->key()) << "': ignoring";
453 return tecINTERNAL;
454 }
455
456 // Bridging two different offers
457 if (bo && so)
458 {
459 AccountID const buyer = (*bo)[sfOwner];
460 AccountID const seller = (*so)[sfOwner];
461
462 auto const nftokenID = (*so)[sfNFTokenID];
463
464 // The amount is what the buyer of the NFT pays:
465 STAmount amount = (*bo)[sfAmount];
466
467 // Three different folks may be paid. The order of operations is
468 // important.
469 //
470 // o The broker is paid the cut they requested.
471 // o The issuer's cut is calculated from what remains after the
472 // broker is paid. The issuer can take up to 50% of the remainder.
473 // o Finally, the seller gets whatever is left.
474 //
475 // It is important that the issuer's cut be calculated after the
476 // broker's portion is already removed. Calculating the issuer's
477 // cut before the broker's cut is removed can result in more money
478 // being paid out than the seller authorized. That would be bad!
479
480 // Send the broker the amount they requested.
481 if (auto const cut = ctx_.tx[~sfNFTokenBrokerFee];
482 cut && cut.value() != beast::zero)
483 {
484 if (auto const r = pay(buyer, account_, cut.value());
485 !isTesSuccess(r))
486 return r;
487
488 amount -= cut.value();
489 }
490
491 // Calculate the issuer's cut, if any.
492 if (auto const fee = nft::getTransferFee(nftokenID);
493 amount != beast::zero && fee != 0)
494 {
495 auto cut = multiply(amount, nft::transferFeeAsRate(fee));
496
497 if (auto const issuer = nft::getIssuer(nftokenID);
498 seller != issuer && buyer != issuer)
499 {
500 if (auto const r = pay(buyer, issuer, cut); !isTesSuccess(r))
501 return r;
502
503 amount -= cut;
504 }
505 }
506
507 // And send whatever remains to the seller.
508 if (amount > beast::zero)
509 {
510 if (auto const r = pay(buyer, seller, amount); !isTesSuccess(r))
511 return r;
512 }
513
514 // Now transfer the NFT:
515 return transferNFToken(buyer, seller, nftokenID);
516 }
517
518 if (bo)
519 return acceptOffer(bo);
520
521 if (so)
522 return acceptOffer(so);
523
524 return tecINTERNAL;
525}
526
527TER
529 ReadView const& view,
530 ApplyFlags const flags,
531 AccountID const id,
532 beast::Journal const j,
533 Issue const& issue)
534{
535 // Only valid for custom currencies
536
537 if (!view.rules().enabled(featureDeepFreeze))
538 {
539 return tesSUCCESS;
540 }
541
542 XRPL_ASSERT(
543 !isXRP(issue.currency),
544 "NFTokenAcceptOffer::checkAcceptAsset : valid to check.");
545 auto const issuerAccount = view.read(keylet::account(issue.account));
546
547 if (!issuerAccount)
548 {
549 JLOG(j.debug())
550 << "delay: can't receive IOUs from non-existent issuer: "
551 << to_string(issue.account);
552
553 return tecNO_ISSUER;
554 }
555
556 // An account can not create a trustline to itself, so no line can exist
557 // to be frozen. Additionally, an issuer can always accept its own
558 // issuance.
559 if (issue.account == id)
560 {
561 return tesSUCCESS;
562 }
563
564 auto const trustLine =
565 view.read(keylet::line(id, issue.account, issue.currency));
566
567 if (!trustLine)
568 {
569 return tesSUCCESS;
570 }
571
572 // There's no difference which side enacted deep freeze, accepting
573 // tokens shouldn't be possible.
574 bool const deepFrozen =
575 (*trustLine)[sfFlags] & (lsfLowDeepFreeze | lsfHighDeepFreeze);
576
577 if (deepFrozen)
578 {
579 return tecFROZEN;
580 }
581
582 return tesSUCCESS;
583}
584
585} // namespace ripple
A generic endpoint for log messages.
Definition: Journal.h:60
Stream fatal() const
Definition: Journal.h:352
Stream debug() const
Definition: Journal.h:328
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:36
AccountID account
Definition: Issue.h:39
Currency currency
Definition: Issue.h:38
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 TER checkAcceptAsset(ReadView const &view, ApplyFlags const flags, AccountID const id, beast::Journal const j, Issue const &issue)
static NotTEC preflight(PreflightContext const &ctx)
A view into a ledger.
Definition: ReadView.h:52
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
AccountID const & getIssuer() const
Definition: STAmount.h:508
Issue const & issue() const
Definition: STAmount.h:496
bool native() const noexcept
Definition: STAmount.h:458
std::uint32_t getFlags() const
Definition: STObject.cpp:537
AccountID const account_
Definition: Transactor.h:93
ApplyView & view()
Definition: Transactor.h:109
beast::Journal const j_
Definition: Transactor.h:91
ApplyContext & ctx_
Definition: Transactor.h:90
Set the fee on a JTx.
Definition: fee.h:37
Match set account flags.
Definition: flags.h:125
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:237
Keylet account(AccountID const &id) noexcept
AccountID root.
Definition: Indexes.cpp:177
Keylet nftoffer(AccountID const &owner, std::uint32_t seq)
An offer from an account to buy or sell an NFT.
Definition: Indexes.cpp:420
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
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
Rate transferFeeAsRate(std::uint16_t fee)
Given a transfer fee (in basis points) convert it to a transfer rate.
Definition: Rate2.cpp:45
Json::Value offer(Account const &account, STAmount const &takerPays, STAmount const &takerGets, std::uint32_t flags)
Create an offer.
Definition: offer.cpp:29
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
STAmount accountFunds(ReadView const &view, AccountID const &id, STAmount const &saDefault, FreezeHandling freezeHandling, beast::Journal j)
Definition: View.cpp:554
@ fhZERO_IF_FROZEN
Definition: View.h:78
bool isXRP(AccountID const &c)
Definition: AccountID.h:91
@ lsfHighDeepFreeze
@ lsfLowDeepFreeze
@ lsfSellNFToken
STAmount multiply(STAmount const &amount, Rate const &rate)
Definition: Rate2.cpp:53
bool isTesSuccess(TER x)
Definition: TER.h:672
NotTEC preflight1(PreflightContext const &ctx)
Performs early sanity checks on the account and fee fields.
Definition: Transactor.cpp:83
NotTEC preflight2(PreflightContext const &ctx)
Checks whether the signature appears valid.
Definition: Transactor.cpp:144
@ tecOBJECT_NOT_FOUND
Definition: TER.h:326
@ tecNO_ISSUER
Definition: TER.h:299
@ tecNFTOKEN_OFFER_TYPE_MISMATCH
Definition: TER.h:323
@ tecFROZEN
Definition: TER.h:303
@ 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:387
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:630
ApplyFlags
Definition: ApplyView.h:31
bool hasExpired(ReadView const &view, std::optional< std::uint32_t > const &exp)
Determines whether the given expiration time has passed.
Definition: View.cpp:176
constexpr std::uint32_t const tfNFTokenAcceptOfferMask
Definition: TxFlags.h:206
TER accountSend(ApplyView &view, AccountID const &uSenderID, AccountID const &uReceiverID, STAmount const &saAmount, beast::Journal j, WaiveTransferFee waiveFee)
Calls static accountSendIOU if saAmount represents Issue.
Definition: View.cpp:1978
TERSubset< CanCvtToNotTEC > NotTEC
Definition: TER.h:603
@ 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:55
ReadView const & view
Definition: Transactor.h:58
beast::Journal const j
Definition: Transactor.h:62
State information when preflighting a tx.
Definition: Transactor.h:34