@ckb-ccc/core 1.9.1 → 1.11.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/ckb/hash.d.ts +18 -0
  3. package/dist/ckb/hash.d.ts.map +1 -0
  4. package/dist/ckb/hash.js +19 -0
  5. package/dist/ckb/index.d.ts +2 -0
  6. package/dist/ckb/index.d.ts.map +1 -1
  7. package/dist/ckb/index.js +2 -0
  8. package/dist/ckb/script.d.ts +12 -0
  9. package/dist/ckb/script.d.ts.map +1 -1
  10. package/dist/ckb/script.js +17 -0
  11. package/dist/ckb/transaction.d.ts +326 -25
  12. package/dist/ckb/transaction.d.ts.map +1 -1
  13. package/dist/ckb/transaction.js +354 -79
  14. package/dist/ckb/transactionErrors.d.ts +15 -0
  15. package/dist/ckb/transactionErrors.d.ts.map +1 -0
  16. package/dist/ckb/transactionErrors.js +21 -0
  17. package/dist/client/cache/cache.d.ts +7 -0
  18. package/dist/client/cache/cache.d.ts.map +1 -1
  19. package/dist/client/cache/cache.js +11 -0
  20. package/dist/client/cache/memory.advanced.d.ts +36 -3
  21. package/dist/client/cache/memory.advanced.d.ts.map +1 -1
  22. package/dist/client/cache/memory.advanced.js +53 -25
  23. package/dist/client/cache/memory.d.ts +10 -1
  24. package/dist/client/cache/memory.d.ts.map +1 -1
  25. package/dist/client/cache/memory.js +13 -2
  26. package/dist/client/client.d.ts.map +1 -1
  27. package/dist/client/client.js +6 -9
  28. package/dist/client/clientTypes.advanced.d.ts +0 -1
  29. package/dist/client/clientTypes.advanced.d.ts.map +1 -1
  30. package/dist/client/clientTypes.advanced.js +0 -1
  31. package/dist/hasher/hasherCkb.d.ts +0 -16
  32. package/dist/hasher/hasherCkb.d.ts.map +1 -1
  33. package/dist/hasher/hasherCkb.js +0 -18
  34. package/dist/molecule/codec.d.ts +30 -10
  35. package/dist/molecule/codec.d.ts.map +1 -1
  36. package/dist/molecule/codec.js +46 -13
  37. package/dist/signer/ckb/signerCkbScriptReadonly.d.ts +18 -6
  38. package/dist/signer/ckb/signerCkbScriptReadonly.d.ts.map +1 -1
  39. package/dist/signer/ckb/signerCkbScriptReadonly.js +18 -6
  40. package/dist/signer/signer/index.d.ts +11 -1
  41. package/dist/signer/signer/index.d.ts.map +1 -1
  42. package/dist/signer/signer/index.js +23 -0
  43. package/dist.commonjs/ckb/hash.d.ts +18 -0
  44. package/dist.commonjs/ckb/hash.d.ts.map +1 -0
  45. package/dist.commonjs/ckb/hash.js +22 -0
  46. package/dist.commonjs/ckb/index.d.ts +2 -0
  47. package/dist.commonjs/ckb/index.d.ts.map +1 -1
  48. package/dist.commonjs/ckb/index.js +2 -0
  49. package/dist.commonjs/ckb/script.d.ts +12 -0
  50. package/dist.commonjs/ckb/script.d.ts.map +1 -1
  51. package/dist.commonjs/ckb/script.js +17 -0
  52. package/dist.commonjs/ckb/transaction.d.ts +326 -25
  53. package/dist.commonjs/ckb/transaction.d.ts.map +1 -1
  54. package/dist.commonjs/ckb/transaction.js +353 -78
  55. package/dist.commonjs/ckb/transactionErrors.d.ts +15 -0
  56. package/dist.commonjs/ckb/transactionErrors.d.ts.map +1 -0
  57. package/dist.commonjs/ckb/transactionErrors.js +26 -0
  58. package/dist.commonjs/client/cache/cache.d.ts +7 -0
  59. package/dist.commonjs/client/cache/cache.d.ts.map +1 -1
  60. package/dist.commonjs/client/cache/cache.js +11 -0
  61. package/dist.commonjs/client/cache/memory.advanced.d.ts +36 -3
  62. package/dist.commonjs/client/cache/memory.advanced.d.ts.map +1 -1
  63. package/dist.commonjs/client/cache/memory.advanced.js +54 -26
  64. package/dist.commonjs/client/cache/memory.d.ts +10 -1
  65. package/dist.commonjs/client/cache/memory.d.ts.map +1 -1
  66. package/dist.commonjs/client/cache/memory.js +12 -1
  67. package/dist.commonjs/client/client.d.ts.map +1 -1
  68. package/dist.commonjs/client/client.js +5 -8
  69. package/dist.commonjs/client/clientTypes.advanced.d.ts +0 -1
  70. package/dist.commonjs/client/clientTypes.advanced.d.ts.map +1 -1
  71. package/dist.commonjs/client/clientTypes.advanced.js +1 -2
  72. package/dist.commonjs/hasher/hasherCkb.d.ts +0 -16
  73. package/dist.commonjs/hasher/hasherCkb.d.ts.map +1 -1
  74. package/dist.commonjs/hasher/hasherCkb.js +2 -21
  75. package/dist.commonjs/molecule/codec.d.ts +30 -10
  76. package/dist.commonjs/molecule/codec.d.ts.map +1 -1
  77. package/dist.commonjs/molecule/codec.js +46 -13
  78. package/dist.commonjs/signer/ckb/signerCkbScriptReadonly.d.ts +18 -6
  79. package/dist.commonjs/signer/ckb/signerCkbScriptReadonly.d.ts.map +1 -1
  80. package/dist.commonjs/signer/ckb/signerCkbScriptReadonly.js +18 -6
  81. package/dist.commonjs/signer/signer/index.d.ts +11 -1
  82. package/dist.commonjs/signer/signer/index.d.ts.map +1 -1
  83. package/dist.commonjs/signer/signer/index.js +23 -0
  84. package/package.json +1 -1
  85. package/prettier.config.mjs +1 -1
  86. package/src/ckb/hash.ts +28 -0
  87. package/src/ckb/index.ts +2 -0
  88. package/src/ckb/script.ts +20 -0
  89. package/src/ckb/transaction.ts +436 -80
  90. package/src/ckb/transactionErrors.ts +36 -0
  91. package/src/client/cache/cache.ts +14 -1
  92. package/src/client/cache/memory.advanced.ts +57 -30
  93. package/src/client/cache/memory.ts +18 -1
  94. package/src/client/client.ts +5 -10
  95. package/src/client/clientTypes.advanced.ts +0 -1
  96. package/src/hasher/hasherCkb.ts +0 -26
  97. package/src/molecule/codec.ts +50 -13
  98. package/src/signer/ckb/signerCkbScriptReadonly.ts +24 -8
  99. package/src/signer/signer/index.ts +34 -0
