mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
feat: Support Dynamic NFT (#1525)
Fix #1471 Clio's changes for supporting DNFT https://github.com/XRPLF/rippled/pull/5048/files
This commit is contained in:
@@ -728,7 +728,230 @@ createMintNftTxWithMetadata(
|
||||
}
|
||||
|
||||
data::TransactionAndMetadata
|
||||
createAcceptNftOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uint32_t fee, std::string_view nftId)
|
||||
createMintNftTxWithMetadataOfCreatedNode(
|
||||
std::string_view accountId,
|
||||
uint32_t seq,
|
||||
uint32_t fee,
|
||||
uint32_t nfTokenTaxon,
|
||||
std::optional<std::string_view> nftID,
|
||||
std::optional<std::string_view> uri,
|
||||
std::optional<std::string_view> pageIndex
|
||||
)
|
||||
{
|
||||
// tx
|
||||
ripple::STObject tx(ripple::sfTransaction);
|
||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_MINT);
|
||||
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||
tx.setAccountID(ripple::sfAccount, account.value());
|
||||
auto amount = ripple::STAmount(fee, false);
|
||||
tx.setFieldAmount(ripple::sfFee, amount);
|
||||
// required field for ttNFTOKEN_MINT
|
||||
tx.setFieldU32(ripple::sfNFTokenTaxon, nfTokenTaxon);
|
||||
tx.setFieldU32(ripple::sfSequence, seq);
|
||||
char const* key = "test";
|
||||
ripple::Slice const slice(key, 4);
|
||||
tx.setFieldVL(ripple::sfSigningPubKey, slice);
|
||||
if (uri)
|
||||
tx.setFieldVL(ripple::sfURI, ripple::Slice(uri->data(), uri->size()));
|
||||
|
||||
// meta
|
||||
ripple::STObject metaObj(ripple::sfTransactionMetaData);
|
||||
ripple::STArray metaArray{1};
|
||||
ripple::STObject node(ripple::sfCreatedNode);
|
||||
node.setFieldU16(ripple::sfLedgerEntryType, ripple::ltNFTOKEN_PAGE);
|
||||
|
||||
ripple::STObject newFields(ripple::sfNewFields);
|
||||
ripple::STArray NFTArray1{1};
|
||||
|
||||
if (nftID) {
|
||||
// finalFields contain new NFT while previousFields does not
|
||||
auto entry = ripple::STObject(ripple::sfNFToken);
|
||||
entry.setFieldH256(ripple::sfNFTokenID, ripple::uint256{*nftID});
|
||||
if (uri)
|
||||
entry.setFieldVL(ripple::sfURI, ripple::Slice(uri->data(), uri->size()));
|
||||
|
||||
NFTArray1.push_back(entry);
|
||||
}
|
||||
newFields.setFieldArray(ripple::sfNFTokens, NFTArray1);
|
||||
node.emplace_back(std::move(newFields));
|
||||
if (pageIndex)
|
||||
node.setFieldH256(ripple::sfLedgerIndex, ripple::uint256{*pageIndex});
|
||||
|
||||
// add a ledger object ahead of nft page
|
||||
ripple::STObject node2(ripple::sfCreatedNode);
|
||||
node2.setFieldU16(ripple::sfLedgerEntryType, ripple::ltACCOUNT_ROOT);
|
||||
metaArray.push_back(node2);
|
||||
|
||||
metaArray.push_back(node);
|
||||
|
||||
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
||||
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
||||
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
|
||||
|
||||
data::TransactionAndMetadata ret;
|
||||
ret.transaction = tx.getSerializer().peekData();
|
||||
ret.metadata = metaObj.getSerializer().peekData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
data::TransactionAndMetadata
|
||||
createNftModifyTxWithMetadata(std::string_view accountId, std::string_view nftID, ripple::Blob uri)
|
||||
{
|
||||
// tx
|
||||
ripple::STObject tx(ripple::sfTransaction);
|
||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_MODIFY);
|
||||
auto account = ripple::parseBase58<ripple::AccountID>(std::string(accountId));
|
||||
tx.setAccountID(ripple::sfAccount, account.value());
|
||||
auto amount = ripple::STAmount(10, false);
|
||||
tx.setFieldAmount(ripple::sfFee, amount);
|
||||
tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
|
||||
tx.setFieldU32(ripple::sfSequence, 100);
|
||||
char const* key = "test";
|
||||
ripple::Slice const slice(key, 4);
|
||||
tx.setFieldVL(ripple::sfSigningPubKey, slice);
|
||||
|
||||
if (!uri.empty()) // sfURI should be absent if empty
|
||||
tx.setFieldVL(ripple::sfURI, uri);
|
||||
|
||||
// meta
|
||||
ripple::STObject metaObj(ripple::sfTransactionMetaData);
|
||||
ripple::STArray metaArray{1};
|
||||
ripple::STObject node(ripple::sfModifiedNode);
|
||||
node.setFieldU16(ripple::sfLedgerEntryType, ripple::ltNFTOKEN_PAGE);
|
||||
|
||||
ripple::STObject finalFields(ripple::sfFinalFields);
|
||||
ripple::STArray NFTArray1{1};
|
||||
ripple::STArray NFTArray2{1};
|
||||
|
||||
// finalFields contain new NFT while previousFields does not
|
||||
auto entry = ripple::STObject(ripple::sfNFToken);
|
||||
entry.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
|
||||
if (!uri.empty())
|
||||
entry.setFieldVL(ripple::sfURI, uri);
|
||||
NFTArray1.push_back(entry);
|
||||
|
||||
auto entry2 = ripple::STObject(ripple::sfNFToken);
|
||||
entry2.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
|
||||
char const* url = "previous";
|
||||
entry2.setFieldVL(ripple::sfURI, ripple::Slice(url, 7));
|
||||
NFTArray2.push_back(entry2);
|
||||
|
||||
finalFields.setFieldArray(ripple::sfNFTokens, NFTArray1);
|
||||
|
||||
ripple::STObject previousFields(ripple::sfPreviousFields);
|
||||
previousFields.setFieldArray(ripple::sfNFTokens, NFTArray2);
|
||||
|
||||
node.emplace_back(std::move(finalFields));
|
||||
node.emplace_back(std::move(previousFields));
|
||||
metaArray.push_back(node);
|
||||
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
||||
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
||||
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
|
||||
|
||||
data::TransactionAndMetadata ret;
|
||||
ret.transaction = tx.getSerializer().peekData();
|
||||
ret.metadata = metaObj.getSerializer().peekData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
data::TransactionAndMetadata
|
||||
createNftBurnTxWithMetadataOfDeletedNode(std::string_view accountId, std::string_view nftID)
|
||||
{
|
||||
// tx
|
||||
ripple::STObject tx(ripple::sfTransaction);
|
||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_BURN);
|
||||
auto account = getAccountIdWithString(accountId);
|
||||
tx.setAccountID(ripple::sfAccount, account);
|
||||
auto amount = ripple::STAmount(10, false);
|
||||
tx.setFieldAmount(ripple::sfFee, amount);
|
||||
tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
|
||||
tx.setFieldU32(ripple::sfSequence, 100);
|
||||
char const* key = "test";
|
||||
ripple::Slice const slice(key, 4);
|
||||
tx.setFieldVL(ripple::sfSigningPubKey, slice);
|
||||
|
||||
// meta
|
||||
ripple::STObject metaObj(ripple::sfTransactionMetaData);
|
||||
ripple::STArray metaArray{1};
|
||||
ripple::STObject node(ripple::sfDeletedNode);
|
||||
node.setFieldU16(ripple::sfLedgerEntryType, ripple::ltNFTOKEN_PAGE);
|
||||
// deleted node should contain finalFields
|
||||
ripple::STObject finalFields(ripple::sfFinalFields);
|
||||
ripple::STArray NFTArray{1};
|
||||
auto entry = ripple::STObject(ripple::sfNFToken);
|
||||
entry.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
|
||||
NFTArray.push_back(entry);
|
||||
finalFields.setFieldArray(ripple::sfNFTokens, NFTArray);
|
||||
|
||||
node.emplace_back(std::move(finalFields));
|
||||
|
||||
// add a ledger object ahead of nft page
|
||||
ripple::STObject node2(ripple::sfCreatedNode);
|
||||
node2.setFieldU16(ripple::sfLedgerEntryType, ripple::ltACCOUNT_ROOT);
|
||||
metaArray.push_back(node2);
|
||||
|
||||
metaArray.push_back(node);
|
||||
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
||||
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
||||
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
|
||||
|
||||
data::TransactionAndMetadata ret;
|
||||
ret.transaction = tx.getSerializer().peekData();
|
||||
ret.metadata = metaObj.getSerializer().peekData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
data::TransactionAndMetadata
|
||||
createNftBurnTxWithMetadataOfModifiedNode(std::string_view accountId, std::string_view nftID)
|
||||
{
|
||||
// tx
|
||||
ripple::STObject tx(ripple::sfTransaction);
|
||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_BURN);
|
||||
auto account = getAccountIdWithString(accountId);
|
||||
tx.setAccountID(ripple::sfAccount, account);
|
||||
auto amount = ripple::STAmount(10, false);
|
||||
tx.setFieldAmount(ripple::sfFee, amount);
|
||||
tx.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
|
||||
tx.setFieldU32(ripple::sfSequence, 100);
|
||||
char const* key = "test";
|
||||
ripple::Slice const slice(key, 4);
|
||||
tx.setFieldVL(ripple::sfSigningPubKey, slice);
|
||||
|
||||
// meta
|
||||
ripple::STObject metaObj(ripple::sfTransactionMetaData);
|
||||
ripple::STArray metaArray{1};
|
||||
ripple::STObject node(ripple::sfModifiedNode);
|
||||
node.setFieldU16(ripple::sfLedgerEntryType, ripple::ltNFTOKEN_PAGE);
|
||||
|
||||
ripple::STObject finalFields(ripple::sfFinalFields);
|
||||
ripple::STArray NFTArray{1};
|
||||
ripple::STObject previousFields(ripple::sfPreviousFields);
|
||||
auto entry = ripple::STObject(ripple::sfNFToken);
|
||||
entry.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftID});
|
||||
NFTArray.push_back(entry);
|
||||
previousFields.setFieldArray(ripple::sfNFTokens, NFTArray);
|
||||
|
||||
node.emplace_back(std::move(previousFields));
|
||||
node.emplace_back(std::move(finalFields));
|
||||
metaArray.push_back(node);
|
||||
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
||||
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
||||
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
|
||||
|
||||
data::TransactionAndMetadata ret;
|
||||
ret.transaction = tx.getSerializer().peekData();
|
||||
ret.metadata = metaObj.getSerializer().peekData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
data::TransactionAndMetadata
|
||||
createAcceptNftBuyerOfferTxWithMetadata(
|
||||
std::string_view accountId,
|
||||
uint32_t seq,
|
||||
uint32_t fee,
|
||||
std::string_view nftId,
|
||||
std::string_view offerId
|
||||
)
|
||||
{
|
||||
// tx
|
||||
ripple::STObject tx(ripple::sfTransaction);
|
||||
@@ -738,7 +961,7 @@ createAcceptNftOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uin
|
||||
auto amount = ripple::STAmount(fee, false);
|
||||
tx.setFieldAmount(ripple::sfFee, amount);
|
||||
tx.setFieldU32(ripple::sfSequence, seq);
|
||||
tx.setFieldH256(ripple::sfNFTokenBuyOffer, ripple::uint256{kINDEX1});
|
||||
tx.setFieldH256(ripple::sfNFTokenBuyOffer, ripple::uint256{offerId});
|
||||
char const* key = "test";
|
||||
ripple::Slice const slice(key, 4);
|
||||
tx.setFieldVL(ripple::sfSigningPubKey, slice);
|
||||
@@ -752,8 +975,11 @@ createAcceptNftOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uin
|
||||
|
||||
ripple::STObject finalFields(ripple::sfFinalFields);
|
||||
finalFields.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftId});
|
||||
// for buyer offer, the offer owner is the nft's new owner
|
||||
finalFields.setAccountID(ripple::sfOwner, account.value());
|
||||
|
||||
node.emplace_back(std::move(finalFields));
|
||||
node.setFieldH256(ripple::sfLedgerIndex, ripple::uint256{offerId});
|
||||
metaArray.push_back(node);
|
||||
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
||||
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
||||
@@ -765,6 +991,99 @@ createAcceptNftOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uin
|
||||
return ret;
|
||||
}
|
||||
|
||||
data::TransactionAndMetadata
|
||||
createAcceptNftSellerOfferTxWithMetadata(
|
||||
std::string_view accountId,
|
||||
uint32_t seq,
|
||||
uint32_t fee,
|
||||
std::string_view nftId,
|
||||
std::string_view offerId,
|
||||
std::string_view pageIndex,
|
||||
bool isNewPageCreated
|
||||
)
|
||||
{
|
||||
// tx
|
||||
ripple::STObject tx(ripple::sfTransaction);
|
||||
tx.setFieldU16(ripple::sfTransactionType, ripple::ttNFTOKEN_ACCEPT_OFFER);
|
||||
auto account = util::parseBase58Wrapper<ripple::AccountID>(std::string(accountId));
|
||||
tx.setAccountID(ripple::sfAccount, account.value());
|
||||
auto amount = ripple::STAmount(fee, false);
|
||||
tx.setFieldAmount(ripple::sfFee, amount);
|
||||
tx.setFieldU32(ripple::sfSequence, seq);
|
||||
tx.setFieldH256(ripple::sfNFTokenSellOffer, ripple::uint256{offerId});
|
||||
char const* key = "test";
|
||||
ripple::Slice const slice(key, 4);
|
||||
tx.setFieldVL(ripple::sfSigningPubKey, slice);
|
||||
|
||||
// meta
|
||||
// create deletedNode with ltNFTOKEN_OFFER
|
||||
ripple::STObject metaObj(ripple::sfTransactionMetaData);
|
||||
ripple::STArray metaArray{1};
|
||||
ripple::STObject node(ripple::sfDeletedNode);
|
||||
node.setFieldU16(ripple::sfLedgerEntryType, ripple::ltNFTOKEN_OFFER);
|
||||
|
||||
ripple::STObject finalFields(ripple::sfFinalFields);
|
||||
finalFields.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftId});
|
||||
// offer owner is not the nft's new owner for seller offer, we need to create other nodes for processing new owner
|
||||
finalFields.setAccountID(ripple::sfOwner, account.value());
|
||||
|
||||
node.emplace_back(std::move(finalFields));
|
||||
node.setFieldH256(ripple::sfLedgerIndex, ripple::uint256{offerId});
|
||||
metaArray.push_back(node);
|
||||
|
||||
// new owner's nft page node changed: 1 new nft page node added 2 old nft page node modified
|
||||
if (isNewPageCreated) {
|
||||
ripple::STObject node2(ripple::sfCreatedNode);
|
||||
node2.setFieldU16(ripple::sfLedgerEntryType, ripple::ltNFTOKEN_PAGE);
|
||||
|
||||
ripple::STObject newFields(ripple::sfNewFields);
|
||||
ripple::STArray nftArray1{1};
|
||||
|
||||
auto entry = ripple::STObject(ripple::sfNFToken);
|
||||
entry.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftId});
|
||||
nftArray1.push_back(entry);
|
||||
|
||||
newFields.setFieldArray(ripple::sfNFTokens, nftArray1);
|
||||
node2.emplace_back(std::move(newFields));
|
||||
node2.setFieldH256(ripple::sfLedgerIndex, ripple::uint256{pageIndex});
|
||||
metaArray.push_back(node2);
|
||||
} else {
|
||||
ripple::STObject node2(ripple::sfModifiedNode);
|
||||
node2.setFieldU16(ripple::sfLedgerEntryType, ripple::ltNFTOKEN_PAGE);
|
||||
|
||||
ripple::STArray nftArray1{2};
|
||||
|
||||
// finalFields contain new NFT while previousFields does not
|
||||
auto entry = ripple::STObject(ripple::sfNFToken);
|
||||
entry.setFieldH256(ripple::sfNFTokenID, ripple::uint256{nftId});
|
||||
nftArray1.push_back(entry);
|
||||
|
||||
auto entry2 = ripple::STObject(ripple::sfNFToken);
|
||||
entry2.setFieldH256(ripple::sfNFTokenID, ripple::uint256{kINDEX1});
|
||||
nftArray1.push_back(entry2);
|
||||
|
||||
finalFields.setFieldArray(ripple::sfNFTokens, nftArray1);
|
||||
|
||||
nftArray1.erase(nftArray1.begin());
|
||||
ripple::STObject previousFields(ripple::sfPreviousFields);
|
||||
previousFields.setFieldArray(ripple::sfNFTokens, nftArray1);
|
||||
|
||||
node2.emplace_back(std::move(finalFields));
|
||||
node2.emplace_back(std::move(previousFields));
|
||||
node2.setFieldH256(ripple::sfLedgerIndex, ripple::uint256{pageIndex});
|
||||
metaArray.push_back(node2);
|
||||
}
|
||||
|
||||
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
||||
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
||||
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
|
||||
|
||||
data::TransactionAndMetadata ret;
|
||||
ret.transaction = tx.getSerializer().peekData();
|
||||
ret.metadata = metaObj.getSerializer().peekData();
|
||||
return ret;
|
||||
}
|
||||
|
||||
// NFTokenCancelOffer can be used to cancel multiple offers
|
||||
data::TransactionAndMetadata
|
||||
createCancelNftOffersTxWithMetadata(
|
||||
|
||||
@@ -302,6 +302,9 @@ createNftTokenPage(
|
||||
std::optional<ripple::uint256> previousPage
|
||||
);
|
||||
|
||||
/**
|
||||
* Create NFToken mint tx, the metadata contained a changed node
|
||||
*/
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createMintNftTxWithMetadata(
|
||||
std::string_view accountId,
|
||||
@@ -311,8 +314,54 @@ createMintNftTxWithMetadata(
|
||||
std::string_view nftID
|
||||
);
|
||||
|
||||
/**
|
||||
* Create NFToken mint tx, the metadata contained a created node
|
||||
*/
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createAcceptNftOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uint32_t fee, std::string_view nftId);
|
||||
createMintNftTxWithMetadataOfCreatedNode(
|
||||
std::string_view accountId,
|
||||
uint32_t seq,
|
||||
uint32_t fee,
|
||||
uint32_t nfTokenTaxon,
|
||||
std::optional<std::string_view> nftID,
|
||||
std::optional<std::string_view> uri,
|
||||
std::optional<std::string_view> pageIndex
|
||||
);
|
||||
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createNftModifyTxWithMetadata(std::string_view accountId, std::string_view nftID, ripple::Blob uri);
|
||||
|
||||
/**
|
||||
* Create NFToken burn tx, tx causes a nft page node deleted
|
||||
*/
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createNftBurnTxWithMetadataOfDeletedNode(std::string_view accountId, std::string_view nftID);
|
||||
|
||||
/**
|
||||
* Create NFToken mint tx, tx causes a nft page node changed
|
||||
*/
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createNftBurnTxWithMetadataOfModifiedNode(std::string_view accountId, std::string_view nftID);
|
||||
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createAcceptNftBuyerOfferTxWithMetadata(
|
||||
std::string_view accountId,
|
||||
uint32_t seq,
|
||||
uint32_t fee,
|
||||
std::string_view nftId,
|
||||
std::string_view offerId
|
||||
);
|
||||
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createAcceptNftSellerOfferTxWithMetadata(
|
||||
std::string_view accountId,
|
||||
uint32_t seq,
|
||||
uint32_t fee,
|
||||
std::string_view nftId,
|
||||
std::string_view offerId,
|
||||
std::string_view pageIndex,
|
||||
bool isNewPageCreated
|
||||
);
|
||||
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createCancelNftOffersTxWithMetadata(
|
||||
@@ -322,9 +371,6 @@ createCancelNftOffersTxWithMetadata(
|
||||
std::vector<std::string> const& nftOffers
|
||||
);
|
||||
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createCreateNftOfferTxWithMetadata(std::string_view accountId, uint32_t seq, uint32_t fee, std::string_view offerId);
|
||||
|
||||
[[nodiscard]] data::TransactionAndMetadata
|
||||
createCreateNftOfferTxWithMetadata(
|
||||
std::string_view accountId,
|
||||
|
||||
Reference in New Issue
Block a user