opnet
Version:
The perfect library for building Bitcoin-based applications.
1,361 lines • 302 kB
JavaScript
import { a as __toESM, r as __exportAll } from "./rolldown-runtime.js";
import { A as bitcoin, C as AddressVerificator, D as fromHex, E as toBase64, M as regtest, N as testnet, O as toHex, S as AddressTypes, T as fromBase64, _ as BinaryWriter, a as ABICoder, b as AddressMap, c as isAbiTuple, d as process$1, f as P2MR_MS, g as Logger, h as TransactionFactory, i as NetEvent, j as opnetTestnet, k as fromBech32, l as ABIDataTypes, m as ChallengeSolution, n as Long, o as abiTypeToSelectorString, p as P2TR_MS, r as pLimit, s as isAbiStruct, t as BigNumber, u as init_dist, v as BinaryReader, w as decompile, x as Address, y as BufferHelper } from "./vendors.js";
import { t as require_protobuf_min } from "./protobuf.js";
//#region src/_version.ts
var version = "1.8.13";
//#endregion
//#region src/interfaces/opnet/OPNetTransactionTypes.ts
var OPNetTransactionTypes = /* @__PURE__ */ function(OPNetTransactionTypes) {
OPNetTransactionTypes["Generic"] = "Generic";
OPNetTransactionTypes["Deployment"] = "Deployment";
OPNetTransactionTypes["Interaction"] = "Interaction";
return OPNetTransactionTypes;
}({});
//#endregion
//#region src/transactions/metadata/TransactionInput.ts
/**
* Transaction input
* @category ITransactions
*/
var TransactionInput = class {
originalTransactionId;
outputTransactionIndex;
scriptSignature;
sequenceId;
transactionInWitness = [];
constructor(data) {
this.originalTransactionId = data.originalTransactionId;
this.outputTransactionIndex = data.outputTransactionIndex;
this.scriptSignature = data.scriptSignature;
this.sequenceId = data.sequenceId;
this.transactionInWitness = data.transactionInWitness || [];
}
};
//#endregion
//#region src/transactions/metadata/TransactionOutput.ts
/**
* Transaction output
* @category Transactions
*/
var TransactionOutput = class {
value;
index;
scriptPubKey;
script;
constructor(data) {
this.value = this.convertValue(data.value);
this.index = data.index;
this.scriptPubKey = data.scriptPubKey;
this.script = decompile(fromHex(this.scriptPubKey.hex));
}
convertValue(value) {
return BigInt(value);
}
};
//#endregion
//#region src/cache/LRUCaching.ts
var LRUCache = class {
cache;
maxSize;
constructor(maxSize) {
this.cache = /* @__PURE__ */ new Map();
this.maxSize = maxSize;
}
get(key) {
const value = this.cache.get(key);
if (value !== void 0) {
this.cache.delete(key);
this.cache.set(key, value);
}
return value;
}
set(key, value) {
if (this.cache.has(key)) this.cache.delete(key);
else if (this.cache.size >= this.maxSize) {
const firstKey = this.cache.keys().next().value;
if (firstKey !== void 0) this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
};
//#endregion
//#region src/cache/P2OPCache.ts
var P2OP_CACHE_MAX_SIZE = 5e3;
var p2opCache = new LRUCache(P2OP_CACHE_MAX_SIZE);
var addressCache = new LRUCache(P2OP_CACHE_MAX_SIZE);
var getP2op = (rawAddress, network) => {
const cacheKey = `${network.bip32}:${network.pubKeyHash}:${network.bech32}:${rawAddress}`;
let cached = p2opCache.get(cacheKey);
if (cached === void 0) {
let addr = addressCache.get(rawAddress);
if (addr === void 0) {
addr = Address.fromString(rawAddress);
addressCache.set(rawAddress, addr);
}
cached = addr.p2op(network);
p2opCache.set(cacheKey, cached);
}
return cached;
};
//#endregion
//#region src/utils/RevertDecoder.ts
/**
* Utility functions for decoding revert data.
*/
var ERROR_SELECTOR_BYTES = Uint8Array.from([
99,
115,
157,
92
]);
function areBytesEqual(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;
}
function startsWithErrorSelector(revertDataBytes) {
return revertDataBytes.length >= 4 && areBytesEqual(revertDataBytes.subarray(0, 4), ERROR_SELECTOR_BYTES);
}
function bytesToHexString(byteArray) {
return Array.from(byteArray, function(byte) {
return ("0" + (byte & 255).toString(16)).slice(-2);
}).join("");
}
/**
* Decode revert data into a human-readable string.
* @param revertDataBytes - The raw revert data bytes.
* @returns The decoded revert message.
*/
function decodeRevertData(revertDataBytes) {
if (startsWithErrorSelector(revertDataBytes)) {
const decoder = new TextDecoder();
const buf = revertDataBytes.subarray(8);
return decoder.decode(buf);
} else return `Unknown Revert: 0x${bytesToHexString(revertDataBytes)}`;
}
//#endregion
//#region src/transactions/metadata/TransactionReceipt.ts
/**
* Transaction receipt
* @category Transactions
*/
var TransactionReceipt = class {
/**
* @description The receipt of the transaction.
*/
receipt;
/**
* @description The receipt proofs of the transaction.
*/
receiptProofs;
/**
* @description The events of the transaction.
*/
events;
rawEvents = {};
/**
* @description If the transaction was reverted, this field will contain the revert message.
*/
rawRevert;
revert;
/**
* @description Whether the transaction failed (reverted) or not.
*/
failed = false;
gasUsed;
specialGasUsed;
constructor(receipt, network) {
this.receipt = receipt.receipt ? fromBase64(receipt.receipt) : void 0;
this.receiptProofs = receipt.receiptProofs || [];
this.events = receipt.events ? this.parseEvents(receipt.events, network) : {};
this.rawRevert = receipt.revert ? fromBase64(receipt.revert) : void 0;
this.revert = this.rawRevert ? decodeRevertData(this.rawRevert) : void 0;
this.failed = receipt.revert !== void 0 && receipt.revert !== null;
this.gasUsed = BigInt(receipt.gasUsed || "0x00") || 0n;
this.specialGasUsed = BigInt(receipt.specialGasUsed || "0x00") || 0n;
}
/**
* @description Parse transaction events.
* @param events - The events to parse.
* @param network - The network to use.
* @private
*/
parseEvents(events, network) {
const parsedEvents = {};
if (!Array.isArray(events)) for (const [key, value] of Object.entries(events)) {
const caP2op = getP2op(key, network);
const v = value.map((event) => {
return this.decodeEvent(event);
});
parsedEvents[caP2op] = v;
this.rawEvents[key] = v;
}
else for (const event of events) {
const parsedEvent = this.decodeEvent(event);
const contractAddress = event.contractAddress;
const caP2op = getP2op(contractAddress, network);
if (!parsedEvents[caP2op]) parsedEvents[caP2op] = [];
parsedEvents[caP2op].push(parsedEvent);
if (!this.rawEvents[contractAddress]) this.rawEvents[contractAddress] = [];
this.rawEvents[contractAddress].push(parsedEvent);
}
return parsedEvents;
}
decodeEvent(event) {
let eventData;
if (typeof event.data === "string") eventData = fromBase64(event.data);
else eventData = event.data;
return new NetEvent(event.type, eventData);
}
};
//#endregion
//#region src/transactions/Transaction.ts
/**
* @description This class is used to provide a base transaction.
* @class Transaction
* @implements {ITransactionBase<T>}
* @template T
* @category Transactions
* @abstract
*/
var TransactionBase = class extends TransactionReceipt {
/**
* @description The transaction ID (hash).
*/
id;
/**
* @description The transaction "hash".
*/
hash;
/**
* @description The index of the transaction in the block.
*/
index;
/**
* @description Returns the amount of satoshi that were burned in the transaction.
*/
burnedBitcoin;
/**
* @description The priority fee of the transaction.
*/
priorityFee;
/**
* @description The maximum amount of gas that can be spent by the transaction.
*/
maxGasSat;
/**
* @description The inputs of the transaction.
*/
inputs;
/**
* @description The outputs of the transaction.
*/
outputs;
/**
* @description The type of the transaction.
*/
OPNetType;
/**
* @description The amount of gas used by the transaction.
*/
gasUsed;
/**
* @description Special gas used by the transaction.
*/
specialGasUsed;
/**
* @description The proof of work challenge.
*/
pow;
/**
* @description The block number in which the transaction was included.
*/
blockNumber;
constructor(transaction, network) {
super({
receipt: transaction.receipt,
receiptProofs: transaction.receiptProofs,
events: transaction.events,
revert: transaction.revert,
gasUsed: transaction.gasUsed,
specialGasUsed: transaction.specialGasUsed
}, network);
this.id = transaction.id;
this.hash = transaction.hash;
this.index = transaction.index;
if (transaction.blockNumber) this.blockNumber = BigInt(transaction.blockNumber);
this.burnedBitcoin = BigInt(transaction.burnedBitcoin) || 0n;
this.priorityFee = BigInt(transaction.priorityFee) || 0n;
this.inputs = transaction.inputs.map((input) => new TransactionInput(input));
this.outputs = transaction.outputs.map((output) => new TransactionOutput(output));
this.OPNetType = transaction.OPNetType;
this.gasUsed = BigInt(transaction.gasUsed || "0x00") || 0n;
this.specialGasUsed = BigInt(transaction.specialGasUsed || "0x00") || 0n;
if (transaction.pow) this.pow = this.decodeProofOfWorkChallenge(transaction.pow);
this.maxGasSat = this.burnedBitcoin + (this.pow?.reward || 0n) - this.priorityFee;
}
decodeProofOfWorkChallenge(challenge) {
return {
preimage: fromBase64(challenge.preimage),
reward: BigInt(challenge.reward) || 0n,
difficulty: BigInt(challenge.difficulty || "0"),
version: challenge.version || 0
};
}
};
//#endregion
//#region src/transactions/decoders/DeploymentTransaction.ts
/**
* @desc This class is used to provide a deployment transaction. Properties could be null if reverted.
* @class DeploymentTransaction
* @category Transactions
*/
var DeploymentTransaction = class extends TransactionBase {
contractAddress;
contractPublicKey;
bytecode;
wasCompressed;
deployerPubKey;
deployerHashedPublicKey;
deployerAddress;
contractSeed;
contractSaltHash;
from;
constructor(transaction, network) {
super(transaction, network);
if (!transaction.deployerAddress && (transaction.revert === null || transaction.revert === void 0)) throw new Error("Deployer address is missing");
try {
this.from = new Address(fromBase64(transaction.from), fromBase64(transaction.fromLegacy));
this.contractAddress = transaction.contractAddress;
this.contractPublicKey = new Address(fromBase64(transaction.contractPublicKey));
this.bytecode = fromBase64(transaction.bytecode);
this.wasCompressed = transaction.wasCompressed;
if (transaction.deployerPubKey) this.deployerPubKey = fromBase64(transaction.deployerPubKey);
if (transaction.deployerAddress) {
const deployerAddr = transaction.deployerAddress;
this.deployerHashedPublicKey = fromHex(deployerAddr.startsWith("0x") ? deployerAddr.slice(2) : deployerAddr);
}
if (this.deployerHashedPublicKey && this.deployerPubKey) this.deployerAddress = new Address(this.deployerHashedPublicKey, this.deployerPubKey);
this.contractSeed = fromBase64(transaction.contractSeed);
this.contractSaltHash = fromBase64(transaction.contractSaltHash);
} catch {}
}
};
//#endregion
//#region src/transactions/decoders/GenericTransaction.ts
/**
* @description This class is used to create a generic transaction.
* @class GenericTransaction
* @extends {TransactionBase<OPNetTransactionTypes.Generic>}
* @implements {IGenericTransaction}
* @category Transactions
*/
var GenericTransaction = class extends TransactionBase {
constructor(transaction, network) {
super(transaction, network);
}
};
//#endregion
//#region src/transactions/decoders/InteractionTransaction.ts
/**
* Interaction transaction. Properties could be null if reverted.
* @category Transactions
*/
var InteractionTransaction = class extends TransactionBase {
/**
* @description The calldata of the transaction.
*/
calldata;
/**
* @description The sender's public key hash.
*/
senderPubKeyHash;
/**
* @description The contract secret.
*/
contractSecret;
/**
* @description The interaction public key.
*/
interactionPubKey;
/**
* @description Whether the transaction data was compressed.
*/
wasCompressed;
/**
* @description The from address of the transaction. (ALWAYS TAPROOT. *This address is generated from the P2TR of the pubkey of the deployer.*)
*/
from;
/**
* @description The contract address where the transaction was sent. (AKA "to").
*/
contractAddress;
/**
* @description The contract tweaked public key.
*/
contractPublicKey;
constructor(transaction, network) {
super(transaction, network);
this.contractPublicKey = new Address(fromBase64(transaction.contractPublicKey));
if (transaction.calldata) this.calldata = fromBase64(transaction.calldata);
this.senderPubKeyHash = fromBase64(transaction.senderPubKeyHash);
this.contractSecret = fromBase64(transaction.contractSecret);
this.interactionPubKey = fromBase64(transaction.interactionPubKey);
this.wasCompressed = transaction.wasCompressed || false;
this.contractAddress = transaction.contractAddress;
try {
if (transaction.from) this.from = new Address(fromBase64(transaction.from), fromBase64(transaction.fromLegacy));
} catch {}
}
};
//#endregion
//#region src/serialize/BigInt.ts
BigInt.prototype.toJSON = function() {
return this.toString();
};
//#endregion
//#region src/transactions/TransactionParser.ts
/**
* Transaction parser
* @category Transactions
*/
var TransactionParser = class {
static parseTransactions(transactions, network) {
if (!transactions) return [];
const transactionArray = [];
for (const transaction of transactions) {
if (!transaction) throw new Error(`Something went wrong while parsing transactions`);
transactionArray.push(this.parseTransaction(transaction, network));
}
return transactionArray;
}
static parseTransaction(transaction, network) {
if (!transaction) throw new Error("Transaction is required");
switch (transaction.OPNetType) {
case OPNetTransactionTypes.Generic: return new GenericTransaction(transaction, network);
case OPNetTransactionTypes.Interaction: return new InteractionTransaction(transaction, network);
case OPNetTransactionTypes.Deployment: return new DeploymentTransaction(transaction, network);
default: throw new Error("Unknown transaction type");
}
}
};
//#endregion
//#region src/block/Block.ts
/**
* @description This class is used to represent a block.
* @class Block
* @category Block
*/
var Block = class {
height;
hash;
previousBlockHash;
previousBlockChecksum;
bits;
nonce;
version;
size;
txCount;
weight;
strippedSize;
time;
medianTime;
checksumRoot;
merkleRoot;
storageRoot;
receiptRoot;
ema;
baseGas;
gasUsed;
checksumProofs;
_rawBlock;
_network;
constructor(block, network) {
if (!block) throw new Error("Invalid block.");
this._rawBlock = block;
this._network = network;
this.height = BigInt(block.height.toString());
this.hash = block.hash;
this.previousBlockHash = block.previousBlockHash;
this.previousBlockChecksum = block.previousBlockChecksum;
this.bits = block.bits;
this.nonce = block.nonce;
this.version = block.version;
this.size = block.size;
this.txCount = block.txCount;
this.ema = BigInt(block.ema);
this.baseGas = BigInt(block.baseGas);
this.gasUsed = BigInt(block.gasUsed);
this.weight = block.weight;
this.strippedSize = block.strippedSize;
this.time = block.time;
this.medianTime = block.medianTime;
this.checksumRoot = block.checksumRoot;
this.merkleRoot = block.merkleRoot;
this.storageRoot = block.storageRoot;
this.receiptRoot = block.receiptRoot;
this.checksumProofs = block.checksumProofs;
}
_transactions;
get transactions() {
if (!this._transactions) this._transactions = TransactionParser.parseTransactions(this._rawBlock.transactions, this._network);
return this._transactions;
}
_deployments;
get deployments() {
if (!this._deployments) this._deployments = this._rawBlock.deployments ? this._rawBlock.deployments.map((address) => Address.fromString(address)) : [];
return this._deployments;
}
get rawTransactions() {
return this._rawBlock.transactions;
}
};
//#endregion
//#region src/block/BlockGasParameters.ts
var BlockGasParameters = class {
blockNumber;
gasUsed;
targetGasLimit;
ema;
baseGas;
gasPerSat;
bitcoin;
constructor(data) {
this.blockNumber = BigInt(data.blockNumber);
this.gasUsed = BigInt(data.gasUsed);
this.targetGasLimit = BigInt(data.targetGasLimit);
this.ema = BigInt(data.ema);
this.baseGas = BigInt(data.baseGas);
this.gasPerSat = BigInt(data.gasPerSat);
this.bitcoin = {
conservative: Number(data.bitcoin.conservative),
recommended: {
low: Number(data.bitcoin.recommended.low),
medium: Number(data.bitcoin.recommended.medium),
high: Number(data.bitcoin.recommended.high)
}
};
}
};
//#endregion
//#region src/utils/StringToBuffer.ts
function stringToBuffer(str) {
return fromHex(str.startsWith("0x") ? str.slice(2) : str);
}
function stringBase64ToBuffer(str) {
return fromBase64(str);
}
//#endregion
//#region src/block/BlockWitness.ts
var BlockWitnessAPI = class {
signature;
timestamp;
proofs;
identity;
publicKey;
constructor(data) {
this.signature = stringBase64ToBuffer(data.signature);
this.timestamp = data.timestamp;
this.proofs = Object.freeze(data.proofs.map((proof) => stringBase64ToBuffer(proof)));
this.identity = data.identity ? stringBase64ToBuffer(data.identity) : void 0;
this.publicKey = data.publicKey ? Address.fromString(data.publicKey) : void 0;
}
};
var BlockWitness = class {
blockNumber;
witnesses;
constructor(data) {
this.blockNumber = typeof data.blockNumber === "string" ? BigInt(data.blockNumber) : data.blockNumber;
this.witnesses = Object.freeze(data.witnesses.map((witness) => new BlockWitnessAPI(witness)));
}
};
function parseBlockWitnesses(rawWitnesses) {
return Object.freeze(rawWitnesses.map((rawWitness) => new BlockWitness(rawWitness)));
}
//#endregion
//#region src/contracts/CallResultSerializer.ts
/**
* Network name enum for serialization.
* @category Contracts
*/
var NetworkName = /* @__PURE__ */ function(NetworkName) {
NetworkName["Mainnet"] = "mainnet";
NetworkName["Testnet"] = "testnet";
NetworkName["OpnetTestnet"] = "opnetTestnet";
NetworkName["Regtest"] = "regtest";
return NetworkName;
}({});
/**
* Version of the serialization format.
* Increment when making breaking changes.
*/
var SERIALIZATION_VERSION = 1;
/**
* Serializer/Deserializer for CallResult offline signing data.
* Uses binary format for efficient transfer (QR codes, files, etc.)
* @category Contracts
*/
var CallResultSerializer = class {
static FEE_PRECISION = 1e6;
/**
* Serializes offline data to a Uint8Array.
* @param {OfflineCallResultData} data - The data to serialize.
* @returns {Uint8Array} The serialized binary data.
*/
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 !== void 0) {
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();
}
/**
* Deserializes a Uint8Array to offline data.
* @param {Uint8Array} buffer - The serialized data.
* @returns {OfflineCallResultData} The deserialized data.
*/
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();
return {
calldata,
to,
contractAddress,
estimatedSatGas,
estimatedRefundedGasInSat,
estimatedGas: estimatedGasRaw > 0n ? estimatedGasRaw : void 0,
refundedGas: refundedGasRaw > 0n ? refundedGasRaw : void 0,
revert: reader.readBoolean() ? reader.readStringWithLength() : void 0,
result: reader.readBytesWithLength(),
accessList: this.readAccessList(reader),
bitcoinFees: reader.readBoolean() ? this.readBitcoinFees(reader) : void 0,
network,
challenge: this.readChallenge(reader),
challengeOriginalPublicKey: reader.readBytesWithLength(),
utxos: this.readUTXOs(reader),
csvAddress: reader.readBoolean() ? {
address: reader.readStringWithLength(),
witnessScript: reader.readBytesWithLength()
} : void 0
};
}
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 submission = reader.readBoolean() ? {
mldsaPublicKey: reader.readStringWithLength(),
legacyPublicKey: reader.readStringWithLength(),
solution: reader.readStringWithLength(),
graffiti: reader.readStringWithLength() || void 0,
signature: reader.readStringWithLength()
} : void 0;
return {
epochNumber,
mldsaPublicKey,
legacyPublicKey,
solution,
salt,
graffiti,
difficulty,
verification: {
epochHash,
epochRoot,
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 witnessScript = reader.readBoolean() ? reader.readBytesWithLength() : void 0;
const redeemScript = reader.readBoolean() ? reader.readBytesWithLength() : void 0;
utxos.push({
transactionId,
outputIndex,
value,
scriptPubKey: {
hex,
address: addressStr || void 0
},
isCSV,
witnessScript,
redeemScript
});
}
return utxos;
}
};
//#endregion
//#region src/contracts/TransactionHelpper.ts
var TransactionHelper = class {
static estimateMiningCost(utxos, extraOutputs, opReturnLen, network, feeRate) {
const vBytes = this.estimateVBytes(utxos, extraOutputs, opReturnLen, network);
return BigInt(Math.ceil(vBytes * feeRate));
}
static varIntLen(n) {
return n < 253 ? 1 : n <= 65535 ? 3 : n <= 4294967295 ? 5 : 9;
}
static estimateVBytes(utxos, extraOutputs, scriptLength, network) {
const INPUT_WU = {
[AddressTypes.P2PKH]: 592,
[AddressTypes.P2SH_OR_P2SH_P2WPKH]: 471,
[AddressTypes.P2WPKH]: 271,
[AddressTypes.P2TR]: 229,
[AddressTypes.P2MR]: 197,
[AddressTypes.P2PK]: 592,
[AddressTypes.P2WSH]: 272,
[AddressTypes.P2OP]: 271,
[AddressTypes.P2WDA]: 417
};
const OUTPUT_BYTES = {
[AddressTypes.P2PKH]: 34,
[AddressTypes.P2SH_OR_P2SH_P2WPKH]: 32,
[AddressTypes.P2WPKH]: 31,
[AddressTypes.P2TR]: 43,
[AddressTypes.P2MR]: 43,
[AddressTypes.P2PK]: 34,
[AddressTypes.P2OP]: 32,
[AddressTypes.P2WSH]: 43,
[AddressTypes.P2WDA]: 43
};
const ins = utxos.length ? utxos : new Array(3).fill(null);
let weight = 0;
weight += 32;
if (utxos.length === 0 || utxos.some((u) => {
const t = AddressVerificator.detectAddressType(u?.scriptPubKey?.address ?? "", network);
return t === AddressTypes.P2WPKH || t === AddressTypes.P2SH_OR_P2SH_P2WPKH || t === AddressTypes.P2TR || t === AddressTypes.P2MR || t === AddressTypes.P2OP || t === AddressTypes.P2WSH;
})) weight += 8;
weight += this.varIntLen(ins.length) * 4;
weight += this.varIntLen(extraOutputs.length) * 4;
for (const u of ins) {
const t = utxos.length === 0 ? AddressTypes.P2TR : AddressVerificator.detectAddressType(u?.scriptPubKey?.address ?? "", network) ?? AddressTypes.P2PKH;
weight += INPUT_WU[t] ?? 440;
}
for (const o of extraOutputs) if ("address" in o) {
const t = AddressVerificator.detectAddressType(o.address, network) ?? AddressTypes.P2PKH;
weight += (OUTPUT_BYTES[t] ?? 40) * 4;
} else if ("script" in o) {
const scriptLen = o.script.length;
const bytes = 8 + this.varIntLen(scriptLen) + scriptLen;
weight += bytes * 4;
} else weight += 136;
const witnessBytes = 1 + 3 * (this.varIntLen(32) + 32);
weight += witnessBytes;
const stackItemScript = this.varIntLen(scriptLength) + scriptLength;
weight += stackItemScript + 34;
return Math.ceil(weight / 4);
}
};
//#endregion
//#region src/contracts/CallResult.ts
var factory = new TransactionFactory();
function extractPackageFailures(packageResult) {
const failures = [];
const results = packageResult.txResults;
for (const [submittedTxid, result] of Object.entries(results)) if (result.error) failures.push(`tx ${submittedTxid} failed: ${result.error}`);
if (failures.length === 0 && packageResult.packageMsg !== "success") failures.push(`package rejected: ${packageResult.packageMsg}`);
return failures;
}
/**
* Represents the result of a contract call.
* @category Contracts
*/
var CallResult = class CallResult {
result;
accessList;
revert;
constant = false;
payable = false;
calldata;
loadedStorage;
estimatedGas;
refundedGas;
properties = {};
estimatedSatGas = 0n;
estimatedRefundedGasInSat = 0n;
events = [];
to;
address;
fromAddress;
csvAddress;
#bitcoinFees;
#rawEvents;
#provider;
#resultBase64;
constructor(callResult, provider) {
this.#provider = provider;
this.#rawEvents = this.parseEvents(callResult.events);
this.accessList = callResult.accessList;
this.loadedStorage = callResult.loadedStorage;
if (callResult.estimatedGas) this.estimatedGas = BigInt(callResult.estimatedGas);
if (callResult.specialGas) this.refundedGas = BigInt(callResult.specialGas);
const revert = typeof callResult.revert === "string" ? this.base64ToUint8Array(callResult.revert) : callResult.revert;
if (revert) this.revert = CallResult.decodeRevertData(revert);
if (typeof callResult.result === "string") {
this.#resultBase64 = callResult.result;
this.result = new BinaryReader(this.base64ToUint8Array(callResult.result));
} else if (callResult.result instanceof Uint8Array) {
this.#resultBase64 = "";
this.result = new BinaryReader(callResult.result);
} else {
this.#resultBase64 = "";
this.result = callResult.result;
}
}
get rawEvents() {
return this.#rawEvents;
}
static decodeRevertData(revertDataBytes) {
return decodeRevertData(revertDataBytes);
}
/**
* Reconstructs a CallResult from offline serialized buffer.
* Use this on a device to sign transactions offline.
* @param {Uint8Array | string} input - The serialized offline data as Uint8Array or hex string.
* @returns {CallResult} A CallResult instance ready for offline signing.
*
* @example
* ```typescript
* // Offline device: reconstruct from buffer
* const buffer = fs.readFileSync('offline-tx.bin');
* const simulation = CallResult.fromOfflineBuffer(buffer);
*
* // Now sign offline
* const signedTx = await simulation.signTransaction({
* signer: wallet.keypair,
* // ... other params
* });
* ```
*/
static fromOfflineBuffer(input) {
const buffer = typeof input === "string" ? fromHex(input) : input;
const data = CallResultSerializer.deserialize(buffer);
const network = CallResult.resolveNetwork(data.network);
const challengeSolution = new ChallengeSolution({
...data.challenge,
legacyPublicKey: "0x" + toHex(data.challengeOriginalPublicKey)
});
const offlineProvider = {
network,
utxoManager: {
getUTXOsForAmount: () => Promise.resolve(data.utxos),
spentUTXO: () => {},
clean: () => {}
},
getChallenge: () => Promise.resolve(challengeSolution),
sendRawTransaction: () => {
return Promise.reject(/* @__PURE__ */ new Error("Cannot broadcast from offline CallResult. Export signed transaction and broadcast online."));
},
sendRawTransactionPackage: () => {
return Promise.reject(/* @__PURE__ */ new Error("Cannot broadcast from offline CallResult. Export signed transaction and broadcast online."));
},
getCSV1ForAddress: () => {
if (!data.csvAddress) throw new Error("CSV address not available in offline data");
return data.csvAddress;
}
};
const callResult = new CallResult({
result: data.result,
accessList: data.accessList,
events: {},
revert: void 0,
estimatedGas: data.estimatedGas?.toString(),
specialGas: data.refundedGas?.toString()
}, offlineProvider);
callResult.revert = data.revert;
callResult.calldata = data.calldata;
callResult.to = data.to;
callResult.address = Address.fromString(data.contractAddress);
callResult.estimatedSatGas = data.estimatedSatGas;
callResult.estimatedRefundedGasInSat = data.estimatedRefundedGasInSat;
callResult.csvAddress = data.csvAddress;
if (data.bitcoinFees) callResult.setBitcoinFee(data.bitcoinFees);
return callResult;
}
/**
* Resolves a NetworkName enum to a Network object.
*/
static resolveNetwork(networkName) {
switch (networkName) {
case NetworkName.Mainnet: return bitcoin;
case NetworkName.Testnet: return testnet;
case NetworkName.OpnetTestnet: return opnetTestnet;
case NetworkName.Regtest: return regtest;
default: return regtest;
}
}
setTo(to, address) {
this.to = to;
this.address = address;
}
setFromAddress(from) {
this.fromAddress = from;
this.csvAddress = this.fromAddress && this.fromAddress.originalPublicKey ? this.#provider.getCSV1ForAddress(this.fromAddress) : void 0;
}
/**
* Signs a bitcoin interaction transaction from a simulated contract call without broadcasting.
* @param {TransactionParameters} interactionParams - The parameters for the transaction.
* @param {bigint} amountAddition - Additional satoshis to request when acquiring UTXOs.
* @returns {Promise<SignedInteractionTransactionReceipt>} The signed transaction data and UTXO tracking info.
*/
async signTransaction(interactionParams, amountAddition = 0n) {
if (!this.address) throw new Error("Contract address not set");
if (!this.calldata) throw new Error("Calldata not set");
if (!this.to) throw new Error("To address not set");
if (this.revert) throw new Error(`Can not send transaction! Simulation reverted: ${this.revert}`);
if (this.constant) throw new Error("Cannot send a transaction on a constant (view) function. Use the returned CallResult directly.");
if (this.payable) {
const hasExtraInputs = interactionParams.extraInputs && interactionParams.extraInputs.length > 0;
const hasExtraOutputs = interactionParams.extraOutputs && interactionParams.extraOutputs.length > 0;
if (!hasExtraInputs && !hasExtraOutputs) throw new Error("Payable function requires extraInputs or extraOutputs in the transaction parameters.");
}
let UTXOs = interactionParams.utxos || await this.acquire(interactionParams, amountAddition);
if (interactionParams.extraInputs) UTXOs = UTXOs.filter((utxo) => {
return interactionParams.extraInputs?.find((input) => {
return input.outputIndex === utxo.outputIndex && input.transactionId === utxo.transactionId;
}) === void 0;
});
if (!UTXOs || UTXOs.length === 0) throw new Error("No UTXOs found");
const priorityFee = interactionParams.priorityFee || 0n;
const challenge = interactionParams.challenge || await this.#provider.getChallenge();
const sharedParams = {
contract: this.address.toHex(),
calldata: this.calldata,
priorityFee,
gasSatFee: this.bigintMax(this.estimatedSatGas, interactionParams.minGas || 0n),
feeRate: interactionParams.feeRate || this.#bitcoinFees?.conservative || 10,
from: interactionParams.refundTo,
utxos: UTXOs,
to: this.to,
network: interactionParams.network,
optionalInputs: interactionParams.extraInputs || [],
optionalOutputs: interactionParams.extraOutputs || [],
note: interactionParams.note,
anchor: interactionParams.anchor || false,
txVersion: interactionParams.txVersion || 2,
linkMLDSAPublicKeyToAddress: interactionParams.linkMLDSAPublicKeyToAddress ?? true,
revealMLDSAPublicKey: interactionParams.revealMLDSAPublicKey ?? false,
subtractExtraUTXOFromAmountRequired: interactionParams.subtractExtraUTXOFromAmountRequired ?? false
};
const params = interactionParams.signer !== null ? {
...sharedParams,
signer: interactionParams.signer,
challenge,
mldsaSigner: interactionParams.mldsaSigner
} : sharedParams;
const transaction = await factory.signInteraction(params);
const csvUTXOs = UTXOs.filter((u) => u.isCSV === true);
const p2wdaUTXOs = UTXOs.filter((u) => u.witnessScript && u.isCSV !== true);
const regularUTXOs = UTXOs.filter((u) => !u.witnessScript && u.isCSV !== true);
const refundAddress = interactionParams.sender || interactionParams.refundTo;
const p2wdaAddress = interactionParams.from?.p2wda(this.#provider.network);
let refundToAddress;
if (this.csvAddress && refundAddress === this.csvAddress.address) refundToAddress = this.csvAddress.address;
else if (p2wdaAddress && refundAddress === p2wdaAddress.address) refundToAddress = p2wdaAddress.address;
else refundToAddress = refundAddress;
const utxoTracking = {
csvUTXOs,
p2wdaUTXOs,
regularUTXOs,
refundAddress,
refundToAddress,
csvAddress: this.csvAddress,
p2wdaAddress: p2wdaAddress ? {
address: p2wdaAddress.address,
witnessScript: p2wdaAddress.witnessScript
} : void 0,
isP2WDA: interactionParams.p2wda || false
};
return {
fundingTransactionRaw: transaction.fundingTransaction,
interactionTransactionRaw: transaction.interactionTransaction,
nextUTXOs: transaction.nextUTXOs,
estimatedFees: transaction.estimatedFees,
challengeSolution: transaction.challenge,
interactionAddress: transaction.interactionAddress,
fundingUTXOs: transaction.fundingUTXOs,
fundingInputUtxos: transaction.fundingInputUtxos,
compiledTargetScript: transaction.compiledTargetScript,
utxoTracking
};
}
/**
* Broadcasts a pre-signed interaction transaction.
* Uses sendRawTransactionPackage for atomic broadcast when a funding tx is present,
* falls back to sendRawTransaction for P2WDA (interaction-only) transactions.
* @param {SignedInteractionTransactionReceipt} signedTx - The signed transaction data.
* @returns {Promise<InteractionTransactionReceipt>} The transaction receipt with broadcast results.
*/
async sendPresignedTransaction(signedTx) {
if (signedTx.utxoTracking.isP2WDA || !signedTx.fundingTransactionRaw) {
const tx = await this.#provider.sendRawTransaction(signedTx.interactionTransactionRaw, false);
if (!tx || tx.error) throw new Error(`Error sending transaction: ${tx?.error || "Unknown error"}`);
if (!tx.result) throw new Error("No transaction ID returned");
if (!tx.success) throw new Error(`Error sending transaction: ${tx.result || "Unknown error"}`);
this.#processUTXOTracking(signedTx);
return {
interactionAddress: signedTx.interactionAddress,
transactionId: tx.result,
peerAcknowledgements: tx.peers || 0,
newUTXOs: signedTx.nextUTXOs,
estimatedFees: signedTx.estimatedFees,
challengeSolution: signedTx.challengeSolution,
rawTransaction: signedTx.interactionTransactionRaw,
fundingUTXOs: signedTx.fundingUTXOs,
fundingInputUtxos: signedTx.fundingInputUtxos,
compiledTargetScript: signedTx.compiledTargetScript
};
}
const result = await this.#provider.sendRawTransactionPackage([signedTx.fundingTransactionRaw, signedTx.interactionTransactionRaw], true);
if (!result.success) throw new Error(`Error sending transaction package: ${result.error || "Unknown error"}`);
if (result.packageResult) {
const failures = extractPackageFailures(result.packageResult);
if (failures.length > 0) throw new Error(`Transaction package failed:\n${failures.join("\n")}`);
}
const interactionSeqResult = result.sequentialResults?.[1];
if (interactionSeqResult && !interactionSeqResult.success) throw new Error(`Interaction transaction failed: ${interactionSeqResult.error || "Unknown error"}`);
const interactionTxId = interactionSeqResult?.txid || signedTx.interactionTransactionRaw;
const peers = interactionSeqResult?.peers || 0;
this.#processUTXOTracking(signedTx);
return {
interactionAddress: signedTx.interactionAddress,
transactionId: interactionTxId,
peerAcknowledgements: peers,
newUTXOs: signedTx.nextUTXOs,
estimatedFees: signedTx.estimatedFees,
challengeSolution: signedTx.challengeSolution,
rawTransaction: signedTx.interactionTransactionRaw,
fundingUTXOs: signedTx.fundingUTXOs,
fundingInputUtxos: signedTx.fundingInputUtxos,
compiledTargetScript: signedTx.compiledTargetScript
};
}
/**
* Signs and broadcasts a bitcoin interaction transaction from a simulated contract call.
* @param {TransactionParameters} interactionParams - The parameters for the transaction.
* @param {bigint} amountAddition - Additional satoshis to request when acquiring UTXOs.
* @returns {Promise<InteractionTransactionReceipt>} The transaction receipt with broadcast results.
*/
async sendTransaction(interactionParams, amountAddition = 0n) {
try {
const signedTx = await this.signTransaction(interactionParams, amountAddition);
return await this.sendPresignedTransaction(signedTx);
} catch (e) {
if (e.message.includes("Insufficient funds to pay the fees") && amountAddition === 0n) return await this.sendTransaction(interactionParams, 200000n);
this.#provider.utxoManager.clean();
throw e;
}
}
/**
* Set the gas estimation values.
* @param {bigint} estimatedGas - The estimated gas in satoshis.
* @param {bigint} refundedGas - The refunded gas in satoshis.
*/
setGasEstimation(estimatedGas, refundedGas) {
this.estimatedSatGas = estimatedGas;
this.estimatedRefundedGasInSat = refundedGas;
}
/**
* Set the Bitcoin fee rates.
* @param {BitcoinFees} fees - The Bitcoin fee rates.
*/
setBitcoinFee(fees) {
this.#bitcoinFees = fees;
}
/**
* Set the decoded contract output properties.
* @param {DecodedOutput} decoded - The decoded output.
*/
setDecoded(decoded) {
this.properties = Object.freeze(decoded.obj);
}
/**
* Set the contract events.
* @param {U} events - The contract events.
*/
setEvents(events) {
this.events = events;
}
/**
* Set the calldata for the transaction.
* @param {Uint8Array} calldata - The calldata.
*/
setCalldata(calldata) {
this.calldata = calldata;
}
/**
* Serializes this CallResult to a Uint8Array.
* Call this on an online device after simulation, then transfer the result
* to an offline device for signing.
*
* @param {string} refundAddress - The address to fetch UTXOs from (your p2tr address).
* @param {bigint} amount - The amount of satoshis needed for the transaction.
* @returns {Promise<Uint8Array>} Serialized buffer ready for offline signing.
*
* @example
* ```typescript
* // Online device: prepare for offline signing
* const simulation = await contract.transfer(recipient, amount);
* const offlineBuffer = await simulation.toOfflineBuffer(wallet.p2tr, 50000n);
*
* // Save to file or encode as base64 for QR code
* fs.writeFileSync('offline-tx.bin', offlineBuffer);
* // Or: const qrData = offlineBuffer.toString('base64');
* ```
*/
async toOfflineBuffer(refundAddress, amount) {
if (!this.calldata) throw new Error("Calldata not set");
if (!this.to) throw new Error("Contract address not set");
if (!this.address) throw new Error("Contract Address object not set");
if (this.revert) throw new Error(`Cannot serialize reverted simulation: ${this.revert}`);
const utxos = await this.#provider.utxoManager.getUTXOsForAmount({
address: refundAddress,
amount: amount + this.estimatedSatGas + 10000n,
throwErrors: true
});
const challengeSolution = await this.#provider.getChallenge();
const networkName = this.#getNetworkName();
return CallResultSerializer.serialize({
calldata: this.calldata,
to: this.to,
contractAddress: this.address.toHex(),
estimatedSatGas: this.estimatedSatGas,
estimatedRefundedGasInSat: this.estimatedRefundedGasInSat,
revert: this.revert,
result: fromBase64(this.#resultBase64),
accessList: this.accessList,
bitcoinFees: this.#bitcoinFees,
network: networkName,
estimatedGas: this.estimatedGas,
refundedGas: this.refundedGas,
challenge: challengeSolution.toRaw(),
challengeOriginalPublicKey: challengeSolution.publicKey.originalPublicKeyBuffer(),
utxos,
csvAddress: this.csvAddress
});
}
/**
* Gets the NetworkName enum from the provider's network.
* @returns {NetworkName} The network name enum value.
*/
#getNetworkName() {
const network = this.#provider.network;
if (network === bitcoin) return NetworkName.Mainnet;
if (network === testnet) return NetworkName.Testnet;
if (network === opnetTestnet) return NetworkName.OpnetTestnet;
if (network === regtest) return NetworkName.Regtest;
return NetworkName.Regtest;
}
/**
* Clone a UTXO and attach a witness script.
* @param {UTXO} utxo - The UTXO to clone.
* @param {Uint8Array} witnessScript - The witness script to attach.
* @returns {UTXO} The cloned UTXO with witness script.
*/
#cloneUTXOWithWitnessScript(utxo, witnessScript) {
const clone = Object.assign(Object.create(Object.getPrototypeOf(utxo)), utxo);
clone.witnessScript = witnessScript;
return clone;
}
/**
* Process UTXO tracking after transaction broadcast.
* @param {SignedInteractionTransactionReceipt} signedTx - The signed transaction receipt.
*/
#processUTXOTracking(signedTx) {
const { csvUTXOs, p2wdaUTXOs, regularUTXOs, refundAddress, refundToAddress, csvAddress, p2wdaAddress } = signedTx.utxoTracking;
if (csvAddress && csvUTXOs.length) {
const finalUTXOs = signedTx.nextUTXOs.map((u) => this.#cloneUTXOWithWitnessScript(u, csvAddress.witnessScript));
this.#provider.utxoManager.spentUTXO(csvAddress.address, csvUTXOs, refundToAddress === csvAddress.address ? finalUTXOs : []);
}
if (p2wdaAddress && p2wdaUTXOs.length) {
const finalUTXOs = signedTx.nextUTXOs.map((u) => this.#cloneUTXOWithWitnessScript(u, p2wdaAddress.witnessScript));
this.#provider.utxoManager.spentUTXO(p2wdaAddress.address, p2wdaUTXOs, refundToAddress === p2wdaAddress.address ? finalUTXOs : []);
}
if (regularUTXOs.length) this.#provider.utxoManager.spentUTXO(refundAddress, regularUTXOs, refundToAddress === refundAddress ? signedTx.nextUTXOs : []);
if (csvAddress && refundToAddress === csvAddress.address && !csvUTXOs.length) {
const finalUTXOs = signedTx.nextUTXOs.map((u) => this.#cloneUTXOWithWitnessScript(u, csvAddress.witnessScript));
this.#provider.utxoManager.spentUTXO(csvAddress.address, [], finalUTXOs);
} else if (p2wdaAddress && refundToAddress === p2wdaAddress.address && !p2wdaUTXOs.length) {
const finalUTXOs = signedTx.nextUTXOs.map((u) => this.#cloneUTXOWithWitnessScript(u, p2wdaAddress.witnessScript));
this.#provider.utxoManager.spentUTXO(p2wdaAddress.address, [], finalUTXOs);
} else if (refundToAddress === refundAddress && !regularUTXOs.length) {
if (!(csvAddress && refundToAddress === csvAddress.address || p2wdaAddress && refundToAddress === p2wdaAddress.address)) this.#provider.utxoManager.spentUTXO(refundAddress, [], signedTx.nextUTXOs);
}
}
max(a, b) {
return a > b ? a : b;
}
ensureUTXOsAvailable(utxos) {
if (!utxos || utxos.length === 0) throw new Error("Wallet optimization required. No UTXOs available. You may need to split your wallet UTXOs so at least one non-extra-input UTXO is available for the funding transaction.");
}
computeRequiredAmount(gasFee, priority, amountAddition, totalOuts, extraInputValue, miningCost = 0n, maximumAllowedSatToSpend = 0n) {
const gross = this.max(gasFee + priority + amountAddition + totalOuts + miningCost, maximumAllowedSatToSpend);
return gross >