UNPKG

@bithomp/xrpl-api

Version:

A Bithomp JavaScript/TypeScript library for interacting with the XRP Ledger

254 lines (253 loc) 8.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.parseCurrencyInformation = parseCurrencyInformation; const Client = __importStar(require("../client")); const utils_1 = require("../parse/utils"); const transaction_1 = require("../parse/transaction"); const client_1 = require("../client"); const common_1 = require("../common"); const amm_info_1 = require("../models/amm_info"); const maxLength = 20; const DECODED_HEX_CURRENCY_REGEX = /[^-\w\(\)\[\]\u0020-\u007E\u00A0-\uFFFF]*/g; async function parseCurrencyInformation(currency) { if (!currency || typeof currency !== "string") { return null; } if (currency.length < 3) { return null; } if (currency.length === 3 && currency.trim().toUpperCase() !== (0, client_1.getNativeCurrency)()) { return decodeSimple(currency); } if (isHex(currency)) { return await decodeCurrencyHex(currency); } return null; } function decodeSimple(currencyCode) { return { type: "simple", currencyCode, currency: currencyCode.trim(), }; } async function decodeCurrencyHex(currencyCode) { const prefix = currencyCode.substring(0, 2); if (prefix === "02" && isXlf15d(currencyCode)) { return await decodeXlf15d(currencyCode); } else if (prefix === "03" && amm_info_1.AMM_LP_TOKEN_REGEX.test(currencyCode)) { return decodeLPTokenHex(currencyCode); } else if (prefix === "01" && isDemurrageHex(currencyCode)) { return decodeDemurrageHex(currencyCode); } else { return decodeHex(currencyCode); } } async function decodeXlf15d(currencyCode) { const hex = currencyCode.toString().replace(/(00)+$/g, ""); const ctiHex = hex.substring(2, 16); const cti = BigInt("0x" + ctiHex); const ctiLedger = Number(ctiLedgerIndex(cti)); const ctiTxIndex = Number(ctiTransactionIndex(cti)); const currencyHex = hex.substring(16, hex.length); const currency = (0, utils_1.hexToString)(currencyHex)?.trim()?.replace(/\0/g, ""); const ledgerInfo = await getLedger(ctiLedger); const ledger = ledgerInfo?.ledger; let ctiVerified = false; let ctiValid = false; let timestamp; let ctiTx = {}; if (ledger) { timestamp = Math.round(new Date(ledger.close_time_human).getTime() / 1000); for (const transaction of ledger.transactions) { if (transaction.metaData.TransactionIndex === ctiTxIndex) { const { Account: account, Destination: destination, LimitAmount: limit, Memos: memos, hash: hash, } = transaction; const type = (0, transaction_1.parseTransactionType)(transaction.TransactionType); ctiTx = (0, common_1.removeUndefined)({ type, account, destination, issuer: limit?.issuer, counterparty: limit?.issuer, hash, memos, }); break; } } } if (ledger) { ctiVerified = true; if (ctiTx.hash) { ctiValid = ctiLedgerCheck(cti) === ctiLedgerCheckGen(ledger.hash || ledger.ledger_hash) && ctiTransactionCheck(cti) === ctiTransactionCheckGen(ctiTx.hash); } } else if (ledgerInfo?.error === "lgrNotFound") { ctiVerified = true; } return { type: "nft", currencyCode, currency, cti: Number(cti), ctiLedger, ctiTxIndex, ctiValid, ctiVerified, timestamp, ctiTx, }; } function decodeHex(currencyHex) { let hex = currencyHex; const prefix = currencyHex.substring(0, 2); if (prefix === "02") { hex = currencyHex.substring(16, currencyHex.length); } const decoded = (0, utils_1.hexToString)(hex) ?.slice(0, maxLength) ?.replace(/^\u0000+/, "") ?.replace(/\u0000+$/, "") ?.trim(); const trimmed = decoded .replace(DECODED_HEX_CURRENCY_REGEX, "") .replace(/[�]*/g, "") .replace(/\s+/g, " ") .trim(); if (trimmed.length > 1 && trimmed.length >= decoded.length / 2 && trimmed.toUpperCase() !== (0, client_1.getNativeCurrency)()) { return { type: "hex", currencyCode: currencyHex, currency: trimmed, }; } return null; } function decodeLPTokenHex(currencyHex) { return { type: "lp_token", currencyCode: currencyHex, currency: "LP Token", }; } function decodeDemurrageHex(currencyHex) { const hex = currencyHex.substring(2, 8); const currencyText = (0, utils_1.hexToString)(hex)?.trim()?.replace(/\0/g, ""); let valuePrefix = ""; const year = 31536000; const decimals = 2; const interestPeriod = hex2double(currencyHex.substring(16, 32)); const interest = Math.exp(year / interestPeriod); const percentage = (interest * 100) - 100; const decimalMultiplier = decimals ? Math.pow(10, decimals) : 100; const rate = Math.round(percentage * decimalMultiplier) / decimalMultiplier; valuePrefix = `(${rate > 0 ? "+" : ""}${rate}%pa)`; const currency = `${currencyText} ${valuePrefix}`; return { type: "demurrage", currencyCode: currencyHex, currency, }; } function isDemurrageHex(currencyHex) { const hex = currencyHex.substring(2, 8); const currencyText = (0, utils_1.hexToString)(hex)?.trim()?.replace(/\0/g, ""); if (!currencyText || currencyText.length < 3) { return false; } if (!currencyText.match(/^[a-zA-Z0-9\s]+$/)) { return false; } const interestPeriod = hex2double(currencyHex.substring(16, 32)); if (isNaN(interestPeriod)) { return false; } const interestStart = Buffer.from(currencyHex.substring(8, 16), "hex").readUInt32BE(0); if (isNaN(interestStart) || interestStart < 0) { return false; } return true; } function hex2double(hex) { const buffer = Buffer.from(hex, "hex"); const view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength); return view.getFloat64(0, false); } async function getLedger(ledgerIndex) { let ledgerInfo = null; try { ledgerInfo = await Client.getLedger({ ledgerIndex, transactions: true, expand: true }); } catch (_err) { } return ledgerInfo; } function ctiTransactionIndex(cti) { return (cti >> 32n) & 0xffffn; } function ctiLedgerIndex(cti) { return cti & 0xffffffffn; } function ctiLedgerCheck(cti) { return (cti >> 52n) & 0xfn; } function ctiTransactionCheck(cti) { return (cti >> 48n) & 0xfn; } function ctiLedgerCheckGen(ledgerHash) { return BigInt(parseInt(ledgerHash.slice(0, 1), 16)); } function ctiTransactionCheckGen(txHash) { return BigInt(parseInt(txHash.slice(0, 1), 16)); } function isXlf15d(currencyHex) { const hex = currencyHex.toString().replace(/(00)+$/g, ""); const xlf15d = Buffer.from(hex, "hex").slice(8).toString("utf-8").slice(0, maxLength).trim(); if (xlf15d.match(/[a-zA-Z0-9]{3,}/) && xlf15d.toUpperCase() !== (0, client_1.getNativeCurrency)()) { return true; } return false; } function isHex(currencyCode) { return !!currencyCode.match(/^[a-fA-F0-9]{40}$/) && !isNaN(parseInt(currencyCode, 16)); }