@ckb-ccc/core 1.5.3 → 1.7.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 (161) hide show
  1. package/CHANGELOG.md +61 -7
  2. package/dist/ckb/script.d.ts +2 -1
  3. package/dist/ckb/script.d.ts.map +1 -1
  4. package/dist/ckb/transaction.d.ts +125 -7
  5. package/dist/ckb/transaction.d.ts.map +1 -1
  6. package/dist/ckb/transaction.js +263 -73
  7. package/dist/client/cache/cache.d.ts +67 -11
  8. package/dist/client/cache/cache.d.ts.map +1 -1
  9. package/dist/client/cache/cache.js +94 -9
  10. package/dist/client/cache/memory.advanced.d.ts +2 -2
  11. package/dist/client/cache/memory.advanced.d.ts.map +1 -1
  12. package/dist/client/cache/memory.advanced.js +19 -7
  13. package/dist/client/cache/memory.d.ts +24 -7
  14. package/dist/client/cache/memory.d.ts.map +1 -1
  15. package/dist/client/cache/memory.js +76 -18
  16. package/dist/client/client.d.ts +30 -7
  17. package/dist/client/client.d.ts.map +1 -1
  18. package/dist/client/client.js +114 -22
  19. package/dist/client/clientPublicMainnet.advanced.d.ts +2 -1
  20. package/dist/client/clientPublicMainnet.advanced.d.ts.map +1 -1
  21. package/dist/client/clientPublicMainnet.advanced.js +1 -1
  22. package/dist/client/clientPublicMainnet.d.ts +2 -1
  23. package/dist/client/clientPublicMainnet.d.ts.map +1 -1
  24. package/dist/client/clientPublicMainnet.js +1 -6
  25. package/dist/client/clientPublicTestnet.advanced.d.ts +2 -1
  26. package/dist/client/clientPublicTestnet.advanced.d.ts.map +1 -1
  27. package/dist/client/clientPublicTestnet.advanced.js +1 -1
  28. package/dist/client/clientPublicTestnet.d.ts +2 -1
  29. package/dist/client/clientPublicTestnet.d.ts.map +1 -1
  30. package/dist/client/clientTypes.advanced.d.ts +3 -0
  31. package/dist/client/clientTypes.advanced.d.ts.map +1 -1
  32. package/dist/client/clientTypes.advanced.js +3 -0
  33. package/dist/client/clientTypes.d.ts +110 -38
  34. package/dist/client/clientTypes.d.ts.map +1 -1
  35. package/dist/client/clientTypes.js +118 -31
  36. package/dist/client/index.d.ts +1 -0
  37. package/dist/client/index.d.ts.map +1 -1
  38. package/dist/client/index.js +1 -0
  39. package/dist/client/jsonRpc/client.d.ts +4 -4
  40. package/dist/client/jsonRpc/client.d.ts.map +1 -1
  41. package/dist/client/jsonRpc/client.js +4 -4
  42. package/dist/client/jsonRpc/transformers.js +3 -3
  43. package/dist/client/knownScript.d.ts +26 -0
  44. package/dist/client/knownScript.d.ts.map +1 -0
  45. package/dist/client/knownScript.js +27 -0
  46. package/dist/jsonRpc/transports/webSocket.d.ts.map +1 -1
  47. package/dist/jsonRpc/transports/webSocket.js +7 -3
  48. package/dist/molecule/predefined.d.ts +10 -10
  49. package/dist/molecule/predefined.d.ts.map +1 -1
  50. package/dist/num/index.d.ts.map +1 -1
  51. package/dist/num/index.js +17 -1
  52. package/dist/signer/ckb/signerCkbPublicKey.d.ts.map +1 -1
  53. package/dist/signer/ckb/signerCkbPublicKey.js +1 -5
  54. package/dist/signer/nostr/index.d.ts +1 -0
  55. package/dist/signer/nostr/index.d.ts.map +1 -1
  56. package/dist/signer/nostr/index.js +1 -0
  57. package/dist/signer/nostr/signerNostr.d.ts +1 -1
  58. package/dist/signer/nostr/signerNostr.d.ts.map +1 -1
  59. package/dist/signer/nostr/signerNostr.js +8 -0
  60. package/dist/signer/nostr/signerNostrPrivateKey.d.ts +5 -7
  61. package/dist/signer/nostr/signerNostrPrivateKey.d.ts.map +1 -1
  62. package/dist/signer/nostr/signerNostrPrivateKey.js +6 -12
  63. package/dist/signer/nostr/signerNostrPublicKeyReadonly.d.ts +11 -0
  64. package/dist/signer/nostr/signerNostrPublicKeyReadonly.d.ts.map +1 -0
  65. package/dist/signer/nostr/signerNostrPublicKeyReadonly.js +22 -0
  66. package/dist/utils/index.d.ts +4 -0
  67. package/dist/utils/index.d.ts.map +1 -1
  68. package/dist/utils/index.js +13 -1
  69. package/dist.commonjs/advanced.js +17 -7
  70. package/dist.commonjs/ckb/script.d.ts +2 -1
  71. package/dist.commonjs/ckb/script.d.ts.map +1 -1
  72. package/dist.commonjs/ckb/transaction.d.ts +125 -7
  73. package/dist.commonjs/ckb/transaction.d.ts.map +1 -1
  74. package/dist.commonjs/ckb/transaction.js +352 -160
  75. package/dist.commonjs/client/cache/cache.d.ts +67 -11
  76. package/dist.commonjs/client/cache/cache.d.ts.map +1 -1
  77. package/dist.commonjs/client/cache/cache.js +94 -9
  78. package/dist.commonjs/client/cache/memory.advanced.d.ts +2 -2
  79. package/dist.commonjs/client/cache/memory.advanced.d.ts.map +1 -1
  80. package/dist.commonjs/client/cache/memory.advanced.js +19 -7
  81. package/dist.commonjs/client/cache/memory.d.ts +24 -7
  82. package/dist.commonjs/client/cache/memory.d.ts.map +1 -1
  83. package/dist.commonjs/client/cache/memory.js +75 -17
  84. package/dist.commonjs/client/client.d.ts +30 -7
  85. package/dist.commonjs/client/client.d.ts.map +1 -1
  86. package/dist.commonjs/client/client.js +112 -20
  87. package/dist.commonjs/client/clientPublicMainnet.advanced.d.ts +2 -1
  88. package/dist.commonjs/client/clientPublicMainnet.advanced.d.ts.map +1 -1
  89. package/dist.commonjs/client/clientPublicMainnet.advanced.js +21 -21
  90. package/dist.commonjs/client/clientPublicMainnet.d.ts +2 -1
  91. package/dist.commonjs/client/clientPublicMainnet.d.ts.map +1 -1
  92. package/dist.commonjs/client/clientPublicMainnet.js +1 -6
  93. package/dist.commonjs/client/clientPublicTestnet.advanced.d.ts +2 -1
  94. package/dist.commonjs/client/clientPublicTestnet.advanced.d.ts.map +1 -1
  95. package/dist.commonjs/client/clientPublicTestnet.advanced.js +21 -21
  96. package/dist.commonjs/client/clientPublicTestnet.d.ts +2 -1
  97. package/dist.commonjs/client/clientPublicTestnet.d.ts.map +1 -1
  98. package/dist.commonjs/client/clientTypes.advanced.d.ts +3 -0
  99. package/dist.commonjs/client/clientTypes.advanced.d.ts.map +1 -1
  100. package/dist.commonjs/client/clientTypes.advanced.js +4 -0
  101. package/dist.commonjs/client/clientTypes.d.ts +110 -38
  102. package/dist.commonjs/client/clientTypes.d.ts.map +1 -1
  103. package/dist.commonjs/client/clientTypes.js +123 -31
  104. package/dist.commonjs/client/index.d.ts +1 -0
  105. package/dist.commonjs/client/index.d.ts.map +1 -1
  106. package/dist.commonjs/client/index.js +1 -0
  107. package/dist.commonjs/client/jsonRpc/client.d.ts +4 -4
  108. package/dist.commonjs/client/jsonRpc/client.d.ts.map +1 -1
  109. package/dist.commonjs/client/jsonRpc/client.js +4 -4
  110. package/dist.commonjs/client/jsonRpc/transformers.js +2 -2
  111. package/dist.commonjs/client/knownScript.d.ts +26 -0
  112. package/dist.commonjs/client/knownScript.d.ts.map +1 -0
  113. package/dist.commonjs/client/knownScript.js +30 -0
  114. package/dist.commonjs/index.js +17 -7
  115. package/dist.commonjs/jsonRpc/transports/webSocket.d.ts.map +1 -1
  116. package/dist.commonjs/jsonRpc/transports/webSocket.js +7 -3
  117. package/dist.commonjs/molecule/index.js +17 -7
  118. package/dist.commonjs/num/index.d.ts.map +1 -1
  119. package/dist.commonjs/num/index.js +17 -1
  120. package/dist.commonjs/signer/ckb/signerCkbPublicKey.d.ts.map +1 -1
  121. package/dist.commonjs/signer/ckb/signerCkbPublicKey.js +1 -5
  122. package/dist.commonjs/signer/nostr/index.d.ts +1 -0
  123. package/dist.commonjs/signer/nostr/index.d.ts.map +1 -1
  124. package/dist.commonjs/signer/nostr/index.js +1 -0
  125. package/dist.commonjs/signer/nostr/signerNostr.d.ts +1 -1
  126. package/dist.commonjs/signer/nostr/signerNostr.d.ts.map +1 -1
  127. package/dist.commonjs/signer/nostr/signerNostr.js +8 -0
  128. package/dist.commonjs/signer/nostr/signerNostrPrivateKey.d.ts +5 -7
  129. package/dist.commonjs/signer/nostr/signerNostrPrivateKey.d.ts.map +1 -1
  130. package/dist.commonjs/signer/nostr/signerNostrPrivateKey.js +6 -12
  131. package/dist.commonjs/signer/nostr/signerNostrPublicKeyReadonly.d.ts +11 -0
  132. package/dist.commonjs/signer/nostr/signerNostrPublicKeyReadonly.d.ts.map +1 -0
  133. package/dist.commonjs/signer/nostr/signerNostrPublicKeyReadonly.js +26 -0
  134. package/dist.commonjs/utils/index.d.ts +4 -0
  135. package/dist.commonjs/utils/index.d.ts.map +1 -1
  136. package/dist.commonjs/utils/index.js +13 -0
  137. package/package.json +1 -1
  138. package/src/ckb/script.ts +2 -1
  139. package/src/ckb/transaction.ts +376 -97
  140. package/src/client/cache/cache.ts +126 -13
  141. package/src/client/cache/memory.advanced.ts +24 -7
  142. package/src/client/cache/memory.ts +130 -30
  143. package/src/client/client.ts +172 -28
  144. package/src/client/clientPublicMainnet.advanced.ts +2 -1
  145. package/src/client/clientPublicMainnet.ts +3 -7
  146. package/src/client/clientPublicTestnet.advanced.ts +2 -1
  147. package/src/client/clientPublicTestnet.ts +2 -1
  148. package/src/client/clientTypes.advanced.ts +4 -0
  149. package/src/client/clientTypes.ts +224 -61
  150. package/src/client/index.ts +1 -0
  151. package/src/client/jsonRpc/client.ts +11 -9
  152. package/src/client/jsonRpc/transformers.ts +2 -2
  153. package/src/client/knownScript.ts +27 -0
  154. package/src/jsonRpc/transports/webSocket.ts +17 -13
  155. package/src/num/index.ts +21 -1
  156. package/src/signer/ckb/signerCkbPublicKey.ts +3 -5
  157. package/src/signer/nostr/index.ts +1 -0
  158. package/src/signer/nostr/signerNostr.ts +3 -1
  159. package/src/signer/nostr/signerNostrPrivateKey.ts +7 -14
  160. package/src/signer/nostr/signerNostrPublicKeyReadonly.ts +29 -0
  161. package/src/utils/index.ts +14 -1
