@hardkas/tx-builder 0.8.20-alpha → 0.9.1-alpha

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.
package/dist/index.d.ts CHANGED
@@ -108,6 +108,49 @@ declare function verifyTxReceiptSemantics(receipt: any): {
108
108
  issues: SemanticVerificationIssue[];
109
109
  };
110
110
 
111
+ interface UtxoProvider {
112
+ getUtxos(address: string): Promise<Utxo[]>;
113
+ getVirtualDaaScore?(): Promise<bigint>;
114
+ }
115
+ interface TxPlanServiceOptions {
116
+ maxInputsPerTx?: number;
117
+ warnInputs?: number;
118
+ marginFeePerInput?: bigint;
119
+ coinbaseMaturity?: bigint;
120
+ }
121
+ interface PlanTransactionRequest {
122
+ fromAddress: string;
123
+ toAddress: string;
124
+ amountSompi: bigint;
125
+ feeRate?: bigint;
126
+ }
127
+ interface ConsolidationRequest {
128
+ fromAddress: string;
129
+ selectedUtxos: Utxo[];
130
+ toAddress: string;
131
+ feeRate?: bigint;
132
+ }
133
+ interface TxPlanResult {
134
+ plan: TxPlan;
135
+ utxoSelection: {
136
+ totalUtxosSeen: number;
137
+ selectedUtxos: number;
138
+ selectionStrategy: "largest-first" | "consolidation-smallest-first";
139
+ purpose?: "wallet-consolidation";
140
+ warnings?: string[];
141
+ };
142
+ }
143
+ declare class TxPlanService {
144
+ private provider;
145
+ readonly maxInputsPerTx: number;
146
+ readonly warnInputs: number;
147
+ readonly marginFeePerInput: bigint;
148
+ readonly coinbaseMaturity: bigint;
149
+ constructor(provider: UtxoProvider, options?: TxPlanServiceOptions);
150
+ planTransaction(request: PlanTransactionRequest): Promise<TxPlanResult>;
151
+ planConsolidation(request: ConsolidationRequest): Promise<TxPlanResult>;
152
+ }
153
+
111
154
  type Sompi = bigint;
112
155
 
113
156
  interface Outpoint {
@@ -155,4 +198,4 @@ declare function createMockUtxo(input: {
155
198
  readonly index?: number;
156
199
  }): Utxo;
157
200
 
158
- export { DUST_THRESHOLD_SOMPI, KASPA_MASS_CONSTANTS, type MassBreakdown, type MassEstimateResult, type Outpoint, type SemanticVerificationIssue, type SemanticVerificationResult, type SemanticVerificationSeverity, type SemanticVerifyContext, type Sompi, type TxBuildRequest, type TxOutput, type TxPlan, type Utxo, buildPaymentPlan, createMockUtxo, estimateFeeFromMass, estimateMass, estimateTransactionMass, verifySignedTxSemantics, verifyTxPlanSemantics, verifyTxReceiptSemantics };
201
+ export { type ConsolidationRequest, DUST_THRESHOLD_SOMPI, KASPA_MASS_CONSTANTS, type MassBreakdown, type MassEstimateResult, type Outpoint, type PlanTransactionRequest, type SemanticVerificationIssue, type SemanticVerificationResult, type SemanticVerificationSeverity, type SemanticVerifyContext, type Sompi, type TxBuildRequest, type TxOutput, type TxPlan, type TxPlanResult, TxPlanService, type TxPlanServiceOptions, type Utxo, type UtxoProvider, buildPaymentPlan, createMockUtxo, estimateFeeFromMass, estimateMass, estimateTransactionMass, verifySignedTxSemantics, verifyTxPlanSemantics, verifyTxReceiptSemantics };
package/dist/index.js CHANGED
@@ -278,6 +278,139 @@ function verifyTxReceiptSemantics(receipt) {
278
278
  };
279
279
  }
280
280
 
