@ckb-ccc/core 1.4.0 → 1.6.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 (109) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/ckb/transaction.d.ts +57 -4
  3. package/dist/ckb/transaction.d.ts.map +1 -1
  4. package/dist/ckb/transaction.js +83 -26
  5. package/dist/client/cache/cache.d.ts +67 -11
  6. package/dist/client/cache/cache.d.ts.map +1 -1
  7. package/dist/client/cache/cache.js +94 -9
  8. package/dist/client/cache/memory.advanced.d.ts +2 -2
  9. package/dist/client/cache/memory.advanced.d.ts.map +1 -1
  10. package/dist/client/cache/memory.advanced.js +19 -7
  11. package/dist/client/cache/memory.d.ts +24 -7
  12. package/dist/client/cache/memory.d.ts.map +1 -1
  13. package/dist/client/cache/memory.js +76 -18
  14. package/dist/client/client.d.ts +27 -6
  15. package/dist/client/client.d.ts.map +1 -1
  16. package/dist/client/client.js +113 -22
  17. package/dist/client/clientPublicMainnet.d.ts.map +1 -1
  18. package/dist/client/clientPublicMainnet.js +13 -2
  19. package/dist/client/clientPublicTestnet.d.ts.map +1 -1
  20. package/dist/client/clientPublicTestnet.js +13 -2
  21. package/dist/client/clientTypes.advanced.d.ts +3 -0
  22. package/dist/client/clientTypes.advanced.d.ts.map +1 -1
  23. package/dist/client/clientTypes.advanced.js +3 -0
  24. package/dist/client/clientTypes.d.ts +110 -13
  25. package/dist/client/clientTypes.d.ts.map +1 -1
  26. package/dist/client/clientTypes.js +118 -4
  27. package/dist/client/jsonRpc/client.d.ts +4 -4
  28. package/dist/client/jsonRpc/client.d.ts.map +1 -1
  29. package/dist/client/jsonRpc/client.js +4 -4
  30. package/dist/client/jsonRpc/transformers.js +3 -3
  31. package/dist/jsonRpc/requestor.d.ts +1 -0
  32. package/dist/jsonRpc/requestor.d.ts.map +1 -1
  33. package/dist/jsonRpc/requestor.js +4 -2
  34. package/dist/jsonRpc/transports/advanced.d.ts +1 -0
  35. package/dist/jsonRpc/transports/advanced.d.ts.map +1 -1
  36. package/dist/jsonRpc/transports/advanced.js +1 -0
  37. package/dist/jsonRpc/transports/fallback.d.ts +8 -0
  38. package/dist/jsonRpc/transports/fallback.d.ts.map +1 -0
  39. package/dist/jsonRpc/transports/fallback.js +22 -0
  40. package/dist/jsonRpc/transports/webSocket.d.ts.map +1 -1
  41. package/dist/jsonRpc/transports/webSocket.js +7 -3
  42. package/dist/molecule/predefined.d.ts +10 -10
  43. package/dist/molecule/predefined.d.ts.map +1 -1
  44. package/dist/utils/index.d.ts +4 -0
  45. package/dist/utils/index.d.ts.map +1 -1
  46. package/dist/utils/index.js +13 -1
  47. package/dist.commonjs/advanced.js +17 -7
  48. package/dist.commonjs/ckb/transaction.d.ts +57 -4
  49. package/dist.commonjs/ckb/transaction.d.ts.map +1 -1
  50. package/dist.commonjs/ckb/transaction.js +83 -26
  51. package/dist.commonjs/client/cache/cache.d.ts +67 -11
  52. package/dist.commonjs/client/cache/cache.d.ts.map +1 -1
  53. package/dist.commonjs/client/cache/cache.js +94 -9
  54. package/dist.commonjs/client/cache/memory.advanced.d.ts +2 -2
  55. package/dist.commonjs/client/cache/memory.advanced.d.ts.map +1 -1
  56. package/dist.commonjs/client/cache/memory.advanced.js +19 -7
  57. package/dist.commonjs/client/cache/memory.d.ts +24 -7
  58. package/dist.commonjs/client/cache/memory.d.ts.map +1 -1
  59. package/dist.commonjs/client/cache/memory.js +75 -17
  60. package/dist.commonjs/client/client.d.ts +27 -6
  61. package/dist.commonjs/client/client.d.ts.map +1 -1
  62. package/dist.commonjs/client/client.js +111 -20
  63. package/dist.commonjs/client/clientPublicMainnet.d.ts.map +1 -1
  64. package/dist.commonjs/client/clientPublicMainnet.js +13 -2
  65. package/dist.commonjs/client/clientPublicTestnet.d.ts.map +1 -1
  66. package/dist.commonjs/client/clientPublicTestnet.js +13 -2
  67. package/dist.commonjs/client/clientTypes.advanced.d.ts +3 -0
  68. package/dist.commonjs/client/clientTypes.advanced.d.ts.map +1 -1
  69. package/dist.commonjs/client/clientTypes.advanced.js +4 -0
  70. package/dist.commonjs/client/clientTypes.d.ts +110 -13
  71. package/dist.commonjs/client/clientTypes.d.ts.map +1 -1
  72. package/dist.commonjs/client/clientTypes.js +123 -4
  73. package/dist.commonjs/client/jsonRpc/client.d.ts +4 -4
  74. package/dist.commonjs/client/jsonRpc/client.d.ts.map +1 -1
  75. package/dist.commonjs/client/jsonRpc/client.js +4 -4
  76. package/dist.commonjs/client/jsonRpc/transformers.js +2 -2
  77. package/dist.commonjs/index.js +17 -7
  78. package/dist.commonjs/jsonRpc/requestor.d.ts +1 -0
  79. package/dist.commonjs/jsonRpc/requestor.d.ts.map +1 -1
  80. package/dist.commonjs/jsonRpc/requestor.js +3 -1
  81. package/dist.commonjs/jsonRpc/transports/advanced.d.ts +1 -0
  82. package/dist.commonjs/jsonRpc/transports/advanced.d.ts.map +1 -1
  83. package/dist.commonjs/jsonRpc/transports/advanced.js +1 -0
  84. package/dist.commonjs/jsonRpc/transports/fallback.d.ts +8 -0
  85. package/dist.commonjs/jsonRpc/transports/fallback.d.ts.map +1 -0
  86. package/dist.commonjs/jsonRpc/transports/fallback.js +26 -0
  87. package/dist.commonjs/jsonRpc/transports/webSocket.d.ts.map +1 -1
  88. package/dist.commonjs/jsonRpc/transports/webSocket.js +7 -3
  89. package/dist.commonjs/molecule/index.js +17 -7
  90. package/dist.commonjs/utils/index.d.ts +4 -0
  91. package/dist.commonjs/utils/index.d.ts.map +1 -1
  92. package/dist.commonjs/utils/index.js +13 -0
  93. package/package.json +1 -1
  94. package/src/ckb/transaction.ts +112 -41
  95. package/src/client/cache/cache.ts +126 -13
  96. package/src/client/cache/memory.advanced.ts +24 -7
  97. package/src/client/cache/memory.ts +130 -30
  98. package/src/client/client.ts +170 -27
  99. package/src/client/clientPublicMainnet.ts +14 -2
  100. package/src/client/clientPublicTestnet.ts +14 -2
  101. package/src/client/clientTypes.advanced.ts +4 -0
  102. package/src/client/clientTypes.ts +224 -33
  103. package/src/client/jsonRpc/client.ts +11 -9
  104. package/src/client/jsonRpc/transformers.ts +2 -2
  105. package/src/jsonRpc/requestor.ts +10 -1
  106. package/src/jsonRpc/transports/advanced.ts +1 -0
  107. package/src/jsonRpc/transports/fallback.ts +27 -0
  108. package/src/jsonRpc/transports/webSocket.ts +17 -13
  109. package/src/utils/index.ts +14 -1