@@ -1,6 +1,12 @@
1
1
  import { Bytes, BytesLike, bytesFrom } from "../bytes/index.js";
2
2
  import type { ClientCollectableSearchKeyFilterLike } from "../client/clientTypes.advanced.js";
3
- import type { CellDepInfoLike, Client, KnownScript } from "../client/index.js";
3
+ import {
4
+ ClientBlockHeader,
5
+ type CellDepInfoLike,
6
+ type Client,
7
+ type ClientBlockHeaderLike,
8
+ } from "../client/index.js";
9
+ import { KnownScript } from "../client/knownScript.js";
4
10
  import {
5
11
  Zero,
6
12
  fixedPointFrom,
@@ -229,8 +235,12 @@ export const CellOutputVec = mol.vector(CellOutput);
229
235
  /**
230
236
  * @public
231
237
  */
232
- export type CellLike = {
233
- outPoint: OutPointLike;
238
+ export type CellLike = (
239
+ | {
240
+ outPoint: OutPointLike;
241
+ }
242
+ | { previousOutput: OutPointLike }
243
+ ) & {
234
244
  cellOutput: CellOutputLike;
235
245
  outputData: HexLike;
236
246
  };
@@ -265,12 +275,133 @@ export class Cell {
265
275
  }
266
276
 
267
277
  return new Cell(
268
- OutPoint.from(cell.outPoint),
278
+ OutPoint.from("outPoint" in cell ? cell.outPoint : cell.previousOutput),
269
279
  CellOutput.from(cell.cellOutput),
270
280
  hexFrom(cell.outputData),
271
281
  );
272
282
  }
273
283
 
284
+ get capacityFree() {
285
+ const occupiedSize = fixedPointFrom(
286
+ this.cellOutput.occupiedSize + bytesFrom(this.outputData).length,
287
+ );
288
+ return this.cellOutput.capacity - occupiedSize;
289
+ }
290
+
291
+ /**
292
+ * Occupied bytes of a cell on chain
293
+ * It's CellOutput.occupiedSize + bytesFrom(outputData).byteLength
294
+ */
295
+ get occupiedSize() {
296
+ return this.cellOutput.occupiedSize + bytesFrom(this.outputData).byteLength;
297
+ }
298
+
299
+ /**
300
+ * Gets confirmed Nervos DAO profit of a Cell
301
+ * It returns non-zero value only when the cell is in withdrawal phase 2
302
+ * See https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md
303
+ *
304
+ * @param client - A client for searching DAO related headers
305
+ * @returns Profit
306
+ *
307
+ * @example
308
+ * ```typescript
309
+ * const profit = await cell.getDaoProfit(client);
310
+ * ```
311
+ */
312
+ async getDaoProfit(client: Client): Promise<Num> {
313
+ if (!(await this.isNervosDao(client, "withdrew"))) {
314
+ return Zero;
315
+ }
316
+
317
+ const { depositHeader, withdrawHeader } =
318
+ await this.getNervosDaoInfo(client);
319
+ if (!withdrawHeader || !depositHeader) {
320
+ throw new Error(
321
+ `Unable to get headers of a Nervos DAO cell ${this.outPoint.txHash}:${this.outPoint.index.toString()}`,
322
+ );
323
+ }
324
+
325
+ return calcDaoProfit(this.capacityFree, depositHeader, withdrawHeader);
326
+ }
327
+
328
+ async isNervosDao(
329
+ client: Client,
330
+ phase?: "deposited" | "withdrew",
331
+ ): Promise<boolean> {
332
+ const { type } = this.cellOutput;
333
+
334
+ const daoType = await client.getKnownScript(KnownScript.NervosDao);
335
+ if (
336
+ !type ||
337
+ type.codeHash !== daoType.codeHash ||
338
+ type.hashType !== daoType.hashType
339
+ ) {
340
+ // Non Nervos DAO cell
341
+ return false;
342
+ }
343
+
344
+ const hasWithdrew = numFrom(this.outputData) !== Zero;
345
+ return (
346
+ !phase ||
347
+ (phase === "deposited" && !hasWithdrew) ||
348
+ (phase === "withdrew" && hasWithdrew)
349
+ );
350
+ }
351
+
352
+ async getNervosDaoInfo(client: Client): Promise<
353
+ // Non Nervos DAO cell
354
+ | {
355
+ depositHeader?: undefined;
356
+ withdrawHeader?: undefined;
357
+ }
358
+ // Deposited Nervos DAO cell
359
+ | {
360
+ depositHeader: ClientBlockHeader;
361
+ withdrawHeader?: undefined;
362
+ }
363
+ // Withdrew Nervos DAO cell
364
+ | {
365
+ depositHeader: ClientBlockHeader;
366
+ withdrawHeader: ClientBlockHeader;
367
+ }
368
+ > {
369
+ if (!(await this.isNervosDao(client))) {
370
+ // Non Nervos DAO cell
371
+ return {};
372
+ }
373
+
374
+ if (numFrom(this.outputData) === Zero) {
375
+ // Deposited Nervos DAO cell
376
+ const depositRes = await client.getCellWithHeader(this.outPoint);
377
+ if (!depositRes?.header) {
378
+ throw new Error(
379
+ `Unable to get header of a Nervos DAO deposited cell ${this.outPoint.txHash}:${this.outPoint.index.toString()}`,
380
+ );
381
+ }
382
+
383
+ return {
384
+ depositHeader: depositRes.header,
385
+ };
386
+ }
387
+
388
+ // Withdrew Nervos DAO cell
389
+ const [depositHeader, withdrawRes] = await Promise.all([
390
+ client.getHeaderByNumber(numFromBytes(this.outputData)),
391
+ client.getCellWithHeader(this.outPoint),
392
+ ]);
393
+ if (!withdrawRes?.header || !depositHeader) {
394
+ throw new Error(
395
+ `Unable to get headers of a Nervos DAO withdrew cell ${this.outPoint.txHash}:${this.outPoint.index.toString()}`,
396
+ );
397
+ }
398
+
399
+ return {
400
+ depositHeader,
401
+ withdrawHeader: withdrawRes.header,
402
+ };
403
+ }
404
+
274
405
  /**
275
406
  * Clone a Cell
276
407
  *
@@ -450,8 +581,12 @@ export class Since extends mol.Entity.Base<SinceLike, Since>() {
450
581
  /**
451
582
  * @public
452
583
  */
453
- export type CellInputLike = {
454
- previousOutput: OutPointLike;
584
+ export type CellInputLike = (
585
+ | {
586
+ previousOutput: OutPointLike;
587
+ }
588
+ | { outPoint: OutPointLike }
589
+ ) & {
455
590
  since?: SinceLike | NumLike | null;
456
591
  cellOutput?: CellOutputLike | null;
457
592
  outputData?: HexLike | null;
@@ -465,10 +600,7 @@ export type CellInputLike = {
465
600
  since: Since,
466
601
  previousOutput: OutPoint,
467
602
  })
468
- .mapIn((encodable: CellInputLike) => ({
469
- ...encodable,
470
- since: encodable.since ?? 0,
471
- })),
603
+ .mapIn((encodable: CellInputLike) => CellInput.from(encodable)),
472
604
  )
473
605
  export class CellInput extends mol.Entity.Base<CellInputLike, CellInput>() {
474
606
  /**
@@ -509,21 +641,40 @@ export class CellInput extends mol.Entity.Base<CellInputLike, CellInput>() {
509
641
  }
510
642
 
511
643
  return new CellInput(
512
- OutPoint.from(cellInput.previousOutput),
644
+ OutPoint.from(
645
+ "previousOutput" in cellInput
646
+ ? cellInput.previousOutput
647
+ : cellInput.outPoint,
648
+ ),
513
649
  Since.from(cellInput.since ?? 0).toNum(),
514
650
  apply(CellOutput.from, cellInput.cellOutput),
515
651
  apply(hexFrom, cellInput.outputData),
516
652
  );
517
653
  }
518
654
 
655
+ async getCell(client: Client): Promise<Cell> {
656
+ await this.completeExtraInfos(client);
657
+ if (!this.cellOutput || !this.outputData) {
658
+ throw new Error("Unable to complete input");
659
+ }
660
+
661
+ return Cell.from({
662
+ outPoint: this.previousOutput,
663
+ cellOutput: this.cellOutput,
664
+ outputData: this.outputData,
665
+ });
666
+ }
667
+
519
668
  /**
520
- * Complete extra infos in the input. Like the output of the out point.
669
+ * Complete extra infos in the input. Including
670
+ * - Previous cell output
671
+ * - Previous cell data
521
672
  * The instance will be modified.
522
673
  *
523
674
  * @returns true if succeed.
524
675
  * @example
525
676
  * ```typescript
526
- * await cellInput.completeExtraInfos();
677
+ * await cellInput.completeExtraInfos(client);
527
678
  * ```
528
679
  */
529
680
  async completeExtraInfos(client: Client): Promise<void> {
@@ -539,6 +690,23 @@ export class CellInput extends mol.Entity.Base<CellInputLike, CellInput>() {
539
690
  this.cellOutput = cell.cellOutput;
540
691
  this.outputData = cell.outputData;
541
692
  }
693
+
694
+ /**
695
+ * The extra capacity created when consume this input.
696
+ * This is usually NervosDAO interest, see https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md.
697
+ * And it can also be miners' income. (But this is not implemented yet)
698
+ */
699
+ async getExtraCapacity(client: Client): Promise<Num> {
700
+ return (await this.getCell(client)).getDaoProfit(client);
701
+ }
702
+
703
+ clone(): CellInput {
704
+ const cloned = super.clone();
705
+ cloned.cellOutput = this.cellOutput;
706
+ cloned.outputData = this.outputData;
707
+
708
+ return cloned;
709
+ }
542
710
  }
543
711
  export const CellInputVec = mol.vector(CellInput);
544
712
 
@@ -684,11 +852,13 @@ export class WitnessArgs extends mol.Entity.Base<
684
852
  }
685
853
 
686
854
  /**
855
+ * Convert a bytes to a num.
856
+ *
687
857
  * @public
688
858
  */
689
859
  export function udtBalanceFrom(dataLike: BytesLike): Num {
690
860
  const data = bytesFrom(dataLike).slice(0, 16);
691
- return numFromBytes(data);
861
+ return data.length === 0 ? Zero : numFromBytes(data);
692
862
  }
693
863
 
694
864
  export const RawTransaction = mol.table({
@@ -891,6 +1061,11 @@ export class Transaction extends mol.Entity.Base<
891
1061
  });
892
1062
  }
