@hardkas/tx-builder 0.8.20-alpha → 0.9.0-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,48 @@ 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
+ };
141
+ }
142
+ declare class TxPlanService {
143
+ private provider;
144
+ readonly maxInputsPerTx: number;
145
+ readonly warnInputs: number;
146
+ readonly marginFeePerInput: bigint;
147
+ readonly coinbaseMaturity: bigint;
148
+ constructor(provider: UtxoProvider, options?: TxPlanServiceOptions);
149
+ planTransaction(request: PlanTransactionRequest): Promise<TxPlanResult>;
150
+ planConsolidation(request: ConsolidationRequest): Promise<TxPlanResult>;
151
+ }
152
+
111
153
  type Sompi = bigint;
112
154
 
113
155
  interface Outpoint {
@@ -155,4 +197,4 @@ declare function createMockUtxo(input: {
155
197
  readonly index?: number;
156
198
  }): Utxo;
157
199
 
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 };
200
+ 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,129 @@ 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(`Insufficient funds: needed ${request.amountSompi} sompi but only found ${selectedAmount} sompi across ${selectedInputsCount} UTXOs.`);
336
+ }
337
+ if (selectedInputsCount > this.maxInputsPerTx) {
338
+ const err = new Error(`TOO_MANY_INPUTS_FOR_SINGLE_TX: Transaction requires ${selectedInputsCount} inputs to cover the amount, which exceeds the safe limit of ${this.maxInputsPerTx} inputs.
339
+ Hint: Run 'hardkas accounts consolidate' to merge dust UTXOs.`);
340
+ err.code = "TOO_MANY_INPUTS_FOR_SINGLE_TX";
341
+ throw err;
342
+ }
343
+ if (selectedInputsCount >= this.warnInputs) {
344
+ console.warn(`\u26A0\uFE0F WARNING: Transaction requires ${selectedInputsCount} inputs. Consider running 'hardkas accounts consolidate'.`);
345
+ }
346
+ const builderPlan = buildPaymentPlan({
347
+ fromAddress: request.fromAddress,
348
+ availableUtxos: builderUtxos,
349
+ outputs: [
350
+ {
351
+ address: request.toAddress,
352
+ amountSompi: request.amountSompi
353
+ }
354
+ ],
355
+ feeRateSompiPerMass: feeRate
356
+ });
357
+ return {
358
+ plan: builderPlan,
359
+ utxoSelection: {
360
+ totalUtxosSeen: allFetchedUtxos.length,
361
+ selectedUtxos: selectedInputsCount,
362
+ selectionStrategy: "largest-first"
363
+ }
364
+ };
365
+ }
366
+ async planConsolidation(request) {
367
+ let totalAmount = 0n;
368
+ const builderUtxos = request.selectedUtxos.map((u) => {
369
+ const amount = BigInt(u.amountSompi);
370
+ totalAmount += amount;
371
+ return u;
372
+ });
373
+ const feeRate = request.feeRate ?? 1n;
374
+ const massPerInput = 1500n;
375
+ const estimatedMass = BigInt(request.selectedUtxos.length) * massPerInput + 500n;
376
+ const expectedFee = estimatedMass * feeRate;
377
+ if (totalAmount <= expectedFee) {
378
+ throw new Error(`Consolidation failed: Total selected UTXO amount (${totalAmount}) is less than or equal to the estimated fee (${expectedFee}).`);
379
+ }
380
+ const outputAmount = totalAmount - expectedFee;
381
+ const builderPlan = buildPaymentPlan({
382
+ fromAddress: request.fromAddress,
383
+ availableUtxos: builderUtxos,
384
+ outputs: [
385
+ {
386
+ address: request.toAddress,
387
+ amountSompi: outputAmount
388
+ }
389
+ ],
390
+ feeRateSompiPerMass: feeRate
391
+ });
392
+ return {
393
+ plan: builderPlan,
394
+ utxoSelection: {
395
+ totalUtxosSeen: request.selectedUtxos.length,
396
+ selectedUtxos: request.selectedUtxos.length,
397
+ selectionStrategy: "consolidation-smallest-first",
398
+ purpose: "wallet-consolidation"
399
+ }
400
+ };
401
+ }
402
+ };
403
+
281
404
  // src/index.ts
282
405
  function buildPaymentPlan(request) {
283
406
  if (request.outputs.length === 0) {
@@ -379,6 +502,7 @@ function createMockUtxo(input) {
379
502
  export {
380
503
  DUST_THRESHOLD_SOMPI,
381
504
  KASPA_MASS_CONSTANTS,
505
+ TxPlanService,
382
506
  buildPaymentPlan,
383
507
  createMockUtxo,
384
508
  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.0-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.0-alpha"
16
16
  },
17
17
  "devDependencies": {
18
18
  "tsup": "^8.3.5",