diff --git a/lib.commonjs/_tests/test-utxo-coinselection.d.ts b/lib.commonjs/_tests/test-utxo-coinselection.d.ts new file mode 100644 index 00000000..c1dd221a --- /dev/null +++ b/lib.commonjs/_tests/test-utxo-coinselection.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=test-utxo-coinselection.d.ts.map \ No newline at end of file diff --git a/lib.commonjs/_tests/test-utxo-coinselection.d.ts.map b/lib.commonjs/_tests/test-utxo-coinselection.d.ts.map new file mode 100644 index 00000000..a0513c5c --- /dev/null +++ b/lib.commonjs/_tests/test-utxo-coinselection.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"test-utxo-coinselection.d.ts","sourceRoot":"","sources":["../../src.ts/_tests/test-utxo-coinselection.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/lib.commonjs/_tests/test-utxo-coinselection.js b/lib.commonjs/_tests/test-utxo-coinselection.js new file mode 100644 index 00000000..866e6d0f --- /dev/null +++ b/lib.commonjs/_tests/test-utxo-coinselection.js @@ -0,0 +1,101 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const tslib_1 = require("tslib"); +const assert_1 = tslib_1.__importDefault(require("assert")); +const coinselector_fewest_1 = require("../transaction/coinselector-fewest"); +const utxo_1 = require("../transaction/utxo"); +const TEST_SPEND_ADDRESS = "0x00539bc2CE3eD0FD039c582CB700EF5398bB0491"; +const TEST_RECEIVE_ADDRESS = "0x02b9B1D30B6cCdc7d908B82739ce891463c3FA19"; +// Utility function to create UTXOs (adjust as necessary) +function createUTXOs(denominations) { + return denominations.map(denomination => ({ + denomination, + address: TEST_SPEND_ADDRESS + })); +} +describe("FewestCoinSelector", function () { + describe("Selecting valid UTXOs", function () { + it("selects a single UTXO that exactly matches the target amount", function () { + const availableUTXOs = createUTXOs([utxo_1.denominations[1], utxo_1.denominations[2], utxo_1.denominations[3]]); // .065 Qi + const targetSpend = { value: utxo_1.denominations[3], address: TEST_RECEIVE_ADDRESS }; // .05 Qi + const selector = new coinselector_fewest_1.FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // A single 0.05 Qi UTXO should have been selected + assert_1.default.strictEqual(result.inputs.length, 1); + assert_1.default.strictEqual(result.inputs[0].denomination, utxo_1.denominations[3]); + // A single new 0.05 Qi UTXO should have been outputed + assert_1.default.strictEqual(result.spendOutputs.length, 1); + assert_1.default.strictEqual(result.spendOutputs[0].denomination, utxo_1.denominations[3]); + // No change should be returned + assert_1.default.strictEqual(result.changeOutputs.length, 0); + }); + it("selects multiple UTXOs whose combined value meets the target amount", function () { + const availableUTXOs = createUTXOs([utxo_1.denominations[1], utxo_1.denominations[2], utxo_1.denominations[2], utxo_1.denominations[3]]); // .075 Qi + const targetSpend = { value: utxo_1.denominations[2] + utxo_1.denominations[3], address: TEST_RECEIVE_ADDRESS }; // .06 Qi + const selector = new coinselector_fewest_1.FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // 2 UTXOs should have been selected for a total of .06 Qi + assert_1.default.strictEqual(result.inputs.length, 2); + const inputValue = result.inputs[0].denomination + result.inputs[1].denomination; + assert_1.default.strictEqual(inputValue, utxo_1.denominations[2] + utxo_1.denominations[3]); + // 2 new UTxOs should have been outputed for a total of .06 Qi + assert_1.default.strictEqual(result.spendOutputs.length, 2); + const spendValue = result.spendOutputs[0].denomination + result.spendOutputs[1].denomination; + assert_1.default.strictEqual(spendValue, utxo_1.denominations[2] + utxo_1.denominations[3]); + // No change should be returned + assert_1.default.strictEqual(result.changeOutputs.length, 0); + }); + it("selects a single UTXO that is larger than the target amount, ensuring change is correctly calculated", function () { + const availableUTXOs = createUTXOs([utxo_1.denominations[2], utxo_1.denominations[4]]); // .11 Qi + const targetSpend = { value: utxo_1.denominations[3], address: TEST_RECEIVE_ADDRESS }; // .05 Qi + const selector = new coinselector_fewest_1.FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // A single 0.1 Qi UTXO should have been selected + assert_1.default.strictEqual(result.inputs.length, 1); + assert_1.default.strictEqual(result.inputs[0].denomination, utxo_1.denominations[4]); + // A single new 0.05 Qi UTXO should have been outputed + assert_1.default.strictEqual(result.spendOutputs.length, 1); + assert_1.default.strictEqual(result.spendOutputs[0].denomination, utxo_1.denominations[3]); + // 0.05 Qi should be returned in change + assert_1.default.strictEqual(result.changeOutputs.length, 1); + assert_1.default.strictEqual(result.changeOutputs[0].denomination, utxo_1.denominations[3]); + }); + it("selects multiple UTXOs where the total exceeds the target amount, ensuring change is correctly calculated", function () { + const availableUTXOs = createUTXOs([ + utxo_1.denominations[2], + utxo_1.denominations[4], + utxo_1.denominations[4], + utxo_1.denominations[4], + utxo_1.denominations[5] + ]); // .56 Qi + const targetSpend = { value: utxo_1.denominations[6], address: TEST_RECEIVE_ADDRESS }; // .5 Qi + const selector = new coinselector_fewest_1.FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // 4 UTXOs should have been selected for a total of .55 Qi + assert_1.default.strictEqual(result.inputs.length, 4); + const inputValue = result.inputs[0].denomination + result.inputs[1].denomination + result.inputs[2].denomination + result.inputs[3].denomination; + assert_1.default.strictEqual(inputValue, utxo_1.denominations[4] + utxo_1.denominations[4] + utxo_1.denominations[4] + utxo_1.denominations[5]); + // A single new 0.5 Qi UTXO should have been outputed + assert_1.default.strictEqual(result.spendOutputs.length, 1); + assert_1.default.strictEqual(result.spendOutputs[0].denomination, utxo_1.denominations[6]); + // 0.05 Qi should be returned in change + assert_1.default.strictEqual(result.changeOutputs.length, 1); + assert_1.default.strictEqual(result.changeOutputs[0].denomination, utxo_1.denominations[3]); + }); + }); + describe("Selecting valid UTXOs", function () { + it("throws an error when there are insufficient funds", function () { + const selector = new coinselector_fewest_1.FewestCoinSelector(createUTXOs([utxo_1.denominations[0], utxo_1.denominations[0]])); + assert_1.default.throws(() => selector.performSelection({ value: utxo_1.denominations[3], address: TEST_RECEIVE_ADDRESS }), /Insufficient funds/); + }); + it("throws an error when no UTXOs are available", function () { + const selector = new coinselector_fewest_1.FewestCoinSelector([]); + assert_1.default.throws(() => selector.performSelection({ value: utxo_1.denominations[2], address: TEST_RECEIVE_ADDRESS }), /No UTXOs available/); + }); + it("throws an error when the target amount is negative", function () { + const selector = new coinselector_fewest_1.FewestCoinSelector(createUTXOs([utxo_1.denominations[2], utxo_1.denominations[2]])); + assert_1.default.throws(() => selector.performSelection({ value: -utxo_1.denominations[1], address: TEST_RECEIVE_ADDRESS }), /Target amount must be greater than 0/); + }); + }); +}); +//# sourceMappingURL=test-utxo-coinselection.js.map \ No newline at end of file diff --git a/lib.commonjs/_tests/test-utxo-coinselection.js.map b/lib.commonjs/_tests/test-utxo-coinselection.js.map new file mode 100644 index 00000000..3b514bcd --- /dev/null +++ b/lib.commonjs/_tests/test-utxo-coinselection.js.map @@ -0,0 +1 @@ +{"version":3,"file":"test-utxo-coinselection.js","sourceRoot":"","sources":["../../src.ts/_tests/test-utxo-coinselection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib.commonjs/transaction/abstract-coinselector.d.ts b/lib.commonjs/transaction/abstract-coinselector.d.ts new file mode 100644 index 00000000..1e73fa8c --- /dev/null +++ b/lib.commonjs/transaction/abstract-coinselector.d.ts @@ -0,0 +1,50 @@ +import { UTXO, UTXOLike } from "./utxo.js"; +export type SpendTarget = { + address: string; + value: bigint; +}; +export type SelectedCoinsResult = { + inputs: UTXO[]; + spendOutputs: UTXO[]; + changeOutputs: UTXO[]; +}; +/** + * An **AbstractCoinSelector** provides a base class for other sub-classes to + * implement the functionality for selecting UTXOs for a spend and to properly + * handle spend and change outputs. + * + * This class is abstract and should not be used directly. Sub-classes should + * implement the [[performSelection]] method to provide the actual coin + * selection logic. + * + * @abstract + */ +export declare abstract class AbstractCoinSelector { + #private; + get availableUXTOs(): UTXO[]; + set availableUXTOs(value: UTXOLike[]); + get spendOutputs(): UTXO[]; + set spendOutputs(value: UTXOLike[]); + get changeOutputs(): UTXO[]; + set changeOutputs(value: UTXOLike[]); + /** + * Constructs a new AbstractCoinSelector instance with an empty UTXO array. + */ + constructor(availableUXTOs?: UTXOLike[]); + /** + * This method should be implemented by sub-classes to provide the actual + * coin selection logic. It should select UTXOs from the available UTXOs + * that sum to the target amount and return the selected UTXOs as well as + * the spend and change outputs. + * @param target The target amount to select UTXOs for. + */ + abstract performSelection(target: SpendTarget): SelectedCoinsResult; + /** + * Validates the provided UTXO instance. In order to be valid for coin + * selection, the UTXO must have a valid address and denomination. + * @param utxo The UTXO instance to validate. + * @throws An error if the UTXO instance is invalid. + */ + protected _validateUTXO(utxo: UTXO): void; +} +//# sourceMappingURL=abstract-coinselector.d.ts.map \ No newline at end of file diff --git a/lib.commonjs/transaction/abstract-coinselector.d.ts.map b/lib.commonjs/transaction/abstract-coinselector.d.ts.map new file mode 100644 index 00000000..c207e70d --- /dev/null +++ b/lib.commonjs/transaction/abstract-coinselector.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"abstract-coinselector.d.ts","sourceRoot":"","sources":["../../src.ts/transaction/abstract-coinselector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,MAAM,WAAW,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAC9B,MAAM,EAAE,IAAI,EAAE,CAAC;IACf,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,aAAa,EAAE,IAAI,EAAE,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,8BAAsB,oBAAoB;;IAKtC,IAAI,cAAc,IAAI,IAAI,EAAE,CAAiC;IAC7D,IAAI,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,EAMnC;IAED,IAAI,YAAY,IAAI,IAAI,EAAE,CAA+B;IACzD,IAAI,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,EAEjC;IAED,IAAI,aAAa,IAAI,IAAI,EAAE,CAAgC;IAC3D,IAAI,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,EAElC;IAED;;OAEG;gBACS,cAAc,GAAE,QAAQ,EAAO;IAU3C;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB;IAEnE;;;;;OAKG;IACH,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;CAY5C"} \ No newline at end of file diff --git a/lib.commonjs/transaction/abstract-coinselector.js b/lib.commonjs/transaction/abstract-coinselector.js new file mode 100644 index 00000000..12c07623 --- /dev/null +++ b/lib.commonjs/transaction/abstract-coinselector.js @@ -0,0 +1,64 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.AbstractCoinSelector = void 0; +const utxo_js_1 = require("./utxo.js"); +/** + * An **AbstractCoinSelector** provides a base class for other sub-classes to + * implement the functionality for selecting UTXOs for a spend and to properly + * handle spend and change outputs. + * + * This class is abstract and should not be used directly. Sub-classes should + * implement the [[performSelection]] method to provide the actual coin + * selection logic. + * + * @abstract + */ +class AbstractCoinSelector { + #availableUXTOs; + #spendOutputs; + #changeOutputs; + get availableUXTOs() { return this.#availableUXTOs; } + set availableUXTOs(value) { + this.#availableUXTOs = value.map((val) => { + const utxo = utxo_js_1.UTXO.from(val); + this._validateUTXO(utxo); + return utxo; + }); + } + get spendOutputs() { return this.#spendOutputs; } + set spendOutputs(value) { + this.#spendOutputs = value.map((utxo) => utxo_js_1.UTXO.from(utxo)); + } + get changeOutputs() { return this.#changeOutputs; } + set changeOutputs(value) { + this.#changeOutputs = value.map((utxo) => utxo_js_1.UTXO.from(utxo)); + } + /** + * Constructs a new AbstractCoinSelector instance with an empty UTXO array. + */ + constructor(availableUXTOs = []) { + this.#availableUXTOs = availableUXTOs.map((val) => { + const utxo = utxo_js_1.UTXO.from(val); + this._validateUTXO(utxo); + return utxo; + }); + this.#spendOutputs = []; + this.#changeOutputs = []; + } + /** + * Validates the provided UTXO instance. In order to be valid for coin + * selection, the UTXO must have a valid address and denomination. + * @param utxo The UTXO instance to validate. + * @throws An error if the UTXO instance is invalid. + */ + _validateUTXO(utxo) { + if (utxo.address == null) { + throw new Error("UTXO address is required"); + } + if (utxo.denomination == null) { + throw new Error("UTXO denomination is required"); + } + } +} +exports.AbstractCoinSelector = AbstractCoinSelector; +//# sourceMappingURL=abstract-coinselector.js.map \ No newline at end of file diff --git a/lib.commonjs/transaction/abstract-coinselector.js.map b/lib.commonjs/transaction/abstract-coinselector.js.map new file mode 100644 index 00000000..a505c8f9 --- /dev/null +++ b/lib.commonjs/transaction/abstract-coinselector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"abstract-coinselector.js","sourceRoot":"","sources":["../../src.ts/transaction/abstract-coinselector.ts"],"names":[],"mappings":";;;AAAA,uCAA2C;AAa3C;;;;;;;;;;GAUG;AACH,MAAsB,oBAAoB;IACtC,eAAe,CAAS;IACxB,aAAa,CAAS;IACtB,cAAc,CAAS;IAEvB,IAAI,cAAc,KAAa,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;IAC7D,IAAI,cAAc,CAAC,KAAiB;QAChC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,GAAa,EAAE,EAAE;YAC/C,MAAM,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,IAAI,YAAY,KAAa,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IACzD,IAAI,YAAY,CAAC,KAAiB;QAC9B,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,aAAa,KAAa,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IAC3D,IAAI,aAAa,CAAC,KAAiB;QAC/B,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,CAAC;IAED;;OAEG;IACH,YAAY,iBAA6B,EAAE;QACvC,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,GAAa,EAAE,EAAE;YACxD,MAAM,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;IAC7B,CAAC;IAWD;;;;;OAKG;IACO,aAAa,CAAC,IAAU;QAC9B,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;SAC/C;QAED,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;SACpD;IACL,CAAC;CAIJ;AAhED,oDAgEC"} \ No newline at end of file diff --git a/lib.commonjs/transaction/coinselector-fewest.d.ts b/lib.commonjs/transaction/coinselector-fewest.d.ts new file mode 100644 index 00000000..8b597438 --- /dev/null +++ b/lib.commonjs/transaction/coinselector-fewest.d.ts @@ -0,0 +1,24 @@ +import { AbstractCoinSelector, SelectedCoinsResult, SpendTarget } from "./abstract-coinselector.js"; +/** + * The FewestCoinSelector class provides a coin selection algorithm that selects + * the fewest UTXOs required to meet the target amount. This algorithm is useful + * for minimizing the size of the transaction and the fees associated with it. + * + * This class is a sub-class of [[AbstractCoinSelector]] and implements the + * [[performSelection]] method to provide the actual coin selection logic. + */ +export declare class FewestCoinSelector extends AbstractCoinSelector { + /** + * The largest first coin selection algorithm. + * + * This algorithm selects the largest UTXOs first, and continues to select UTXOs until the + * target amount is reached. If the total value of the selected UTXOs is greater than the + * target amount, the remaining value is returned as a change output. + * @param target The target amount to select UTXOs for. + */ + performSelection(target: SpendTarget): SelectedCoinsResult; + private sortUTXOsByDenomination; + private validateTarget; + private validateUTXOs; +} +//# sourceMappingURL=coinselector-fewest.d.ts.map \ No newline at end of file diff --git a/lib.commonjs/transaction/coinselector-fewest.d.ts.map b/lib.commonjs/transaction/coinselector-fewest.d.ts.map new file mode 100644 index 00000000..d6100187 --- /dev/null +++ b/lib.commonjs/transaction/coinselector-fewest.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"coinselector-fewest.d.ts","sourceRoot":"","sources":["../../src.ts/transaction/coinselector-fewest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAIpG;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,oBAAoB;IAExD;;;;;;;OAOG;IACH,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB;IA8G1D,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;CAMxB"} \ No newline at end of file diff --git a/lib.commonjs/transaction/coinselector-fewest.js b/lib.commonjs/transaction/coinselector-fewest.js new file mode 100644 index 00000000..38be7696 --- /dev/null +++ b/lib.commonjs/transaction/coinselector-fewest.js @@ -0,0 +1,143 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.FewestCoinSelector = void 0; +const maths_js_1 = require("../utils/maths.js"); +const abstract_coinselector_js_1 = require("./abstract-coinselector.js"); +const utxo_js_1 = require("./utxo.js"); +/** + * The FewestCoinSelector class provides a coin selection algorithm that selects + * the fewest UTXOs required to meet the target amount. This algorithm is useful + * for minimizing the size of the transaction and the fees associated with it. + * + * This class is a sub-class of [[AbstractCoinSelector]] and implements the + * [[performSelection]] method to provide the actual coin selection logic. + */ +class FewestCoinSelector extends abstract_coinselector_js_1.AbstractCoinSelector { + /** + * The largest first coin selection algorithm. + * + * This algorithm selects the largest UTXOs first, and continues to select UTXOs until the + * target amount is reached. If the total value of the selected UTXOs is greater than the + * target amount, the remaining value is returned as a change output. + * @param target The target amount to select UTXOs for. + */ + performSelection(target) { + this.validateTarget(target); + this.validateUTXOs(); + const sortedUTXOs = this.sortUTXOsByDenomination(this.availableUXTOs, "desc"); + let totalValue = BigInt(0); + let selectedUTXOs = []; + // Get UTXOs that meets or exceeds the target value + const UTXOsEqualOrGreaterThanTarget = sortedUTXOs.filter(utxo => utxo.denomination && utxo.denomination >= target.value); + if (UTXOsEqualOrGreaterThanTarget.length > 0) { + // Find the smallest UTXO that meets or exceeds the target value + const optimalUTXO = UTXOsEqualOrGreaterThanTarget.reduce((minDenominationUTXO, currentUTXO) => { + if (!currentUTXO.denomination) + return minDenominationUTXO; + return currentUTXO.denomination < minDenominationUTXO.denomination ? currentUTXO : minDenominationUTXO; + }, UTXOsEqualOrGreaterThanTarget[0]); // Initialize with the first UTXO in the list + selectedUTXOs.push(optimalUTXO); + totalValue += optimalUTXO.denomination; + } + else { + // If no single UTXO meets or exceeds the target, aggregate smaller denominations + // until the target is met/exceeded or there are no more UTXOs to aggregate + while (sortedUTXOs.length > 0 && totalValue < target.value) { + const nextOptimalUTXO = sortedUTXOs.reduce((closest, utxo) => { + if (!utxo.denomination) + return closest; + // Prioritize UTXOs that bring totalValue closer to target.value + const absThisDiff = (0, maths_js_1.bigIntAbs)(target.value - (totalValue + utxo.denomination)); + const currentClosestDiff = closest && closest.denomination + ? (0, maths_js_1.bigIntAbs)(target.value - (totalValue + closest.denomination)) + : BigInt(Infinity); + return absThisDiff < currentClosestDiff ? utxo : closest; + }, sortedUTXOs[0]); + // Add the selected UTXO to the selection and update totalValue + selectedUTXOs.push(nextOptimalUTXO); + totalValue += nextOptimalUTXO.denomination; + // Remove the selected UTXO from the list of available UTXOs + const index = sortedUTXOs.findIndex(utxo => utxo.denomination === nextOptimalUTXO.denomination && utxo.address === nextOptimalUTXO.address); + sortedUTXOs.splice(index, 1); + } + } + // Check if the selected UTXOs meet or exceed the target amount + if (totalValue < target.value) { + throw new Error("Insufficient funds"); + } + // // Check if any denominations can be removed from the input set and it still remain valid + selectedUTXOs = this.sortUTXOsByDenomination(selectedUTXOs, "asc"); + let runningTotal = totalValue; + let lastRemovableIndex = -1; // Index of the last UTXO that can be removed + // Iterate through selectedUTXOs to find the last removable UTXO + for (let i = 0; i < selectedUTXOs.length; i++) { + const utxo = selectedUTXOs[i]; + if (utxo.denomination) { + if (runningTotal - utxo.denomination >= target.value) { + runningTotal -= utxo.denomination; + lastRemovableIndex = i; + } + else { + // Once a UTXO makes the total less than target.value, stop the loop + break; + } + } + } + if (lastRemovableIndex >= 0) { + totalValue -= selectedUTXOs[lastRemovableIndex].denomination; + selectedUTXOs.splice(lastRemovableIndex, 1); + } + // Break down the total spend into properly denominatated UTXOs + const spendDenominations = (0, utxo_js_1.denominate)(target.value); + this.spendOutputs = spendDenominations.map(denomination => { + const utxo = new utxo_js_1.UTXO(); + utxo.denomination = denomination; + utxo.address = target.address; + return utxo; + }); + // Calculate change to be returned + const change = totalValue - target.value; + // If there's change, break it down into properly denominatated UTXOs + if (change > BigInt(0)) { + const changeDenominations = (0, utxo_js_1.denominate)(change); + this.changeOutputs = changeDenominations.map(denomination => { + const utxo = new utxo_js_1.UTXO(); + utxo.denomination = denomination; + // We do not have access to change addresses here so leave it null + return utxo; + }); + } + else { + this.changeOutputs = []; + } + return { + inputs: selectedUTXOs, + spendOutputs: this.spendOutputs, + changeOutputs: this.changeOutputs, + }; + } + sortUTXOsByDenomination(utxos, direction) { + if (direction === "asc") { + return [...utxos].sort((a, b) => { + const diff = (a.denomination ?? BigInt(0)) - (b.denomination ?? BigInt(0)); + return diff > 0 ? 1 : diff < 0 ? -1 : 0; + }); + } + return [...utxos].sort((a, b) => { + const diff = (b.denomination ?? BigInt(0)) - (a.denomination ?? BigInt(0)); + return diff > 0 ? 1 : diff < 0 ? -1 : 0; + }); + } + validateTarget(target) { + if (target.value <= BigInt(0)) { + throw new Error("Target amount must be greater than 0"); + } + } + validateUTXOs() { + if (this.availableUXTOs.length === 0) { + throw new Error("No UTXOs available"); + } + } +} +exports.FewestCoinSelector = FewestCoinSelector; +//# sourceMappingURL=coinselector-fewest.js.map \ No newline at end of file diff --git a/lib.commonjs/transaction/coinselector-fewest.js.map b/lib.commonjs/transaction/coinselector-fewest.js.map new file mode 100644 index 00000000..e967110d --- /dev/null +++ b/lib.commonjs/transaction/coinselector-fewest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"coinselector-fewest.js","sourceRoot":"","sources":["../../src.ts/transaction/coinselector-fewest.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib.commonjs/transaction/utxo.d.ts b/lib.commonjs/transaction/utxo.d.ts new file mode 100644 index 00000000..7fa6146b --- /dev/null +++ b/lib.commonjs/transaction/utxo.d.ts @@ -0,0 +1,58 @@ +import type { BigNumberish } from "../utils/index"; +export type OutPoint = { + txhash: string; + index: number; +}; +export type UTXOTransactionInput = { + previousOutPoint: OutPoint; + pubKey: Uint8Array; +}; +export interface UTXOEntry { + denomination: null | bigint; + address: null | string; +} +export type UTXOTransactionOutput = UTXOEntry; +export type UTXOTransaction = { + chainId: bigint; + inputs: UTXOTransactionInput[]; + outputs: UTXOTransactionOutput[]; + signature?: Uint8Array; +}; +export interface UTXOLike extends UTXOEntry { + txhash?: null | string; + index?: null | number; +} +export declare const denominations: bigint[]; +/** + * Given a value, returns an array of supported denominations that sum to the value. + * @param value The value to denominate. + * @returns Array of supported denominations that sum to the value. + */ +export declare function denominate(value: bigint): bigint[]; +export declare class UTXO implements UTXOLike { + #private; + get txhash(): null | string; + set txhash(value: null | string); + get index(): null | number; + set index(value: null | number); + get address(): null | string; + set address(value: null | string); + get denomination(): null | bigint; + set denomination(value: null | BigNumberish); + /** + * Constructs a new UTXO instance with null properties. + */ + constructor(); + /** + * Converts the UTXO instance to a JSON object. + * @returns A JSON representation of the UTXO instance. + */ + toJSON(): any; + /** + * Creates a UTXO instance from a UTXOLike object. + * @param utxo The UTXOLike object to create the UTXO instance from. + * @returns A new UTXO instance. + */ + static from(utxo: UTXOLike): UTXO; +} +//# sourceMappingURL=utxo.d.ts.map \ No newline at end of file diff --git a/lib.commonjs/transaction/utxo.d.ts.map b/lib.commonjs/transaction/utxo.d.ts.map new file mode 100644 index 00000000..d22529c2 --- /dev/null +++ b/lib.commonjs/transaction/utxo.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"utxo.d.ts","sourceRoot":"","sources":["../../src.ts/transaction/utxo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,MAAM,QAAQ,GAAG;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,gBAAgB,EAAE,QAAQ,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,SAAS;IACtB,YAAY,EAAE,IAAI,GAAG,MAAM,CAAC;IAC5B,OAAO,EAAE,IAAI,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE,UAAU,CAAC;CAC1B,CAAC;AAEF,MAAM,WAAW,QAAS,SAAQ,SAAS;IACvC,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,EAkBjC,CAAC;AAsBF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BlD;AAED,qBAAa,IAAK,YAAW,QAAQ;;IAMjC,IAAI,MAAM,IAAI,IAAI,GAAG,MAAM,CAAyB;IACpD,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAE9B;IAED,IAAI,KAAK,IAAI,IAAI,GAAG,MAAM,CAAwB;IAClD,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAE7B;IAED,IAAI,OAAO,IAAI,IAAI,GAAG,MAAM,CAA0B;IACtD,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAE/B;IAED,IAAI,YAAY,IAAI,IAAI,GAAG,MAAM,CAA+B;IAChE,IAAI,YAAY,CAAC,KAAK,EAAE,IAAI,GAAG,YAAY,EAY1C;IAED;;OAEG;;IAQH;;;OAGG;IACH,MAAM,IAAI,GAAG;IASb;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;CAWpC"} \ No newline at end of file diff --git a/lib.commonjs/transaction/utxo.js b/lib.commonjs/transaction/utxo.js new file mode 100644 index 00000000..556d6582 --- /dev/null +++ b/lib.commonjs/transaction/utxo.js @@ -0,0 +1,148 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.UTXO = exports.denominate = exports.denominations = void 0; +const index_1 = require("../address/index"); +const index_2 = require("../utils/index"); +; +exports.denominations = [ + BigInt(1), + BigInt(5), + BigInt(10), + BigInt(50), + BigInt(100), + BigInt(250), + BigInt(500), + BigInt(1000), + BigInt(5000), + BigInt(10000), + BigInt(20000), + BigInt(50000), + BigInt(100000), + BigInt(1000000), + BigInt(10000000), + BigInt(100000000), + BigInt(1000000000), // 1000000 Qi +]; +/** + * Checks if the provided denomination is valid. + * @param denomination The denomination to check. + * @returns True if the denomination is valid, false otherwise. + */ +function isValidDenomination(denomination) { + return exports.denominations.includes(denomination); +} +/** + * Handles conversion of string to bigint, specifically for transaction parameters. + * @param value The string value to convert. + * @param param The parameter name for error context. + * @returns The bigint representation of the input string. + */ +function handleBigInt(value, param) { + if (value === "0x") { + return BigInt(0); + } + return (0, index_2.getBigInt)(value, param); +} +/** + * Given a value, returns an array of supported denominations that sum to the value. + * @param value The value to denominate. + * @returns Array of supported denominations that sum to the value. + */ +function denominate(value) { + if (value <= BigInt(0)) { + throw new Error("Value must be greater than 0"); + } + const result = []; + let remainingValue = value; + // Iterate through denominations in descending order + for (let i = exports.denominations.length - 1; i >= 0; i--) { + const denomination = exports.denominations[i]; + // Add the denomination to the result array as many times as possible + while (remainingValue >= denomination) { + result.push(denomination); + remainingValue -= denomination; + } + } + if (remainingValue > 0) { + throw new Error("Unable to match the value with available denominations"); + } + return result; +} +exports.denominate = denominate; +class UTXO { + #txhash; + #index; + #address; + #denomination; + get txhash() { return this.#txhash; } + set txhash(value) { + this.#txhash = value; + } + get index() { return this.#index; } + set index(value) { + this.#index = value; + } + get address() { return this.#address; } + set address(value) { + this.#address = (value == null) ? null : (0, index_1.getAddress)(value); + } + get denomination() { return this.#denomination; } + set denomination(value) { + if (value == null) { + this.#denomination = null; + return; + } + const denominationBigInt = handleBigInt(value.toString(), "denomination"); + if (!isValidDenomination(denominationBigInt)) { + throw new Error("Invalid denomination value"); + } + this.#denomination = denominationBigInt; + } + /** + * Constructs a new UTXO instance with null properties. + */ + constructor() { + this.#txhash = null; + this.#index = null; + this.#address = null; + this.#denomination = null; + } + /** + * Converts the UTXO instance to a JSON object. + * @returns A JSON representation of the UTXO instance. + */ + toJSON() { + return { + txhash: this.txhash, + index: this.index, + address: this.address, + denomination: this.denomination, + }; + } + /** + * Creates a UTXO instance from a UTXOLike object. + * @param utxo The UTXOLike object to create the UTXO instance from. + * @returns A new UTXO instance. + */ + static from(utxo) { + if (utxo === null) { + return new UTXO(); + } + const result = utxo instanceof UTXO ? utxo : new UTXO(); + if (utxo.txhash != null) { + result.txhash = utxo.txhash; + } + if (utxo.index != null) { + result.index = utxo.index; + } + if (utxo.address != null) { + result.address = utxo.address; + } + if (utxo.denomination != null) { + result.denomination = utxo.denomination; + } + return result; + } +} +exports.UTXO = UTXO; +//# sourceMappingURL=utxo.js.map \ No newline at end of file diff --git a/lib.commonjs/transaction/utxo.js.map b/lib.commonjs/transaction/utxo.js.map new file mode 100644 index 00000000..63c3d8fe --- /dev/null +++ b/lib.commonjs/transaction/utxo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"utxo.js","sourceRoot":"","sources":["../../src.ts/transaction/utxo.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib.commonjs/utils/maths.d.ts b/lib.commonjs/utils/maths.d.ts index 661f0dfd..676f5294 100644 --- a/lib.commonjs/utils/maths.d.ts +++ b/lib.commonjs/utils/maths.d.ts @@ -30,6 +30,10 @@ export declare function mask(_value: BigNumberish, _bits: Numeric): bigint; * a BigInt, then an ArgumentError will be thrown for %%name%%. */ export declare function getBigInt(value: BigNumberish, name?: string): bigint; +/** + * Returns absolute value of bigint %%value%%. + */ +export declare function bigIntAbs(value: BigNumberish): bigint; /** * Returns %%value%% as a bigint, validating it is valid as a bigint * value and that it is positive. diff --git a/lib.commonjs/utils/maths.d.ts.map b/lib.commonjs/utils/maths.d.ts.map index 626b19a4..f544e6fb 100644 --- a/lib.commonjs/utils/maths.d.ts.map +++ b/lib.commonjs/utils/maths.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"maths.d.ts","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,CAAC;AAY5C;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAetE;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAoBpE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAIjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAmBpE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlE;AAQD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAWjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAkBpE;AAGD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAEjE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAsBtE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAe1D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,CAKlE"} \ No newline at end of file +{"version":3,"file":"maths.d.ts","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,CAAC;AAY5C;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAetE;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAoBpE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAIjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAmBpE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAQrD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlE;AAQD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAWjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAkBpE;AAGD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAEjE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAsBtE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAe1D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,CAKlE"} \ No newline at end of file diff --git a/lib.commonjs/utils/maths.js b/lib.commonjs/utils/maths.js index 6ceb9fb9..aeb2cc55 100644 --- a/lib.commonjs/utils/maths.js +++ b/lib.commonjs/utils/maths.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.toQuantity = exports.toBeArray = exports.toBeHex = exports.toNumber = exports.getNumber = exports.toBigInt = exports.getUint = exports.getBigInt = exports.mask = exports.toTwos = exports.fromTwos = void 0; +exports.toQuantity = exports.toBeArray = exports.toBeHex = exports.toNumber = exports.getNumber = exports.toBigInt = exports.getUint = exports.bigIntAbs = exports.getBigInt = exports.mask = exports.toTwos = exports.fromTwos = void 0; /** * Some mathematic operations. * @@ -96,6 +96,18 @@ function getBigInt(value, name) { (0, errors_js_1.assertArgument)(false, "invalid BigNumberish value", name || "value", value); } exports.getBigInt = getBigInt; +/** + * Returns absolute value of bigint %%value%%. + */ +function bigIntAbs(value) { + value = getBigInt(value); + // if value is negative (including -0), return -value, else return value + if (value === -BN_0 || value < BN_0) { + return -value; + } + return value; +} +exports.bigIntAbs = bigIntAbs; /** * Returns %%value%% as a bigint, validating it is valid as a bigint * value and that it is positive. diff --git a/lib.commonjs/utils/maths.js.map b/lib.commonjs/utils/maths.js.map index d9d0e2a5..f59e45f3 100644 --- a/lib.commonjs/utils/maths.js.map +++ b/lib.commonjs/utils/maths.js.map @@ -1 +1 @@ -{"version":3,"file":"maths.js","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"maths.js","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib.esm/_tests/test-utxo-coinselection.d.ts b/lib.esm/_tests/test-utxo-coinselection.d.ts new file mode 100644 index 00000000..c1dd221a --- /dev/null +++ b/lib.esm/_tests/test-utxo-coinselection.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=test-utxo-coinselection.d.ts.map \ No newline at end of file diff --git a/lib.esm/_tests/test-utxo-coinselection.d.ts.map b/lib.esm/_tests/test-utxo-coinselection.d.ts.map new file mode 100644 index 00000000..a0513c5c --- /dev/null +++ b/lib.esm/_tests/test-utxo-coinselection.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"test-utxo-coinselection.d.ts","sourceRoot":"","sources":["../../src.ts/_tests/test-utxo-coinselection.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/lib.esm/_tests/test-utxo-coinselection.js b/lib.esm/_tests/test-utxo-coinselection.js new file mode 100644 index 00000000..98879c90 --- /dev/null +++ b/lib.esm/_tests/test-utxo-coinselection.js @@ -0,0 +1,98 @@ +import assert from "assert"; +import { FewestCoinSelector } from "../transaction/coinselector-fewest"; +import { denominations } from "../transaction/utxo"; +const TEST_SPEND_ADDRESS = "0x00539bc2CE3eD0FD039c582CB700EF5398bB0491"; +const TEST_RECEIVE_ADDRESS = "0x02b9B1D30B6cCdc7d908B82739ce891463c3FA19"; +// Utility function to create UTXOs (adjust as necessary) +function createUTXOs(denominations) { + return denominations.map(denomination => ({ + denomination, + address: TEST_SPEND_ADDRESS + })); +} +describe("FewestCoinSelector", function () { + describe("Selecting valid UTXOs", function () { + it("selects a single UTXO that exactly matches the target amount", function () { + const availableUTXOs = createUTXOs([denominations[1], denominations[2], denominations[3]]); // .065 Qi + const targetSpend = { value: denominations[3], address: TEST_RECEIVE_ADDRESS }; // .05 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // A single 0.05 Qi UTXO should have been selected + assert.strictEqual(result.inputs.length, 1); + assert.strictEqual(result.inputs[0].denomination, denominations[3]); + // A single new 0.05 Qi UTXO should have been outputed + assert.strictEqual(result.spendOutputs.length, 1); + assert.strictEqual(result.spendOutputs[0].denomination, denominations[3]); + // No change should be returned + assert.strictEqual(result.changeOutputs.length, 0); + }); + it("selects multiple UTXOs whose combined value meets the target amount", function () { + const availableUTXOs = createUTXOs([denominations[1], denominations[2], denominations[2], denominations[3]]); // .075 Qi + const targetSpend = { value: denominations[2] + denominations[3], address: TEST_RECEIVE_ADDRESS }; // .06 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // 2 UTXOs should have been selected for a total of .06 Qi + assert.strictEqual(result.inputs.length, 2); + const inputValue = result.inputs[0].denomination + result.inputs[1].denomination; + assert.strictEqual(inputValue, denominations[2] + denominations[3]); + // 2 new UTxOs should have been outputed for a total of .06 Qi + assert.strictEqual(result.spendOutputs.length, 2); + const spendValue = result.spendOutputs[0].denomination + result.spendOutputs[1].denomination; + assert.strictEqual(spendValue, denominations[2] + denominations[3]); + // No change should be returned + assert.strictEqual(result.changeOutputs.length, 0); + }); + it("selects a single UTXO that is larger than the target amount, ensuring change is correctly calculated", function () { + const availableUTXOs = createUTXOs([denominations[2], denominations[4]]); // .11 Qi + const targetSpend = { value: denominations[3], address: TEST_RECEIVE_ADDRESS }; // .05 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // A single 0.1 Qi UTXO should have been selected + assert.strictEqual(result.inputs.length, 1); + assert.strictEqual(result.inputs[0].denomination, denominations[4]); + // A single new 0.05 Qi UTXO should have been outputed + assert.strictEqual(result.spendOutputs.length, 1); + assert.strictEqual(result.spendOutputs[0].denomination, denominations[3]); + // 0.05 Qi should be returned in change + assert.strictEqual(result.changeOutputs.length, 1); + assert.strictEqual(result.changeOutputs[0].denomination, denominations[3]); + }); + it("selects multiple UTXOs where the total exceeds the target amount, ensuring change is correctly calculated", function () { + const availableUTXOs = createUTXOs([ + denominations[2], + denominations[4], + denominations[4], + denominations[4], + denominations[5] + ]); // .56 Qi + const targetSpend = { value: denominations[6], address: TEST_RECEIVE_ADDRESS }; // .5 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + // 4 UTXOs should have been selected for a total of .55 Qi + assert.strictEqual(result.inputs.length, 4); + const inputValue = result.inputs[0].denomination + result.inputs[1].denomination + result.inputs[2].denomination + result.inputs[3].denomination; + assert.strictEqual(inputValue, denominations[4] + denominations[4] + denominations[4] + denominations[5]); + // A single new 0.5 Qi UTXO should have been outputed + assert.strictEqual(result.spendOutputs.length, 1); + assert.strictEqual(result.spendOutputs[0].denomination, denominations[6]); + // 0.05 Qi should be returned in change + assert.strictEqual(result.changeOutputs.length, 1); + assert.strictEqual(result.changeOutputs[0].denomination, denominations[3]); + }); + }); + describe("Selecting valid UTXOs", function () { + it("throws an error when there are insufficient funds", function () { + const selector = new FewestCoinSelector(createUTXOs([denominations[0], denominations[0]])); + assert.throws(() => selector.performSelection({ value: denominations[3], address: TEST_RECEIVE_ADDRESS }), /Insufficient funds/); + }); + it("throws an error when no UTXOs are available", function () { + const selector = new FewestCoinSelector([]); + assert.throws(() => selector.performSelection({ value: denominations[2], address: TEST_RECEIVE_ADDRESS }), /No UTXOs available/); + }); + it("throws an error when the target amount is negative", function () { + const selector = new FewestCoinSelector(createUTXOs([denominations[2], denominations[2]])); + assert.throws(() => selector.performSelection({ value: -denominations[1], address: TEST_RECEIVE_ADDRESS }), /Target amount must be greater than 0/); + }); + }); +}); +//# sourceMappingURL=test-utxo-coinselection.js.map \ No newline at end of file diff --git a/lib.esm/_tests/test-utxo-coinselection.js.map b/lib.esm/_tests/test-utxo-coinselection.js.map new file mode 100644 index 00000000..0c56d7cb --- /dev/null +++ b/lib.esm/_tests/test-utxo-coinselection.js.map @@ -0,0 +1 @@ +{"version":3,"file":"test-utxo-coinselection.js","sourceRoot":"","sources":["../../src.ts/_tests/test-utxo-coinselection.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib.esm/transaction/abstract-coinselector.d.ts b/lib.esm/transaction/abstract-coinselector.d.ts new file mode 100644 index 00000000..1e73fa8c --- /dev/null +++ b/lib.esm/transaction/abstract-coinselector.d.ts @@ -0,0 +1,50 @@ +import { UTXO, UTXOLike } from "./utxo.js"; +export type SpendTarget = { + address: string; + value: bigint; +}; +export type SelectedCoinsResult = { + inputs: UTXO[]; + spendOutputs: UTXO[]; + changeOutputs: UTXO[]; +}; +/** + * An **AbstractCoinSelector** provides a base class for other sub-classes to + * implement the functionality for selecting UTXOs for a spend and to properly + * handle spend and change outputs. + * + * This class is abstract and should not be used directly. Sub-classes should + * implement the [[performSelection]] method to provide the actual coin + * selection logic. + * + * @abstract + */ +export declare abstract class AbstractCoinSelector { + #private; + get availableUXTOs(): UTXO[]; + set availableUXTOs(value: UTXOLike[]); + get spendOutputs(): UTXO[]; + set spendOutputs(value: UTXOLike[]); + get changeOutputs(): UTXO[]; + set changeOutputs(value: UTXOLike[]); + /** + * Constructs a new AbstractCoinSelector instance with an empty UTXO array. + */ + constructor(availableUXTOs?: UTXOLike[]); + /** + * This method should be implemented by sub-classes to provide the actual + * coin selection logic. It should select UTXOs from the available UTXOs + * that sum to the target amount and return the selected UTXOs as well as + * the spend and change outputs. + * @param target The target amount to select UTXOs for. + */ + abstract performSelection(target: SpendTarget): SelectedCoinsResult; + /** + * Validates the provided UTXO instance. In order to be valid for coin + * selection, the UTXO must have a valid address and denomination. + * @param utxo The UTXO instance to validate. + * @throws An error if the UTXO instance is invalid. + */ + protected _validateUTXO(utxo: UTXO): void; +} +//# sourceMappingURL=abstract-coinselector.d.ts.map \ No newline at end of file diff --git a/lib.esm/transaction/abstract-coinselector.d.ts.map b/lib.esm/transaction/abstract-coinselector.d.ts.map new file mode 100644 index 00000000..c207e70d --- /dev/null +++ b/lib.esm/transaction/abstract-coinselector.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"abstract-coinselector.d.ts","sourceRoot":"","sources":["../../src.ts/transaction/abstract-coinselector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,MAAM,WAAW,GAAG;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAC9B,MAAM,EAAE,IAAI,EAAE,CAAC;IACf,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,aAAa,EAAE,IAAI,EAAE,CAAC;CACzB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,8BAAsB,oBAAoB;;IAKtC,IAAI,cAAc,IAAI,IAAI,EAAE,CAAiC;IAC7D,IAAI,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,EAMnC;IAED,IAAI,YAAY,IAAI,IAAI,EAAE,CAA+B;IACzD,IAAI,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,EAEjC;IAED,IAAI,aAAa,IAAI,IAAI,EAAE,CAAgC;IAC3D,IAAI,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,EAElC;IAED;;OAEG;gBACS,cAAc,GAAE,QAAQ,EAAO;IAU3C;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB;IAEnE;;;;;OAKG;IACH,SAAS,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;CAY5C"} \ No newline at end of file diff --git a/lib.esm/transaction/abstract-coinselector.js b/lib.esm/transaction/abstract-coinselector.js new file mode 100644 index 00000000..a3c8dc34 --- /dev/null +++ b/lib.esm/transaction/abstract-coinselector.js @@ -0,0 +1,60 @@ +import { UTXO } from "./utxo.js"; +/** + * An **AbstractCoinSelector** provides a base class for other sub-classes to + * implement the functionality for selecting UTXOs for a spend and to properly + * handle spend and change outputs. + * + * This class is abstract and should not be used directly. Sub-classes should + * implement the [[performSelection]] method to provide the actual coin + * selection logic. + * + * @abstract + */ +export class AbstractCoinSelector { + #availableUXTOs; + #spendOutputs; + #changeOutputs; + get availableUXTOs() { return this.#availableUXTOs; } + set availableUXTOs(value) { + this.#availableUXTOs = value.map((val) => { + const utxo = UTXO.from(val); + this._validateUTXO(utxo); + return utxo; + }); + } + get spendOutputs() { return this.#spendOutputs; } + set spendOutputs(value) { + this.#spendOutputs = value.map((utxo) => UTXO.from(utxo)); + } + get changeOutputs() { return this.#changeOutputs; } + set changeOutputs(value) { + this.#changeOutputs = value.map((utxo) => UTXO.from(utxo)); + } + /** + * Constructs a new AbstractCoinSelector instance with an empty UTXO array. + */ + constructor(availableUXTOs = []) { + this.#availableUXTOs = availableUXTOs.map((val) => { + const utxo = UTXO.from(val); + this._validateUTXO(utxo); + return utxo; + }); + this.#spendOutputs = []; + this.#changeOutputs = []; + } + /** + * Validates the provided UTXO instance. In order to be valid for coin + * selection, the UTXO must have a valid address and denomination. + * @param utxo The UTXO instance to validate. + * @throws An error if the UTXO instance is invalid. + */ + _validateUTXO(utxo) { + if (utxo.address == null) { + throw new Error("UTXO address is required"); + } + if (utxo.denomination == null) { + throw new Error("UTXO denomination is required"); + } + } +} +//# sourceMappingURL=abstract-coinselector.js.map \ No newline at end of file diff --git a/lib.esm/transaction/abstract-coinselector.js.map b/lib.esm/transaction/abstract-coinselector.js.map new file mode 100644 index 00000000..1736d803 --- /dev/null +++ b/lib.esm/transaction/abstract-coinselector.js.map @@ -0,0 +1 @@ +{"version":3,"file":"abstract-coinselector.js","sourceRoot":"","sources":["../../src.ts/transaction/abstract-coinselector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAY,MAAM,WAAW,CAAC;AAa3C;;;;;;;;;;GAUG;AACH,MAAM,OAAgB,oBAAoB;IACtC,eAAe,CAAS;IACxB,aAAa,CAAS;IACtB,cAAc,CAAS;IAEvB,IAAI,cAAc,KAAa,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC;IAC7D,IAAI,cAAc,CAAC,KAAiB;QAChC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,GAAa,EAAE,EAAE;YAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;IACP,CAAC;IAED,IAAI,YAAY,KAAa,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IACzD,IAAI,YAAY,CAAC,KAAiB;QAC9B,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,aAAa,KAAa,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IAC3D,IAAI,aAAa,CAAC,KAAiB;QAC/B,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAc,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,CAAC;IAED;;OAEG;IACH,YAAY,iBAA6B,EAAE;QACvC,IAAI,CAAC,eAAe,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,GAAa,EAAE,EAAE;YACxD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;IAC7B,CAAC;IAWD;;;;;OAKG;IACO,aAAa,CAAC,IAAU;QAC9B,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,EAAE;YACtB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;SAC/C;QAED,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;SACpD;IACL,CAAC;CAIJ"} \ No newline at end of file diff --git a/lib.esm/transaction/coinselector-fewest.d.ts b/lib.esm/transaction/coinselector-fewest.d.ts new file mode 100644 index 00000000..8b597438 --- /dev/null +++ b/lib.esm/transaction/coinselector-fewest.d.ts @@ -0,0 +1,24 @@ +import { AbstractCoinSelector, SelectedCoinsResult, SpendTarget } from "./abstract-coinselector.js"; +/** + * The FewestCoinSelector class provides a coin selection algorithm that selects + * the fewest UTXOs required to meet the target amount. This algorithm is useful + * for minimizing the size of the transaction and the fees associated with it. + * + * This class is a sub-class of [[AbstractCoinSelector]] and implements the + * [[performSelection]] method to provide the actual coin selection logic. + */ +export declare class FewestCoinSelector extends AbstractCoinSelector { + /** + * The largest first coin selection algorithm. + * + * This algorithm selects the largest UTXOs first, and continues to select UTXOs until the + * target amount is reached. If the total value of the selected UTXOs is greater than the + * target amount, the remaining value is returned as a change output. + * @param target The target amount to select UTXOs for. + */ + performSelection(target: SpendTarget): SelectedCoinsResult; + private sortUTXOsByDenomination; + private validateTarget; + private validateUTXOs; +} +//# sourceMappingURL=coinselector-fewest.d.ts.map \ No newline at end of file diff --git a/lib.esm/transaction/coinselector-fewest.d.ts.map b/lib.esm/transaction/coinselector-fewest.d.ts.map new file mode 100644 index 00000000..d6100187 --- /dev/null +++ b/lib.esm/transaction/coinselector-fewest.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"coinselector-fewest.d.ts","sourceRoot":"","sources":["../../src.ts/transaction/coinselector-fewest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAIpG;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,oBAAoB;IAExD;;;;;;;OAOG;IACH,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB;IA8G1D,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;CAMxB"} \ No newline at end of file diff --git a/lib.esm/transaction/coinselector-fewest.js b/lib.esm/transaction/coinselector-fewest.js new file mode 100644 index 00000000..b76a92a0 --- /dev/null +++ b/lib.esm/transaction/coinselector-fewest.js @@ -0,0 +1,139 @@ +import { bigIntAbs } from "../utils/maths.js"; +import { AbstractCoinSelector } from "./abstract-coinselector.js"; +import { UTXO, denominate } from "./utxo.js"; +/** + * The FewestCoinSelector class provides a coin selection algorithm that selects + * the fewest UTXOs required to meet the target amount. This algorithm is useful + * for minimizing the size of the transaction and the fees associated with it. + * + * This class is a sub-class of [[AbstractCoinSelector]] and implements the + * [[performSelection]] method to provide the actual coin selection logic. + */ +export class FewestCoinSelector extends AbstractCoinSelector { + /** + * The largest first coin selection algorithm. + * + * This algorithm selects the largest UTXOs first, and continues to select UTXOs until the + * target amount is reached. If the total value of the selected UTXOs is greater than the + * target amount, the remaining value is returned as a change output. + * @param target The target amount to select UTXOs for. + */ + performSelection(target) { + this.validateTarget(target); + this.validateUTXOs(); + const sortedUTXOs = this.sortUTXOsByDenomination(this.availableUXTOs, "desc"); + let totalValue = BigInt(0); + let selectedUTXOs = []; + // Get UTXOs that meets or exceeds the target value + const UTXOsEqualOrGreaterThanTarget = sortedUTXOs.filter(utxo => utxo.denomination && utxo.denomination >= target.value); + if (UTXOsEqualOrGreaterThanTarget.length > 0) { + // Find the smallest UTXO that meets or exceeds the target value + const optimalUTXO = UTXOsEqualOrGreaterThanTarget.reduce((minDenominationUTXO, currentUTXO) => { + if (!currentUTXO.denomination) + return minDenominationUTXO; + return currentUTXO.denomination < minDenominationUTXO.denomination ? currentUTXO : minDenominationUTXO; + }, UTXOsEqualOrGreaterThanTarget[0]); // Initialize with the first UTXO in the list + selectedUTXOs.push(optimalUTXO); + totalValue += optimalUTXO.denomination; + } + else { + // If no single UTXO meets or exceeds the target, aggregate smaller denominations + // until the target is met/exceeded or there are no more UTXOs to aggregate + while (sortedUTXOs.length > 0 && totalValue < target.value) { + const nextOptimalUTXO = sortedUTXOs.reduce((closest, utxo) => { + if (!utxo.denomination) + return closest; + // Prioritize UTXOs that bring totalValue closer to target.value + const absThisDiff = bigIntAbs(target.value - (totalValue + utxo.denomination)); + const currentClosestDiff = closest && closest.denomination + ? bigIntAbs(target.value - (totalValue + closest.denomination)) + : BigInt(Infinity); + return absThisDiff < currentClosestDiff ? utxo : closest; + }, sortedUTXOs[0]); + // Add the selected UTXO to the selection and update totalValue + selectedUTXOs.push(nextOptimalUTXO); + totalValue += nextOptimalUTXO.denomination; + // Remove the selected UTXO from the list of available UTXOs + const index = sortedUTXOs.findIndex(utxo => utxo.denomination === nextOptimalUTXO.denomination && utxo.address === nextOptimalUTXO.address); + sortedUTXOs.splice(index, 1); + } + } + // Check if the selected UTXOs meet or exceed the target amount + if (totalValue < target.value) { + throw new Error("Insufficient funds"); + } + // // Check if any denominations can be removed from the input set and it still remain valid + selectedUTXOs = this.sortUTXOsByDenomination(selectedUTXOs, "asc"); + let runningTotal = totalValue; + let lastRemovableIndex = -1; // Index of the last UTXO that can be removed + // Iterate through selectedUTXOs to find the last removable UTXO + for (let i = 0; i < selectedUTXOs.length; i++) { + const utxo = selectedUTXOs[i]; + if (utxo.denomination) { + if (runningTotal - utxo.denomination >= target.value) { + runningTotal -= utxo.denomination; + lastRemovableIndex = i; + } + else { + // Once a UTXO makes the total less than target.value, stop the loop + break; + } + } + } + if (lastRemovableIndex >= 0) { + totalValue -= selectedUTXOs[lastRemovableIndex].denomination; + selectedUTXOs.splice(lastRemovableIndex, 1); + } + // Break down the total spend into properly denominatated UTXOs + const spendDenominations = denominate(target.value); + this.spendOutputs = spendDenominations.map(denomination => { + const utxo = new UTXO(); + utxo.denomination = denomination; + utxo.address = target.address; + return utxo; + }); + // Calculate change to be returned + const change = totalValue - target.value; + // If there's change, break it down into properly denominatated UTXOs + if (change > BigInt(0)) { + const changeDenominations = denominate(change); + this.changeOutputs = changeDenominations.map(denomination => { + const utxo = new UTXO(); + utxo.denomination = denomination; + // We do not have access to change addresses here so leave it null + return utxo; + }); + } + else { + this.changeOutputs = []; + } + return { + inputs: selectedUTXOs, + spendOutputs: this.spendOutputs, + changeOutputs: this.changeOutputs, + }; + } + sortUTXOsByDenomination(utxos, direction) { + if (direction === "asc") { + return [...utxos].sort((a, b) => { + const diff = (a.denomination ?? BigInt(0)) - (b.denomination ?? BigInt(0)); + return diff > 0 ? 1 : diff < 0 ? -1 : 0; + }); + } + return [...utxos].sort((a, b) => { + const diff = (b.denomination ?? BigInt(0)) - (a.denomination ?? BigInt(0)); + return diff > 0 ? 1 : diff < 0 ? -1 : 0; + }); + } + validateTarget(target) { + if (target.value <= BigInt(0)) { + throw new Error("Target amount must be greater than 0"); + } + } + validateUTXOs() { + if (this.availableUXTOs.length === 0) { + throw new Error("No UTXOs available"); + } + } +} +//# sourceMappingURL=coinselector-fewest.js.map \ No newline at end of file diff --git a/lib.esm/transaction/coinselector-fewest.js.map b/lib.esm/transaction/coinselector-fewest.js.map new file mode 100644 index 00000000..bcb304f2 --- /dev/null +++ b/lib.esm/transaction/coinselector-fewest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"coinselector-fewest.js","sourceRoot":"","sources":["../../src.ts/transaction/coinselector-fewest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAoC,MAAM,4BAA4B,CAAC;AACpG,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAG7C;;;;;;;GAOG;AACH,MAAM,OAAO,kBAAmB,SAAQ,oBAAoB;IAExD;;;;;;;OAOG;IACH,gBAAgB,CAAC,MAAmB;QAChC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC5B,IAAI,CAAC,aAAa,EAAE,CAAC;QAErB,MAAM,WAAW,GAAG,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QAE9E,IAAI,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,aAAa,GAAW,EAAE,CAAC;QAE/B,mDAAmD;QACnD,MAAM,6BAA6B,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;QAEzH,IAAI,6BAA6B,CAAC,MAAM,GAAG,CAAC,EAAE;YAC1C,gEAAgE;YAChE,MAAM,WAAW,GAAG,6BAA6B,CAAC,MAAM,CAAC,CAAC,mBAAmB,EAAE,WAAW,EAAE,EAAE;gBAC1F,IAAI,CAAC,WAAW,CAAC,YAAY;oBAAE,OAAO,mBAAmB,CAAC;gBAC1D,OAAO,WAAW,CAAC,YAAY,GAAG,mBAAmB,CAAC,YAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,mBAAmB,CAAC;YAC5G,CAAC,EAAE,6BAA6B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,6CAA6C;YAEnF,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAChC,UAAU,IAAI,WAAW,CAAC,YAAa,CAAC;SAC3C;aAAM;YACH,iFAAiF;YACjF,2EAA2E;YAC3E,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE;gBACxD,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,CAAO,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;oBAC/D,IAAI,CAAC,IAAI,CAAC,YAAY;wBAAE,OAAO,OAAO,CAAC;oBAEvC,gEAAgE;oBAChE,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;oBAC/E,MAAM,kBAAkB,GAAG,OAAO,IAAI,OAAO,CAAC,YAAY;wBACtD,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;wBAC/D,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBAEvB,OAAO,WAAW,GAAG,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;gBAE7D,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;gBAEnB,+DAA+D;gBAC/D,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;gBACpC,UAAU,IAAI,eAAe,CAAC,YAAa,CAAC;gBAE5C,4DAA4D;gBAC5D,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,KAAK,eAAe,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,KAAK,eAAe,CAAC,OAAO,CAAC,CAAC;gBAC5I,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;aAChC;SACJ;QAED,+DAA+D;QAC/D,IAAI,UAAU,GAAG,MAAM,CAAC,KAAK,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;SACzC;QAED,4FAA4F;QAC5F,aAAa,GAAG,IAAI,CAAC,uBAAuB,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAEnE,IAAI,YAAY,GAAG,UAAU,CAAC;QAC9B,IAAI,kBAAkB,GAAG,CAAC,CAAC,CAAC,CAAC,6CAA6C;QAE1E,gEAAgE;QAChE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YAC3C,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,IAAI,CAAC,YAAY,EAAE;gBACnB,IAAI,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC,KAAK,EAAE;oBAClD,YAAY,IAAI,IAAI,CAAC,YAAY,CAAC;oBAClC,kBAAkB,GAAG,CAAC,CAAC;iBAC1B;qBAAM;oBACH,oEAAoE;oBACpE,MAAM;iBACT;aACJ;SACJ;QAED,IAAI,kBAAkB,IAAI,CAAC,EAAE;YACzB,UAAU,IAAI,aAAa,CAAC,kBAAkB,CAAC,CAAC,YAAa,CAAC;YAC9D,aAAa,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;SAC/C;QAED,+DAA+D;QAC/D,MAAM,kBAAkB,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpD,IAAI,CAAC,YAAY,GAAG,kBAAkB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE;YACtD,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;YACjC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YAC9B,OAAO,IAAI,CAAC;QAChB,CAAC,CAAC,CAAC;QAEH,kCAAkC;QAClC,MAAM,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC;QAEzC,qEAAqE;QACrE,IAAI,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE;YACpB,MAAM,mBAAmB,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,CAAC,aAAa,GAAG,mBAAmB,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE;gBACxD,MAAM,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;gBACjC,kEAAkE;gBAClE,OAAO,IAAI,CAAC;YAChB,CAAC,CAAC,CAAC;SACN;aAAM;YACH,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;SAC3B;QAED,OAAO;YACH,MAAM,EAAE,aAAa;YACrB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,aAAa,EAAE,IAAI,CAAC,aAAa;SACpC,CAAC;IACN,CAAC;IAEO,uBAAuB,CAAC,KAAa,EAAE,SAAyB;QACpE,IAAI,SAAS,KAAK,KAAK,EAAE;YACrB,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3E,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;SACN;QACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3E,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,cAAc,CAAC,MAAmB;QACtC,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE;YAC3B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;SAC3D;IACL,CAAC;IAEO,aAAa;QACjB,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE;YAClC,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;SACzC;IACL,CAAC;CAEJ"} \ No newline at end of file diff --git a/lib.esm/transaction/utxo.d.ts b/lib.esm/transaction/utxo.d.ts new file mode 100644 index 00000000..7fa6146b --- /dev/null +++ b/lib.esm/transaction/utxo.d.ts @@ -0,0 +1,58 @@ +import type { BigNumberish } from "../utils/index"; +export type OutPoint = { + txhash: string; + index: number; +}; +export type UTXOTransactionInput = { + previousOutPoint: OutPoint; + pubKey: Uint8Array; +}; +export interface UTXOEntry { + denomination: null | bigint; + address: null | string; +} +export type UTXOTransactionOutput = UTXOEntry; +export type UTXOTransaction = { + chainId: bigint; + inputs: UTXOTransactionInput[]; + outputs: UTXOTransactionOutput[]; + signature?: Uint8Array; +}; +export interface UTXOLike extends UTXOEntry { + txhash?: null | string; + index?: null | number; +} +export declare const denominations: bigint[]; +/** + * Given a value, returns an array of supported denominations that sum to the value. + * @param value The value to denominate. + * @returns Array of supported denominations that sum to the value. + */ +export declare function denominate(value: bigint): bigint[]; +export declare class UTXO implements UTXOLike { + #private; + get txhash(): null | string; + set txhash(value: null | string); + get index(): null | number; + set index(value: null | number); + get address(): null | string; + set address(value: null | string); + get denomination(): null | bigint; + set denomination(value: null | BigNumberish); + /** + * Constructs a new UTXO instance with null properties. + */ + constructor(); + /** + * Converts the UTXO instance to a JSON object. + * @returns A JSON representation of the UTXO instance. + */ + toJSON(): any; + /** + * Creates a UTXO instance from a UTXOLike object. + * @param utxo The UTXOLike object to create the UTXO instance from. + * @returns A new UTXO instance. + */ + static from(utxo: UTXOLike): UTXO; +} +//# sourceMappingURL=utxo.d.ts.map \ No newline at end of file diff --git a/lib.esm/transaction/utxo.d.ts.map b/lib.esm/transaction/utxo.d.ts.map new file mode 100644 index 00000000..d22529c2 --- /dev/null +++ b/lib.esm/transaction/utxo.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"utxo.d.ts","sourceRoot":"","sources":["../../src.ts/transaction/utxo.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,MAAM,QAAQ,GAAG;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IAC/B,gBAAgB,EAAE,QAAQ,CAAC;IAC3B,MAAM,EAAE,UAAU,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,SAAS;IACtB,YAAY,EAAE,IAAI,GAAG,MAAM,CAAC;IAC5B,OAAO,EAAE,IAAI,GAAG,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,OAAO,EAAE,qBAAqB,EAAE,CAAC;IACjC,SAAS,CAAC,EAAE,UAAU,CAAC;CAC1B,CAAC;AAEF,MAAM,WAAW,QAAS,SAAQ,SAAS;IACvC,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,EAkBjC,CAAC;AAsBF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BlD;AAED,qBAAa,IAAK,YAAW,QAAQ;;IAMjC,IAAI,MAAM,IAAI,IAAI,GAAG,MAAM,CAAyB;IACpD,IAAI,MAAM,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAE9B;IAED,IAAI,KAAK,IAAI,IAAI,GAAG,MAAM,CAAwB;IAClD,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAE7B;IAED,IAAI,OAAO,IAAI,IAAI,GAAG,MAAM,CAA0B;IACtD,IAAI,OAAO,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,EAE/B;IAED,IAAI,YAAY,IAAI,IAAI,GAAG,MAAM,CAA+B;IAChE,IAAI,YAAY,CAAC,KAAK,EAAE,IAAI,GAAG,YAAY,EAY1C;IAED;;OAEG;;IAQH;;;OAGG;IACH,MAAM,IAAI,GAAG;IASb;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;CAWpC"} \ No newline at end of file diff --git a/lib.esm/transaction/utxo.js b/lib.esm/transaction/utxo.js new file mode 100644 index 00000000..9faf735f --- /dev/null +++ b/lib.esm/transaction/utxo.js @@ -0,0 +1,143 @@ +import { getAddress } from "../address/index"; +import { getBigInt } from "../utils/index"; +; +export const denominations = [ + BigInt(1), + BigInt(5), + BigInt(10), + BigInt(50), + BigInt(100), + BigInt(250), + BigInt(500), + BigInt(1000), + BigInt(5000), + BigInt(10000), + BigInt(20000), + BigInt(50000), + BigInt(100000), + BigInt(1000000), + BigInt(10000000), + BigInt(100000000), + BigInt(1000000000), // 1000000 Qi +]; +/** + * Checks if the provided denomination is valid. + * @param denomination The denomination to check. + * @returns True if the denomination is valid, false otherwise. + */ +function isValidDenomination(denomination) { + return denominations.includes(denomination); +} +/** + * Handles conversion of string to bigint, specifically for transaction parameters. + * @param value The string value to convert. + * @param param The parameter name for error context. + * @returns The bigint representation of the input string. + */ +function handleBigInt(value, param) { + if (value === "0x") { + return BigInt(0); + } + return getBigInt(value, param); +} +/** + * Given a value, returns an array of supported denominations that sum to the value. + * @param value The value to denominate. + * @returns Array of supported denominations that sum to the value. + */ +export function denominate(value) { + if (value <= BigInt(0)) { + throw new Error("Value must be greater than 0"); + } + const result = []; + let remainingValue = value; + // Iterate through denominations in descending order + for (let i = denominations.length - 1; i >= 0; i--) { + const denomination = denominations[i]; + // Add the denomination to the result array as many times as possible + while (remainingValue >= denomination) { + result.push(denomination); + remainingValue -= denomination; + } + } + if (remainingValue > 0) { + throw new Error("Unable to match the value with available denominations"); + } + return result; +} +export class UTXO { + #txhash; + #index; + #address; + #denomination; + get txhash() { return this.#txhash; } + set txhash(value) { + this.#txhash = value; + } + get index() { return this.#index; } + set index(value) { + this.#index = value; + } + get address() { return this.#address; } + set address(value) { + this.#address = (value == null) ? null : getAddress(value); + } + get denomination() { return this.#denomination; } + set denomination(value) { + if (value == null) { + this.#denomination = null; + return; + } + const denominationBigInt = handleBigInt(value.toString(), "denomination"); + if (!isValidDenomination(denominationBigInt)) { + throw new Error("Invalid denomination value"); + } + this.#denomination = denominationBigInt; + } + /** + * Constructs a new UTXO instance with null properties. + */ + constructor() { + this.#txhash = null; + this.#index = null; + this.#address = null; + this.#denomination = null; + } + /** + * Converts the UTXO instance to a JSON object. + * @returns A JSON representation of the UTXO instance. + */ + toJSON() { + return { + txhash: this.txhash, + index: this.index, + address: this.address, + denomination: this.denomination, + }; + } + /** + * Creates a UTXO instance from a UTXOLike object. + * @param utxo The UTXOLike object to create the UTXO instance from. + * @returns A new UTXO instance. + */ + static from(utxo) { + if (utxo === null) { + return new UTXO(); + } + const result = utxo instanceof UTXO ? utxo : new UTXO(); + if (utxo.txhash != null) { + result.txhash = utxo.txhash; + } + if (utxo.index != null) { + result.index = utxo.index; + } + if (utxo.address != null) { + result.address = utxo.address; + } + if (utxo.denomination != null) { + result.denomination = utxo.denomination; + } + return result; + } +} +//# sourceMappingURL=utxo.js.map \ No newline at end of file diff --git a/lib.esm/transaction/utxo.js.map b/lib.esm/transaction/utxo.js.map new file mode 100644 index 00000000..3a3d55bc --- /dev/null +++ b/lib.esm/transaction/utxo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"utxo.js","sourceRoot":"","sources":["../../src.ts/transaction/utxo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAgB1C,CAAC;AAgBF,MAAM,CAAC,MAAM,aAAa,GAAa;IACnC,MAAM,CAAC,CAAC,CAAC;IACT,MAAM,CAAC,CAAC,CAAC;IACT,MAAM,CAAC,EAAE,CAAC;IACV,MAAM,CAAC,EAAE,CAAC;IACV,MAAM,CAAC,GAAG,CAAC;IACX,MAAM,CAAC,GAAG,CAAC;IACX,MAAM,CAAC,GAAG,CAAC;IACX,MAAM,CAAC,IAAI,CAAC;IACZ,MAAM,CAAC,IAAI,CAAC;IACZ,MAAM,CAAC,KAAK,CAAC;IACb,MAAM,CAAC,KAAK,CAAC;IACb,MAAM,CAAC,KAAK,CAAC;IACb,MAAM,CAAC,MAAM,CAAC;IACd,MAAM,CAAC,OAAO,CAAC;IACf,MAAM,CAAC,QAAQ,CAAC;IAChB,MAAM,CAAC,SAAS,CAAC;IACjB,MAAM,CAAC,UAAU,CAAC,EAAG,aAAa;CACrC,CAAC;AAEF;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,YAAoB;IAC7C,OAAO,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,KAAa,EAAE,KAAa;IAC9C,IAAI,KAAK,KAAK,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;KAAE;IACzC,OAAO,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa;IACpC,IAAI,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE;QACpB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;KACnD;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,cAAc,GAAG,KAAK,CAAC;IAE3B,oDAAoD;IACpD,KAAK,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE;QAChD,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QAEtC,qEAAqE;QACrE,OAAO,cAAc,IAAI,YAAY,EAAE;YACnC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC1B,cAAc,IAAI,YAAY,CAAC;SAClC;KACJ;IAED,IAAI,cAAc,GAAG,CAAC,EAAE;QACpB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;KAC7E;IAID,OAAO,MAAM,CAAC;AAClB,CAAC;AAED,MAAM,OAAO,IAAI;IACb,OAAO,CAAgB;IACvB,MAAM,CAAgB;IACtB,QAAQ,CAAgB;IACxB,aAAa,CAAgB;IAE7B,IAAI,MAAM,KAAoB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACpD,IAAI,MAAM,CAAC,KAAoB;QAC3B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,IAAI,KAAK,KAAoB,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,KAAoB;QAC1B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACxB,CAAC;IAED,IAAI,OAAO,KAAoB,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACtD,IAAI,OAAO,CAAC,KAAoB;QAC5B,IAAI,CAAC,QAAQ,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,YAAY,KAAoB,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;IAChE,IAAI,YAAY,CAAC,KAA0B;QACvC,IAAI,KAAK,IAAI,IAAI,EAAE;YACf,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC1B,OAAO;SACV;QAED,MAAM,kBAAkB,GAAG,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,cAAc,CAAC,CAAC;QAC1E,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,EAAE;YAC1C,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SACjD;QAED,IAAI,CAAC,aAAa,GAAG,kBAAkB,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH;QACI,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACH,MAAM;QACF,OAAO;YACH,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,YAAY,EAAE,IAAI,CAAC,YAAY;SAClC,CAAC;IACN,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,IAAI,CAAC,IAAc;QACtB,IAAI,IAAI,KAAK,IAAI,EAAE;YAAE,OAAO,IAAI,IAAI,EAAE,CAAC;SAAE;QAEzC,MAAM,MAAM,GAAG,IAAI,YAAY,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;QACxD,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE;YAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;SAAE;QACzD,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,EAAE;YAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;SAAE;QACtD,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,EAAE;YAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;SAAE;QAC5D,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE;YAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;SAAE;QAE3E,OAAO,MAAM,CAAC;IAClB,CAAC;CACJ"} \ No newline at end of file diff --git a/lib.esm/utils/maths.d.ts b/lib.esm/utils/maths.d.ts index 661f0dfd..676f5294 100644 --- a/lib.esm/utils/maths.d.ts +++ b/lib.esm/utils/maths.d.ts @@ -30,6 +30,10 @@ export declare function mask(_value: BigNumberish, _bits: Numeric): bigint; * a BigInt, then an ArgumentError will be thrown for %%name%%. */ export declare function getBigInt(value: BigNumberish, name?: string): bigint; +/** + * Returns absolute value of bigint %%value%%. + */ +export declare function bigIntAbs(value: BigNumberish): bigint; /** * Returns %%value%% as a bigint, validating it is valid as a bigint * value and that it is positive. diff --git a/lib.esm/utils/maths.d.ts.map b/lib.esm/utils/maths.d.ts.map index 626b19a4..f544e6fb 100644 --- a/lib.esm/utils/maths.d.ts.map +++ b/lib.esm/utils/maths.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"maths.d.ts","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,CAAC;AAY5C;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAetE;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAoBpE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAIjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAmBpE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlE;AAQD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAWjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAkBpE;AAGD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAEjE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAsBtE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAe1D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,CAKlE"} \ No newline at end of file +{"version":3,"file":"maths.d.ts","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,CAAC;AAY5C;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAetE;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAoBpE;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAIjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAmBpE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAQrD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlE;AAQD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAWjE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAkBpE;AAGD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,GAAG,MAAM,CAEjE;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAsBtE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,UAAU,CAe1D;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,GAAG,MAAM,CAKlE"} \ No newline at end of file diff --git a/lib.esm/utils/maths.js b/lib.esm/utils/maths.js index c669dd36..9d37bb87 100644 --- a/lib.esm/utils/maths.js +++ b/lib.esm/utils/maths.js @@ -89,6 +89,17 @@ export function getBigInt(value, name) { } assertArgument(false, "invalid BigNumberish value", name || "value", value); } +/** + * Returns absolute value of bigint %%value%%. + */ +export function bigIntAbs(value) { + value = getBigInt(value); + // if value is negative (including -0), return -value, else return value + if (value === -BN_0 || value < BN_0) { + return -value; + } + return value; +} /** * Returns %%value%% as a bigint, validating it is valid as a bigint * value and that it is positive. diff --git a/lib.esm/utils/maths.js.map b/lib.esm/utils/maths.js.map index bd7b9fa6..87b3491a 100644 --- a/lib.esm/utils/maths.js.map +++ b/lib.esm/utils/maths.js.map @@ -1 +1 @@ -{"version":3,"file":"maths.js","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"maths.js","sourceRoot":"","sources":["../../src.ts/utils/maths.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 84d20d1c..05c774f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,13 +22,16 @@ }, "devDependencies": { "@rollup/plugin-node-resolve": "15.0.2", + "@types/expect": "^24.3.0", "@types/google-protobuf": "^3.15.12", - "@types/mocha": "9.1.1", + "@types/mocha": "^9.1.1", "@types/semver": "7.5.0", + "axios": "^1.6.7", "c8": "7.12.0", "mocha": "10.0.0", "rollup": "3.21.5", "semver": "7.5.4", + "ts-mocha": "^10.0.0", "typescript": "5.0.4", "uglify-js": "3.17.0" }, @@ -41,6 +44,184 @@ "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==" }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", @@ -56,6 +237,47 @@ "node": ">=8" } }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -150,12 +372,28 @@ } } }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/expect": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-24.3.0.tgz", + "integrity": "sha512-aq5Z+YFBz5o2b6Sp1jigx5nsmoZMK5Ceurjwy6PZmRv7dEi1jLtkARfvB1ME+OXJUG+7TZUDcv3WoCr/aor6dQ==", + "deprecated": "This is a stub types definition. expect provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "expect": "*" + } + }, "node_modules/@types/google-protobuf": { "version": "3.15.12", "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.15.12.tgz", @@ -168,6 +406,31 @@ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "optional": true + }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -191,6 +454,27 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -254,15 +538,26 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, "node_modules/axios": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dev": true, "dependencies": { "follow-redirects": "^1.15.4", "form-data": "^4.0.0", @@ -311,6 +606,12 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, "node_modules/builtin-modules": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", @@ -404,6 +705,21 @@ "fsevents": "~2.3.2" } }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -437,6 +753,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -518,6 +835,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -531,6 +849,15 @@ "node": ">=0.3.1" } }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/dotenv": { "version": "16.4.5", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", @@ -575,6 +902,22 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -616,6 +959,7 @@ "version": "1.15.5", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "dev": true, "funding": [ { "type": "individual", @@ -648,6 +992,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -754,6 +1099,12 @@ "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==" }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -953,6 +1304,88 @@ "node": ">=8" } }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -965,6 +1398,19 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1023,10 +1469,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "engines": { "node": ">= 0.6" } @@ -1035,6 +1501,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -1054,6 +1521,27 @@ "node": ">=10" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mocha": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.0.0.tgz", @@ -1230,10 +1718,37 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true }, "node_modules/randombytes": { "version": "2.1.0", @@ -1244,6 +1759,12 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1384,6 +1905,55 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1410,6 +1980,16 @@ "node": ">=8" } }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -1494,6 +2074,71 @@ "node": ">=8.0" } }, + "node_modules/ts-mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz", + "integrity": "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==", + "dev": true, + "dependencies": { + "ts-node": "7.0.1" + }, + "bin": { + "ts-mocha": "bin/ts-mocha" + }, + "engines": { + "node": ">= 6.X.X" + }, + "optionalDependencies": { + "tsconfig-paths": "^3.5.0" + }, + "peerDependencies": { + "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" + } + }, + "node_modules/ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "dependencies": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, "node_modules/tslib": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", @@ -1659,6 +2304,15 @@ "node": ">=10" } }, + "node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index fe669516..14944972 100644 --- a/package.json +++ b/package.json @@ -24,12 +24,15 @@ "devDependencies": { "@rollup/plugin-node-resolve": "15.0.2", "@types/google-protobuf": "^3.15.12", - "@types/mocha": "9.1.1", + "@types/expect": "^24.3.0", + "@types/mocha": "^9.1.1", "@types/semver": "7.5.0", + "axios": "^1.6.7", "c8": "7.12.0", "mocha": "10.0.0", "rollup": "3.21.5", "semver": "7.5.4", + "ts-mocha": "^10.0.0", "typescript": "5.0.4", "uglify-js": "3.17.0" }, diff --git a/src.ts/_tests/test-utxo-coinselection.ts b/src.ts/_tests/test-utxo-coinselection.ts new file mode 100644 index 00000000..91979404 --- /dev/null +++ b/src.ts/_tests/test-utxo-coinselection.ts @@ -0,0 +1,119 @@ +import assert from "assert"; +import { FewestCoinSelector } from "../transaction/coinselector-fewest"; +import { UTXOLike, denominations } from "../transaction/utxo"; + +const TEST_SPEND_ADDRESS = "0x00539bc2CE3eD0FD039c582CB700EF5398bB0491"; +const TEST_RECEIVE_ADDRESS = "0x02b9B1D30B6cCdc7d908B82739ce891463c3FA19"; + +// Utility function to create UTXOs (adjust as necessary) +function createUTXOs(denominations: bigint[]): UTXOLike[] { + return denominations.map(denomination => ({ + denomination, + address: TEST_SPEND_ADDRESS + })); +} + +describe("FewestCoinSelector", function () { + describe("Selecting valid UTXOs", function () { + it("selects a single UTXO that exactly matches the target amount", function () { + const availableUTXOs = createUTXOs([denominations[1], denominations[2], denominations[3]]); // .065 Qi + const targetSpend = { value: denominations[3], address: TEST_RECEIVE_ADDRESS }; // .05 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + + // A single 0.05 Qi UTXO should have been selected + assert.strictEqual(result.inputs.length, 1); + assert.strictEqual(result.inputs[0].denomination, denominations[3]); + + // A single new 0.05 Qi UTXO should have been outputed + assert.strictEqual(result.spendOutputs.length, 1); + assert.strictEqual(result.spendOutputs[0].denomination, denominations[3]); + + // No change should be returned + assert.strictEqual(result.changeOutputs.length, 0); + }); + + it("selects multiple UTXOs whose combined value meets the target amount", function () { + const availableUTXOs = createUTXOs([denominations[1], denominations[2], denominations[2], denominations[3]]); // .075 Qi + const targetSpend = { value: denominations[2] + denominations[3], address: TEST_RECEIVE_ADDRESS }; // .06 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + + // 2 UTXOs should have been selected for a total of .06 Qi + assert.strictEqual(result.inputs.length, 2); + const inputValue = result.inputs[0].denomination! + result.inputs[1].denomination!; + assert.strictEqual(inputValue, denominations[2] + denominations[3]); + + // 2 new UTxOs should have been outputed for a total of .06 Qi + assert.strictEqual(result.spendOutputs.length, 2); + const spendValue = result.spendOutputs[0].denomination! + result.spendOutputs[1].denomination!; + assert.strictEqual(spendValue, denominations[2] + denominations[3]); + + // No change should be returned + assert.strictEqual(result.changeOutputs.length, 0); + }); + + it("selects a single UTXO that is larger than the target amount, ensuring change is correctly calculated", function () { + const availableUTXOs = createUTXOs([denominations[2], denominations[4]]); // .11 Qi + const targetSpend = { value: denominations[3], address: TEST_RECEIVE_ADDRESS }; // .05 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + + // A single 0.1 Qi UTXO should have been selected + assert.strictEqual(result.inputs.length, 1); + assert.strictEqual(result.inputs[0].denomination, denominations[4]); + + // A single new 0.05 Qi UTXO should have been outputed + assert.strictEqual(result.spendOutputs.length, 1); + assert.strictEqual(result.spendOutputs[0].denomination, denominations[3]); + + // 0.05 Qi should be returned in change + assert.strictEqual(result.changeOutputs.length, 1); + assert.strictEqual(result.changeOutputs[0].denomination, denominations[3]); + }); + + it("selects multiple UTXOs where the total exceeds the target amount, ensuring change is correctly calculated", function () { + const availableUTXOs = createUTXOs([ + denominations[2], + denominations[4], + denominations[4], + denominations[4], + denominations[5] + ]); // .56 Qi + const targetSpend = { value: denominations[6], address: TEST_RECEIVE_ADDRESS }; // .5 Qi + const selector = new FewestCoinSelector(availableUTXOs); + const result = selector.performSelection(targetSpend); + + // 4 UTXOs should have been selected for a total of .55 Qi + assert.strictEqual(result.inputs.length, 4); + const inputValue = result.inputs[0].denomination! + result.inputs[1].denomination! + result.inputs[2].denomination! + result.inputs[3].denomination!; + assert.strictEqual(inputValue, denominations[4] + denominations[4] + denominations[4] + denominations[5]); + + // A single new 0.5 Qi UTXO should have been outputed + assert.strictEqual(result.spendOutputs.length, 1); + assert.strictEqual(result.spendOutputs[0].denomination, denominations[6]); + + // 0.05 Qi should be returned in change + assert.strictEqual(result.changeOutputs.length, 1); + assert.strictEqual(result.changeOutputs[0].denomination, denominations[3]); + + }); + }); + + describe("Selecting valid UTXOs", function () { + it("throws an error when there are insufficient funds", function () { + const selector = new FewestCoinSelector(createUTXOs([denominations[0], denominations[0]])); + assert.throws(() => selector.performSelection({ value: denominations[3], address: TEST_RECEIVE_ADDRESS }), /Insufficient funds/); + }); + + it("throws an error when no UTXOs are available", function () { + const selector = new FewestCoinSelector([]); + assert.throws(() => selector.performSelection({ value: denominations[2], address: TEST_RECEIVE_ADDRESS }), /No UTXOs available/); + }); + + it("throws an error when the target amount is negative", function () { + const selector = new FewestCoinSelector(createUTXOs([denominations[2], denominations[2]])); + assert.throws(() => selector.performSelection({ value: -denominations[1], address: TEST_RECEIVE_ADDRESS }), /Target amount must be greater than 0/); + }); + }); +}); \ No newline at end of file diff --git a/src.ts/transaction/abstract-coinselector.ts b/src.ts/transaction/abstract-coinselector.ts new file mode 100644 index 00000000..a93f030e --- /dev/null +++ b/src.ts/transaction/abstract-coinselector.ts @@ -0,0 +1,89 @@ +import { UTXO, UTXOLike } from "./utxo.js"; + +export type SpendTarget = { + address: string; + value: bigint; +}; + +export type SelectedCoinsResult = { + inputs: UTXO[]; + spendOutputs: UTXO[]; + changeOutputs: UTXO[]; +}; + +/** + * An **AbstractCoinSelector** provides a base class for other sub-classes to + * implement the functionality for selecting UTXOs for a spend and to properly + * handle spend and change outputs. + * + * This class is abstract and should not be used directly. Sub-classes should + * implement the [[performSelection]] method to provide the actual coin + * selection logic. + * + * @abstract + */ +export abstract class AbstractCoinSelector { + #availableUXTOs: UTXO[]; + #spendOutputs: UTXO[]; + #changeOutputs: UTXO[]; + + get availableUXTOs(): UTXO[] { return this.#availableUXTOs; } + set availableUXTOs(value: UTXOLike[]) { + this.#availableUXTOs = value.map((val: UTXOLike) => { + const utxo = UTXO.from(val); + this._validateUTXO(utxo); + return utxo; + }); + } + + get spendOutputs(): UTXO[] { return this.#spendOutputs; } + set spendOutputs(value: UTXOLike[]) { + this.#spendOutputs = value.map((utxo: UTXOLike) => UTXO.from(utxo)); + } + + get changeOutputs(): UTXO[] { return this.#changeOutputs; } + set changeOutputs(value: UTXOLike[]) { + this.#changeOutputs = value.map((utxo: UTXOLike) => UTXO.from(utxo)); + } + + /** + * Constructs a new AbstractCoinSelector instance with an empty UTXO array. + */ + constructor(availableUXTOs: UTXOLike[] = []) { + this.#availableUXTOs = availableUXTOs.map((val: UTXOLike) => { + const utxo = UTXO.from(val); + this._validateUTXO(utxo); + return utxo; + }); + this.#spendOutputs = []; + this.#changeOutputs = []; + } + + /** + * This method should be implemented by sub-classes to provide the actual + * coin selection logic. It should select UTXOs from the available UTXOs + * that sum to the target amount and return the selected UTXOs as well as + * the spend and change outputs. + * @param target The target amount to select UTXOs for. + */ + abstract performSelection(target: SpendTarget): SelectedCoinsResult; + + /** + * Validates the provided UTXO instance. In order to be valid for coin + * selection, the UTXO must have a valid address and denomination. + * @param utxo The UTXO instance to validate. + * @throws An error if the UTXO instance is invalid. + */ + protected _validateUTXO(utxo: UTXO): void { + if (utxo.address == null) { + throw new Error("UTXO address is required"); + } + + if (utxo.denomination == null) { + throw new Error("UTXO denomination is required"); + } + } + + + +} diff --git a/src.ts/transaction/coinselector-fewest.ts b/src.ts/transaction/coinselector-fewest.ts new file mode 100644 index 00000000..deea4c03 --- /dev/null +++ b/src.ts/transaction/coinselector-fewest.ts @@ -0,0 +1,159 @@ +import { bigIntAbs } from "../utils/maths.js"; +import { AbstractCoinSelector, SelectedCoinsResult, SpendTarget } from "./abstract-coinselector.js"; +import { UTXO, denominate } from "./utxo.js"; + + +/** + * The FewestCoinSelector class provides a coin selection algorithm that selects + * the fewest UTXOs required to meet the target amount. This algorithm is useful + * for minimizing the size of the transaction and the fees associated with it. + * + * This class is a sub-class of [[AbstractCoinSelector]] and implements the + * [[performSelection]] method to provide the actual coin selection logic. + */ +export class FewestCoinSelector extends AbstractCoinSelector { + + /** + * The largest first coin selection algorithm. + * + * This algorithm selects the largest UTXOs first, and continues to select UTXOs until the + * target amount is reached. If the total value of the selected UTXOs is greater than the + * target amount, the remaining value is returned as a change output. + * @param target The target amount to select UTXOs for. + */ + performSelection(target: SpendTarget): SelectedCoinsResult { + this.validateTarget(target); + this.validateUTXOs(); + + const sortedUTXOs = this.sortUTXOsByDenomination(this.availableUXTOs, "desc"); + + let totalValue = BigInt(0); + let selectedUTXOs: UTXO[] = []; + + // Get UTXOs that meets or exceeds the target value + const UTXOsEqualOrGreaterThanTarget = sortedUTXOs.filter(utxo => utxo.denomination && utxo.denomination >= target.value); + + if (UTXOsEqualOrGreaterThanTarget.length > 0) { + // Find the smallest UTXO that meets or exceeds the target value + const optimalUTXO = UTXOsEqualOrGreaterThanTarget.reduce((minDenominationUTXO, currentUTXO) => { + if (!currentUTXO.denomination) return minDenominationUTXO; + return currentUTXO.denomination < minDenominationUTXO.denomination! ? currentUTXO : minDenominationUTXO; + }, UTXOsEqualOrGreaterThanTarget[0]); // Initialize with the first UTXO in the list + + selectedUTXOs.push(optimalUTXO); + totalValue += optimalUTXO.denomination!; + } else { + // If no single UTXO meets or exceeds the target, aggregate smaller denominations + // until the target is met/exceeded or there are no more UTXOs to aggregate + while (sortedUTXOs.length > 0 && totalValue < target.value) { + const nextOptimalUTXO = sortedUTXOs.reduce((closest, utxo) => { + if (!utxo.denomination) return closest; + + // Prioritize UTXOs that bring totalValue closer to target.value + const absThisDiff = bigIntAbs(target.value - (totalValue + utxo.denomination)); + const currentClosestDiff = closest && closest.denomination + ? bigIntAbs(target.value - (totalValue + closest.denomination)) + : BigInt(Infinity); + + return absThisDiff < currentClosestDiff ? utxo : closest; + + }, sortedUTXOs[0]); + + // Add the selected UTXO to the selection and update totalValue + selectedUTXOs.push(nextOptimalUTXO); + totalValue += nextOptimalUTXO.denomination!; + + // Remove the selected UTXO from the list of available UTXOs + const index = sortedUTXOs.findIndex(utxo => utxo.denomination === nextOptimalUTXO.denomination && utxo.address === nextOptimalUTXO.address); + sortedUTXOs.splice(index, 1); + } + } + + // Check if the selected UTXOs meet or exceed the target amount + if (totalValue < target.value) { + throw new Error("Insufficient funds"); + } + + // // Check if any denominations can be removed from the input set and it still remain valid + selectedUTXOs = this.sortUTXOsByDenomination(selectedUTXOs, "asc"); + + let runningTotal = totalValue; + let lastRemovableIndex = -1; // Index of the last UTXO that can be removed + + // Iterate through selectedUTXOs to find the last removable UTXO + for (let i = 0; i < selectedUTXOs.length; i++) { + const utxo = selectedUTXOs[i]; + if (utxo.denomination) { + if (runningTotal - utxo.denomination >= target.value) { + runningTotal -= utxo.denomination; + lastRemovableIndex = i; + } else { + // Once a UTXO makes the total less than target.value, stop the loop + break; + } + } + } + + if (lastRemovableIndex >= 0) { + totalValue -= selectedUTXOs[lastRemovableIndex].denomination!; + selectedUTXOs.splice(lastRemovableIndex, 1); + } + + // Break down the total spend into properly denominatated UTXOs + const spendDenominations = denominate(target.value); + this.spendOutputs = spendDenominations.map(denomination => { + const utxo = new UTXO(); + utxo.denomination = denomination; + utxo.address = target.address; + return utxo; + }); + + // Calculate change to be returned + const change = totalValue - target.value; + + // If there's change, break it down into properly denominatated UTXOs + if (change > BigInt(0)) { + const changeDenominations = denominate(change); + this.changeOutputs = changeDenominations.map(denomination => { + const utxo = new UTXO(); + utxo.denomination = denomination; + // We do not have access to change addresses here so leave it null + return utxo; + }); + } else { + this.changeOutputs = []; + } + + return { + inputs: selectedUTXOs, + spendOutputs: this.spendOutputs, + changeOutputs: this.changeOutputs, + }; + } + + private sortUTXOsByDenomination(utxos: UTXO[], direction: "asc" | "desc"): UTXO[] { + if (direction === "asc") { + return [...utxos].sort((a, b) => { + const diff = (a.denomination ?? BigInt(0)) - (b.denomination ?? BigInt(0)); + return diff > 0 ? 1 : diff < 0 ? -1 : 0; + }); + } + return [...utxos].sort((a, b) => { + const diff = (b.denomination ?? BigInt(0)) - (a.denomination ?? BigInt(0)); + return diff > 0 ? 1 : diff < 0 ? -1 : 0; + }); + } + + private validateTarget(target: SpendTarget) { + if (target.value <= BigInt(0)) { + throw new Error("Target amount must be greater than 0"); + } + } + + private validateUTXOs() { + if (this.availableUXTOs.length === 0) { + throw new Error("No UTXOs available"); + } + } + +} diff --git a/src.ts/transaction/utxo.ts b/src.ts/transaction/utxo.ts new file mode 100644 index 00000000..32574956 --- /dev/null +++ b/src.ts/transaction/utxo.ts @@ -0,0 +1,182 @@ +import { getAddress } from "../address/index"; +import { getBigInt } from "../utils/index"; +import type { BigNumberish } from "../utils/index"; + +export type OutPoint = { + txhash: string; + index: number; +}; + +export type UTXOTransactionInput = { + previousOutPoint: OutPoint; + pubKey: Uint8Array; +}; + +export interface UTXOEntry { + denomination: null | bigint; + address: null | string; +}; + +export type UTXOTransactionOutput = UTXOEntry; + +export type UTXOTransaction = { + chainId: bigint; + inputs: UTXOTransactionInput[]; + outputs: UTXOTransactionOutput[]; + signature?: Uint8Array; +}; + +export interface UTXOLike extends UTXOEntry { + txhash?: null | string; + index?: null | number; +} + +export const denominations: bigint[] = [ + BigInt(1), // 0.001 Qi + BigInt(5), // 0.005 Qi + BigInt(10), // 0.01 Qi + BigInt(50), // 0.05 Qi + BigInt(100), // 0.1 Qi + BigInt(250), // 0.25 Qi + BigInt(500), // 0.5 Qi + BigInt(1000), // 1 Qi + BigInt(5000), // 5 Qi + BigInt(10000), // 10 Qi + BigInt(20000), // 20 Qi + BigInt(50000), // 50 Qi + BigInt(100000), // 100 Qi + BigInt(1000000), // 1000 Qi + BigInt(10000000), // 10000 Qi + BigInt(100000000), // 100000 Qi + BigInt(1000000000), // 1000000 Qi +]; + +/** + * Checks if the provided denomination is valid. + * @param denomination The denomination to check. + * @returns True if the denomination is valid, false otherwise. + */ +function isValidDenomination(denomination: bigint): boolean { + return denominations.includes(denomination); +} + +/** + * Handles conversion of string to bigint, specifically for transaction parameters. + * @param value The string value to convert. + * @param param The parameter name for error context. + * @returns The bigint representation of the input string. + */ +function handleBigInt(value: string, param: string): bigint { + if (value === "0x") { return BigInt(0); } + return getBigInt(value, param); +} + +/** + * Given a value, returns an array of supported denominations that sum to the value. + * @param value The value to denominate. + * @returns Array of supported denominations that sum to the value. + */ +export function denominate(value: bigint): bigint[] { + if (value <= BigInt(0)) { + throw new Error("Value must be greater than 0"); + } + + const result: bigint[] = []; + let remainingValue = value; + + // Iterate through denominations in descending order + for (let i = denominations.length - 1; i >= 0; i--) { + const denomination = denominations[i]; + + // Add the denomination to the result array as many times as possible + while (remainingValue >= denomination) { + result.push(denomination); + remainingValue -= denomination; + } + } + + if (remainingValue > 0) { + throw new Error("Unable to match the value with available denominations"); + } + + + + return result; +} + +export class UTXO implements UTXOLike { + #txhash: null | string; + #index: null | number; + #address: null | string; + #denomination: null | bigint; + + get txhash(): null | string { return this.#txhash; } + set txhash(value: null | string) { + this.#txhash = value; + } + + get index(): null | number { return this.#index; } + set index(value: null | number) { + this.#index = value; + } + + get address(): null | string { return this.#address; } + set address(value: null | string) { + this.#address = (value == null) ? null : getAddress(value); + } + + get denomination(): null | bigint { return this.#denomination; } + set denomination(value: null | BigNumberish) { + if (value == null) { + this.#denomination = null; + return; + } + + const denominationBigInt = handleBigInt(value.toString(), "denomination"); + if (!isValidDenomination(denominationBigInt)) { + throw new Error("Invalid denomination value"); + } + + this.#denomination = denominationBigInt; + } + + /** + * Constructs a new UTXO instance with null properties. + */ + constructor() { + this.#txhash = null; + this.#index = null; + this.#address = null; + this.#denomination = null; + } + + /** + * Converts the UTXO instance to a JSON object. + * @returns A JSON representation of the UTXO instance. + */ + toJSON(): any { + return { + txhash: this.txhash, + index: this.index, + address: this.address, + denomination: this.denomination, + }; + } + + /** + * Creates a UTXO instance from a UTXOLike object. + * @param utxo The UTXOLike object to create the UTXO instance from. + * @returns A new UTXO instance. + */ + static from(utxo: UTXOLike): UTXO { + if (utxo === null) { return new UTXO(); } + + const result = utxo instanceof UTXO ? utxo : new UTXO(); + if (utxo.txhash != null) { result.txhash = utxo.txhash; } + if (utxo.index != null) { result.index = utxo.index; } + if (utxo.address != null) { result.address = utxo.address; } + if (utxo.denomination != null) { result.denomination = utxo.denomination; } + + return result; + } +} diff --git a/src.ts/utils/maths.ts b/src.ts/utils/maths.ts index 0200c27a..6a8bd278 100644 --- a/src.ts/utils/maths.ts +++ b/src.ts/utils/maths.ts @@ -93,7 +93,7 @@ export function mask(_value: BigNumberish, _bits: Numeric): bigint { * a BigInt, then an ArgumentError will be thrown for %%name%%. */ export function getBigInt(value: BigNumberish, name?: string): bigint { - switch (typeof(value)) { + switch (typeof (value)) { case "bigint": return value; case "number": assertArgument(Number.isInteger(value), "underflow", name || "value", value); @@ -106,13 +106,26 @@ export function getBigInt(value: BigNumberish, name?: string): bigint { return -BigInt(value.substring(1)); } return BigInt(value); - } catch(e: any) { - assertArgument(false, `invalid BigNumberish string: ${ e.message }`, name || "value", value); + } catch (e: any) { + assertArgument(false, `invalid BigNumberish string: ${e.message}`, name || "value", value); } } assertArgument(false, "invalid BigNumberish value", name || "value", value); } +/** + * Returns absolute value of bigint %%value%%. + */ +export function bigIntAbs(value: BigNumberish): bigint { + value = getBigInt(value); + + // if value is negative (including -0), return -value, else return value + if (value === -BN_0 || value < BN_0) { + return -value; + } + return value; +} + /** * Returns %%value%% as a bigint, validating it is valid as a bigint * value and that it is positive. @@ -149,7 +162,7 @@ export function toBigInt(value: BigNumberish | Uint8Array): bigint { * a //number//, then an ArgumentError will be thrown for %%name%%. */ export function getNumber(value: BigNumberish, name?: string): number { - switch (typeof(value)) { + switch (typeof (value)) { case "bigint": assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value); return Number(value); @@ -161,8 +174,8 @@ export function getNumber(value: BigNumberish, name?: string): number { try { if (value === "") { throw new Error("empty string"); } return getNumber(BigInt(value), name); - } catch(e: any) { - assertArgument(false, `invalid numeric string: ${ e.message }`, name || "value", value); + } catch (e: any) { + assertArgument(false, `invalid numeric string: ${e.message}`, name || "value", value); } } assertArgument(false, "invalid numeric value", name || "value", value); @@ -191,7 +204,7 @@ export function toBeHex(_value: BigNumberish, _width?: Numeric): string { if (result.length % 2) { result = "0" + result; } } else { const width = getNumber(_width, "width"); - assert(width * 2 >= result.length, `value exceeds width (${ width } bytes)`, "NUMERIC_FAULT", { + assert(width * 2 >= result.length, `value exceeds width (${width} bytes)`, "NUMERIC_FAULT", { operation: "toBeHex", fault: "overflow", value: _value @@ -211,7 +224,7 @@ export function toBeHex(_value: BigNumberish, _width?: Numeric): string { export function toBeArray(_value: BigNumberish): Uint8Array { const value = getUint(_value, "value"); - if (value === BN_0) { return new Uint8Array([ ]); } + if (value === BN_0) { return new Uint8Array([]); } let hex = value.toString(16); if (hex.length % 2) { hex = "0" + hex; } @@ -233,7 +246,7 @@ export function toBeArray(_value: BigNumberish): Uint8Array { * numeric values. */ export function toQuantity(value: BytesLike | BigNumberish): string { - let result = hexlify(isBytesLike(value) ? value: toBeArray(value)).substring(2); + let result = hexlify(isBytesLike(value) ? value : toBeArray(value)).substring(2); while (result.startsWith("0")) { result = result.substring(1); } if (result === "") { result = "0"; } return "0x" + result;