893
1063
 
1064
+ /**
1065
+ * @deprecated
1066
+ * Use ccc.stringify instead.
1067
+ * stringify the tx to JSON string.
1068
+ */
894
1069
  stringify(): string {
895
1070
  return JSON.stringify(this, (_, value) => {
896
1071
  if (typeof value === "bigint") {
@@ -991,13 +1166,9 @@ export class Transaction extends mol.Entity.Base<
991
1166
  for (let i = 0; i < this.witnesses.length; i += 1) {
992
1167
  const input = this.inputs[i];
993
1168
  if (input) {
994
- await input.completeExtraInfos(client);
1169
+ const { cellOutput } = await input.getCell(client);
995
1170
 
996
- if (!input.cellOutput) {
997
- throw new Error("Unable to complete input");
998
- }
999
-
1000
- if (!script.eq(input.cellOutput.lock)) {
1171
+ if (!script.eq(cellOutput.lock)) {
1001
1172
  continue;
1002
1173
  }
1003
1174
 
@@ -1042,15 +1213,11 @@ export class Transaction extends mol.Entity.Base<
1042
1213
  const script = Script.from({ ...scriptIdLike, args: "0x" });
1043
1214
 
1044
1215
  for (let i = 0; i < this.inputs.length; i += 1) {
1045
- const input = this.inputs[i];
1046
- await input.completeExtraInfos(client);
1047
- if (!input.cellOutput) {
1048
- throw new Error("Unable to complete input");
1049
- }
1216
+ const { cellOutput } = await this.inputs[i].getCell(client);
1050
1217
 
1051
1218
  if (
1052
- script.codeHash === input.cellOutput.lock.codeHash &&
1053
- script.hashType === input.cellOutput.lock.hashType
1219
+ script.codeHash === cellOutput.lock.codeHash &&
1220
+ script.hashType === cellOutput.lock.hashType
1054
1221
  ) {
1055
1222
  return i;
1056
1223
  }
@@ -1076,13 +1243,9 @@ export class Transaction extends mol.Entity.Base<
1076
1243
  const script = Script.from(scriptLike);
1077
1244
 
1078
1245
  for (let i = 0; i < this.inputs.length; i += 1) {
1079
- const input = this.inputs[i];
1080
- await input.completeExtraInfos(client);
1081
- if (!input.cellOutput) {
1082
- throw new Error("Unable to complete input");
1083
- }
1246
+ const { cellOutput } = await this.inputs[i].getCell(client);
1084
1247
 
1085
- if (script.eq(input.cellOutput.lock)) {
1248
+ if (script.eq(cellOutput.lock)) {
1086
1249
  return i;
1087
1250
  }
1088
1251
  }
@@ -1107,13 +1270,9 @@ export class Transaction extends mol.Entity.Base<
1107
1270
  const script = Script.from(scriptLike);
1108
1271
 
1109
1272
  for (let i = this.inputs.length - 1; i >= 0; i -= 1) {
1110
- const input = this.inputs[i];
1111
- await input.completeExtraInfos(client);
1112
- if (!input.cellOutput) {
1113
- throw new Error("Unable to complete input");
1114
- }
1273
+ const { cellOutput } = await this.inputs[i].getCell(client);
1115
1274
 
1116
- if (script.eq(input.cellOutput.lock)) {
1275
+ if (script.eq(cellOutput.lock)) {
1117
1276
  return i;
1118
1277
  }
1119
1278
  }
@@ -1230,6 +1389,62 @@ export class Transaction extends mol.Entity.Base<
1230
1389
  this.outputsData[index] = hexFrom(witness);
1231
1390
  }
1232
1391
 
1392
+ /**
1393
+ * get input
1394
+ *
1395
+ * @param index - The cell input index
1396
+ *
1397
+ * @example
1398
+ * ```typescript
1399
+ * await tx.getInput(0);
1400
+ * ```
1401
+ */
1402
+ getInput(index: NumLike): CellInput | undefined {
1403
+ return this.inputs[Number(numFrom(index))];
1404
+ }
1405
+ /**
1406
+ * add input
1407
+ *
1408
+ * @param inputLike - The cell input.
1409
+ *
1410
+ * @example
1411
+ * ```typescript
1412
+ * await tx.addInput({ });
1413
+ * ```
1414
+ */
1415
+ addInput(inputLike: CellInputLike): number {
1416
+ if (this.witnesses.length > this.inputs.length) {
1417
+ this.witnesses.splice(this.inputs.length, 0, "0x");
1418
+ }
1419
+
1420
+ return this.inputs.push(CellInput.from(inputLike));
1421
+ }
1422
+
1423
+ /**
1424
+ * get output
1425
+ *
1426
+ * @param index - The cell output index
1427
+ *
1428
+ * @example
1429
+ * ```typescript
1430
+ * await tx.getOutput(0);
1431
+ * ```
1432
+ */
1433
+ getOutput(index: NumLike):
1434
+ | {
1435
+ cellOutput: CellOutput;
1436
+ outputData: Hex;
1437
+ }
1438
+ | undefined {
1439
+ const i = Number(numFrom(index));
1440
+ if (i >= this.outputs.length) {
1441
+ return;
1442
+ }
1443
+ return {
1444
+ cellOutput: this.outputs[i],
1445
+ outputData: this.outputsData[i] ?? "0x",
1446
+ };
1447
+ }
1233
1448
  /**
1234
1449
  * Add output
1235
1450
  *
@@ -1245,7 +1460,7 @@ export class Transaction extends mol.Entity.Base<
1245
1460
  outputLike: Omit<CellOutputLike, "capacity"> &
1246
1461
  Partial<Pick<CellOutputLike, "capacity">>,
1247
1462
  outputData: HexLike = "0x",
1248
- ): void {
1463
+ ): number {
1249
1464
  const output = CellOutput.from({
1250
1465
  ...outputLike,
1251
1466
  capacity: outputLike.capacity ?? 0,
@@ -1255,8 +1470,10 @@ export class Transaction extends mol.Entity.Base<
1255
1470
  output.occupiedSize + bytesFrom(outputData).length,
1256
1471
  );
1257
1472
  }
1258
- const i = this.outputs.push(output) - 1;
1259
- this.setOutputDataAt(i, outputData);
1473
+ const len = this.outputs.push(output);
1474
+ this.setOutputDataAt(len - 1, outputData);
1475
+
1476
+ return len;
1260
1477
  }
1261
1478
 
1262
1479
  /**
@@ -1289,6 +1506,21 @@ export class Transaction extends mol.Entity.Base<
1289
1506
  * ```
1290
1507
  */
1291
1508
  setWitnessArgsAt(index: number, witness: WitnessArgs): void {
1509
+ this.setWitnessAt(index, witness.toBytes());
1510
+ }
1511
+
1512
+ /**
1513
+ * Set witness at index
1514
+ *
1515
+ * @param index - The index of the witness.
1516
+ * @param witness - The witness to set.
1517
+ *
1518
+ * @example
1519
+ * ```typescript
1520
+ * await tx.setWitnessAt(0, witness);
1521
+ * ```
1522
+ */
1523
+ setWitnessAt(index: number, witness: HexLike): void {
1292
1524
  if (this.witnesses.length < index) {
1293
1525
  this.witnesses.push(
1294
1526
  ...Array.from(
@@ -1298,7 +1530,7 @@ export class Transaction extends mol.Entity.Base<
1298
1530
  );
1299
1531
  }
1300
1532
 
1301
- this.witnesses[index] = hexFrom(witness.toBytes());
1533
+ this.witnesses[index] = hexFrom(witness);
1302
1534
  }
1303
1535
 
1304
1536
  /**
@@ -1329,20 +1561,31 @@ export class Transaction extends mol.Entity.Base<
1329
1561
  this.setWitnessArgsAt(position, witness);
1330
1562
  }
1331
1563
 
1332
- async getInputsCapacity(client: Client): Promise<Num> {
1564
+ async getInputsCapacityExtra(client: Client): Promise<Num> {
1333
1565
  return reduceAsync(
1334
1566
  this.inputs,
1335
- async (acc, input) => {
1336
- await input.completeExtraInfos(client);
1337
- if (!input.cellOutput) {
1338
- throw new Error("Unable to complete input");
1339
- }
1340
- return acc + input.cellOutput.capacity;
1341
- },
1567
+ async (acc, input) => acc + (await input.getExtraCapacity(client)),
1342
1568
  numFrom(0),
1343
1569
  );
1344
1570
  }
1345
1571
 
1572
+ // This also includes extra amount
1573
+ async getInputsCapacity(client: Client): Promise<Num> {
1574
+ return (
1575
+ (await reduceAsync(
1576
+ this.inputs,
1577
+ async (acc, input) => {
1578
+ const {
1579
+ cellOutput: { capacity },
1580
+ } = await input.getCell(client);
1581
+
1582
+ return acc + capacity;
1583
+ },
1584
+ numFrom(0),
1585
+ )) + (await this.getInputsCapacityExtra(client))
1586
+ );
1587
+ }
1588
+
1346
1589
  getOutputsCapacity(): Num {
1347
1590
  return this.outputs.reduce(
1348
1591
  (acc, { capacity }) => acc + capacity,
@@ -1354,15 +1597,12 @@ export class Transaction extends mol.Entity.Base<
1354
1597
  return reduceAsync(
1355
1598
  this.inputs,
1356
1599
  async (acc, input) => {
1357
- await input.completeExtraInfos(client);
1358
- if (!input.cellOutput || !input.outputData) {
1359
- throw new Error("Unable to complete input");
1360
- }
1361
- if (!input.cellOutput.type?.eq(type)) {
1600
+ const { cellOutput, outputData } = await input.getCell(client);
1601
+ if (!cellOutput.type?.eq(type)) {
1362
1602
  return;
1363
1603
  }
1364
1604
 
1365
- return acc + udtBalanceFrom(input.outputData);
1605
+ return acc + udtBalanceFrom(outputData);
1366
1606
  },
1367
1607
  numFrom(0),
1368
1608
  );
@@ -1415,15 +1655,7 @@ export class Transaction extends mol.Entity.Base<
1415
1655
  acc = next;
1416
1656
  }
1417
1657
 
1418
- this.inputs.push(
1419
- ...collectedCells.map(({ outPoint, outputData, cellOutput }) =>
1420
- CellInput.from({
1421
- previousOutput: outPoint,
1422
- outputData,
1423
- cellOutput,
1424
- }),
1425
- ),
1426
- );
1658
+ collectedCells.forEach((cell) => this.addInput(cell));
1427
1659
  if (fulfilled) {
1428
1660
  return {
1429
1661
  addedCount: collectedCells.length,
@@ -1526,31 +1758,18 @@ export class Transaction extends mol.Entity.Base<
1526
1758
  from: Signer,
1527
1759
  filter?: ClientCollectableSearchKeyFilterLike,
1528
1760
  ): Promise<number> {
1529
- for await (const cell of from.findCells(
1761
+ const { addedCount, accumulated } = await this.completeInputs(
1762
+ from,
1530
1763
  filter ?? {
1531
1764
  scriptLenRange: [0, 1],
1532
1765
  outputDataLenRange: [0, 1],
1533
1766
  },
1767
+ () => undefined,
1534
1768
  true,
1535
- undefined,
1536
- 1,
1537
- )) {
1538
- if (
1539
- this.inputs.some(({ previousOutput }) =>
1540
- previousOutput.eq(cell.outPoint),
1541
- )
1542
- ) {
1543
- continue;
1544
- }
1769
+ );
1545
1770
 
1546
- this.inputs.push(
1547
- CellInput.from({
1548
- previousOutput: cell.outPoint,
1549
- outputData: cell.outputData,
1550
- cellOutput: cell.cellOutput,
1551
- }),
1552
- );
1553
- return 1;
1771
+ if (accumulated === undefined) {
1772
+ return addedCount;
1554
1773
  }
1555
1774
 
1556
1775
  throw new Error(`Insufficient CKB, need at least one new cell`);
@@ -1567,9 +1786,21 @@ export class Transaction extends mol.Entity.Base<
1567
1786
  return this.completeInputsAddOne(from, filter);
1568
1787
  }
1569
1788
 
1789
+ async getFee(client: Client): Promise<Num> {
1790
+ return (await this.getInputsCapacity(client)) - this.getOutputsCapacity();
1791
+ }
1792
+
1793
+ async getFeeRate(client: Client): Promise<Num> {
1794
+ return (
1795
+ ((await this.getFee(client)) * numFrom(1000)) /
1796
+ numFrom(this.toBytes().length + 4)
1797
+ );
1798
+ }
1799
+
1570
1800
  estimateFee(feeRate: NumLike): Num {
1571
1801
  const txSize = this.toBytes().length + 4;
1572
- return (numFrom(txSize) * numFrom(feeRate) + numFrom(1000)) / numFrom(1000);
1802
+ // + 999 then / 1000 to ceil the calculated fee
1803
+ return (numFrom(txSize) * numFrom(feeRate) + numFrom(999)) / numFrom(1000);
1573
1804
  }
1574
1805
 
1575
1806
  async completeFee(
@@ -1577,8 +1808,11 @@ export class Transaction extends mol.Entity.Base<
1577
1808
  change: (tx: Transaction, capacity: Num) => Promise<NumLike> | NumLike,
1578
1809
  expectedFeeRate?: NumLike,
1579
1810
  filter?: ClientCollectableSearchKeyFilterLike,
1811
+ options?: { feeRateBlockRange?: NumLike; maxFeeRate?: NumLike },
1580
1812
  ): Promise<[number, boolean]> {
1581
- const feeRate = expectedFeeRate ?? (await from.client.getFeeRate());
1813
+ const feeRate =
1814
+ expectedFeeRate ??
1815
+ (await from.client.getFeeRate(options?.feeRateBlockRange, options));
1582
1816
 
1583
1817
  // Complete all inputs extra infos for cache
1584
1818
  await this.getInputsCapacity(from.client);
@@ -1609,27 +1843,21 @@ export class Transaction extends mol.Entity.Base<
1609
1843
  // The initial fee is calculated based on prepared transaction
1610
1844
  leastFee = tx.estimateFee(feeRate);
1611
1845
  }
1612
- const extraCapacity =
1613
- (await tx.getInputsCapacity(from.client)) - tx.getOutputsCapacity();
1846
+ const fee = await tx.getFee(from.client);
1614
1847
  // The extra capacity paid the fee without a change
1615
- if (extraCapacity === leastFee) {
1848
+ if (fee === leastFee) {
1616
1849
  this.copy(tx);
1617
1850
  return [collected, false];
1618
1851
  }
1619
1852
 
1620
- const needed = numFrom(
1621
- await Promise.resolve(change(tx, extraCapacity - leastFee)),
1622
- );
1853
+ const needed = numFrom(await Promise.resolve(change(tx, fee - leastFee)));
1623
1854
  // No enough extra capacity to create new cells for change
1624
1855
  if (needed > Zero) {
1625
1856
  leastExtraCapacity = needed;
1626
1857
  continue;
1627
1858
  }
1628
1859
 
1629
- if (
1630
- (await tx.getInputsCapacity(from.client)) - tx.getOutputsCapacity() !==
1631
- leastFee
1632
- ) {
1860
+ if ((await tx.getFee(from.client)) !== leastFee) {
1633
1861
  throw new Error(
1634
1862
  "The change function doesn't use all available capacity",
1635
1863
  );
@@ -1708,3 +1936,54 @@ export class Transaction extends mol.Entity.Base<
1708
1936
  );
1709
1937
  }
1710
1938
  }
1939
+
1940
+ /**
1941
+ * Calculate Nervos DAO profit between two blocks
1942
+ */
1943
+ export function calcDaoProfit(
1944
+ profitableCapacity: NumLike,
1945
+ depositHeaderLike: ClientBlockHeaderLike,
1946
+ withdrawHeaderLike: ClientBlockHeaderLike,
1947
+ ): Num {
1948
+ const depositHeader = ClientBlockHeader.from(depositHeaderLike);
1949
+ const withdrawHeader = ClientBlockHeader.from(withdrawHeaderLike);
1950
+
1951
+ const profitableSize = numFrom(profitableCapacity);
1952
+
1953
+ return (
1954
+ (profitableSize * withdrawHeader.dao.ar) / depositHeader.dao.ar -
1955
+ profitableSize
1956
+ );
1957
+ }
1958
+
1959
+ /**
1960
+ * Calculate claimable epoch for Nervos DAO withdrawal
1961
+ * See https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0023-dao-deposit-withdraw/0023-dao-deposit-withdraw.md
1962
+ */
1963
+ export function calcDaoClaimEpoch(
1964
+ depositHeader: ClientBlockHeaderLike,
1965
+ withdrawHeader: ClientBlockHeaderLike,
1966
+ ): Epoch {
1967
+ const depositEpoch = ClientBlockHeader.from(depositHeader).epoch;
1968
+ const withdrawEpoch = ClientBlockHeader.from(withdrawHeader).epoch;
1969
+ const intDiff = withdrawEpoch[0] - depositEpoch[0];
1970
+ // deposit[1] withdraw[1]
1971
+ // ---------- <= -----------
1972
+ // deposit[2] withdraw[2]
1973
+ if (
1974
+ intDiff % numFrom(180) !== numFrom(0) ||
1975
+ depositEpoch[1] * withdrawEpoch[2] <= depositEpoch[2] * withdrawEpoch[1]
1976
+ ) {
1977
+ return [
1978
+ depositEpoch[0] + (intDiff / numFrom(180) + numFrom(1)) * numFrom(180),
1979
+ depositEpoch[1],
1980
+ depositEpoch[2],
1981
+ ];
1982
+ }
1983
+
1984
+ return [
1985
+ depositEpoch[0] + (intDiff / numFrom(180)) * numFrom(180),
1986
+ depositEpoch[1],
1987
+ depositEpoch[2],
1988
+ ];
1989
+ }