mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
Add parseNFTokenID and tests (#1961)
* Add parseNFTokenID and tests * Lint * Move parseNFTokenID to utils * Lint * Move the NFTokenID into models * Move NFTokenID type out of models * Lint * Remove extra type and lint
This commit is contained in:
@@ -6,6 +6,7 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
|
||||
### Added
|
||||
* `federator_info` RPC support
|
||||
* Helper method for creating a cross-chain payment to/from a sidechain
|
||||
* Helper method for parsing an NFTokenID
|
||||
|
||||
### Fixed
|
||||
* Type of TrustSet transaction edited, specifically LimitAmount property type (fixed typescript issue)
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
hashEscrow,
|
||||
hashPaymentChannel,
|
||||
} from './hashes'
|
||||
import parseNFTokenID from './parseNFTokenID'
|
||||
import {
|
||||
percentToTransferRate,
|
||||
decimalToTransferRate,
|
||||
@@ -215,4 +216,5 @@ export {
|
||||
encodeForSigning,
|
||||
encodeForSigningClaim,
|
||||
createCrossChainPayment,
|
||||
parseNFTokenID,
|
||||
}
|
||||
|
||||
81
packages/xrpl/src/utils/parseNFTokenID.ts
Normal file
81
packages/xrpl/src/utils/parseNFTokenID.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/* eslint-disable @typescript-eslint/no-magic-numbers -- Doing hex string parsing. */
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { encodeAccountID } from 'ripple-address-codec'
|
||||
|
||||
import { XrplError } from '../errors'
|
||||
|
||||
/**
|
||||
* An issuer may issue several NFTs with the same taxon; to ensure that NFTs are
|
||||
* spread across multiple pages we lightly mix the taxon up by using the sequence
|
||||
* (which is not under the issuer's direct control) as the seed for a simple linear
|
||||
* congruential generator.
|
||||
*
|
||||
* From the Hull-Dobell theorem we know that f(x)=(m*x+c) mod n will yield a
|
||||
* permutation of [0, n) when n is a power of 2 if m is congruent to 1 mod 4 and
|
||||
* c is odd. By doing a bitwise XOR with this permutation we can scramble/unscramble
|
||||
* the taxon.
|
||||
*
|
||||
* The XLS-20d proposal fixes m = 384160001 and c = 2459.
|
||||
* We then take the modulus of 2^32 which is 4294967296.
|
||||
*
|
||||
* @param taxon - The scrambled or unscrambled taxon (The XOR is both the encoding and decoding)
|
||||
* @param tokenSeq - The account sequence when the token was minted. Used as a psuedorandom seed.
|
||||
* @returns the opposite taxon. If the taxon was scrambled it becomes unscrambled, and vice versa.
|
||||
*/
|
||||
function unscrambleTaxon(taxon: number, tokenSeq: number): number {
|
||||
/* eslint-disable no-bitwise -- XOR is part of the encode/decode scheme. */
|
||||
return (taxon ^ (384160001 * tokenSeq + 2459)) % 4294967296
|
||||
/* eslint-enable no-bitwise */
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an NFTokenID into the information it is encoding.
|
||||
*
|
||||
* Example decoding:
|
||||
*
|
||||
* 000B 0539 C35B55AA096BA6D87A6E6C965A6534150DC56E5E 12C5D09E 0000000C
|
||||
* +--- +--- +--------------------------------------- +------- +-------
|
||||
* | | | | |
|
||||
* | | | | `---> Sequence: 12
|
||||
* | | | |
|
||||
* | | | `---> Scrambled Taxon: 314,953,886
|
||||
* | | | Unscrambled Taxon: 1337
|
||||
* | | |
|
||||
* | | `---> Issuer: rJoxBSzpXhPtAuqFmqxQtGKjA13jUJWthE
|
||||
* | |
|
||||
* | `---> TransferFee: 1337.0 bps or 13.37%
|
||||
* |
|
||||
* `---> Flags: 11 -> lsfBurnable, lsfOnlyXRP and lsfTransferable
|
||||
*
|
||||
* @param tokenID - A hex string which identifies an NFToken on the ledger.
|
||||
* @throws XrplError when given an invalid tokenID.
|
||||
* @returns a decoded tokenID with all fields encoded within.
|
||||
*/
|
||||
export default function parseNFTokenID(tokenID: string): {
|
||||
TokenID: string
|
||||
Flags: number
|
||||
TransferFee: number
|
||||
Issuer: string
|
||||
Taxon: number
|
||||
Sequence: number
|
||||
} {
|
||||
const expectedLength = 64
|
||||
if (tokenID.length !== expectedLength) {
|
||||
throw new XrplError(`Attempting to parse a tokenID with length ${tokenID.length}
|
||||
, but expected a token with length ${expectedLength}`)
|
||||
}
|
||||
|
||||
const scrambledTaxon = new BigNumber(tokenID.substring(48, 56), 16).toNumber()
|
||||
const sequence = new BigNumber(tokenID.substring(56, 64), 16).toNumber()
|
||||
|
||||
const NFTokenIDData = {
|
||||
TokenID: tokenID,
|
||||
Flags: new BigNumber(tokenID.substring(0, 4), 16).toNumber(),
|
||||
TransferFee: new BigNumber(tokenID.substring(4, 8), 16).toNumber(),
|
||||
Issuer: encodeAccountID(Buffer.from(tokenID.substring(8, 48), 'hex')),
|
||||
Taxon: unscrambleTaxon(scrambledTaxon, sequence),
|
||||
Sequence: sequence,
|
||||
}
|
||||
|
||||
return NFTokenIDData
|
||||
}
|
||||
27
packages/xrpl/test/utils/parseNFTokenID.ts
Normal file
27
packages/xrpl/test/utils/parseNFTokenID.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { assert } from 'chai'
|
||||
import { parseNFTokenID } from 'xrpl-local'
|
||||
|
||||
import { assertResultMatch } from '../testUtils'
|
||||
|
||||
describe('parseNFTokenID', function () {
|
||||
it('decode a valid NFTokenID', function () {
|
||||
const tokenID =
|
||||
'000B0539C35B55AA096BA6D87A6E6C965A6534150DC56E5E12C5D09E0000000C'
|
||||
const result = parseNFTokenID(tokenID)
|
||||
const expected = {
|
||||
TokenID: tokenID,
|
||||
Flags: 11,
|
||||
TransferFee: 1337,
|
||||
Issuer: 'rJoxBSzpXhPtAuqFmqxQtGKjA13jUJWthE',
|
||||
Taxon: 1337,
|
||||
Sequence: 12,
|
||||
}
|
||||
assertResultMatch(result, expected)
|
||||
})
|
||||
|
||||
it('fail when given invalid NFTokenID', function () {
|
||||
assert.throws(() => {
|
||||
parseNFTokenID('ABCD')
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user