UNPKG

@multiversx/sdk-transaction-decoder

Version:
269 lines (268 loc) 9.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TokenTransferProperties = exports.TransactionMetadataTransfer = exports.TransactionMetadata = exports.TransactionToDecode = exports.TransactionDecoder = void 0; const bech32_1 = require("bech32"); const SMART_CONTRACT_HEX_PUBKEY_PREFIX = "0".repeat(16); class TransactionDecoder { getTransactionMetadata(transaction) { const metadata = this.getNormalTransactionMetadata(transaction); const esdtMetadata = this.getEsdtTransactionMetadata(metadata); if (esdtMetadata) { return esdtMetadata; } const nftMetadata = this.getNftTransferMetadata(metadata); if (nftMetadata) { return nftMetadata; } const multiMetadata = this.getMultiTransferMetadata(metadata); if (multiMetadata) { return multiMetadata; } return metadata; } getNormalTransactionMetadata(transaction) { const metadata = new TransactionMetadata(); metadata.sender = transaction.sender; metadata.receiver = transaction.receiver; metadata.value = BigInt(transaction.value); if (!transaction.data) { metadata.functionName = 'transfer'; metadata.functionArgs = undefined; return metadata; } const decodedData = this.base64Decode(transaction.data); const dataComponents = decodedData.split('@'); const args = dataComponents.slice(1); if (args.every((x) => this.isSmartContractArgument(x))) { metadata.functionName = dataComponents[0]; metadata.functionArgs = args; } if (args.length === 0 && !this.isSmartContract(transaction.receiver)) { metadata.functionName = 'transfer'; metadata.functionArgs = undefined; } if (metadata.functionName === 'relayedTx' && metadata.functionArgs && metadata.functionArgs.length === 1) { try { const relayedTransaction = JSON.parse(this.hexToString(metadata.functionArgs[0])); relayedTransaction.value = relayedTransaction.value.toString(); relayedTransaction.sender = this.bech32Encode(this.base64ToHex(relayedTransaction.sender)); relayedTransaction.receiver = this.bech32Encode(this.base64ToHex(relayedTransaction.receiver)); return this.getNormalTransactionMetadata(relayedTransaction); } catch (error) { // nothing special } } if (metadata.functionName === 'relayedTxV2' && metadata.functionArgs && metadata.functionArgs.length === 4) { try { const relayedTransaction = new TransactionToDecode(); relayedTransaction.sender = transaction.receiver; relayedTransaction.receiver = this.bech32Encode(metadata.functionArgs[0]); relayedTransaction.data = this.base64Encode(this.hexToString(metadata.functionArgs[2])); relayedTransaction.value = '0'; return this.getNormalTransactionMetadata(relayedTransaction); } catch (error) { // nothing special } } return metadata; } getMultiTransferMetadata(metadata) { if (metadata.sender !== metadata.receiver) { return undefined; } if (metadata.functionName !== 'MultiESDTNFTTransfer') { return undefined; } const args = metadata.functionArgs; if (!args) { return undefined; } if (args.length < 3) { return undefined; } if (!this.isAddressValid(args[0])) { return undefined; } const receiver = this.bech32Encode(args[0]); const transferCount = this.hexToNumber(args[1]); const result = new TransactionMetadata(); if (!result.transfers) { result.transfers = []; } let index = 2; for (let i = 0; i < transferCount; i++) { const identifier = this.hexToString(args[index++]); const nonce = args[index++]; const value = this.hexToBigInt(args[index++]); if (nonce && this.hexToNumber(nonce) > 0) { result.transfers.push({ value, properties: { collection: identifier, identifier: `${identifier}-${nonce}`, }, }); } else { result.transfers.push({ value, properties: { token: identifier, }, }); } } result.sender = metadata.sender; result.receiver = receiver; if (args.length > index) { result.functionName = this.hexToString(args[index++]); result.functionArgs = args.slice(index++); } return result; } getNftTransferMetadata(metadata) { if (metadata.sender !== metadata.receiver) { return undefined; } if (metadata.functionName !== 'ESDTNFTTransfer') { return undefined; } const args = metadata.functionArgs; if (!args) { return undefined; } if (args.length < 4) { return undefined; } if (!this.isAddressValid(args[3])) { return undefined; } const collectionIdentifier = this.hexToString(args[0]); const nonce = args[1]; const value = this.hexToBigInt(args[2]); const receiver = this.bech32Encode(args[3]); const result = new TransactionMetadata(); result.sender = metadata.sender; result.receiver = receiver; result.value = value; if (args.length > 4) { result.functionName = this.hexToString(args[4]); result.functionArgs = args.slice(5); } result.transfers = [{ value, properties: { collection: collectionIdentifier, identifier: `${collectionIdentifier}-${nonce}`, }, }]; return result; } base64Encode(str) { return Buffer.from(str).toString('base64'); } base64Decode(str) { return Buffer.from(str, 'base64').toString('binary'); } hexToNumber(hex) { return parseInt(hex, 16); } getEsdtTransactionMetadata(metadata) { if (metadata.functionName !== 'ESDTTransfer') { return undefined; } const args = metadata.functionArgs; if (!args) { return undefined; } if (args.length < 2) { return undefined; } const tokenIdentifier = this.hexToString(args[0]); const value = this.hexToBigInt(args[1]); const result = new TransactionMetadata(); result.sender = metadata.sender; result.receiver = metadata.receiver; if (args.length > 2) { result.functionName = this.hexToString(args[2]); result.functionArgs = args.slice(3); } result.transfers = [{ value, properties: { identifier: tokenIdentifier, }, }]; result.value = value; return result; } bech32Encode(address) { const pubKey = Buffer.from(address, "hex"); const words = bech32_1.bech32.toWords(pubKey); return bech32_1.bech32.encode('erd', words); } bech32Decode(address) { const decoded = bech32_1.bech32.decodeUnsafe(address); return decoded ? Buffer.from(bech32_1.bech32.fromWords(decoded.words)).toString('hex') : undefined; } isAddressValid(address) { return Buffer.from(address, "hex").length == 32; } isSmartContract(address) { var _a, _b; return (_b = (_a = this.bech32Decode(address)) === null || _a === void 0 ? void 0 : _a.startsWith(SMART_CONTRACT_HEX_PUBKEY_PREFIX)) !== null && _b !== void 0 ? _b : false; } isSmartContractArgument(arg) { if (!this.isHex(arg)) { return false; } if (arg.length % 2 !== 0) { return false; } return true; } isHex(value) { return new RegExp(/[^a-f0-9]/gi).test(value) === false; } base64ToHex(str) { return Buffer.from(str, 'base64').toString('hex'); } hexToString(hex) { return Buffer.from(hex, 'hex').toString('ascii'); } hexToBigInt(hex) { if (!hex) { return BigInt(0); } return BigInt('0x' + hex); } } exports.TransactionDecoder = TransactionDecoder; class TransactionToDecode { constructor() { this.sender = ''; this.receiver = ''; this.data = ''; this.value = '0'; } } exports.TransactionToDecode = TransactionToDecode; class TransactionMetadata { constructor() { this.sender = ''; this.receiver = ''; this.value = BigInt(0); } } exports.TransactionMetadata = TransactionMetadata; class TransactionMetadataTransfer { constructor() { this.value = BigInt(0); } } exports.TransactionMetadataTransfer = TransactionMetadataTransfer; class TokenTransferProperties { } exports.TokenTransferProperties = TokenTransferProperties;