UNPKG

charms-js

Version:

TypeScript SDK for decoding Bitcoin transactions containing Charms data

129 lines (128 loc) 5.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.reconstructAppId = reconstructAppId; exports.createCharmInstances = createCharmInstances; const utils_1 = require("./utils"); /** * Attempts to reconstruct a canonical Charm APP ID from `app_public_inputs`. * This handles both cases: when appId is already decoded correctly, and when it needs reconstruction. * * @param appData - The app data object containing app_public_inputs. * @param appId - The original appId (could be $0000 or already decoded). * @returns The reconstructed APP ID, or the original `appId` if reconstruction fails. */ function reconstructAppId(appData, appId) { // If we have a valid canonical appId already (from proper decoding), use it if (appId && appId !== '$0000' && appId.startsWith('t/') && appId.includes('/')) { return appId; } // Check if _app_public_inputs_raw contains the canonical app ID if (appData?._app_public_inputs_raw) { const rawInput = appData._app_public_inputs_raw; // If it's already in canonical format, return it if (typeof rawInput === 'string' && rawInput.startsWith('t/') && rawInput.includes('/')) { return rawInput; } } // Fallback: try to reconstruct from raw decimal data (legacy approach) const candidates = [ appData?._app_public_inputs_raw, appData?.app_public_inputs, appData?.appPublicInputs, appData?.publicInputs, appData?.inputs ].filter(Boolean); // Helper to normalize various data structures into a string. const toStringCandidate = (src) => { if (typeof src === 'string') return src; if (Array.isArray(src)) return src.join(','); if (typeof src === 'object') { for (const [k, v] of Object.entries(src)) { if (typeof v === 'string' && v.startsWith('t,')) return v; if (typeof k === 'string' && k.startsWith('t,')) return k; } } return null; }; for (const candidate of candidates) { const inputStr = toStringCandidate(candidate); if (!inputStr || !inputStr.startsWith('t,')) continue; try { const parts = inputStr.split(','); // A valid App ID requires 't' plus 64 bytes for the two hashes. if (parts.length >= 65) { const hash1Bytes = parts.slice(1, 33).map(x => parseInt(x.trim(), 10)); const hash1 = Buffer.from(hash1Bytes).toString('hex'); const hash2Bytes = parts.slice(33, 65).map(x => parseInt(x.trim(), 10)); const hash2 = Buffer.from(hash2Bytes).toString('hex'); return `t/${hash1}/${hash2}`; } } catch (error) { // Ignore parsing errors and try the next candidate. continue; } } // Fallback to the original appId if reconstruction is not possible. return appId || '$0000'; } // Converts parsed charm data to CharmInstance array with UTXO details function createCharmInstances(charmInfo, txId) { try { if (!charmInfo || 'error' in charmInfo) { return { error: charmInfo?.error || 'Invalid charm information' }; } const result = []; charmInfo.outs.forEach((output, outputIndex) => { if (!output.charms) return; Object.keys(output.charms).forEach(appId => { const charmData = output.charms[appId]; const appData = charmInfo.apps[appId]; // Reconstruct the real appId from app_public_inputs const reconstructedAppId = reconstructAppId(appData, appId); // Clean app data by removing internal fields before exposing let cleanAppData = null; if (appData && typeof appData === 'object' && appData !== null && !Array.isArray(appData)) { cleanAppData = Object.assign({}, appData); if (cleanAppData._app_public_inputs_raw) { delete cleanAppData._app_public_inputs_raw; } } const charmInstance = { utxo: { tx: txId, index: outputIndex }, address: output.address || 'unknown', appId: reconstructedAppId, app: cleanAppData, appType: cleanAppData ? (0, utils_1.getAppType)(cleanAppData) : undefined, verified: charmInfo.verified }; // Process charm data (numeric or object) if (typeof charmData === 'number') { charmInstance.value = charmData; } else if (typeof charmData === 'object' && charmData !== null) { // Copy all fields dynamically using spread Object.assign(charmInstance, charmData); // Set default custom field if not present if (!charmInstance.custom) { charmInstance.custom = {}; } } result.push(charmInstance); }); }); return result; } catch (error) { return { error: `Failed to create charm instances: ${error.message}` }; } }