@@ -28,6 +28,7 @@ const index_js_7 = require("../num/index.js");
28
28
  const index_js_8 = require("../utils/index.js");
29
29
  const script_js_1 = require("./script.js");
30
30
  const transaction_advanced_js_1 = require("./transaction.advanced.js");
31
+ const transactionErrors_js_1 = require("./transactionErrors.js");
31
32
  exports.DepTypeCodec = index_js_6.mol.Codec.from({
32
33
  byteLength: 1,
33
34
  encode: depTypeToBytes,
@@ -127,6 +128,34 @@ let OutPoint = OutPoint_1 = class OutPoint extends index_js_6.mol.Entity.Base()
127
128
  }
128
129
  return new OutPoint_1((0, index_js_5.hexFrom)(outPoint.txHash), (0, index_js_7.numFrom)(outPoint.index));
129
130
  }
131
+ /**
132
+ * Clone a OutPoint.
133
+ *
134
+ * @returns A cloned OutPoint instance.
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const outPoint1 = outPoint0.clone();
139
+ * ```
140
+ */
141
+ clone() {
142
+ return new OutPoint_1(this.txHash, this.index);
143
+ }
144
+ /**
145
+ * Check if the OutPoint is equal to another OutPoint.
146
+ * @public
147
+ * @param other - The other OutPoint to compare with
148
+ * @returns True if the OutPoints are equal, false otherwise
149
+ *
150
+ * @example
151
+ * ```typescript
152
+ * const isEqual = outPoint0.eq(outPoint1);
153
+ * ```
154
+ */
155
+ eq(other) {
156
+ other = OutPoint_1.from(other);
157
+ return this.txHash === other.txHash && this.index === other.index;
158
+ }
130
159
  };
131
160
  exports.OutPoint = OutPoint;
