ecash-agora
Version:
Library for interacting with the eCash Agora protocol
154 lines • 6.34 kB
JavaScript
;
// Copyright (c) 2025 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAgoraAdFuelSats = exports.getAgoraCancelFuelInputs = exports.getAgoraOneshotAcceptFuelInputs = exports.getAgoraPartialAcceptFuelInputs = exports.DUMMY_KEYPAIR = void 0;
const ecash_lib_1 = require("ecash-lib");
const DUMMY_TXID = '1111111111111111111111111111111111111111111111111111111111111111';
const DUMMY_WALLET_HASH = (0, ecash_lib_1.fromHex)('12'.repeat(20));
const DUMMY_SUFFICIENT_CANCEL_VALUE = 10000n;
const DUMMY_SCRIPT = ecash_lib_1.Script.p2pkh(DUMMY_WALLET_HASH);
exports.DUMMY_KEYPAIR = {
sk: (0, ecash_lib_1.fromHex)('33'.repeat(32)),
pk: (0, ecash_lib_1.fromHex)('023c72addb4fdf09af94f0c94d7fe92a386a7e70cf8a1d85916386bb2535c7b1b1'),
};
// Used for accept and cancel fee estimation of agora partial offers
const DUMMY_INPUT = {
input: {
prevOut: {
txid: DUMMY_TXID,
outIdx: 1,
},
signData: {
sats: DUMMY_SUFFICIENT_CANCEL_VALUE,
outputScript: DUMMY_SCRIPT,
},
},
signatory: (0, ecash_lib_1.P2PKHSignatory)(exports.DUMMY_KEYPAIR.sk, exports.DUMMY_KEYPAIR.pk, ecash_lib_1.ALL_BIP143),
};
/**
* Determine input utxos to cover an Agora Partial accept offer
*/
const getAgoraPartialAcceptFuelInputs = (agoraOffer, utxos, acceptedAtoms, feePerKb = ecash_lib_1.DEFAULT_FEE_SATS_PER_KB) => {
const fuelInputs = [];
const dummyInputs = [];
let inputSatoshis = 0n;
for (const utxo of utxos) {
// Accumulative utxo selection
fuelInputs.push(utxo);
// Match our fuelInput count with dummyInputs
dummyInputs.push(DUMMY_INPUT);
inputSatoshis += utxo.sats;
const askedSats = agoraOffer.askedSats(BigInt(acceptedAtoms));
// Get the tx fee for this tx
const acceptFeeSats = agoraOffer.acceptFeeSats({
recipientScript: DUMMY_SCRIPT,
extraInputs: dummyInputs,
acceptedAtoms,
feePerKb,
});
// We need to cover the tx fee and the asking price
const requiredSats = acceptFeeSats + askedSats;
if (inputSatoshis >= requiredSats) {
return fuelInputs;
}
}
throw new Error('Insufficient utxos to accept this offer');
};
exports.getAgoraPartialAcceptFuelInputs = getAgoraPartialAcceptFuelInputs;
/**
* Determine input utxos to cover an Agora ONESHOT accept offer
* Note: we could refactor getAgoraPartialAcceptFuelInputs to work with ONESHOT offers
* However there is some ambiguity involved with the acceptedAtoms param
* Cleaner to keep distinct functions
*/
const getAgoraOneshotAcceptFuelInputs = (agoraOffer, utxos, feePerKb = ecash_lib_1.DEFAULT_FEE_SATS_PER_KB) => {
const fuelInputs = [];
const dummyInputs = [];
let inputSatoshis = 0n;
for (const utxo of utxos) {
// Accumulative utxo selection
fuelInputs.push(utxo);
// Match our fuelInput count with dummyInputs
dummyInputs.push(DUMMY_INPUT);
inputSatoshis += utxo.sats;
const askedSats = agoraOffer.askedSats();
// Get the tx fee for this tx
const acceptFeeSats = agoraOffer.acceptFeeSats({
recipientScript: DUMMY_SCRIPT,
extraInputs: dummyInputs,
feePerKb,
});
// We need to cover the tx fee and the asking price
const requiredSats = acceptFeeSats + askedSats;
if (inputSatoshis >= requiredSats) {
return fuelInputs;
}
}
throw new Error('Insufficient utxos to accept this offer');
};
exports.getAgoraOneshotAcceptFuelInputs = getAgoraOneshotAcceptFuelInputs;
/**
* Determine input utxos to cancel an Agora offer (Partial or ONESHOT)
*/
const getAgoraCancelFuelInputs = (agoraOffer, utxos, feePerKb = ecash_lib_1.DEFAULT_FEE_SATS_PER_KB) => {
const fuelInputs = [];
const dummyInputs = [];
let inputSatoshis = 0n;
for (const utxo of utxos) {
// Accumulative utxo selection
fuelInputs.push(utxo);
// Match our fuelInput count with dummyInputs
dummyInputs.push(DUMMY_INPUT);
inputSatoshis += utxo.sats;
// Get the tx fee for this tx
// In practice, this is always bigger than dust
// So we do not check to make sure the output we cover is at least dust
const cancelFeeSats = agoraOffer.cancelFeeSats({
recipientScript: DUMMY_SCRIPT,
extraInputs: dummyInputs,
feePerKb,
});
// There is no asking price for cancellation
// cancelFeeSats is the size of the output we need
if (inputSatoshis >= cancelFeeSats) {
return fuelInputs;
}
}
throw new Error('Insufficient utxos to cancel this offer');
};
exports.getAgoraCancelFuelInputs = getAgoraCancelFuelInputs;
/**
* Calculate the fee required for an offer transaction that spends an ad script output.
* This is used to determine how much fuel needs to be included in the ad setup transaction.
*/
const getAgoraAdFuelSats = (redeemScript, signatory, offerOutputs, satsPerKb) => {
// Build a dummy offer tx to measure its size
const dummyOfferTx = new ecash_lib_1.TxBuilder({
inputs: [
{
input: {
prevOut: {
// Use a placeholder 32-byte txid
txid: DUMMY_TXID,
// The outIdx will always be 1 in practice
outIdx: 1,
},
signData: {
// Arbitrary value that we know will cover the fee for this tx
sats: 100000n,
redeemScript,
},
},
signatory,
},
],
outputs: offerOutputs,
});
const measureTx = dummyOfferTx.sign({ ecc: new ecash_lib_1.EccDummy() });
const dummyOfferTxSats = BigInt(Math.ceil((measureTx.serSize() * Number(satsPerKb)) / 1000));
return dummyOfferTxSats;
};
exports.getAgoraAdFuelSats = getAgoraAdFuelSats;
//# sourceMappingURL=inputs.js.map