UNPKG

xrpl

Version:

A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser

377 lines 14.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateMPTokenMetadata = exports.decodeMPTokenMetadata = exports.encodeMPTokenMetadata = exports.MPT_META_WARNING_HEADER = exports.MAX_MPT_META_BYTE_LENGTH = void 0; const utils_1 = require("@xrplf/isomorphic/utils"); const fast_json_stable_stringify_1 = __importDefault(require("fast-json-stable-stringify")); const common_1 = require("../transactions/common"); const _1 = require("."); exports.MAX_MPT_META_BYTE_LENGTH = 1024; exports.MPT_META_WARNING_HEADER = 'MPTokenMetadata is not properly formatted as JSON as per the XLS-89 standard. ' + "While adherence to this standard is not mandatory, such non-compliant MPToken's might not be discoverable " + 'by Explorers and Indexers in the XRPL ecosystem.'; const MPT_META_URI_FIELDS = [ { long: 'uri', compact: 'u', }, { long: 'category', compact: 'c', }, { long: 'title', compact: 't', }, ]; const MPT_META_ALL_FIELDS = [ { long: 'ticker', compact: 't', validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if (!(0, common_1.isString)(value) || !/^[A-Z0-9]{1,6}$/u.test(value)) { return [ `${this.long}/${this.compact}: should have uppercase letters (A-Z) and digits (0-9) only. Max 6 characters recommended.`, ]; } return []; }, }, { long: 'name', compact: 'n', validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if (!(0, common_1.isString)(value) || value.length === 0) { return [`${this.long}/${this.compact}: should be a non-empty string.`]; } return []; }, }, { long: 'icon', compact: 'i', validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if (!(0, common_1.isString)(value) || value.length === 0) { return [`${this.long}/${this.compact}: should be a non-empty string.`]; } return []; }, }, { long: 'asset_class', compact: 'ac', validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; const MPT_META_ASSET_CLASSES = [ 'rwa', 'memes', 'wrapped', 'gaming', 'defi', 'other', ]; if (!(0, common_1.isString)(value) || !MPT_META_ASSET_CLASSES.includes(value)) { return [ `${this.long}/${this.compact}: should be one of ${MPT_META_ASSET_CLASSES.join(', ')}.`, ]; } return []; }, }, { long: 'issuer_name', compact: 'in', validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if (!(0, common_1.isString)(value) || value.length === 0) { return [`${this.long}/${this.compact}: should be a non-empty string.`]; } return []; }, }, { long: 'desc', compact: 'd', validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } if (obj[this.long] === undefined && obj[this.compact] === undefined) { return []; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if (!(0, common_1.isString)(value) || value.length === 0) { return [`${this.long}/${this.compact}: should be a non-empty string.`]; } return []; }, }, { long: 'asset_subclass', compact: 'as', required: false, validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if ((obj.asset_class === 'rwa' || obj.ac === 'rwa') && value === undefined) { return [ `${this.long}/${this.compact}: required when asset_class is rwa.`, ]; } if (obj[this.long] === undefined && obj[this.compact] === undefined) { return []; } const MPT_META_ASSET_SUB_CLASSES = [ 'stablecoin', 'commodity', 'real_estate', 'private_credit', 'equity', 'treasury', 'other', ]; if (!(0, common_1.isString)(value) || !MPT_META_ASSET_SUB_CLASSES.includes(value)) { return [ `${this.long}/${this.compact}: should be one of ${MPT_META_ASSET_SUB_CLASSES.join(', ')}.`, ]; } return []; }, }, { long: 'uris', compact: 'us', required: false, validate(obj) { var _a, _b, _c, _d; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } if (obj[this.long] === undefined && obj[this.compact] === undefined) { return []; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if (!Array.isArray(value) || value.length === 0) { return [`${this.long}/${this.compact}: should be a non-empty array.`]; } const messages = []; for (const uriObj of value) { if (!(0, common_1.isRecord)(uriObj) || Object.keys(uriObj).length !== MPT_META_URI_FIELDS.length) { messages.push(`${this.long}/${this.compact}: should be an array of objects each with uri/u, category/c, and title/t properties.`); continue; } for (const uriField of MPT_META_URI_FIELDS) { if (uriObj[uriField.long] != null && uriObj[uriField.compact] != null) { messages.push(`${this.long}/${this.compact}: should not have both ${uriField.long} and ${uriField.compact} fields.`); break; } } const uri = (_b = uriObj.uri) !== null && _b !== void 0 ? _b : uriObj.u; const category = (_c = uriObj.category) !== null && _c !== void 0 ? _c : uriObj.c; const title = (_d = uriObj.title) !== null && _d !== void 0 ? _d : uriObj.t; if (!(0, common_1.isString)(uri) || !(0, common_1.isString)(category) || !(0, common_1.isString)(title)) { messages.push(`${this.long}/${this.compact}: should be an array of objects each with uri/u, category/c, and title/t properties.`); } } return messages; }, }, { long: 'additional_info', compact: 'ai', required: false, validate(obj) { var _a; if (obj[this.long] != null && obj[this.compact] != null) { return [ `${this.long}/${this.compact}: both long and compact forms present. expected only one.`, ]; } if (obj[this.long] === undefined && obj[this.compact] === undefined) { return []; } const value = (_a = obj[this.long]) !== null && _a !== void 0 ? _a : obj[this.compact]; if (!(0, common_1.isString)(value) && !(0, common_1.isRecord)(value)) { return [ `${this.long}/${this.compact}: should be a string or JSON object.`, ]; } return []; }, }, ]; function shortenKeys(input, mappings) { const output = {}; for (const [key, value] of Object.entries(input)) { const mapping = mappings.find(({ long, compact }) => long === key || compact === key); if (mapping === undefined) { output[key] = value; continue; } if (input[mapping.long] !== undefined && input[mapping.compact] !== undefined) { output[key] = value; continue; } output[mapping.compact] = value; } return output; } function encodeMPTokenMetadata(mptokenMetadata) { let input = mptokenMetadata; if (!(0, common_1.isRecord)(input)) { throw new Error('MPTokenMetadata must be JSON object.'); } input = shortenKeys(input, MPT_META_ALL_FIELDS); if (Array.isArray(input.uris)) { input.uris = input.uris.map((uri) => { if ((0, common_1.isRecord)(uri)) { return shortenKeys(uri, MPT_META_URI_FIELDS); } return uri; }); } if (Array.isArray(input.us)) { input.us = input.us.map((uri) => { if ((0, common_1.isRecord)(uri)) { return shortenKeys(uri, MPT_META_URI_FIELDS); } return uri; }); } return (0, utils_1.stringToHex)((0, fast_json_stable_stringify_1.default)(input)).toUpperCase(); } exports.encodeMPTokenMetadata = encodeMPTokenMetadata; function expandKeys(input, mappings) { const output = {}; for (const [key, value] of Object.entries(input)) { const mapping = mappings.find(({ long, compact }) => long === key || compact === key); if (mapping === undefined) { output[key] = value; continue; } if (input[mapping.long] !== undefined && input[mapping.compact] !== undefined) { output[key] = value; continue; } output[mapping.long] = value; } return output; } function decodeMPTokenMetadata(input) { if (!(0, _1.isHex)(input)) { throw new Error('MPTokenMetadata must be in hex format.'); } let jsonMetaData; try { jsonMetaData = JSON.parse((0, utils_1.hexToString)(input)); } catch (err) { throw new Error(`MPTokenMetadata is not properly formatted as JSON - ${String(err)}`); } if (!(0, common_1.isRecord)(jsonMetaData)) { throw new Error('MPTokenMetadata must be a JSON object.'); } let output = jsonMetaData; output = expandKeys(output, MPT_META_ALL_FIELDS); if (Array.isArray(output.uris)) { output.uris = output.uris.map((uri) => { if ((0, common_1.isRecord)(uri)) { return expandKeys(uri, MPT_META_URI_FIELDS); } return uri; }); } if (Array.isArray(output.us)) { output.us = output.us.map((uri) => { if ((0, common_1.isRecord)(uri)) { return expandKeys(uri, MPT_META_URI_FIELDS); } return uri; }); } return output; } exports.decodeMPTokenMetadata = decodeMPTokenMetadata; function validateMPTokenMetadata(input) { const validationMessages = []; if (!(0, _1.isHex)(input)) { validationMessages.push(`MPTokenMetadata must be in hex format.`); return validationMessages; } if (input.length / 2 > exports.MAX_MPT_META_BYTE_LENGTH) { validationMessages.push(`MPTokenMetadata must be max ${exports.MAX_MPT_META_BYTE_LENGTH} bytes.`); return validationMessages; } let jsonMetaData; try { jsonMetaData = JSON.parse((0, utils_1.hexToString)(input)); } catch (err) { validationMessages.push(`MPTokenMetadata is not properly formatted as JSON - ${String(err)}`); return validationMessages; } if (!(0, common_1.isRecord)(jsonMetaData)) { validationMessages.push('MPTokenMetadata is not properly formatted JSON object as per XLS-89.'); return validationMessages; } if (Object.keys(jsonMetaData).length > MPT_META_ALL_FIELDS.length) { validationMessages.push(`MPTokenMetadata must not contain more than ${MPT_META_ALL_FIELDS.length} top-level fields (found ${Object.keys(jsonMetaData).length}).`); } const obj = jsonMetaData; for (const property of MPT_META_ALL_FIELDS) { validationMessages.push(...property.validate(obj)); } return validationMessages; } exports.validateMPTokenMetadata = validateMPTokenMetadata; //# sourceMappingURL=mptokenMetadata.js.map