use alloc::borrow::Cow;
use crate::{
core::addresscodec::encode_classic_address,
models::{transactions::nftoken_mint::NFTokenMintFlag, FlagCollection},
utils::exceptions::XRPLNFTIdException,
};
use super::exceptions::XRPLUtilsResult;
pub struct NFTokenId<'a> {
pub nftoken_id: Cow<'a, str>,
pub flags: FlagCollection<NFTokenMintFlag>,
pub transfer_fee: u32,
pub issuer: Cow<'a, str>,
pub taxon: u64,
pub sequence: u32,
}
pub fn unscramble_taxon(taxon: u64, token_seq: u64) -> u64 {
(taxon ^ (384160001 * token_seq + 2459)) % 4294967296
}
pub fn parse_nftoken_id(nft_id: Cow<'_, str>) -> XRPLUtilsResult<NFTokenId<'_>> {
const EXPECTED_LEN: usize = 64;
if nft_id.len() != EXPECTED_LEN {
return Err(XRPLNFTIdException::InvalidNFTIdLength {
expected: EXPECTED_LEN,
found: nft_id.len(),
}
.into());
}
let scrambled_taxon = u64::from_str_radix(&nft_id[48..56], 16)?;
let sequence = u32::from_str_radix(&nft_id[56..64], 16)?;
let flags = u32::from_str_radix(&nft_id[0..4], 16)?;
let transfer_fee = u32::from_str_radix(&nft_id[4..8], 16)?;
let issuer_bytes = hex::decode(&nft_id[8..48])?;
let issuer = encode_classic_address(issuer_bytes.as_ref())?;
Ok(NFTokenId {
nftoken_id: nft_id,
flags: flags.try_into()?,
transfer_fee,
issuer: issuer.into(),
taxon: unscramble_taxon(scrambled_taxon, sequence as u64),
sequence,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_nftoken_id() {
let nft_id = "000B0539C35B55AA096BA6D87A6E6C965A6534150DC56E5E12C5D09E0000000C";
let nftoken_id = parse_nftoken_id(Cow::Borrowed(nft_id)).unwrap();
assert_eq!(nftoken_id.flags.len(), 3);
assert_eq!(nftoken_id.transfer_fee, 1337);
assert_eq!(nftoken_id.issuer, "rJoxBSzpXhPtAuqFmqxQtGKjA13jUJWthE");
assert_eq!(nftoken_id.taxon, 1337);
assert_eq!(nftoken_id.sequence, 12);
}
}