mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
XLS-52d: NFTokenMintOffer (#4845)
This commit is contained in:
@@ -3171,6 +3171,26 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// minter mint and offer to buyer
|
||||
if (features[featureNFTokenMintOffer])
|
||||
{
|
||||
// enable flag
|
||||
env(fset(buyer, asfDisallowIncomingNFTokenOffer));
|
||||
// a sell offer from the minter to the buyer should be rejected
|
||||
env(token::mint(minter),
|
||||
token::amount(drops(1)),
|
||||
token::destination(buyer),
|
||||
ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
|
||||
// disable flag
|
||||
env(fclear(buyer, asfDisallowIncomingNFTokenOffer));
|
||||
env(token::mint(minter),
|
||||
token::amount(drops(1)),
|
||||
token::destination(buyer));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -6566,6 +6586,281 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testFeatMintWithOffer(FeatureBitset features)
|
||||
{
|
||||
testcase("NFTokenMint with Create NFTokenOffer");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
if (!features[featureNFTokenMintOffer])
|
||||
{
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const buyer("buyer");
|
||||
|
||||
env.fund(XRP(10000), alice, buyer);
|
||||
env.close();
|
||||
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(10000)),
|
||||
ter(temDISABLED));
|
||||
env.close();
|
||||
|
||||
env(token::mint(alice),
|
||||
token::destination("buyer"),
|
||||
ter(temDISABLED));
|
||||
env.close();
|
||||
|
||||
env(token::mint(alice),
|
||||
token::expiration(lastClose(env) + 25),
|
||||
ter(temDISABLED));
|
||||
env.close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The remaining tests assume featureNFTokenMintOffer is enabled.
|
||||
{
|
||||
Env env{*this, features};
|
||||
Account const alice("alice");
|
||||
Account const buyer{"buyer"};
|
||||
Account const gw("gw");
|
||||
Account const issuer("issuer");
|
||||
Account const minter("minter");
|
||||
Account const bob("bob");
|
||||
IOU const gwAUD(gw["AUD"]);
|
||||
|
||||
env.fund(XRP(10000), alice, buyer, gw, issuer, minter);
|
||||
env.close();
|
||||
|
||||
{
|
||||
// Destination field specified but Amount field not specified
|
||||
env(token::mint(alice),
|
||||
token::destination(buyer),
|
||||
ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// Expiration field specified but Amount field not specified
|
||||
env(token::mint(alice),
|
||||
token::expiration(lastClose(env) + 25),
|
||||
ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// The destination may not be the account submitting the
|
||||
// transaction.
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(1000)),
|
||||
token::destination(alice),
|
||||
ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// The destination must be an account already established in the
|
||||
// ledger.
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(1000)),
|
||||
token::destination(Account("demon")),
|
||||
ter(tecNO_DST));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Set a bad expiration.
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(1000)),
|
||||
token::expiration(0),
|
||||
ter(temBAD_EXPIRATION));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// The new NFTokenOffer may not have passed its expiration time.
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(1000)),
|
||||
token::expiration(lastClose(env)),
|
||||
ter(tecEXPIRED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
}
|
||||
|
||||
{
|
||||
// Set an invalid amount.
|
||||
env(token::mint(alice),
|
||||
token::amount(buyer["USD"](1)),
|
||||
txflags(tfOnlyXRP),
|
||||
ter(temBAD_AMOUNT));
|
||||
env(token::mint(alice),
|
||||
token::amount(buyer["USD"](0)),
|
||||
ter(temBAD_AMOUNT));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// Issuer (alice) must have a trust line for the offered funds.
|
||||
env(token::mint(alice),
|
||||
token::amount(gwAUD(1000)),
|
||||
txflags(tfTransferable),
|
||||
token::xferFee(10),
|
||||
ter(tecNO_LINE));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// If the IOU issuer and the NFToken issuer are the same,
|
||||
// then that issuer does not need a trust line to accept their
|
||||
// fee.
|
||||
env(token::mint(gw),
|
||||
token::amount(gwAUD(1000)),
|
||||
txflags(tfTransferable),
|
||||
token::xferFee(10));
|
||||
env.close();
|
||||
|
||||
// Give alice the needed trust line, but freeze it.
|
||||
env(trust(gw, alice["AUD"](999), tfSetFreeze));
|
||||
env.close();
|
||||
|
||||
// Issuer (alice) must have a trust line for the offered funds
|
||||
// and the trust line may not be frozen.
|
||||
env(token::mint(alice),
|
||||
token::amount(gwAUD(1000)),
|
||||
txflags(tfTransferable),
|
||||
token::xferFee(10),
|
||||
ter(tecFROZEN));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// Seller (alice) must have a trust line may not be frozen.
|
||||
env(token::mint(alice),
|
||||
token::amount(gwAUD(1000)),
|
||||
ter(tecFROZEN));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
|
||||
// Unfreeze alice's trustline.
|
||||
env(trust(gw, alice["AUD"](999), tfClearFreeze));
|
||||
env.close();
|
||||
}
|
||||
|
||||
{
|
||||
// check reserve
|
||||
auto const acctReserve =
|
||||
env.current()->fees().accountReserve(0);
|
||||
auto const incReserve = env.current()->fees().increment;
|
||||
|
||||
env.fund(acctReserve + incReserve, bob);
|
||||
env.close();
|
||||
|
||||
// doesn't have reserve for 2 objects (NFTokenPage, Offer)
|
||||
env(token::mint(bob),
|
||||
token::amount(XRP(0)),
|
||||
ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
|
||||
// have reserve for NFTokenPage, Offer
|
||||
env(pay(env.master, bob, incReserve + drops(10)));
|
||||
env.close();
|
||||
env(token::mint(bob), token::amount(XRP(0)));
|
||||
env.close();
|
||||
|
||||
// doesn't have reserve for Offer
|
||||
env(pay(env.master, bob, drops(10)));
|
||||
env.close();
|
||||
env(token::mint(bob),
|
||||
token::amount(XRP(0)),
|
||||
ter(tecINSUFFICIENT_RESERVE));
|
||||
env.close();
|
||||
|
||||
// have reserve for Offer
|
||||
env(pay(env.master, bob, incReserve + drops(10)));
|
||||
env.close();
|
||||
env(token::mint(bob), token::amount(XRP(0)));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// Amount field specified
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
env(token::mint(alice), token::amount(XRP(10)));
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
env.close();
|
||||
|
||||
// Amount field and Destination field, Expiration field specified
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(10)),
|
||||
token::destination(buyer),
|
||||
token::expiration(lastClose(env) + 25));
|
||||
env.close();
|
||||
|
||||
// With TransferFee field
|
||||
env(trust(alice, gwAUD(1000)));
|
||||
env.close();
|
||||
env(token::mint(alice),
|
||||
token::amount(gwAUD(1)),
|
||||
token::destination(buyer),
|
||||
token::expiration(lastClose(env) + 25),
|
||||
txflags(tfTransferable),
|
||||
token::xferFee(10));
|
||||
env.close();
|
||||
|
||||
// Can be canceled by the issuer.
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(10)),
|
||||
token::destination(buyer),
|
||||
token::expiration(lastClose(env) + 25));
|
||||
uint256 const offerAliceSellsToBuyer =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::cancelOffer(alice, {offerAliceSellsToBuyer}));
|
||||
env.close();
|
||||
|
||||
// Can be canceled by the buyer.
|
||||
env(token::mint(buyer),
|
||||
token::amount(XRP(10)),
|
||||
token::destination(alice),
|
||||
token::expiration(lastClose(env) + 25));
|
||||
uint256 const offerBuyerSellsToAlice =
|
||||
keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::cancelOffer(alice, {offerBuyerSellsToAlice}));
|
||||
env.close();
|
||||
|
||||
env(token::setMinter(issuer, minter));
|
||||
env.close();
|
||||
|
||||
// Minter will have offer not issuer
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
env(token::mint(minter),
|
||||
token::issuer(issuer),
|
||||
token::amount(drops(1)));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
}
|
||||
|
||||
// Test sell offers with a destination with and without
|
||||
// fixNFTokenNegOffer.
|
||||
for (auto const& tweakedFeatures :
|
||||
{features - fixNFTokenNegOffer - featureNonFungibleTokensV1_1,
|
||||
features | fixNFTokenNegOffer})
|
||||
{
|
||||
Env env{*this, tweakedFeatures};
|
||||
Account const alice("alice");
|
||||
|
||||
env.fund(XRP(1000000), alice);
|
||||
|
||||
TER const offerCreateTER = tweakedFeatures[fixNFTokenNegOffer]
|
||||
? static_cast<TER>(temBAD_AMOUNT)
|
||||
: static_cast<TER>(tesSUCCESS);
|
||||
|
||||
// Make offers with negative amounts for the NFTs
|
||||
env(token::mint(alice),
|
||||
token::amount(XRP(-2)),
|
||||
ter(offerCreateTER));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testTxJsonMetaFields(FeatureBitset features)
|
||||
{
|
||||
@@ -6796,6 +7091,15 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
verifyNFTokenIDsInCancelOffer({nftId});
|
||||
}
|
||||
|
||||
if (features[featureNFTokenMintOffer])
|
||||
{
|
||||
uint256 const aliceMintWithOfferIndex1 =
|
||||
keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::mint(alice), token::amount(XRP(0)));
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceMintWithOfferIndex1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -7112,6 +7416,164 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testNFTIssuerIsIOUIssuer(FeatureBitset features)
|
||||
{
|
||||
testcase("Test fix NFT issuer is IOU issuer");
|
||||
|
||||
using namespace test::jtx;
|
||||
|
||||
Account const issuer{"issuer"};
|
||||
Account const becky{"becky"};
|
||||
Account const cheri{"cheri"};
|
||||
IOU const isISU(issuer["ISU"]);
|
||||
|
||||
// This test case covers issue...
|
||||
// https://github.com/XRPLF/rippled/issues/4941
|
||||
//
|
||||
// If an NFToken has a transfer fee then, when an offer is accepted,
|
||||
// a portion of the sale price goes to the issuer.
|
||||
//
|
||||
// It is possible for an issuer to issue both an IOU (for remittances)
|
||||
// and NFTokens. If the issuer's IOU is used to pay for the transfer
|
||||
// of one of the issuer's NFTokens, then paying the fee for that
|
||||
// transfer will fail with a tecNO_LINE.
|
||||
//
|
||||
// The problem occurs because the NFT code looks for a trust line to
|
||||
// pay the transfer fee. However the issuer of an IOU does not need
|
||||
// a trust line to accept their own issuance and, in fact, is not
|
||||
// allowed to have a trust line to themselves.
|
||||
//
|
||||
// This test looks at a situation where transfer of an NFToken is
|
||||
// prevented by this bug:
|
||||
// 1. Issuer issues an IOU (e.g, isISU).
|
||||
// 2. Becky and Cheri get trust lines for, and acquire, some isISU.
|
||||
// 3. Issuer mints NFToken with transfer fee.
|
||||
// 4. Becky acquires the NFToken, paying with XRP.
|
||||
// 5. Becky attempts to create an offer to sell the NFToken for
|
||||
// isISU(100). The attempt fails with `tecNO_LINE`.
|
||||
//
|
||||
// The featureNFTokenMintOffer amendment addresses this oversight.
|
||||
//
|
||||
// We remove the fixRemoveNFTokenAutoTrustLine amendment. Otherwise
|
||||
// we can't create NFTokens with tfTrustLine enabled.
|
||||
FeatureBitset const localFeatures =
|
||||
features - fixRemoveNFTokenAutoTrustLine;
|
||||
|
||||
Env env{*this, localFeatures};
|
||||
env.fund(XRP(1000), issuer, becky, cheri);
|
||||
env.close();
|
||||
|
||||
// Set trust lines so becky and cheri can use isISU.
|
||||
env(trust(becky, isISU(1000)));
|
||||
env(trust(cheri, isISU(1000)));
|
||||
env.close();
|
||||
env(pay(issuer, cheri, isISU(500)));
|
||||
env.close();
|
||||
|
||||
// issuer creates two NFTs: one with and one without AutoTrustLine.
|
||||
std::uint16_t xferFee = 5000; // 5%
|
||||
uint256 const nftAutoTrustID{token::getNextID(
|
||||
env, issuer, 0u, tfTransferable | tfTrustLine, xferFee)};
|
||||
env(token::mint(issuer, 0u),
|
||||
token::xferFee(xferFee),
|
||||
txflags(tfTransferable | tfTrustLine));
|
||||
env.close();
|
||||
|
||||
uint256 const nftNoAutoTrustID{
|
||||
token::getNextID(env, issuer, 0u, tfTransferable, xferFee)};
|
||||
env(token::mint(issuer, 0u),
|
||||
token::xferFee(xferFee),
|
||||
txflags(tfTransferable));
|
||||
env.close();
|
||||
|
||||
// becky buys the nfts for 1 drop each.
|
||||
{
|
||||
uint256 const beckyBuyOfferIndex1 =
|
||||
keylet::nftoffer(becky, env.seq(becky)).key;
|
||||
env(token::createOffer(becky, nftAutoTrustID, drops(1)),
|
||||
token::owner(issuer));
|
||||
|
||||
uint256 const beckyBuyOfferIndex2 =
|
||||
keylet::nftoffer(becky, env.seq(becky)).key;
|
||||
env(token::createOffer(becky, nftNoAutoTrustID, drops(1)),
|
||||
token::owner(issuer));
|
||||
|
||||
env.close();
|
||||
env(token::acceptBuyOffer(issuer, beckyBuyOfferIndex1));
|
||||
env(token::acceptBuyOffer(issuer, beckyBuyOfferIndex2));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// Behavior from here down diverges significantly based on
|
||||
// featureNFTokenMintOffer.
|
||||
if (!localFeatures[featureNFTokenMintOffer])
|
||||
{
|
||||
// Without featureNFTokenMintOffer becky simply can't
|
||||
// create an offer for a non-tfTrustLine NFToken that would
|
||||
// pay the transfer fee in issuer's own IOU.
|
||||
env(token::createOffer(becky, nftNoAutoTrustID, isISU(100)),
|
||||
txflags(tfSellNFToken),
|
||||
ter(tecNO_LINE));
|
||||
env.close();
|
||||
|
||||
// And issuer can't create a trust line to themselves.
|
||||
env(trust(issuer, isISU(1000)), ter(temDST_IS_SRC));
|
||||
env.close();
|
||||
|
||||
// However if the NFToken has the tfTrustLine flag set,
|
||||
// then becky can create the offer.
|
||||
uint256 const beckyAutoTrustOfferIndex =
|
||||
keylet::nftoffer(becky, env.seq(becky)).key;
|
||||
env(token::createOffer(becky, nftAutoTrustID, isISU(100)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
|
||||
// And cheri can accept the offer.
|
||||
env(token::acceptSellOffer(cheri, beckyAutoTrustOfferIndex));
|
||||
env.close();
|
||||
|
||||
// We verify that issuer got their transfer fee by seeing that
|
||||
// ISU(5) has disappeared out of cheri's and becky's balances.
|
||||
BEAST_EXPECT(env.balance(becky, isISU) == isISU(95));
|
||||
BEAST_EXPECT(env.balance(cheri, isISU) == isISU(400));
|
||||
}
|
||||
else
|
||||
{
|
||||
// With featureNFTokenMintOffer things go better.
|
||||
// becky creates offers to sell the nfts for ISU.
|
||||
uint256 const beckyNoAutoTrustOfferIndex =
|
||||
keylet::nftoffer(becky, env.seq(becky)).key;
|
||||
env(token::createOffer(becky, nftNoAutoTrustID, isISU(100)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
uint256 const beckyAutoTrustOfferIndex =
|
||||
keylet::nftoffer(becky, env.seq(becky)).key;
|
||||
env(token::createOffer(becky, nftAutoTrustID, isISU(100)),
|
||||
txflags(tfSellNFToken));
|
||||
env.close();
|
||||
|
||||
// cheri accepts becky's offers. Behavior is uniform:
|
||||
// issuer gets paid.
|
||||
env(token::acceptSellOffer(cheri, beckyAutoTrustOfferIndex));
|
||||
env.close();
|
||||
|
||||
// We verify that issuer got their transfer fee by seeing that
|
||||
// ISU(5) has disappeared out of cheri's and becky's balances.
|
||||
BEAST_EXPECT(env.balance(becky, isISU) == isISU(95));
|
||||
BEAST_EXPECT(env.balance(cheri, isISU) == isISU(400));
|
||||
|
||||
env(token::acceptSellOffer(cheri, beckyNoAutoTrustOfferIndex));
|
||||
env.close();
|
||||
|
||||
// We verify that issuer got their transfer fee by seeing that
|
||||
// an additional ISU(5) has disappeared out of cheri's and
|
||||
// becky's balances.
|
||||
BEAST_EXPECT(env.balance(becky, isISU) == isISU(190));
|
||||
BEAST_EXPECT(env.balance(cheri, isISU) == isISU(300));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testWithFeats(FeatureBitset features)
|
||||
{
|
||||
@@ -7144,8 +7606,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
testIOUWithTransferFee(features);
|
||||
testBrokeredSaleToSelf(features);
|
||||
testFixNFTokenRemint(features);
|
||||
testFeatMintWithOffer(features);
|
||||
testTxJsonMetaFields(features);
|
||||
testFixNFTokenBuyerReserve(features);
|
||||
testNFTIssuerIsIOUIssuer(features);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -7156,15 +7620,17 @@ public:
|
||||
static FeatureBitset const all{supported_amendments()};
|
||||
static FeatureBitset const fixNFTDir{fixNFTokenDirV1};
|
||||
|
||||
static std::array<FeatureBitset, 6> const feats{
|
||||
static std::array<FeatureBitset, 7> const feats{
|
||||
all - fixNFTDir - fixNonFungibleTokensV1_2 - fixNFTokenRemint -
|
||||
fixNFTokenReserve,
|
||||
fixNFTokenReserve - featureNFTokenMintOffer,
|
||||
all - disallowIncoming - fixNonFungibleTokensV1_2 -
|
||||
fixNFTokenRemint - fixNFTokenReserve,
|
||||
fixNFTokenRemint - fixNFTokenReserve - featureNFTokenMintOffer,
|
||||
all - fixNonFungibleTokensV1_2 - fixNFTokenRemint -
|
||||
fixNFTokenReserve,
|
||||
all - fixNFTokenRemint - fixNFTokenReserve,
|
||||
all - fixNFTokenReserve,
|
||||
fixNFTokenReserve - featureNFTokenMintOffer,
|
||||
all - fixNFTokenRemint - fixNFTokenReserve -
|
||||
featureNFTokenMintOffer,
|
||||
all - fixNFTokenReserve - featureNFTokenMintOffer,
|
||||
all - featureNFTokenMintOffer,
|
||||
all};
|
||||
|
||||
if (BEAST_EXPECT(instance < feats.size()))
|
||||
@@ -7217,12 +7683,21 @@ class NFTokenWOTokenReserve_test : public NFTokenBaseUtil_test
|
||||
}
|
||||
};
|
||||
|
||||
class NFTokenWOMintOffer_test : public NFTokenBaseUtil_test
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
NFTokenBaseUtil_test::run(5);
|
||||
}
|
||||
};
|
||||
|
||||
class NFTokenAllFeatures_test : public NFTokenBaseUtil_test
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
NFTokenBaseUtil_test::run(5, true);
|
||||
NFTokenBaseUtil_test::run(6, true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7231,6 +7706,7 @@ BEAST_DEFINE_TESTSUITE_PRIO(NFTokenDisallowIncoming, tx, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(NFTokenWOfixV1, tx, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(NFTokenWOTokenRemint, tx, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(NFTokenWOTokenReserve, tx, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(NFTokenWOMintOffer, tx, ripple, 2);
|
||||
BEAST_DEFINE_TESTSUITE_PRIO(NFTokenAllFeatures, tx, ripple, 2);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -57,6 +57,12 @@ uri::operator()(Env& env, JTx& jt) const
|
||||
jt.jv[sfURI.jsonName] = uri_;
|
||||
}
|
||||
|
||||
void
|
||||
amount::operator()(Env& env, JTx& jt) const
|
||||
{
|
||||
jt.jv[sfAmount.jsonName] = amount_.getJson(JsonOptions::none);
|
||||
}
|
||||
|
||||
uint256
|
||||
getNextID(
|
||||
jtx::Env const& env,
|
||||
|
||||
@@ -83,6 +83,21 @@ public:
|
||||
operator()(Env&, JTx& jtx) const;
|
||||
};
|
||||
|
||||
/** Sets the optional amount field on an NFTokenMint. */
|
||||
class amount
|
||||
{
|
||||
private:
|
||||
STAmount const amount_;
|
||||
|
||||
public:
|
||||
explicit amount(STAmount const amount) : amount_(amount)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(Env&, JTx& jtx) const;
|
||||
};
|
||||
|
||||
/** Get the next NFTokenID that will be issued. */
|
||||
uint256
|
||||
getNextID(
|
||||
|
||||
Reference in New Issue
Block a user