@koralabs/kora-labs-common 6.16.0 → 6.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addVkeyWitnesses = exports.selectWalletInputs = exports.finalizeScriptTx = exports.certificateDeposit = exports.computeScriptDataHash = exports.withMinAda = void 0;
3
+ exports.addVkeyWitnesses = exports.finalizeWalletFundedTx = exports.selectWalletInputs = exports.finalizeScriptTx = exports.InsufficientInputsError = exports.certificateDeposit = exports.computeScriptDataHash = exports.withMinAda = void 0;
4
4
  // Deterministic Plutus transaction finalizer on @cardano-sdk/core (no input selection, no Helios).
5
5
  //
6
6
  // The caller fixes every input; this module derives redeemer pointers from the ledger's canonical
@@ -87,6 +87,15 @@ const sumValues = (values) => {
87
87
  }
88
88
  return { coins, assets };
89
89
  };
90
+ /** The inputs cannot pay the outputs + fee (+ a min-UTxO change output); `shortfall` more lovelace would. */
91
+ class InsufficientInputsError extends Error {
92
+ constructor(message, shortfall) {
93
+ super(message);
94
+ this.shortfall = shortfall;
95
+ this.name = 'InsufficientInputsError';
96
+ }
97
+ }
98
+ exports.InsufficientInputsError = InsufficientInputsError;
90
99
  const finalizeScriptTx = async (plan, params, evaluate) => {
91
100
  var _a, _b, _c;
92
101
  const inputs = [...plan.inputs].sort((a, b) => byTxIn(a.utxo[0], b.utxo[0]));
@@ -112,7 +121,7 @@ const finalizeScriptTx = async (plan, params, evaluate) => {
112
121
  var _a, _b, _c;
113
122
  const coins = inValue.coins + withdrawn - deposited - produced.coins - fee;
114
123
  if (coins < BigInt(0))
115
- throw new Error(`Inputs do not cover outputs + fee: ${-coins} lovelace short`);
124
+ throw new InsufficientInputsError(`Inputs do not cover outputs + fee: ${-coins} lovelace short`, -coins);
116
125
  const assets = new Map();
117
126
  const ids = new Set([...inValue.assets.keys(), ...tokenMap.keys(), ...produced.assets.keys()]);
118
127
  for (const id of ids) {
@@ -192,8 +201,9 @@ const finalizeScriptTx = async (plan, params, evaluate) => {
192
201
  throw new Error('Execution units did not converge');
193
202
  }
194
203
  const { tx, change, redeemers } = assemble(fee, exUnits, new Map());
195
- if (change.value.coins < (0, fees_1.minAdaForOutputSize)(params.coinsPerUtxoByte, outputSize(change))) {
196
- throw new Error(`Inputs do not cover outputs + fee: change of ${change.value.coins} lovelace is below min-UTxO`);
204
+ const changeMinAda = (0, fees_1.minAdaForOutputSize)(params.coinsPerUtxoByte, outputSize(change));
205
+ if (change.value.coins < changeMinAda) {
206
+ throw new InsufficientInputsError(`Inputs do not cover outputs + fee: change of ${change.value.coins} lovelace is below min-UTxO`, changeMinAda - change.value.coins);
197
207
  }
198
208
  const signedSize = sizeOf(fee, exUnits);
199
209
  if (signedSize > params.maxTxSize)
@@ -231,6 +241,35 @@ const selectWalletInputs = (utxos, lovelace, exclude = new Set()) => {
231
241
  return selected;
232
242
  };
233
243
  exports.selectWalletInputs = selectWalletInputs;
244
+ const coinsOf = (utxos) => utxos.reduce((sum, [, out]) => sum + out.value.coins, BigInt(0));
245
+ /**
246
+ * Coin selection + finalization in one: walks the `selectWalletInputs` order (ADA-only first, largest
247
+ * first) one UTxO at a time and returns the first selection `finalizeScriptTx` can balance: inputs
248
+ * covering outputs + fee with a min-UTxO change output. Pure: no chain reads, so chained (unconfirmed)
249
+ * wallet UTxOs are spendable. Throws `InsufficientInputsError` (shortfall vs. the whole wallet) when
250
+ * no selection works.
251
+ */
252
+ const finalizeWalletFundedTx = async ({ walletUtxos, signerCount, inputs = [], ...plan }, params, evaluate) => {
253
+ const available = coinsOf(walletUtxos);
254
+ let required = plan.outputs.reduce((sum, o) => sum + o.value.coins, BigInt(0));
255
+ // Each selection is the previous one plus the next UTxO in order (+1 lovelace past its total).
256
+ for (let lovelace = BigInt(1); lovelace <= available;) {
257
+ const walletInputs = (0, exports.selectWalletInputs)(walletUtxos, lovelace);
258
+ const selected = coinsOf(walletInputs);
259
+ try {
260
+ const tx = await (0, exports.finalizeScriptTx)({ ...plan, inputs: [...inputs, ...walletInputs.map((utxo) => ({ utxo }))], signerCount: signerCount(walletInputs) }, params, evaluate);
261
+ return { ...tx, walletInputs };
262
+ }
263
+ catch (error) {
264
+ if (!(error instanceof InsufficientInputsError))
265
+ throw error;
266
+ required = selected + error.shortfall;
267
+ lovelace = selected + BigInt(1);
268
+ }
269
+ }
270
+ throw new InsufficientInputsError(`Wallet holds ${available} spendable lovelace; about ${required} needed`, required - available);
271
+ };
272
+ exports.finalizeWalletFundedTx = finalizeWalletFundedTx;
234
273
  /**
235
274
  * Add vkey witnesses to an unsigned tx by splicing them into its witness set byte-for-byte
236
275
  * (`mergeWitnessSet`): the body (so the tx id and every signature) and the redeemer bytes the