UNPKG

@btc-vision/transaction

Version:

OPNet transaction library allows you to create and sign transactions for the OPNet network.

800 lines 32.8 kB
import { createHash } from 'crypto'; import { fromHex, toHex } from '@btc-vision/bitcoin'; function stripHexPrefix(str) { return str.startsWith('0x') ? str.slice(2) : str; } import { BinaryWriter } from '../../buffer/BinaryWriter.js'; import { BinaryReader } from '../../buffer/BinaryReader.js'; import { SERIALIZATION_FORMAT_VERSION, SERIALIZATION_MAGIC_BYTE, } from './interfaces/ISerializableState.js'; import { TransactionType } from '../enums/TransactionType.js'; /** * Serializes and deserializes transaction state for offline signing. * Uses binary format for compact size. */ export class TransactionSerializer { /** * Serialize transaction state to binary format * @param state - The transaction state to serialize * @returns Uint8Array containing serialized state with checksum */ static serialize(state) { const writer = new BinaryWriter(); // Write header this.writeHeader(writer, state.header); // Write base params this.writeBaseParams(writer, state.baseParams); // Write UTXOs this.writeUTXOArray(writer, state.utxos); this.writeUTXOArray(writer, state.optionalInputs); // Write optional outputs this.writeOutputArray(writer, state.optionalOutputs); // Write signer mappings writer.writeBoolean(state.addressRotationEnabled); this.writeSignerMappings(writer, state.signerMappings); // Write type-specific data this.writeTypeSpecificData(writer, state.typeSpecificData); // Write precomputed data this.writePrecomputedData(writer, state.precomputedData); // Get buffer and calculate checksum const dataBuffer = writer.getBuffer(); const checksum = this.calculateChecksum(dataBuffer); // Concatenate data + checksum const result = new Uint8Array(dataBuffer.length + checksum.length); result.set(dataBuffer, 0); result.set(checksum, dataBuffer.length); return result; } /** * Deserialize binary format to transaction state * @param data - Uint8Array containing serialized state * @returns Deserialized transaction state * @throws Error if checksum validation fails or format is invalid */ static deserialize(data) { // Verify checksum (last 32 bytes) if (data.length < 32) { throw new Error('Invalid serialized data: too short'); } const checksum = data.subarray(-32); const payload = data.subarray(0, -32); const expectedChecksum = this.calculateChecksum(payload); if (!this.bytesEqual(checksum, expectedChecksum)) { throw new Error('Invalid checksum - data may be corrupted'); } const reader = new BinaryReader(payload); // Read header const header = this.readHeader(reader); // Verify format version if (header.formatVersion > SERIALIZATION_FORMAT_VERSION) { throw new Error(`Unsupported format version: ${header.formatVersion}`); } // Read base params const baseParams = this.readBaseParams(reader); // Read UTXOs const utxos = this.readUTXOArray(reader); const optionalInputs = this.readUTXOArray(reader); // Read optional outputs const optionalOutputs = this.readOutputArray(reader); // Read signer mappings const addressRotationEnabled = reader.readBoolean(); const signerMappings = this.readSignerMappings(reader); // Read type-specific data const typeSpecificData = this.readTypeSpecificData(reader, header.transactionType); // Read precomputed data const precomputedData = this.readPrecomputedData(reader); return { header, baseParams, utxos, optionalInputs, optionalOutputs, addressRotationEnabled, signerMappings, typeSpecificData, precomputedData, }; } /** * Export state as base64 string (for transport) * @param state - Transaction state to export * @returns Base64-encoded string */ static toBase64(state) { const bytes = this.serialize(state); return this.uint8ArrayToBase64(bytes); } /** * Import state from base64 string * @param base64 - Base64-encoded state * @returns Deserialized transaction state */ static fromBase64(base64) { return this.deserialize(this.base64ToUint8Array(base64)); } /** * Export state as hex string * @param state - Transaction state to export * @returns Hex-encoded string */ static toHex(state) { return toHex(this.serialize(state)); } /** * Import state from hex string * @param hex - Hex-encoded state * @returns Deserialized transaction state */ static fromHex(hex) { return this.deserialize(fromHex(hex)); } static writeHeader(writer, header) { writer.writeU8(SERIALIZATION_MAGIC_BYTE); writer.writeU8(header.formatVersion); writer.writeU8(header.consensusVersion); writer.writeU8(header.transactionType); writer.writeU32(header.chainId); writer.writeU64(BigInt(header.timestamp)); } static readHeader(reader) { const magic = reader.readU8(); if (magic !== SERIALIZATION_MAGIC_BYTE) { throw new Error(`Invalid magic byte: expected 0x${SERIALIZATION_MAGIC_BYTE.toString(16)}, got 0x${magic.toString(16)}`); } return { formatVersion: reader.readU8(), consensusVersion: reader.readU8(), transactionType: reader.readU8(), chainId: reader.readU32(), timestamp: Number(reader.readU64()), }; } static writeBaseParams(writer, params) { writer.writeStringWithLength(params.from); writer.writeBoolean(params.to !== undefined); if (params.to !== undefined) { writer.writeStringWithLength(params.to); } writer.writeU32(Math.floor(params.feeRate * 1000)); // Store as milli-sat/vB for precision writer.writeU64(BigInt(params.priorityFee)); writer.writeU64(BigInt(params.gasSatFee)); writer.writeU8(this.networkNameToU8(params.networkName)); writer.writeU8(params.txVersion); writer.writeBoolean(params.note !== undefined); if (params.note !== undefined) { writer.writeBytesWithLength(fromHex(params.note)); } writer.writeBoolean(params.anchor); writer.writeBoolean(params.debugFees ?? false); } static readBaseParams(reader) { const from = reader.readStringWithLength(); const hasTo = reader.readBoolean(); const to = hasTo ? reader.readStringWithLength() : undefined; const feeRate = reader.readU32() / 1000; // Convert back from milli-sat/vB const priorityFee = reader.readU64().toString(); const gasSatFee = reader.readU64().toString(); const networkName = this.u8ToNetworkName(reader.readU8()); const txVersion = reader.readU8(); const hasNote = reader.readBoolean(); const note = hasNote ? toHex(reader.readBytesWithLength()) : undefined; const anchor = reader.readBoolean(); const debugFees = reader.readBoolean(); return { from, feeRate, priorityFee, gasSatFee, networkName, txVersion, anchor, debugFees, ...(to !== undefined ? { to } : {}), ...(note !== undefined ? { note } : {}), }; } static writeUTXOArray(writer, utxos) { writer.writeU16(utxos.length); for (const utxo of utxos) { this.writeUTXO(writer, utxo); } } static writeUTXO(writer, utxo) { // Transaction ID (32 bytes) writer.writeBytes(fromHex(utxo.transactionId)); writer.writeU32(utxo.outputIndex); writer.writeU64(BigInt(utxo.value)); writer.writeBytesWithLength(fromHex(utxo.scriptPubKeyHex)); // Optional address writer.writeBoolean(utxo.scriptPubKeyAddress !== undefined); if (utxo.scriptPubKeyAddress !== undefined) { writer.writeStringWithLength(utxo.scriptPubKeyAddress); } // Optional scripts writer.writeBoolean(utxo.redeemScript !== undefined); if (utxo.redeemScript !== undefined) { writer.writeBytesWithLength(fromHex(utxo.redeemScript)); } writer.writeBoolean(utxo.witnessScript !== undefined); if (utxo.witnessScript !== undefined) { writer.writeBytesWithLength(fromHex(utxo.witnessScript)); } writer.writeBoolean(utxo.nonWitnessUtxo !== undefined); if (utxo.nonWitnessUtxo !== undefined) { writer.writeBytesWithLength(fromHex(utxo.nonWitnessUtxo)); } } static readUTXOArray(reader) { const count = reader.readU16(); const utxos = []; for (let i = 0; i < count; i++) { utxos.push(this.readUTXO(reader)); } return utxos; } static readUTXO(reader) { const transactionId = toHex(reader.readBytes(32)); const outputIndex = reader.readU32(); const value = reader.readU64().toString(); const scriptPubKeyHex = toHex(reader.readBytesWithLength()); const hasAddress = reader.readBoolean(); const scriptPubKeyAddress = hasAddress ? reader.readStringWithLength() : undefined; const hasRedeemScript = reader.readBoolean(); const redeemScript = hasRedeemScript ? toHex(reader.readBytesWithLength()) : undefined; const hasWitnessScript = reader.readBoolean(); const witnessScript = hasWitnessScript ? toHex(reader.readBytesWithLength()) : undefined; const hasNonWitnessUtxo = reader.readBoolean(); const nonWitnessUtxo = hasNonWitnessUtxo ? toHex(reader.readBytesWithLength()) : undefined; return { transactionId, outputIndex, value, scriptPubKeyHex, ...(scriptPubKeyAddress !== undefined ? { scriptPubKeyAddress } : {}), ...(redeemScript !== undefined ? { redeemScript } : {}), ...(witnessScript !== undefined ? { witnessScript } : {}), ...(nonWitnessUtxo !== undefined ? { nonWitnessUtxo } : {}), }; } static writeOutputArray(writer, outputs) { writer.writeU16(outputs.length); for (const output of outputs) { this.writeOutput(writer, output); } } static writeOutput(writer, output) { writer.writeU64(BigInt(output.value)); writer.writeBoolean(output.address !== undefined); if (output.address !== undefined) { writer.writeStringWithLength(output.address); } writer.writeBoolean(output.script !== undefined); if (output.script !== undefined) { writer.writeBytesWithLength(fromHex(output.script)); } writer.writeBoolean(output.tapInternalKey !== undefined); if (output.tapInternalKey !== undefined) { writer.writeBytesWithLength(fromHex(output.tapInternalKey)); } } static readOutputArray(reader) { const count = reader.readU16(); const outputs = []; for (let i = 0; i < count; i++) { outputs.push(this.readOutput(reader)); } return outputs; } static readOutput(reader) { const value = Number(reader.readU64()); const hasAddress = reader.readBoolean(); const address = hasAddress ? reader.readStringWithLength() : undefined; const hasScript = reader.readBoolean(); const script = hasScript ? toHex(reader.readBytesWithLength()) : undefined; const hasTapInternalKey = reader.readBoolean(); const tapInternalKey = hasTapInternalKey ? toHex(reader.readBytesWithLength()) : undefined; return { value, ...(address !== undefined ? { address } : {}), ...(script !== undefined ? { script } : {}), ...(tapInternalKey !== undefined ? { tapInternalKey } : {}), }; } static writeSignerMappings(writer, mappings) { writer.writeU16(mappings.length); for (const mapping of mappings) { writer.writeStringWithLength(mapping.address); writer.writeU16(mapping.inputIndices.length); for (const idx of mapping.inputIndices) { writer.writeU16(idx); } } } static readSignerMappings(reader) { const count = reader.readU16(); const mappings = []; for (let i = 0; i < count; i++) { const address = reader.readStringWithLength(); const indicesCount = reader.readU16(); const inputIndices = []; for (let j = 0; j < indicesCount; j++) { inputIndices.push(reader.readU16()); } mappings.push({ address, inputIndices }); } return mappings; } static writeTypeSpecificData(writer, data) { switch (data.type) { case TransactionType.FUNDING: this.writeFundingData(writer, data); break; case TransactionType.DEPLOYMENT: this.writeDeploymentData(writer, data); break; case TransactionType.INTERACTION: this.writeInteractionData(writer, data); break; case TransactionType.MULTI_SIG: this.writeMultiSigData(writer, data); break; case TransactionType.CUSTOM_CODE: this.writeCustomScriptData(writer, data); break; case TransactionType.CANCEL: this.writeCancelData(writer, data); break; default: throw new Error(`Unsupported transaction type: ${data.type}`); } } static readTypeSpecificData(reader, type) { switch (type) { case TransactionType.FUNDING: return this.readFundingData(reader); case TransactionType.DEPLOYMENT: return this.readDeploymentData(reader); case TransactionType.INTERACTION: return this.readInteractionData(reader); case TransactionType.MULTI_SIG: return this.readMultiSigData(reader); case TransactionType.CUSTOM_CODE: return this.readCustomScriptData(reader); case TransactionType.CANCEL: return this.readCancelData(reader); default: throw new Error(`Unsupported transaction type: ${type}`); } } // Funding static writeFundingData(writer, data) { writer.writeU64(BigInt(data.amount)); writer.writeU16(data.splitInputsInto); } static readFundingData(reader) { return { type: TransactionType.FUNDING, amount: reader.readU64().toString(), splitInputsInto: reader.readU16(), }; } // Deployment static writeDeploymentData(writer, data) { writer.writeBytesWithLength(fromHex(data.bytecode)); writer.writeBoolean(data.calldata !== undefined); if (data.calldata !== undefined) { writer.writeBytesWithLength(fromHex(data.calldata)); } this.writeChallenge(writer, data.challenge); writer.writeBoolean(data.revealMLDSAPublicKey ?? false); writer.writeBoolean(data.linkMLDSAPublicKeyToAddress ?? false); writer.writeBoolean(data.hashedPublicKey !== undefined); if (data.hashedPublicKey !== undefined) { writer.writeBytesWithLength(fromHex(data.hashedPublicKey)); } } static readDeploymentData(reader) { const bytecode = toHex(reader.readBytesWithLength()); const hasCalldata = reader.readBoolean(); const calldata = hasCalldata ? toHex(reader.readBytesWithLength()) : undefined; const challenge = this.readChallenge(reader); const revealMLDSAPublicKey = reader.readBoolean(); const linkMLDSAPublicKeyToAddress = reader.readBoolean(); const hasHashedPublicKey = reader.readBoolean(); const hashedPublicKey = hasHashedPublicKey ? toHex(reader.readBytesWithLength()) : undefined; return { type: TransactionType.DEPLOYMENT, bytecode, challenge, revealMLDSAPublicKey, linkMLDSAPublicKeyToAddress, ...(calldata !== undefined ? { calldata } : {}), ...(hashedPublicKey !== undefined ? { hashedPublicKey } : {}), }; } // Interaction static writeInteractionData(writer, data) { writer.writeBytesWithLength(fromHex(data.calldata)); writer.writeBoolean(data.contract !== undefined); if (data.contract !== undefined) { writer.writeStringWithLength(data.contract); } this.writeChallenge(writer, data.challenge); writer.writeBoolean(data.loadedStorage !== undefined); if (data.loadedStorage !== undefined) { this.writeLoadedStorage(writer, data.loadedStorage); } writer.writeBoolean(data.isCancellation ?? false); writer.writeBoolean(data.disableAutoRefund ?? false); writer.writeBoolean(data.revealMLDSAPublicKey ?? false); writer.writeBoolean(data.linkMLDSAPublicKeyToAddress ?? false); writer.writeBoolean(data.hashedPublicKey !== undefined); if (data.hashedPublicKey !== undefined) { writer.writeBytesWithLength(fromHex(data.hashedPublicKey)); } } static readInteractionData(reader) { const calldata = toHex(reader.readBytesWithLength()); const hasContract = reader.readBoolean(); const contract = hasContract ? reader.readStringWithLength() : undefined; const challenge = this.readChallenge(reader); const hasLoadedStorage = reader.readBoolean(); const loadedStorage = hasLoadedStorage ? this.readLoadedStorage(reader) : undefined; const isCancellation = reader.readBoolean(); const disableAutoRefund = reader.readBoolean(); const revealMLDSAPublicKey = reader.readBoolean(); const linkMLDSAPublicKeyToAddress = reader.readBoolean(); const hasHashedPublicKey = reader.readBoolean(); const hashedPublicKey = hasHashedPublicKey ? toHex(reader.readBytesWithLength()) : undefined; return { type: TransactionType.INTERACTION, calldata, challenge, isCancellation, disableAutoRefund, revealMLDSAPublicKey, linkMLDSAPublicKeyToAddress, ...(contract !== undefined ? { contract } : {}), ...(loadedStorage !== undefined ? { loadedStorage } : {}), ...(hashedPublicKey !== undefined ? { hashedPublicKey } : {}), }; } // MultiSig static writeMultiSigData(writer, data) { writer.writeU16(data.pubkeys.length); for (const pubkey of data.pubkeys) { writer.writeBytesWithLength(fromHex(pubkey)); } writer.writeU8(data.minimumSignatures); writer.writeStringWithLength(data.receiver); writer.writeU64(BigInt(data.requestedAmount)); writer.writeStringWithLength(data.refundVault); writer.writeU16(data.originalInputCount); writer.writeBoolean(data.existingPsbtBase64 !== undefined); if (data.existingPsbtBase64 !== undefined) { writer.writeStringWithLength(data.existingPsbtBase64); } } static readMultiSigData(reader) { const pubkeysCount = reader.readU16(); const pubkeys = []; for (let i = 0; i < pubkeysCount; i++) { pubkeys.push(toHex(reader.readBytesWithLength())); } const minimumSignatures = reader.readU8(); const receiver = reader.readStringWithLength(); const requestedAmount = reader.readU64().toString(); const refundVault = reader.readStringWithLength(); const originalInputCount = reader.readU16(); const hasExistingPsbt = reader.readBoolean(); const existingPsbtBase64 = hasExistingPsbt ? reader.readStringWithLength() : undefined; return { type: TransactionType.MULTI_SIG, pubkeys, minimumSignatures, receiver, requestedAmount, refundVault, originalInputCount, ...(existingPsbtBase64 !== undefined ? { existingPsbtBase64 } : {}), }; } // Custom Script static writeCustomScriptData(writer, data) { writer.writeU16(data.scriptElements.length); for (const element of data.scriptElements) { this.writeScriptElement(writer, element); } writer.writeU16(data.witnesses.length); for (const witness of data.witnesses) { writer.writeBytesWithLength(fromHex(witness)); } writer.writeBoolean(data.annex !== undefined); if (data.annex !== undefined) { writer.writeBytesWithLength(fromHex(data.annex)); } } static writeScriptElement(writer, element) { writer.writeU8(element.elementType === 'buffer' ? 0 : 1); if (element.elementType === 'buffer') { writer.writeBytesWithLength(fromHex(element.value)); } else { writer.writeU32(element.value); } } static readCustomScriptData(reader) { const elementsCount = reader.readU16(); const scriptElements = []; for (let i = 0; i < elementsCount; i++) { scriptElements.push(this.readScriptElement(reader)); } const witnessesCount = reader.readU16(); const witnesses = []; for (let i = 0; i < witnessesCount; i++) { witnesses.push(toHex(reader.readBytesWithLength())); } const hasAnnex = reader.readBoolean(); const annex = hasAnnex ? toHex(reader.readBytesWithLength()) : undefined; return { type: TransactionType.CUSTOM_CODE, scriptElements, witnesses, ...(annex !== undefined ? { annex } : {}), }; } static readScriptElement(reader) { const typeFlag = reader.readU8(); if (typeFlag === 0) { return { elementType: 'buffer', value: toHex(reader.readBytesWithLength()), }; } else { return { elementType: 'opcode', value: reader.readU32(), }; } } // Cancel static writeCancelData(writer, data) { writer.writeBytesWithLength(fromHex(data.compiledTargetScript)); } static readCancelData(reader) { return { type: TransactionType.CANCEL, compiledTargetScript: toHex(reader.readBytesWithLength()), }; } static writeChallenge(writer, challenge) { writer.writeU64(BigInt(challenge.epochNumber)); writer.writeStringWithLength(challenge.mldsaPublicKey); writer.writeStringWithLength(challenge.legacyPublicKey); writer.writeBytesWithLength(fromHex(stripHexPrefix(challenge.solution))); writer.writeBytesWithLength(fromHex(stripHexPrefix(challenge.salt))); writer.writeBytesWithLength(fromHex(stripHexPrefix(challenge.graffiti))); writer.writeU8(challenge.difficulty); // Verification this.writeChallengeVerification(writer, challenge.verification); // Optional submission writer.writeBoolean(challenge.submission !== undefined); if (challenge.submission !== undefined) { writer.writeStringWithLength(challenge.submission.mldsaPublicKey); writer.writeStringWithLength(challenge.submission.legacyPublicKey); writer.writeBytesWithLength(fromHex(stripHexPrefix(challenge.submission.solution))); writer.writeBoolean(challenge.submission.graffiti !== undefined); if (challenge.submission.graffiti !== undefined) { writer.writeBytesWithLength(fromHex(stripHexPrefix(challenge.submission.graffiti))); } writer.writeBytesWithLength(fromHex(stripHexPrefix(challenge.submission.signature))); } } static writeChallengeVerification(writer, verification) { writer.writeBytesWithLength(fromHex(stripHexPrefix(verification.epochHash))); writer.writeBytesWithLength(fromHex(stripHexPrefix(verification.epochRoot))); writer.writeBytesWithLength(fromHex(stripHexPrefix(verification.targetHash))); writer.writeBytesWithLength(fromHex(stripHexPrefix(verification.targetChecksum))); writer.writeU64(BigInt(verification.startBlock)); writer.writeU64(BigInt(verification.endBlock)); writer.writeU16(verification.proofs.length); for (const proof of verification.proofs) { writer.writeBytesWithLength(fromHex(stripHexPrefix(proof))); } } static readChallenge(reader) { const epochNumber = reader.readU64().toString(); const mldsaPublicKey = reader.readStringWithLength(); const legacyPublicKey = reader.readStringWithLength(); const solution = '0x' + toHex(reader.readBytesWithLength()); const salt = '0x' + toHex(reader.readBytesWithLength()); const graffiti = '0x' + toHex(reader.readBytesWithLength()); const difficulty = reader.readU8(); const verification = this.readChallengeVerification(reader); const hasSubmission = reader.readBoolean(); let submission; if (hasSubmission) { const subMldsaPublicKey = reader.readStringWithLength(); const subLegacyPublicKey = reader.readStringWithLength(); const subSolution = '0x' + toHex(reader.readBytesWithLength()); const hasGraffiti = reader.readBoolean(); const subGraffiti = hasGraffiti ? '0x' + toHex(reader.readBytesWithLength()) : undefined; const subSignature = '0x' + toHex(reader.readBytesWithLength()); submission = { mldsaPublicKey: subMldsaPublicKey, legacyPublicKey: subLegacyPublicKey, solution: subSolution, signature: subSignature, ...(subGraffiti !== undefined ? { graffiti: subGraffiti } : {}), }; } return { epochNumber, mldsaPublicKey, legacyPublicKey, solution, salt, graffiti, difficulty, verification, ...(submission !== undefined ? { submission } : {}), }; } static readChallengeVerification(reader) { const epochHash = '0x' + toHex(reader.readBytesWithLength()); const epochRoot = '0x' + toHex(reader.readBytesWithLength()); const targetHash = '0x' + toHex(reader.readBytesWithLength()); const targetChecksum = '0x' + toHex(reader.readBytesWithLength()); const startBlock = reader.readU64().toString(); const endBlock = reader.readU64().toString(); const proofsCount = reader.readU16(); const proofs = []; for (let i = 0; i < proofsCount; i++) { proofs.push('0x' + toHex(reader.readBytesWithLength())); } return { epochHash, epochRoot, targetHash, targetChecksum, startBlock, endBlock, proofs, }; } static writeLoadedStorage(writer, storage) { const keys = Object.keys(storage); writer.writeU16(keys.length); for (const key of keys) { writer.writeStringWithLength(key); writer.writeStringArray(storage[key]); } } static readLoadedStorage(reader) { const count = reader.readU16(); const storage = {}; for (let i = 0; i < count; i++) { const key = reader.readStringWithLength(); storage[key] = reader.readStringArray(); } return storage; } static writePrecomputedData(writer, data) { writer.writeBoolean(data.compiledTargetScript !== undefined); if (data.compiledTargetScript !== undefined) { writer.writeBytesWithLength(fromHex(data.compiledTargetScript)); } writer.writeBoolean(data.randomBytes !== undefined); if (data.randomBytes !== undefined) { writer.writeBytesWithLength(fromHex(data.randomBytes)); } writer.writeBoolean(data.estimatedFees !== undefined); if (data.estimatedFees !== undefined) { writer.writeU64(BigInt(data.estimatedFees)); } writer.writeBoolean(data.contractSeed !== undefined); if (data.contractSeed !== undefined) { writer.writeStringWithLength(data.contractSeed); } writer.writeBoolean(data.contractAddress !== undefined); if (data.contractAddress !== undefined) { writer.writeStringWithLength(data.contractAddress); } } static readPrecomputedData(reader) { const hasCompiledTargetScript = reader.readBoolean(); const compiledTargetScript = hasCompiledTargetScript ? toHex(reader.readBytesWithLength()) : undefined; const hasRandomBytes = reader.readBoolean(); const randomBytes = hasRandomBytes ? toHex(reader.readBytesWithLength()) : undefined; const hasEstimatedFees = reader.readBoolean(); const estimatedFees = hasEstimatedFees ? reader.readU64().toString() : undefined; const hasContractSeed = reader.readBoolean(); const contractSeed = hasContractSeed ? reader.readStringWithLength() : undefined; const hasContractAddress = reader.readBoolean(); const contractAddress = hasContractAddress ? reader.readStringWithLength() : undefined; return { ...(compiledTargetScript !== undefined ? { compiledTargetScript } : {}), ...(randomBytes !== undefined ? { randomBytes } : {}), ...(estimatedFees !== undefined ? { estimatedFees } : {}), ...(contractSeed !== undefined ? { contractSeed } : {}), ...(contractAddress !== undefined ? { contractAddress } : {}), }; } /** * Calculate double SHA256 checksum (Bitcoin standard) */ static calculateChecksum(data) { const hash1 = createHash('sha256').update(data).digest(); return new Uint8Array(createHash('sha256').update(hash1).digest()); } /** * Compare two Uint8Arrays for equality */ static bytesEqual(a, b) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) return false; } return true; } /** * Encode Uint8Array to base64 string */ static uint8ArrayToBase64(bytes) { let binary = ''; for (let i = 0; i < bytes.length; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } /** * Decode base64 string to Uint8Array */ static base64ToUint8Array(base64) { const binary = atob(base64); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes; } static networkNameToU8(name) { switch (name) { case 'mainnet': return 0; case 'testnet': return 1; case 'regtest': return 2; case 'opnetTestnet': return 3; default: throw new Error(`Unknown network: ${name}`); } } static u8ToNetworkName(value) { switch (value) { case 0: return 'mainnet'; case 1: return 'testnet'; case 2: return 'regtest'; case 3: return 'opnetTestnet'; default: throw new Error(`Unknown network value: ${value}`); } } } //# sourceMappingURL=TransactionSerializer.js.map