Skip to content

Instantly share code, notes, and snippets.

@N3TC4T
Last active November 30, 2023 10:17
Show Gist options
  • Save N3TC4T/a20fb528931ed009ebdd708be4938748 to your computer and use it in GitHub Desktop.
Save N3TC4T/a20fb528931ed009ebdd708be4938748 to your computer and use it in GitHub Desktop.
Calculate NFToken ID from transaction
const { decodeAccountID } = require('ripple-address-codec');
const BigNumber = require('bignumber.js');
const getTokenID = (transaction) => {
const { Account, Issuer, NFTokenTaxon, TransferFee, Flags, meta } = transaction || {};
// Validate transaction input data
if (!Account || !Issuer || !NFTokenTaxon || !TransferFee || !Flags) {
throw new Error('Invalid transaction data.');
}
// Validate meta data
if (typeof meta !== 'object' || !Array.isArray(meta.AffectedNodes)) {
throw new Error('Invalid meta data.');
}
// fixNFTokenRemint amendment will include the minted nfTokenId in the metaData
// Amendment: AE35ABDEFBDE520372B31C957020B34A7A4A9DC3115A69803A44016477C84D6E
// Check if 'nftoken_id' presence in meta
if (Object.prototype.hasOwnProperty.call(meta, 'nftoken_id')) {
return meta.nftoken_id;
}
// Fetch minted token sequence
let TokenSequence;
let NextTokenSequence;
let FirstNFTokenSequence;
meta.AffectedNodes.forEach((node) => {
if (node.ModifiedNode && node.ModifiedNode.LedgerEntryType === 'AccountRoot') {
const { PreviousFields, FinalFields } = node.ModifiedNode;
if (PreviousFields && FinalFields && FinalFields.Account === (Issuer || Account)) {
TokenSequence = PreviousFields.MintedNFTokens;
NextTokenSequence = FinalFields.MintedNFTokens;
FirstNFTokenSequence = PreviousFields.FirstNFTokenSequence || FinalFields.FirstNFTokenSequence;
}
}
});
// First minted token, set token sequence to zero
if (typeof TokenSequence === 'undefined' && NextTokenSequence === 1) {
TokenSequence = 0;
}
// Include first NFToken Sequence, introduced in `fixNFTokenRemint`
TokenSequence += FirstNFTokenSequence ?? 0;
// Decode account id
const DecodedIssuerID = decodeAccountID(Issuer || Account);
// Using BigNumber.js guarantees that we are getting correct result when TokenSequence value is big
const UnscrambleTaxon = new BigNumber(384160001)
.multipliedBy(TokenSequence)
.modulo(4294967296)
.plus(2459)
.modulo(4294967296)
.toNumber();
// Calculate ciphered taxon
const CipheredTaxon = (NFTokenTaxon ^ UnscrambleTaxon) >>> 0;
const TokenID = Buffer.concat([
Buffer.from([(Flags >> 8) & 0xff, Flags & 0xff]),
Buffer.from([(TransferFee >> 8) & 0xff, TransferFee & 0xff]),
DecodedIssuerID,
Buffer.from([
(CipheredTaxon >> 24) & 0xff,
(CipheredTaxon >> 16) & 0xff,
(CipheredTaxon >> 8) & 0xff,
CipheredTaxon & 0xff,
]),
Buffer.from([
(TokenSequence >> 24) & 0xff,
(TokenSequence >> 16) & 0xff,
(TokenSequence >> 8) & 0xff,
TokenSequence & 0xff,
]),
]);
// should be 32 bytes
if (TokenID.length !== 32) {
throw new Error(`Calculated TokenID have unexpected length, expected 32, received ${TokenID.length}`);
}
return TokenID.toString('hex').toUpperCase();
};
const tokenID = getTokenID(transaction);
console.log(tokenID);
@WietseWind
Copy link

WietseWind commented Oct 25, 2023

@N3TC4T

Voting is reaching go-live on this amendment:
https://xrpl.org/known-amendments.html#fixnftokenremint

And since we use the sequence (minted tokens) here:
https://gist.github.com/N3TC4T/a20fb528931ed009ebdd708be4938748

To compute NFT, this amendment would change this calculation as that field from accountroot becomes part of the NFT sequence.

Changes required to make this compatible with the new amendment (and keep it backwards compatible):

image

This is backwards compatible because it will do nothing if the AccountRoot didn't have, and didn't get, the new FirstNFTokenSequence field.

If the field was there or has been created, it adds the value to the TokenSequence as per new behaviour past activation of the amendment: https://xrpl.org/known-amendments.html#fixnftokenremint

Add (on top)

let FirstNFTokenSequence;

Add (in AccountRoot fields condition)

FirstNFTokenSequence = PreviousFields?.FirstNFTokenSequence || FinalFields?.FirstNFTokenSequence;

After the TokenSequence = 0 fallback:

TokenSequence += FirstNFTokenSequence ?? 0

@martypaz
Copy link

Thank god someone is here to help us!! Thanks for publishing this mate and making it public.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment