ecash-lib
Version:
Library for eCash transaction building
168 lines • 5.58 kB
JavaScript
;
// Copyright (c) 2025 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseAlp = void 0;
const hex_js_1 = require("../io/hex.js");
const str_js_1 = require("../io/str.js");
const bytes_js_1 = require("../io/bytes.js");
const alp_js_1 = require("./alp.js");
const common_js_1 = require("./common.js");
/**
* Parse the given ALP pushdata. eMPP allows multiple pushdata per OP_RETURN.
*
* For data that's clearly not ALP (i.e. doesn't start with LOKAD ID "SLP2"),
* it will return `undefined`.
*
* For an unknown token type, it'll return AlpUnknown.
*
* For a known token type, it'll parse the remaining data, or throw an error if
* the format is invalid or if there's an unknown tx type.
*
* This behavior mirrors that of Chronik for consistency.
**/
function parseAlp(pushdata) {
// Must have at least 4 bytes for the LOKAD ID
if (pushdata.length < alp_js_1.ALP_LOKAD_ID.length) {
return undefined;
}
const bytes = new bytes_js_1.Bytes(pushdata);
// If the pushdata doesn't start with "SLP2" (ALP's LOKAD ID), return undefined
const lokadId = (0, str_js_1.bytesToStr)(bytes.readBytes(alp_js_1.ALP_LOKAD_ID.length));
if (lokadId != (0, str_js_1.bytesToStr)(alp_js_1.ALP_LOKAD_ID)) {
return undefined;
}
// Return UNKNOWN for unknown token types (only "STANDARD" known so far)
const tokenType = readU8(bytes, 'tokenType');
if (tokenType != alp_js_1.ALP_STANDARD) {
return {
txType: 'UNKNOWN',
tokenType,
};
}
// Parse tx type (GENESIS, MINT, SEND, BURN)
const txType = (0, str_js_1.bytesToStr)(readVarBytes(bytes, 'txType'));
// Handle tx type specific parsing
switch (txType) {
case common_js_1.GENESIS_STR:
return readGenesis(bytes, tokenType);
case common_js_1.MINT_STR:
return readMint(bytes, tokenType);
case common_js_1.SEND_STR:
return readSend(bytes, tokenType);
case common_js_1.BURN_STR:
return readBurn(bytes, tokenType);
default:
throw new Error('Unknown txType');
}
}
exports.parseAlp = parseAlp;
function readGenesis(bytes, tokenType) {
const tokenTicker = (0, str_js_1.bytesToStr)(readVarBytes(bytes, 'tokenTicker'));
const tokenName = (0, str_js_1.bytesToStr)(readVarBytes(bytes, 'tokenName'));
const url = (0, str_js_1.bytesToStr)(readVarBytes(bytes, 'url'));
const data = readVarBytes(bytes, 'data');
const authPubkey = readVarBytes(bytes, 'authPubkey');
const decimals = readU8(bytes, 'decimals');
const mintData = readMintData(bytes);
ensureEnd(bytes, 'GENESIS');
return {
txType: common_js_1.GENESIS_STR,
tokenType,
genesisInfo: {
tokenTicker,
tokenName,
url,
data: (0, hex_js_1.toHex)(data),
authPubkey: (0, hex_js_1.toHex)(authPubkey),
decimals,
},
mintData,
};
}
function readMint(bytes, tokenType) {
const tokenId = readTokenId(bytes);
const mintData = readMintData(bytes);
ensureEnd(bytes, 'MINT');
return {
txType: common_js_1.MINT_STR,
tokenType,
tokenId,
mintData,
};
}
function readSend(bytes, tokenType) {
const tokenId = readTokenId(bytes);
const sendAtomsArray = readAtomsArray(bytes, 'sendAtomsArray');
ensureEnd(bytes, 'SEND');
return {
txType: common_js_1.SEND_STR,
tokenType,
tokenId,
sendAtomsArray,
};
}
function readBurn(bytes, tokenType) {
const tokenId = readTokenId(bytes);
const burnAtoms = readU48(bytes, 'burnAtoms');
ensureEnd(bytes, 'BURN');
return {
txType: common_js_1.BURN_STR,
tokenType,
tokenId,
burnAtoms,
};
}
function readU8(bytes, name) {
if (bytes.idx >= bytes.data.length) {
throw new Error(`Missing ${name}`);
}
return bytes.readU8();
}
function readU48(bytes, name) {
if (bytes.idx >= bytes.data.length) {
throw new Error(`Missing ${name}`);
}
return bytes.readU48();
}
function readTokenId(bytes) {
// Note: ALP token ID endianness is little-endian (like in prevOut)
return (0, hex_js_1.toHexRev)(bytes.readBytes(common_js_1.TOKEN_ID_NUM_BYTES));
}
function readSize(bytes, name) {
const size = readU8(bytes, name);
if (size > alp_js_1.ALP_MAX_SIZE) {
throw new Error(`Size must be between 0 and ${alp_js_1.ALP_MAX_SIZE}`);
}
return size;
}
function readVarBytes(bytes, name) {
const numBytes = readSize(bytes, name);
return bytes.readBytes(numBytes);
}
function readAtomsArray(bytes, name) {
const numAtoms = readSize(bytes, name);
const atomsArray = [];
for (let i = 0; i < numAtoms; ++i) {
atomsArray.push(bytes.readU48());
}
return atomsArray;
}
function readMintData(bytes) {
const atomsArray = readAtomsArray(bytes, 'atomsArray');
const numBatons = readU8(bytes, 'numBatons');
if (numBatons > alp_js_1.ALP_MAX_SIZE) {
throw new Error(`numBatons must be between 0 and ${alp_js_1.ALP_MAX_SIZE}`);
}
return {
atomsArray,
numBatons,
};
}
function ensureEnd(bytes, txType) {
if (bytes.idx < bytes.data.length) {
throw new Error(`Superfluous ${txType} bytes`);
}
}
//# sourceMappingURL=alp.parse.js.map