opnet
Version:
The perfect library for building Bitcoin-based applications.
317 lines (316 loc) • 12.9 kB
JavaScript
import { fromHex } from '@btc-vision/bitcoin';
import { BinaryReader, BinaryWriter } from '@btc-vision/transaction';
export var NetworkName;
(function (NetworkName) {
NetworkName["Mainnet"] = "mainnet";
NetworkName["Testnet"] = "testnet";
NetworkName["OpnetTestnet"] = "opnetTestnet";
NetworkName["Regtest"] = "regtest";
})(NetworkName || (NetworkName = {}));
const SERIALIZATION_VERSION = 1;
export class CallResultSerializer {
static FEE_PRECISION = 1000000;
static serialize(data) {
const writer = new BinaryWriter();
writer.writeU8(SERIALIZATION_VERSION);
writer.writeU8(this.networkNameToU8(data.network));
writer.writeBytesWithLength(data.calldata);
writer.writeStringWithLength(data.to);
writer.writeStringWithLength(data.contractAddress);
writer.writeU256(data.estimatedSatGas);
writer.writeU256(data.estimatedRefundedGasInSat);
writer.writeU256(data.estimatedGas ?? 0n);
writer.writeU256(data.refundedGas ?? 0n);
if (data.revert !== undefined) {
writer.writeBoolean(true);
writer.writeStringWithLength(data.revert);
}
else {
writer.writeBoolean(false);
}
writer.writeBytesWithLength(data.result);
this.writeAccessList(writer, data.accessList);
if (data.bitcoinFees) {
writer.writeBoolean(true);
this.writeBitcoinFees(writer, data.bitcoinFees);
}
else {
writer.writeBoolean(false);
}
this.writeChallenge(writer, data.challenge);
writer.writeBytesWithLength(data.challengeOriginalPublicKey);
this.writeUTXOs(writer, data.utxos);
if (data.csvAddress) {
writer.writeBoolean(true);
writer.writeStringWithLength(data.csvAddress.address);
writer.writeBytesWithLength(data.csvAddress.witnessScript);
}
else {
writer.writeBoolean(false);
}
return writer.getBuffer();
}
static deserialize(buffer) {
const reader = new BinaryReader(buffer);
const version = reader.readU8();
if (version !== SERIALIZATION_VERSION) {
throw new Error(`Unsupported serialization version: ${version}. Expected: ${SERIALIZATION_VERSION}`);
}
const network = this.u8ToNetworkName(reader.readU8());
const calldata = reader.readBytesWithLength();
const to = reader.readStringWithLength();
const contractAddress = reader.readStringWithLength();
const estimatedSatGas = reader.readU256();
const estimatedRefundedGasInSat = reader.readU256();
const estimatedGasRaw = reader.readU256();
const refundedGasRaw = reader.readU256();
const estimatedGas = estimatedGasRaw > 0n ? estimatedGasRaw : undefined;
const refundedGas = refundedGasRaw > 0n ? refundedGasRaw : undefined;
const hasRevert = reader.readBoolean();
const revert = hasRevert ? reader.readStringWithLength() : undefined;
const result = reader.readBytesWithLength();
const accessList = this.readAccessList(reader);
const hasBitcoinFees = reader.readBoolean();
const bitcoinFees = hasBitcoinFees ? this.readBitcoinFees(reader) : undefined;
const challenge = this.readChallenge(reader);
const challengeOriginalPublicKey = reader.readBytesWithLength();
const utxos = this.readUTXOs(reader);
const hasCsvAddress = reader.readBoolean();
const csvAddress = hasCsvAddress
? {
address: reader.readStringWithLength(),
witnessScript: reader.readBytesWithLength(),
}
: undefined;
return {
calldata,
to,
contractAddress,
estimatedSatGas,
estimatedRefundedGasInSat,
estimatedGas,
refundedGas,
revert,
result,
accessList,
bitcoinFees,
network,
challenge,
challengeOriginalPublicKey,
utxos,
csvAddress,
};
}
static networkNameToU8(network) {
switch (network) {
case NetworkName.Mainnet:
return 0;
case NetworkName.Testnet:
return 1;
case NetworkName.Regtest:
return 2;
case NetworkName.OpnetTestnet:
return 3;
default:
return 2;
}
}
static u8ToNetworkName(value) {
switch (value) {
case 0:
return NetworkName.Mainnet;
case 1:
return NetworkName.Testnet;
case 2:
return NetworkName.Regtest;
case 3:
return NetworkName.OpnetTestnet;
default:
return NetworkName.Regtest;
}
}
static writeAccessList(writer, accessList) {
const contracts = Object.keys(accessList);
writer.writeU16(contracts.length);
for (const contract of contracts) {
writer.writeStringWithLength(contract);
const slots = accessList[contract];
const slotKeys = Object.keys(slots);
writer.writeU16(slotKeys.length);
for (const key of slotKeys) {
writer.writeStringWithLength(key);
writer.writeStringWithLength(slots[key]);
}
}
}
static readAccessList(reader) {
const accessList = {};
const contractCount = reader.readU16();
for (let i = 0; i < contractCount; i++) {
const contract = reader.readStringWithLength();
const slotCount = reader.readU16();
accessList[contract] = {};
for (let j = 0; j < slotCount; j++) {
const key = reader.readStringWithLength();
const value = reader.readStringWithLength();
accessList[contract][key] = value;
}
}
return accessList;
}
static writeBitcoinFees(writer, fees) {
writer.writeU64(BigInt(Math.round(fees.conservative * this.FEE_PRECISION)));
writer.writeU64(BigInt(Math.round(fees.recommended.low * this.FEE_PRECISION)));
writer.writeU64(BigInt(Math.round(fees.recommended.medium * this.FEE_PRECISION)));
writer.writeU64(BigInt(Math.round(fees.recommended.high * this.FEE_PRECISION)));
}
static readBitcoinFees(reader) {
return {
conservative: Number(reader.readU64()) / this.FEE_PRECISION,
recommended: {
low: Number(reader.readU64()) / this.FEE_PRECISION,
medium: Number(reader.readU64()) / this.FEE_PRECISION,
high: Number(reader.readU64()) / this.FEE_PRECISION,
},
};
}
static writeChallenge(writer, challenge) {
writer.writeStringWithLength(challenge.epochNumber);
writer.writeStringWithLength(challenge.mldsaPublicKey);
writer.writeStringWithLength(challenge.legacyPublicKey);
writer.writeStringWithLength(challenge.solution);
writer.writeStringWithLength(challenge.salt);
writer.writeStringWithLength(challenge.graffiti);
writer.writeU256(BigInt(challenge.difficulty));
writer.writeStringWithLength(challenge.verification.epochHash);
writer.writeStringWithLength(challenge.verification.epochRoot);
writer.writeStringWithLength(challenge.verification.targetHash);
writer.writeStringWithLength(challenge.verification.targetChecksum);
writer.writeStringWithLength(challenge.verification.startBlock);
writer.writeStringWithLength(challenge.verification.endBlock);
writer.writeU16(challenge.verification.proofs.length);
for (const proof of challenge.verification.proofs) {
writer.writeStringWithLength(proof);
}
if (challenge.submission) {
writer.writeBoolean(true);
writer.writeStringWithLength(challenge.submission.mldsaPublicKey);
writer.writeStringWithLength(challenge.submission.legacyPublicKey);
writer.writeStringWithLength(challenge.submission.solution);
writer.writeStringWithLength(challenge.submission.graffiti || '');
writer.writeStringWithLength(challenge.submission.signature);
}
else {
writer.writeBoolean(false);
}
}
static readChallenge(reader) {
const epochNumber = reader.readStringWithLength();
const mldsaPublicKey = reader.readStringWithLength();
const legacyPublicKey = reader.readStringWithLength();
const solution = reader.readStringWithLength();
const salt = reader.readStringWithLength();
const graffiti = reader.readStringWithLength();
const difficulty = Number(reader.readU256());
const epochHash = reader.readStringWithLength();
const epochRoot = reader.readStringWithLength();
const targetHash = reader.readStringWithLength();
const targetChecksum = reader.readStringWithLength();
const startBlock = reader.readStringWithLength();
const endBlock = reader.readStringWithLength();
const proofCount = reader.readU16();
const proofs = [];
for (let i = 0; i < proofCount; i++) {
proofs.push(reader.readStringWithLength());
}
const hasSubmission = reader.readBoolean();
const submission = hasSubmission
? {
mldsaPublicKey: reader.readStringWithLength(),
legacyPublicKey: reader.readStringWithLength(),
solution: reader.readStringWithLength(),
graffiti: reader.readStringWithLength() || undefined,
signature: reader.readStringWithLength(),
}
: undefined;
return {
epochNumber,
mldsaPublicKey,
legacyPublicKey,
solution,
salt,
graffiti,
difficulty,
verification: {
epochHash,
epochRoot,
targetHash: targetHash,
targetChecksum,
startBlock,
endBlock,
proofs,
},
submission,
};
}
static writeUTXOs(writer, utxos) {
writer.writeU16(utxos.length);
for (const utxo of utxos) {
writer.writeStringWithLength(utxo.transactionId);
writer.writeU32(utxo.outputIndex);
writer.writeU64(utxo.value);
writer.writeStringWithLength(utxo.scriptPubKey.hex);
writer.writeStringWithLength(utxo.scriptPubKey.address || '');
writer.writeBoolean(!!utxo.isCSV);
if (utxo.witnessScript) {
writer.writeBoolean(true);
const witnessScriptBytes = typeof utxo.witnessScript === 'string'
? fromHex(utxo.witnessScript)
: utxo.witnessScript;
writer.writeBytesWithLength(witnessScriptBytes);
}
else {
writer.writeBoolean(false);
}
if (utxo.redeemScript) {
writer.writeBoolean(true);
const redeemScriptBytes = typeof utxo.redeemScript === 'string'
? fromHex(utxo.redeemScript)
: utxo.redeemScript;
writer.writeBytesWithLength(redeemScriptBytes);
}
else {
writer.writeBoolean(false);
}
}
}
static readUTXOs(reader) {
const count = reader.readU16();
const utxos = [];
for (let i = 0; i < count; i++) {
const transactionId = reader.readStringWithLength();
const outputIndex = reader.readU32();
const value = reader.readU64();
const hex = reader.readStringWithLength();
const addressStr = reader.readStringWithLength();
const isCSV = reader.readBoolean();
const hasWitnessScript = reader.readBoolean();
const witnessScript = hasWitnessScript ? reader.readBytesWithLength() : undefined;
const hasRedeemScript = reader.readBoolean();
const redeemScript = hasRedeemScript ? reader.readBytesWithLength() : undefined;
utxos.push({
transactionId,
outputIndex,
value,
scriptPubKey: {
hex,
address: addressStr || undefined,
},
isCSV,
witnessScript,
redeemScript,
});
}
return utxos;
}
}