@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
800 lines • 32.8 kB
JavaScript
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