281
+ // src/service.ts
282
+ var TxPlanService = class {
283
+ constructor(provider, options = {}) {
284
+ this.provider = provider;
285
+ this.maxInputsPerTx = options.maxInputsPerTx ?? 512;
286
+ this.warnInputs = options.warnInputs ?? 128;
287
+ this.marginFeePerInput = options.marginFeePerInput ?? 1500n;
288
+ this.coinbaseMaturity = options.coinbaseMaturity ?? 1000n;
289
+ }
290
+ provider;
291
+ maxInputsPerTx;
292
+ warnInputs;
293
+ marginFeePerInput;
294
+ coinbaseMaturity;
295
+ async planTransaction(request) {
296
+ const rpcUtxos = await this.provider.getUtxos(request.fromAddress);
297
+ let virtualDaaScore;
298
+ if (this.provider.getVirtualDaaScore) {
299
+ try {
300
+ virtualDaaScore = await this.provider.getVirtualDaaScore();
301
+ } catch {
302
+ }
303
+ }
304
+ const matureUtxos = virtualDaaScore !== void 0 ? rpcUtxos.filter((u) => {
305
+ if (!u.isCoinbase) return true;
306
+ const score = u.blockDaaScore;
307
+ if (score === void 0) return true;
308
+ return virtualDaaScore - score >= this.coinbaseMaturity;
309
+ }) : rpcUtxos;
310
+ const allFetchedUtxos = matureUtxos;
311
+ const sortedUtxos = [...allFetchedUtxos].sort((a, b) => {
312
+ if (a.amountSompi > b.amountSompi) return -1;
313
+ if (a.amountSompi < b.amountSompi) return 1;
314
+ return 0;
315
+ });
316
+ const feeRate = request.feeRate ?? 1n;
317
+ const marginFee = this.marginFeePerInput * feeRate;
318
+ let selectedAmount = 0n;
319
+ let selectedInputsCount = 0;
320
+ const builderUtxos = [];
321
+ const HARD_LIMIT = 1e3;
322
+ for (const utxo of sortedUtxos) {
323
+ builderUtxos.push(utxo);
324
+ selectedAmount += utxo.amountSompi;
325
+ selectedInputsCount++;
326
+ const requiredTotal = request.amountSompi + BigInt(selectedInputsCount) * marginFee;
327
+ if (selectedAmount >= requiredTotal) {
328
+ break;
329
+ }
330
+ if (selectedInputsCount >= HARD_LIMIT) {
331
+ break;
332
+ }
333
+ }
334
+ if (selectedAmount < request.amountSompi) {
335
+ throw new Error(
336
+ `Insufficient funds: needed ${request.amountSompi} sompi but only found ${selectedAmount} sompi across ${selectedInputsCount} UTXOs.`
337
+ );
338
+ }
339
+ if (selectedInputsCount > this.maxInputsPerTx) {
340
+ const err = new Error(
341
+ `TOO_MANY_INPUTS_FOR_SINGLE_TX: Transaction requires ${selectedInputsCount} inputs to cover the amount, which exceeds the safe limit of ${this.maxInputsPerTx} inputs.
342
+ Hint: Run 'hardkas accounts consolidate' to merge dust UTXOs.`
343
+ );
344
+ err.code = "TOO_MANY_INPUTS_FOR_SINGLE_TX";
345
+ throw err;
346
+ }
347
+ const warnings = [];
348
+ if (selectedInputsCount >= this.warnInputs) {
349
+ warnings.push(
350
+ `Transaction requires ${selectedInputsCount} inputs. Consider running 'hardkas accounts consolidate'.`
351
+ );
352
+ }
353
+ const builderPlan = buildPaymentPlan({
354
+ fromAddress: request.fromAddress,
355
+ availableUtxos: builderUtxos,
356
+ outputs: [
357
+ {
358
+ address: request.toAddress,
359
+ amountSompi: request.amountSompi
360
+ }
361
+ ],
362
+ feeRateSompiPerMass: feeRate
363
+ });
364
+ return {
365
+ plan: builderPlan,
366
+ utxoSelection: {
367
+ totalUtxosSeen: allFetchedUtxos.length,
368
+ selectedUtxos: selectedInputsCount,
369
+ selectionStrategy: "largest-first",
370
+ warnings
371
+ }
372
+ };
373
+ }
374
+ async planConsolidation(request) {
375
+ let totalAmount = 0n;
376
+ const builderUtxos = request.selectedUtxos.map((u) => {
377
+ const amount = BigInt(u.amountSompi);
378
+ totalAmount += amount;
379
+ return u;
380
+ });
381
+ const feeRate = request.feeRate ?? 1n;
382
+ const massPerInput = 1500n;
383
+ const estimatedMass = BigInt(request.selectedUtxos.length) * massPerInput + 500n;
384
+ const expectedFee = estimatedMass * feeRate;
385
+ if (totalAmount <= expectedFee) {
386
+ throw new Error(
387
+ `Consolidation failed: Total selected UTXO amount (${totalAmount}) is less than or equal to the estimated fee (${expectedFee}).`
388
+ );
389
+ }
390
+ const outputAmount = totalAmount - expectedFee;
391
+ const builderPlan = buildPaymentPlan({
392
+ fromAddress: request.fromAddress,
393
+ availableUtxos: builderUtxos,
394
+ outputs: [
395
+ {
396
+ address: request.toAddress,
397
+ amountSompi: outputAmount
398
+ }
399
+ ],
400
+ feeRateSompiPerMass: feeRate
401
+ });
402
+ return {
403
+ plan: builderPlan,
404
+ utxoSelection: {
405
+ totalUtxosSeen: request.selectedUtxos.length,
406
+ selectedUtxos: request.selectedUtxos.length,
407
+ selectionStrategy: "consolidation-smallest-first",
408
+ purpose: "wallet-consolidation"
409
+ }
410
+ };
411
+ }
412
+ };
413
+
281
414
  // src/index.ts
282
415
  function buildPaymentPlan(request) {
283
416
  if (request.outputs.length === 0) {
@@ -379,6 +512,7 @@ function createMockUtxo(input) {
379
512
  export {
380
513
  DUST_THRESHOLD_SOMPI,
381
514
  KASPA_MASS_CONSTANTS,
515
+ TxPlanService,
382
516
  buildPaymentPlan,
383
517
  createMockUtxo,
384
518
  estimateFeeFromMass,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardkas/tx-builder",
3
- "version": "0.8.20-alpha",
3
+ "version": "0.9.1-alpha",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@hardkas/core": "0.8.20-alpha"
15
+ "@hardkas/core": "0.9.1-alpha"
16
16
  },
17
17
  "devDependencies": {
18
18
  "tsup": "^8.3.5",