132
161
  exports.OutPoint = OutPoint = OutPoint_1 = __decorate([
@@ -155,26 +184,28 @@ let CellOutput = CellOutput_1 = class CellOutput extends index_js_6.mol.Entity.B
155
184
  get occupiedSize() {
156
185
  return 8 + this.lock.occupiedSize + (this.type?.occupiedSize ?? 0);
157
186
  }
187
+ static from(cellOutput, outputData) {
188
+ if (cellOutput instanceof CellOutput_1) {
189
+ return cellOutput;
190
+ }
191
+ const output = new CellOutput_1((0, index_js_7.numFrom)(cellOutput.capacity ?? 0), script_js_1.Script.from(cellOutput.lock), (0, index_js_8.apply)(script_js_1.Script.from, cellOutput.type));
192
+ if (output.capacity === index_js_3.Zero) {
193
+ output.capacity = (0, index_js_3.fixedPointFrom)(output.occupiedSize + (0, index_js_1.bytesFrom)(outputData ?? "0x").length);
194
+ }
195
+ return output;
196
+ }
158
197
  /**
159
- * Creates a CellOutput instance from a CellOutputLike object.
198
+ * Clone a CellOutput.
160
199
  *
161
- * @param cellOutput - A CellOutputLike object or an instance of CellOutput.
162
- * @returns A CellOutput instance.
200
+ * @returns A cloned CellOutput instance.
163
201
  *
164
202
  * @example
165
203
  * ```typescript
166
- * const cellOutput = CellOutput.from({
167
- * capacity: 1000n,
168
- * lock: { codeHash: "0x...", hashType: "type", args: "0x..." },
169
- * type: { codeHash: "0x...", hashType: "type", args: "0x..." }
170
- * });
204
+ * const cellOutput1 = cellOutput0.clone();
171
205
  * ```
172
206
  */
173
- static from(cellOutput) {
174
- if (cellOutput instanceof CellOutput_1) {
175
- return cellOutput;
176
- }
177
- return new CellOutput_1((0, index_js_7.numFrom)(cellOutput.capacity), script_js_1.Script.from(cellOutput.lock), (0, index_js_8.apply)(script_js_1.Script.from, cellOutput.type));
207
+ clone() {
208
+ return new CellOutput_1(this.capacity, this.lock.clone(), this.type?.clone());
178
209
  }
179
210
  };
180
211
  exports.CellOutput = CellOutput;
@@ -204,15 +235,43 @@ class Cell {
204
235
  }
205
236
  /**
206
237
  * Creates a Cell instance from a CellLike object.
238
+ * This method accepts either `outPoint` or `previousOutput` to specify the cell's location,
239
+ * and supports automatic capacity calculation for the cell output.
207
240
  *
208
- * @param cell - A CellLike object or an instance of Cell.
241
+ * @param cell - A CellLike object or an instance of Cell. The object can use either:
242
+ * - `outPoint`: For referencing a cell output
243
+ * - `previousOutput`: For referencing a cell input (alternative name for outPoint)
244
+ * The cellOutput can omit capacity for automatic calculation.
209
245
  * @returns A Cell instance.
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * // Using outPoint with explicit capacity
250
+ * const cell1 = Cell.from({
251
+ * outPoint: { txHash: "0x...", index: 0 },
252
+ * cellOutput: {
253
+ * capacity: 1000n,
254
+ * lock: { codeHash: "0x...", hashType: "type", args: "0x..." }
255
+ * },
256
+ * outputData: "0x"
257
+ * });
258
+ *
259
+ * // Using previousOutput with automatic capacity calculation
260
+ * const cell2 = Cell.from({
261
+ * previousOutput: { txHash: "0x...", index: 0 },
262
+ * cellOutput: {
263
+ * lock: { codeHash: "0x...", hashType: "type", args: "0x..." }
264
+ * // capacity will be calculated automatically
265
+ * },
266
+ * outputData: "0x1234"
267
+ * });
268
+ * ```
210
269
  */
211
270
  static from(cell) {
212
271
  if (cell instanceof Cell) {
213
272
  return cell;
214
273
  }
215
- return new Cell(OutPoint.from("outPoint" in cell ? cell.outPoint : cell.previousOutput), CellOutput.from(cell.cellOutput), (0, index_js_5.hexFrom)(cell.outputData));
274
+ return new Cell(OutPoint.from("outPoint" in cell ? cell.outPoint : cell.previousOutput), CellOutput.from(cell.cellOutput, cell.outputData), (0, index_js_5.hexFrom)(cell.outputData));
216
275
  }
217
276
  get capacityFree() {
218
277
  const occupiedSize = (0, index_js_3.fixedPointFrom)(this.cellOutput.occupiedSize + (0, index_js_1.bytesFrom)(this.outputData).length);
@@ -506,11 +565,18 @@ let CellInput = CellInput_1 = class CellInput extends index_js_6.mol.Entity.Base
506
565
  async getExtraCapacity(client) {
507
566
  return (await this.getCell(client)).getDaoProfit(client);
508
567
  }
568
+ /**
569
+ * Clone a CellInput.
570
+ *
571
+ * @returns A cloned CellInput instance.
572
+ *
573
+ * @example
574
+ * ```typescript
575
+ * const cellInput1 = cellInput0.clone();
576
+ * ```
577
+ */
509
578
  clone() {
510
- const cloned = super.clone();
511
- cloned.cellOutput = this.cellOutput;
512
- cloned.outputData = this.outputData;
513
- return cloned;
579
+ return new CellInput_1(this.previousOutput.clone(), this.since, this.cellOutput?.clone(), this.outputData);
514
580
  }
515
581
  };
516
582
  exports.CellInput = CellInput;
@@ -538,19 +604,6 @@ let CellDep = CellDep_1 = class CellDep extends index_js_6.mol.Entity.Base() {
538
604
  this.outPoint = outPoint;
539
605
  this.depType = depType;
540
606
  }
541
- /**
542
- * Clone a CellDep.
543
- *
544
- * @returns A cloned CellDep instance.
545
- *
546
- * @example
547
- * ```typescript
548
- * const cellDep1 = cellDep0.clone();
549
- * ```
550
- */
551
- clone() {
552
- return new CellDep_1(this.outPoint.clone(), this.depType);
553
- }
554
607
  /**
555
608
  * Creates a CellDep instance from a CellDepLike object.
556
609
  *
@@ -571,6 +624,19 @@ let CellDep = CellDep_1 = class CellDep extends index_js_6.mol.Entity.Base() {
571
624
  }
572
625
  return new CellDep_1(OutPoint.from(cellDep.outPoint), depTypeFrom(cellDep.depType));
573
626
  }
627
+ /**
628
+ * Clone a CellDep.
629
+ *
630
+ * @returns A cloned CellDep instance.
631
+ *
632
+ * @example
633
+ * ```typescript
634
+ * const cellDep1 = cellDep0.clone();
635
+ * ```
636
+ */
637
+ clone() {
638
+ return new CellDep_1(this.outPoint.clone(), this.depType);
639
+ }
574
640
  };
575
641
  exports.CellDep = CellDep;
576
642
  exports.CellDep = CellDep = CellDep_1 = __decorate([
@@ -684,6 +750,9 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
684
750
  }
685
751
  /**
686
752
  * Copy every properties from another transaction.
753
+ * This method replaces all properties of the current transaction with those from the provided transaction.
754
+ *
755
+ * @param txLike - The transaction-like object to copy properties from.
687
756
  *
688
757
  * @example
689
758
  * ```typescript
@@ -700,6 +769,46 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
700
769
  this.outputsData = tx.outputsData;
701
770
  this.witnesses = tx.witnesses;
702
771
  }
772
+ /**
773
+ * Creates a deep copy of the transaction.
774
+ * This method creates a new Transaction instance with all nested objects cloned,
775
+ * ensuring that modifications to the cloned transaction do not affect the original.
776
+ *
777
+ * @returns A new Transaction instance that is a deep copy of the current transaction.
778
+ *
779
+ * @example
780
+ * ```typescript
781
+ * const originalTx = Transaction.from({
782
+ * version: 0,
783
+ * inputs: [{ previousOutput: { txHash: "0x...", index: 0 } }],
784
+ * outputs: [{ capacity: 1000n, lock: lockScript }],
785
+ * outputsData: ["0x"],
786
+ * witnesses: ["0x"]
787
+ * });
788
+ *
789
+ * const clonedTx = originalTx.clone();
790
+ *
791
+ * // Modifications to clonedTx won't affect originalTx
792
+ * clonedTx.addOutput({ capacity: 2000n, lock: anotherLockScript });
793
+ * console.log(originalTx.outputs.length); // Still 1
794
+ * console.log(clonedTx.outputs.length); // Now 2
795
+ * ```
796
+ *
797
+ * @remarks
798
+ * The clone operation performs deep copying for:
799
+ * - Cell dependencies (cellDeps) - each CellDep is cloned
800
+ * - Inputs - each CellInput is cloned
801
+ * - Outputs - each CellOutput is cloned
802
+ *
803
+ * The following are shallow copied (references to immutable data):
804
+ * - Header dependencies (headerDeps) - Hex strings are immutable
805
+ * - Output data (outputsData) - Hex strings are immutable
806
+ * - Witnesses - Hex strings are immutable
807
+ * - Version - bigint is immutable
808
+ */
809
+ clone() {
810
+ return new Transaction_1(this.version, this.cellDeps.map((c) => c.clone()), this.headerDeps.map((h) => h), this.inputs.map((i) => i.clone()), this.outputs.map((o) => o.clone()), this.outputsData.map((o) => o), this.witnesses.map((w) => w));
811
+ }
703
812
  /**
704
813
  * Creates a Transaction instance from a TransactionLike object.
705
814
  *
@@ -723,17 +832,7 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
723
832
  if (tx instanceof Transaction_1) {
724
833
  return tx;
725
834
  }
726
- const outputs = tx.outputs?.map((output, i) => {
727
- const o = CellOutput.from({
728
- ...output,
729
- capacity: output.capacity ?? 0,
730
- });
731
- if (o.capacity === index_js_3.Zero) {
732
- o.capacity = (0, index_js_3.fixedPointFrom)(o.occupiedSize +
733
- ((0, index_js_8.apply)(index_js_1.bytesFrom, tx.outputsData?.[i])?.length ?? 0));
734
- }
735
- return o;
736
- }) ?? [];
835
+ const outputs = tx.outputs?.map((output, i) => CellOutput.from(output, tx.outputsData?.[i] ?? [])) ?? [];
737
836
  const outputsData = outputs.map((_, i) => (0, index_js_5.hexFrom)(tx.outputsData?.[i] ?? "0x"));
738
837
  if (tx.outputsData != null && outputsData.length < tx.outputsData.length) {
739
838
  outputsData.push(...tx.outputsData.slice(outputsData.length).map((d) => (0, index_js_5.hexFrom)(d)));
@@ -916,7 +1015,7 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
916
1015
  *
917
1016
  * @param scriptLike - The script associated with the transaction, represented as a ScriptLike object.
918
1017
  * @param client - The client for complete extra infos in the transaction.
919
- * @returns A promise that resolves to the prepared transaction
1018
+ * @returns A promise that resolves to the found index, or undefined if no matching input is found.
920
1019
  *
921
1020
  * @example
922
1021
  * ```typescript
@@ -937,7 +1036,7 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
937
1036
  *
938
1037
  * @param scriptLike - The script associated with the transaction, represented as a ScriptLike object.
939
1038
  * @param client - The client for complete extra infos in the transaction.
940
- * @returns A promise that resolves to the prepared transaction
1039
+ * @returns A promise that resolves to the found index, or undefined if no matching input is found.
941
1040
  *
942
1041
  * @example
943
1042
  * ```typescript
@@ -1025,18 +1124,18 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1025
1124
  * Set output data at index.
1026
1125
  *
1027
1126
  * @param index - The index of the output data.
1028
- * @param witness - The data to set.
1127
+ * @param data - The data to set.
1029
1128
  *
1030
1129
  * @example
1031
1130
  * ```typescript
1032
- * await tx.setOutputDataAt(0, "0x00");
1131
+ * tx.setOutputDataAt(0, "0x00");
1033
1132
  * ```
1034
1133
  */
1035
- setOutputDataAt(index, witness) {
1134
+ setOutputDataAt(index, data) {
1036
1135
  if (this.outputsData.length < index) {
1037
1136
  this.outputsData.push(...Array.from(new Array(index - this.outputsData.length), () => "0x"));
1038
1137
  }
1039
- this.outputsData[index] = (0, index_js_5.hexFrom)(witness);
1138
+ this.outputsData[index] = (0, index_js_5.hexFrom)(data);
1040
1139
  }
1041
1140
  /**
1042
1141
  * get input
@@ -1099,14 +1198,7 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1099
1198
  * ```
1100
1199
  */
1101
1200
  addOutput(outputLike, outputData = "0x") {
1102
- const output = CellOutput.from({
1103
- ...outputLike,
1104
- capacity: outputLike.capacity ?? 0,
1105
- });
1106
- if (output.capacity === index_js_3.Zero) {
1107
- output.capacity = (0, index_js_3.fixedPointFrom)(output.occupiedSize + (0, index_js_1.bytesFrom)(outputData).length);
1108
- }
1109
- const len = this.outputs.push(output);
1201
+ const len = this.outputs.push(CellOutput.from(outputLike, outputData));
1110
1202
  this.setOutputDataAt(len - 1, outputData);
1111
1203
  return len;
1112
1204
  }
@@ -1253,7 +1345,7 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1253
1345
  if (accumulated === undefined) {
1254
1346
  return addedCount;
1255
1347
  }
1256
- throw new Error(`Insufficient CKB, need ${(0, index_js_3.fixedPointToString)(expectedCapacity - accumulated)} extra CKB`);
1348
+ throw new transactionErrors_js_1.ErrorTransactionInsufficientCapacity(expectedCapacity - accumulated);
1257
1349
  }
1258
1350
  async completeInputsAll(from, filter) {
1259
1351
  const { addedCount } = await this.completeInputs(from, filter ?? {
@@ -1303,7 +1395,7 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1303
1395
  if (accumulated === undefined || accumulated >= expectedBalance) {
1304
1396
  return addedCount;
1305
1397
  }
1306
- throw new Error(`Insufficient coin, need ${expectedBalance - accumulated} extra coin`);
1398
+ throw new transactionErrors_js_1.ErrorTransactionInsufficientCoin(expectedBalance - accumulated, type);
1307
1399
  }
1308
1400
  async completeInputsAddOne(from, filter) {
1309
1401
  const { addedCount, accumulated } = await this.completeInputs(from, filter ?? {
@@ -1333,6 +1425,44 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1333
1425
  // + 999 then / 1000 to ceil the calculated fee
1334
1426
  return ((0, index_js_7.numFrom)(txSize) * (0, index_js_7.numFrom)(feeRate) + (0, index_js_7.numFrom)(999)) / (0, index_js_7.numFrom)(1000);
1335
1427
  }
1428
+ /**
1429
+ * Completes the transaction fee by adding inputs and handling change outputs.
1430
+ * This method automatically calculates the required fee based on the transaction size and fee rate,
1431
+ * adds necessary inputs to cover the fee, and handles change outputs through the provided change function.
1432
+ *
1433
+ * @param from - The signer to complete inputs from and prepare the transaction.
1434
+ * @param change - A function that handles change capacity. It receives the transaction and excess capacity,
1435
+ * and should return the additional capacity needed (0 if change is handled successfully,
1436
+ * positive number if more capacity is needed for change cell creation).
1437
+ * @param expectedFeeRate - The expected fee rate in shannons per 1000 bytes. If not provided,
1438
+ * it will be fetched from the client.
1439
+ * @param filter - Optional filter for selecting cells when adding inputs.
1440
+ * @param options - Optional configuration object.
1441
+ * @param options.feeRateBlockRange - Block range for fee rate calculation when expectedFeeRate is not provided.
1442
+ * @param options.maxFeeRate - Maximum allowed fee rate.
1443
+ * @param options.shouldAddInputs - Whether to add inputs automatically. Defaults to true.
1444
+ * @returns A promise that resolves to a tuple containing:
1445
+ * - The number of inputs added during the process
1446
+ * - A boolean indicating whether change outputs were created (true) or fee was paid without change (false)
1447
+ *
1448
+ * @throws {ErrorTransactionInsufficientCapacity} When there's not enough capacity to cover the fee.
1449
+ * @throws {Error} When the change function doesn't properly handle the available capacity.
1450
+ *
1451
+ * @example
1452
+ * ```typescript
1453
+ * const [addedInputs, hasChange] = await tx.completeFee(
1454
+ * signer,
1455
+ * (tx, capacity) => {
1456
+ * if (capacity >= 61_00000000n) { // Minimum for a change cell
1457
+ * tx.addOutput({ capacity, lock: changeScript });
1458
+ * return 0;
1459
+ * }
1460
+ * return 61_00000000n; // Need more capacity for change cell
1461
+ * },
1462
+ * 1000n // 1000 shannons per 1000 bytes
1463
+ * );
1464
+ * ```
1465
+ */
1336
1466
  async completeFee(from, change, expectedFeeRate, filter, options) {
1337
1467
  const feeRate = expectedFeeRate ??
1338
1468
  (await from.client.getFeeRate(options?.feeRateBlockRange, options));
@@ -1340,33 +1470,53 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1340
1470
  await this.getInputsCapacity(from.client);
1341
1471
  let leastFee = index_js_3.Zero;
1342
1472
  let leastExtraCapacity = index_js_3.Zero;
1473
+ let collected = 0;
1474
+ // ===
1475
+ // Usually, for the worst situation, three iterations are needed
1476
+ // 1. First attempt to complete the transaction.
1477
+ // 2. Not enough capacity for the change cell.
1478
+ // 3. Fee increased by the change cell.
1479
+ // ===
1343
1480
  while (true) {
1344
- const tx = this.clone();
1345
- const collected = await (async () => {
1481
+ collected += await (async () => {
1482
+ if (!(options?.shouldAddInputs ?? true)) {
1483
+ return 0;
1484
+ }
1346
1485
  try {
1347
- return await tx.completeInputsByCapacity(from, leastFee + leastExtraCapacity, filter);
1486
+ return await this.completeInputsByCapacity(from, leastFee + leastExtraCapacity, filter);
1348
1487
  }
1349
1488
  catch (err) {
1350
- if (leastExtraCapacity !== index_js_3.Zero) {
1351
- throw new Error("Not enough capacity for the change cell");
1489
+ if (err instanceof transactionErrors_js_1.ErrorTransactionInsufficientCapacity &&
1490
+ leastExtraCapacity !== index_js_3.Zero) {
1491
+ throw new transactionErrors_js_1.ErrorTransactionInsufficientCapacity(err.amount, {
1492
+ isForChange: true,
1493
+ });
1352
1494
  }
1353
1495
  throw err;
1354
1496
  }
1355
1497
  })();
1356
- await from.prepareTransaction(tx);
1498
+ const fee = await this.getFee(from.client);
1499
+ if (fee < leastFee + leastExtraCapacity) {
1500
+ // Not enough capacity are collected, it should only happens when shouldAddInputs is false
1501
+ throw new transactionErrors_js_1.ErrorTransactionInsufficientCapacity(leastFee + leastExtraCapacity - fee, { isForChange: leastExtraCapacity !== index_js_3.Zero });
1502
+ }
1503
+ await from.prepareTransaction(this);
1357
1504
  if (leastFee === index_js_3.Zero) {
1358
1505
  // The initial fee is calculated based on prepared transaction
1359
- leastFee = tx.estimateFee(feeRate);
1506
+ // This should only happens during the first iteration
1507
+ leastFee = this.estimateFee(feeRate);
1360
1508
  }
1361
- const fee = await tx.getFee(from.client);
1362
1509
  // The extra capacity paid the fee without a change
1510
+ // leastExtraCapacity should be 0 here, otherwise we should failed in the previous check
1511
+ // So this only happens in the first iteration
1363
1512
  if (fee === leastFee) {
1364
- this.copy(tx);
1365
1513
  return [collected, false];
1366
1514
  }
1515
+ // Invoke the change function on a transaction multiple times may cause problems, so we clone it
1516
+ const tx = this.clone();
1367
1517
  const needed = (0, index_js_7.numFrom)(await Promise.resolve(change(tx, fee - leastFee)));
1368
- // No enough extra capacity to create new cells for change
1369
1518
  if (needed > index_js_3.Zero) {
1519
+ // No enough extra capacity to create new cells for change, collect inputs again
1370
1520
  leastExtraCapacity = needed;
1371
1521
  continue;
1372
1522
  }
@@ -1388,7 +1538,39 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1388
1538
  leastFee = changedFee;
1389
1539
  }
1390
1540
  }
1391
- completeFeeChangeToLock(from, change, feeRate, filter) {
1541
+ /**
1542
+ * Completes the transaction fee by adding inputs and creating a change output with the specified lock script.
1543
+ * This is a convenience method that automatically creates a change cell with the provided lock script
1544
+ * when there's excess capacity after paying the transaction fee.
1545
+ *
1546
+ * @param from - The signer to complete inputs from and prepare the transaction.
1547
+ * @param change - The lock script for the change output cell.
1548
+ * @param feeRate - Optional fee rate in shannons per 1000 bytes. If not provided, it will be fetched from the client.
1549
+ * @param filter - Optional filter for selecting cells when adding inputs.
1550
+ * @param options - Optional configuration object.
1551
+ * @param options.feeRateBlockRange - Block range for fee rate calculation when feeRate is not provided.
1552
+ * @param options.maxFeeRate - Maximum allowed fee rate.
1553
+ * @param options.shouldAddInputs - Whether to add inputs automatically. Defaults to true.
1554
+ * @returns A promise that resolves to a tuple containing:
1555
+ * - The number of inputs added during the process
1556
+ * - A boolean indicating whether change outputs were created (true) or fee was paid without change (false)
1557
+ *
1558
+ * @example
1559
+ * ```typescript
1560
+ * const changeScript = Script.from({
1561
+ * codeHash: "0x...",
1562
+ * hashType: "type",
1563
+ * args: "0x..."
1564
+ * });
1565
+ *
1566
+ * const [addedInputs, hasChange] = await tx.completeFeeChangeToLock(
1567
+ * signer,
1568
+ * changeScript,
1569
+ * 1000n // 1000 shannons per 1000 bytes
1570
+ * );
1571
+ * ```
1572
+ */
1573
+ completeFeeChangeToLock(from, change, feeRate, filter, options) {
1392
1574
  const script = script_js_1.Script.from(change);
1393
1575
  return this.completeFee(from, (tx, capacity) => {
1394
1576
  const changeCell = CellOutput.from({ capacity: 0, lock: script });
@@ -1399,13 +1581,69 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1399
1581
  changeCell.capacity = capacity;
1400
1582
  tx.addOutput(changeCell);
1401
1583
  return 0;
1402
- }, feeRate, filter);
1584
+ }, feeRate, filter, options);
1403
1585
  }
1404
- async completeFeeBy(from, feeRate, filter) {
1586
+ /**
1587
+ * Completes the transaction fee using the signer's recommended address for change.
1588
+ * This is a convenience method that automatically uses the signer's recommended
1589
+ * address as the change destination, making it easier to complete transactions
1590
+ * without manually specifying a change address.
1591
+ *
1592
+ * @param from - The signer to complete inputs from and prepare the transaction.
1593
+ * @param feeRate - Optional fee rate in shannons per 1000 bytes. If not provided, it will be fetched from the client.
1594
+ * @param filter - Optional filter for selecting cells when adding inputs.
1595
+ * @param options - Optional configuration object.
1596
+ * @param options.feeRateBlockRange - Block range for fee rate calculation when feeRate is not provided.
1597
+ * @param options.maxFeeRate - Maximum allowed fee rate.
1598
+ * @param options.shouldAddInputs - Whether to add inputs automatically. Defaults to true.
1599
+ * @returns A promise that resolves to a tuple containing:
1600
+ * - The number of inputs added during the process
1601
+ * - A boolean indicating whether change outputs were created (true) or fee was paid without change (false)
1602
+ *
1603
+ * @example
1604
+ * ```typescript
1605
+ * const [addedInputs, hasChange] = await tx.completeFeeBy(
1606
+ * signer,
1607
+ * 1000n // 1000 shannons per 1000 bytes
1608
+ * );
1609
+ *
1610
+ * // Change will automatically go to signer's recommended address
1611
+ * ```
1612
+ */
1613
+ async completeFeeBy(from, feeRate, filter, options) {
1405
1614
  const { script } = await from.getRecommendedAddressObj();
1406
- return this.completeFeeChangeToLock(from, script, feeRate, filter);
1615
+ return this.completeFeeChangeToLock(from, script, feeRate, filter, options);
1407
1616
  }
1408
- completeFeeChangeToOutput(from, index, feeRate, filter) {
1617
+ /**
1618
+ * Completes the transaction fee by adding excess capacity to an existing output.
1619
+ * Instead of creating a new change output, this method adds any excess capacity
1620
+ * to the specified existing output in the transaction.
1621
+ *
1622
+ * @param from - The signer to complete inputs from and prepare the transaction.
1623
+ * @param index - The index of the existing output to add excess capacity to.
1624
+ * @param feeRate - Optional fee rate in shannons per 1000 bytes. If not provided, it will be fetched from the client.
1625
+ * @param filter - Optional filter for selecting cells when adding inputs.
1626
+ * @param options - Optional configuration object.
1627
+ * @param options.feeRateBlockRange - Block range for fee rate calculation when feeRate is not provided.
1628
+ * @param options.maxFeeRate - Maximum allowed fee rate.
1629
+ * @param options.shouldAddInputs - Whether to add inputs automatically. Defaults to true.
1630
+ * @returns A promise that resolves to a tuple containing:
1631
+ * - The number of inputs added during the process
1632
+ * - A boolean indicating whether change was applied (true) or fee was paid without change (false)
1633
+ *
1634
+ * @throws {Error} When the specified output index doesn't exist.
1635
+ *
1636
+ * @example
1637
+ * ```typescript
1638
+ * // Add excess capacity to the first output (index 0)
1639
+ * const [addedInputs, hasChange] = await tx.completeFeeChangeToOutput(
1640
+ * signer,
1641
+ * 0, // Output index
1642
+ * 1000n // 1000 shannons per 1000 bytes
1643
+ * );
1644
+ * ```
1645
+ */
1646
+ completeFeeChangeToOutput(from, index, feeRate, filter, options) {
1409
1647
  const change = Number((0, index_js_7.numFrom)(index));
1410
1648
  if (!this.outputs[change]) {
1411
1649
  throw new Error("Non-existed output to change");
@@ -1413,7 +1651,7 @@ let Transaction = Transaction_1 = class Transaction extends index_js_6.mol.Entit
1413
1651
  return this.completeFee(from, (tx, capacity) => {
1414
1652
  tx.outputs[change].capacity += capacity;
1415
1653
  return 0;
1416
- }, feeRate, filter);
1654
+ }, feeRate, filter, options);
1417
1655
  }
1418
1656
  };
1419
1657
  exports.Transaction = Transaction;
@@ -1433,7 +1671,26 @@ exports.Transaction = Transaction = Transaction_1 = __decorate([
1433
1671
  .mapOut((tx) => Transaction.from({ ...tx.raw, witnesses: tx.witnesses })))
1434
1672
  ], Transaction);
1435
1673
  /**
1436
- * Calculate Nervos DAO profit between two blocks
1674
+ * Calculate Nervos DAO profit between two blocks.
1675
+ * This function computes the profit earned from a Nervos DAO deposit
1676
+ * based on the capacity and the time period between deposit and withdrawal.
1677
+ *
1678
+ * @param profitableCapacity - The capacity that earns profit (total capacity minus occupied capacity).
1679
+ * @param depositHeaderLike - The block header when the DAO deposit was made.
1680
+ * @param withdrawHeaderLike - The block header when the DAO withdrawal is made.
1681
+ * @returns The profit amount in CKB (capacity units).
1682
+ *
1683
+ * @example
1684
+ * ```typescript
1685
+ * const profit = calcDaoProfit(
1686
+ * ccc.fixedPointFrom(100), // 100 CKB profitable capacity
1687
+ * depositHeader,
1688
+ * withdrawHeader
1689
+ * );
1690
+ * console.log(`Profit: ${profit} shannons`);
1691
+ * ```
1692
+ *
1693
+ * @see {@link https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md | Nervos DAO RFC}
1437
1694
  */
1438
1695
  function calcDaoProfit(profitableCapacity, depositHeaderLike, withdrawHeaderLike) {
1439
1696
  const depositHeader = index_js_2.ClientBlockHeader.from(depositHeaderLike);
@@ -1443,8 +1700,26 @@ function calcDaoProfit(profitableCapacity, depositHeaderLike, withdrawHeaderLike
1443
1700
  profitableSize);
1444
1701
  }
1445
1702
  /**
1446
- * Calculate claimable epoch for Nervos DAO withdrawal
1447
- * See https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md
1703
+ * Calculate claimable epoch for Nervos DAO withdrawal.
1704
+ * This function determines the earliest epoch when a Nervos DAO withdrawal
1705
+ * can be claimed based on the deposit and withdrawal epochs.
1706
+ *
1707
+ * @param depositHeader - The block header when the DAO deposit was made.
1708
+ * @param withdrawHeader - The block header when the DAO withdrawal was initiated.
1709
+ * @returns The epoch when the withdrawal can be claimed, represented as [number, index, length].
1710
+ *
1711
+ * @example
1712
+ * ```typescript
1713
+ * const claimEpoch = calcDaoClaimEpoch(depositHeader, withdrawHeader);
1714
+ * console.log(`Can claim at epoch: ${claimEpoch[0]}, index: ${claimEpoch[1]}, length: ${claimEpoch[2]}`);
1715
+ * ```
1716
+ *
1717
+ * @remarks
1718
+ * The Nervos DAO has a minimum lock period of 180 epochs (~30 days).
1719
+ * This function calculates the exact epoch when the withdrawal becomes claimable
1720
+ * based on the deposit epoch and withdrawal epoch timing.
1721
+ *
1722
+ * @see {@link https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md | Nervos DAO RFC}
1448
1723
  */
1449
1724
  function calcDaoClaimEpoch(depositHeader, withdrawHeader) {
1450
1725
  const depositEpoch = index_js_2.ClientBlockHeader.from(depositHeader).epoch;
@@ -0,0 +1,15 @@
1
+ import { Num, NumLike } from "../num/index.js";
2
+ import { Script, ScriptLike } from "./script.js";
3
+ export declare class ErrorTransactionInsufficientCapacity extends Error {
4
+ readonly amount: Num;
5
+ readonly isForChange: boolean;
6
+ constructor(amountLike: NumLike, reason?: {
7
+ isForChange?: boolean;
8
+ });
9
+ }
10
+ export declare class ErrorTransactionInsufficientCoin extends Error {
11
+ readonly amount: Num;
12
+ readonly type: Script;
13
+ constructor(amountLike: NumLike, typeLike: ScriptLike);
14
+ }
15
+ //# sourceMappingURL=transactionErrors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transactionErrors.d.ts","sourceRoot":"","sources":["../../src/ckb/transactionErrors.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAW,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjD,qBAAa,oCAAqC,SAAQ,KAAK;IAC7D,SAAgB,MAAM,EAAE,GAAG,CAAC;IAC5B,SAAgB,WAAW,EAAE,OAAO,CAAC;gBAGnC,UAAU,EAAE,OAAO,EACnB,MAAM,CAAC,EAAE;QACP,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB;CAUJ;AAED,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,SAAgB,MAAM,EAAE,GAAG,CAAC;IAC5B,SAAgB,IAAI,EAAE,MAAM,CAAC;gBAEjB,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU;CAOtD"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ErrorTransactionInsufficientCoin = exports.ErrorTransactionInsufficientCapacity = void 0;
4
+ const index_js_1 = require("../fixedPoint/index.js");
5
+ const index_js_2 = require("../num/index.js");
6
+ const script_js_1 = require("./script.js");
7
+ class ErrorTransactionInsufficientCapacity extends Error {
8
+ constructor(amountLike, reason) {
9
+ const amount = (0, index_js_2.numFrom)(amountLike);
10
+ const isForChange = reason?.isForChange ?? false;
11
+ super(`Insufficient CKB, need ${(0, index_js_1.fixedPointToString)(amount)} extra CKB${isForChange ? " for the change cell" : ""}`);
12
+ this.amount = amount;
13
+ this.isForChange = isForChange;
14
+ }
15
+ }
16
+ exports.ErrorTransactionInsufficientCapacity = ErrorTransactionInsufficientCapacity;
17
+ class ErrorTransactionInsufficientCoin extends Error {
18
+ constructor(amountLike, typeLike) {
19
+ const amount = (0, index_js_2.numFrom)(amountLike);
20
+ const type = script_js_1.Script.from(typeLike);
21
+ super(`Insufficient coin, need ${amount} extra coin`);
22
+ this.amount = amount;
23
+ this.type = type;
24
+ }
25
+ }
26
+ exports.ErrorTransactionInsufficientCoin = ErrorTransactionInsufficientCoin;