@bithomp/xrpl-api
Version:
A Bithomp JavaScript/TypeScript library for interacting with the XRP Ledger
254 lines (253 loc) • 8.8 kB
JavaScript
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));
}
;