@@ -9,11 +9,16 @@ import {
9
9
  } from "../ckb/index.js";
10
10
  import { Zero } from "../fixedPoint/index.js";
11
11
  import { Hex, HexLike, hexFrom } from "../hex/index.js";
12
- import { Num, NumLike, numFrom, numMax } from "../num/index.js";
12
+ import { Num, NumLike, numFrom, numMax, numMin } from "../num/index.js";
13
13
  import { reduceAsync, sleep } from "../utils/index.js";
14
14
  import { ClientCache } from "./cache/index.js";
15
15
  import { ClientCacheMemory } from "./cache/memory.js";
16
- import { ClientCollectableSearchKeyLike } from "./clientTypes.advanced.js";
16
+ import {
17
+ CONFIRMED_BLOCK_TIME,
18
+ ClientCollectableSearchKeyLike,
19
+ DEFAULT_MAX_FEE_RATE,
20
+ DEFAULT_MIN_FEE_RATE,
21
+ } from "./clientTypes.advanced.js";
17
22
  import {
18
23
  CellDepInfo,
19
24
  CellDepInfoLike,
@@ -26,12 +31,17 @@ import {
26
31
  ClientIndexerSearchKeyLike,
27
32
  ClientIndexerSearchKeyTransactionLike,
28
33
  ClientTransactionResponse,
34
+ ErrorClientMaxFeeRateExceeded,
29
35
  ErrorClientWaitTransactionTimeout,
30
36
  KnownScript,
31
37
  OutputsValidator,
32
38
  ScriptInfo,
33
39
  } from "./clientTypes.js";
34
40
 
41
+ function hasHeaderConfirmed(header: ClientBlockHeader): boolean {
42
+ return numFrom(Date.now()) - header.timestamp >= CONFIRMED_BLOCK_TIME;
43
+ }
44
+
35
45
  /**
36
46
  * @public
37
47
  */
@@ -50,30 +60,113 @@ export abstract class Client {
50
60
  abstract getFeeRateStatistics(
51
61
  blockRange?: NumLike,
52
62
  ): Promise<{ mean: Num; median: Num }>;
53
- async getFeeRate(blockRange?: NumLike): Promise<Num> {
54
- return numMax((await this.getFeeRateStatistics(blockRange)).median, 1000);
63
+ async getFeeRate(
64
+ blockRange?: NumLike,
65
+ options?: { maxFeeRate?: NumLike },
66
+ ): Promise<Num> {
67
+ const feeRate = numMax(
68
+ (await this.getFeeRateStatistics(blockRange)).median,
69
+ DEFAULT_MIN_FEE_RATE,
70
+ );
71
+
72
+ const maxFeeRate = numFrom(options?.maxFeeRate ?? DEFAULT_MAX_FEE_RATE);
73
+ if (maxFeeRate === Zero) {
74
+ return feeRate;
75
+ }
76
+
77
+ return numMin(feeRate, maxFeeRate);
55
78
  }
56
79
 
57
80
  abstract getTip(): Promise<Num>;
58
81
  abstract getTipHeader(verbosity?: number | null): Promise<ClientBlockHeader>;
59
- abstract getBlockByNumber(
82
+ abstract getBlockByNumberNoCache(
60
83
  blockNumber: NumLike,
61
84
  verbosity?: number | null,
62
85
  withCycles?: boolean | null,
63
86
  ): Promise<ClientBlock | undefined>;
64
- abstract getBlockByHash(
87
+ abstract getBlockByHashNoCache(
65
88
  blockHash: HexLike,
66
89
  verbosity?: number | null,
67
90
  withCycles?: boolean | null,
68
91
  ): Promise<ClientBlock | undefined>;
69
- abstract getHeaderByNumber(
92
+ abstract getHeaderByNumberNoCache(
70
93
  blockNumber: NumLike,
71
94
  verbosity?: number | null,
72
95
  ): Promise<ClientBlockHeader | undefined>;
73
- abstract getHeaderByHash(
96
+ abstract getHeaderByHashNoCache(
74
97
  blockHash: HexLike,
75
98
  verbosity?: number | null,
76
99
  ): Promise<ClientBlockHeader | undefined>;
100
+ async getBlockByNumber(
101
+ blockNumber: NumLike,
102
+ verbosity?: number | null,
103
+ withCycles?: boolean | null,
104
+ ): Promise<ClientBlock | undefined> {
105
+ const block = await this.cache.getBlockByNumber(blockNumber);
106
+ if (block) {
107
+ return block;
108
+ }
109
+
110
+ const res = await this.getBlockByNumberNoCache(
111
+ blockNumber,
112
+ verbosity,
113
+ withCycles,
114
+ );
115
+ if (res && hasHeaderConfirmed(res.header)) {
116
+ await this.cache.recordBlocks(res);
117
+ }
118
+ return res;
119
+ }
120
+ async getBlockByHash(
121
+ blockHash: HexLike,
122
+ verbosity?: number | null,
123
+ withCycles?: boolean | null,
124
+ ): Promise<ClientBlock | undefined> {
125
+ const block = await this.cache.getBlockByHash(blockHash);
126
+ if (block) {
127
+ return block;
128
+ }
129
+
130
+ const res = await this.getBlockByHashNoCache(
131
+ blockHash,
132
+ verbosity,
133
+ withCycles,
134
+ );
135
+ if (res && hasHeaderConfirmed(res.header)) {
136
+ await this.cache.recordBlocks(res);
137
+ }
138
+ return res;
139
+ }
140
+ async getHeaderByNumber(
141
+ blockNumber: NumLike,
142
+ verbosity?: number | null,
143
+ ): Promise<ClientBlockHeader | undefined> {
144
+ const header = await this.cache.getHeaderByNumber(blockNumber);
145
+ if (header) {
146
+ return header;
147
+ }
148
+
149
+ const res = await this.getHeaderByNumberNoCache(blockNumber, verbosity);
150
+ if (res && hasHeaderConfirmed(res)) {
151
+ await this.cache.recordHeaders(res);
152
+ }
153
+ return res;
154
+ }
155
+ async getHeaderByHash(
156
+ blockHash: HexLike,
157
+ verbosity?: number | null,
158
+ ): Promise<ClientBlockHeader | undefined> {
159
+ const header = await this.cache.getHeaderByHash(blockHash);
160
+ if (header) {
161
+ return header;
162
+ }
163
+
164
+ const res = await this.getHeaderByHashNoCache(blockHash, verbosity);
165
+ if (res && hasHeaderConfirmed(res)) {
166
+ await this.cache.recordHeaders(res);
167
+ }
168
+ return res;
169
+ }
77
170
 
78
171
  abstract estimateCycles(transaction: TransactionLike): Promise<Num>;
79
172
  abstract sendTransactionDry(
@@ -97,25 +190,47 @@ export abstract class Client {
97
190
  return cached;
98
191
  }
99
192
 
100
- const transaction = await this.getTransactionNoCache(outPoint.txHash);
193
+ const transaction = await this.getTransaction(outPoint.txHash);
101
194
  if (!transaction) {
102
195
  return;
103
196
  }
104
-
105
- const index = Number(numFrom(outPoint.index));
106
- if (index >= transaction.transaction.outputs.length) {
197
+ const output = transaction.transaction.getOutput(outPoint.index);
198
+ if (!output) {
107
199
  return;
108
200
  }
109
201
 
110
202
  const cell = Cell.from({
111
203
  outPoint,
112
- cellOutput: transaction.transaction.outputs[index],
113
- outputData: transaction.transaction.outputsData[index] ?? "0x",
204
+ ...output,
114
205
  });
115
206
  await this.cache.recordCells(cell);
116
207
  return cell;
117
208
  }
118
209
 
210
+ async getCellWithHeader(
211
+ outPointLike: OutPointLike,
212
+ ): Promise<{ cell: Cell; header?: ClientBlockHeader } | undefined> {
213
+ const outPoint = OutPoint.from(outPointLike);
214
+
215
+ const res = await this.getTransactionWithHeader(outPoint.txHash);
216
+ if (!res) {
217
+ return;
218
+ }
219
+ const { transaction, header } = res;
220
+
221
+ const output = transaction.transaction.getOutput(outPoint.index);
222
+ if (!output) {
223
+ return;
224
+ }
225
+
226
+ const cell = Cell.from({
227
+ outPoint,
228
+ ...output,
229
+ });
230
+ await this.cache.recordCells(cell);
231
+ return { cell, header };
232
+ }
233
+
119
234
  abstract getCellLiveNoCache(
120
235
  outPointLike: OutPointLike,
121
236
  withData?: boolean | null,
@@ -476,14 +591,19 @@ export abstract class Client {
476
591
  async sendTransaction(
477
592
  transaction: TransactionLike,
478
593
  validator?: OutputsValidator,
594
+ options?: { maxFeeRate?: NumLike },
479
595
  ): Promise<Hex> {
480
596
  const tx = Transaction.from(transaction);
481
597
 
598
+ const maxFeeRate = numFrom(options?.maxFeeRate ?? DEFAULT_MAX_FEE_RATE);
599
+ const fee = await tx.feeRate(this);
600
+ if (maxFeeRate > Zero && fee > maxFeeRate) {
601
+ throw new ErrorClientMaxFeeRateExceeded(maxFeeRate, fee);
602
+ }
603
+
482
604
  const txHash = await this.sendTransactionNoCache(tx, validator);
483
605
 
484
- await this.cache.recordTransactions(tx);
485
606
  await this.cache.markTransactions(tx);
486
-
487
607
  return txHash;
488
608
  }
489
609
 
@@ -492,25 +612,48 @@ export abstract class Client {
492
612
  ): Promise<ClientTransactionResponse | undefined> {
493
613
  const txHash = hexFrom(txHashLike);
494
614
  const res = await this.getTransactionNoCache(txHash);
495
- if (res?.transaction) {
615
+ if (res) {
616
+ await this.cache.recordTransactionResponses(res);
496
617
  return res;
497
618
  }
498
619
 
499
- const tx = await this.cache.getTransaction(txHash);
500
- if (!tx) {
501
- return;
620
+ return this.cache.getTransactionResponse(txHash);
621
+ }
622
+
623
+ /**
624
+ * This method gets specified transaction with its block header (if existed).
625
+ * This is mainly for caching because we need the header to test if we can safely trust the cached tx status.
626
+ * @param txHashLike
627
+ */
628
+ async getTransactionWithHeader(
629
+ txHashLike: HexLike,
630
+ ): Promise<
631
+ | { transaction: ClientTransactionResponse; header?: ClientBlockHeader }
632
+ | undefined
633
+ > {
634
+ const txHash = hexFrom(txHashLike);
635
+ const tx = await this.cache.getTransactionResponse(txHash);
636
+ if (tx?.blockHash) {
637
+ const header = await this.getHeaderByHash(tx.blockHash);
638
+ if (header && hasHeaderConfirmed(header)) {
639
+ return {
640
+ transaction: tx,
641
+ header,
642
+ };
643
+ }
502
644
  }
503
645
 
646
+ const res = await this.getTransactionNoCache(txHash);
504
647
  if (!res) {
505
- return {
506
- transaction: tx,
507
- status: "sent",
508
- };
648
+ return;
509
649
  }
510
650
 
651
+ await this.cache.recordTransactionResponses(res);
511
652
  return {
512
- ...res,
513
- transaction: tx,
653
+ transaction: res,
654
+ header: res.blockHash
655
+ ? await this.getHeaderByHash(res.blockHash)
656
+ : undefined,
514
657
  };
515
658
  }
516
659
 
@@ -552,7 +695,7 @@ export abstract class Client {
552
695
  }
553
696
 
554
697
  if (Date.now() - startTime + interval >= timeout) {
555
- throw new ErrorClientWaitTransactionTimeout();
698
+ throw new ErrorClientWaitTransactionTimeout(timeout);
556
699
  }
557
700
  await sleep(interval);
558
701
  }
@@ -13,12 +13,24 @@ export class ClientPublicMainnet extends ClientJsonRpc {
13
13
  scripts?: Record<KnownScript, ScriptInfoLike | undefined>;
14
14
  },
15
15
  ) {
16
+ const hasWebSocket = typeof WebSocket !== "undefined";
16
17
  super(
17
18
  config?.url ??
18
- (typeof WebSocket !== "undefined"
19
+ (hasWebSocket
19
20
  ? "wss://mainnet.ckb.dev/ws"
20
21
  : "https://mainnet.ckb.dev/"),
21
- config,
22
+ {
23
+ ...config,
24
+ fallbacks:
25
+ config?.fallbacks ??
26
+ (hasWebSocket
27
+ ? [
28
+ "wss://mainnet.ckb.dev/ws",
29
+ "https://mainnet.ckb.dev/",
30
+ "https://mainnet.ckbapp.dev/",
31
+ ]
32
+ : ["https://mainnet.ckb.dev/", "https://mainnet.ckbapp.dev/"]),
33
+ },
22
34
  );
23
35
  }
24
36
 
@@ -13,12 +13,24 @@ export class ClientPublicTestnet extends ClientJsonRpc {
13
13
  scripts?: Record<KnownScript, ScriptInfoLike | undefined>;
14
14
  },
15
15
  ) {
16
+ const hasWebSocket = typeof WebSocket !== "undefined";
16
17
  super(
17
18
  config?.url ??
18
- (typeof WebSocket !== "undefined"
19
+ (hasWebSocket
19
20
  ? "wss://testnet.ckb.dev/ws"
20
21
  : "https://testnet.ckb.dev/"),
21
- config,
22
+ {
23
+ ...config,
24
+ fallbacks:
25
+ config?.fallbacks ??
26
+ (hasWebSocket
27
+ ? [
28
+ "wss://testnet.ckb.dev/ws",
29
+ "https://testnet.ckb.dev/",
30
+ "https://testnet.ckbapp.dev/",
31
+ ]
32
+ : ["https://testnet.ckb.dev/", "https://testnet.ckbapp.dev/"]),
33
+ },
22
34
  );
23
35
  }
24
36
 
@@ -2,6 +2,10 @@ import { ScriptLike } from "../ckb/index.js";
2
2
  import { HexLike } from "../hex/index.js";
3
3
  import { Num, numFrom, NumLike } from "../num/index.js";
4
4
 
5
+ export const CONFIRMED_BLOCK_TIME = numFrom(1000 * 10 * 50); // 50 blocks * 10s
6
+ export const DEFAULT_MAX_FEE_RATE = numFrom(10000000);
7
+ export const DEFAULT_MIN_FEE_RATE = numFrom(1000);
8
+
5
9
  export function clientSearchKeyRangeFrom([a, b]: [NumLike, NumLike]): [
6
10
  Num,
7
11
  Num,