@ckb-ccc/core 1.16.1 → 1.18.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 (55) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/address.advanced-Dw4qPDGb.mjs +2 -0
  3. package/dist/address.advanced-Dw4qPDGb.mjs.map +1 -0
  4. package/dist/advanced.d.mts +1 -1
  5. package/dist/advanced.mjs +1 -1
  6. package/{dist.commonjs/advancedBarrel-CiJnF8me.d.ts → dist/advancedBarrel-BbbHx4z4.d.mts} +1188 -163
  7. package/dist/advancedBarrel-BbbHx4z4.d.mts.map +1 -0
  8. package/dist/advancedBarrel.d.mts +1 -1
  9. package/dist/advancedBarrel.mjs +1 -1
  10. package/dist/barrel-BqMBxl8y.mjs +7 -0
  11. package/dist/barrel-BqMBxl8y.mjs.map +1 -0
  12. package/dist/barrel.d.mts +2 -2
  13. package/dist/barrel.mjs +1 -1
  14. package/dist/index.d.mts +2 -2
  15. package/dist/index.mjs +1 -1
  16. package/dist.commonjs/address.advanced-DG1gpW7t.js +2 -0
  17. package/dist.commonjs/address.advanced-DG1gpW7t.js.map +1 -0
  18. package/dist.commonjs/advanced.d.ts +1 -1
  19. package/dist.commonjs/advanced.js +1 -1
  20. package/{dist/advancedBarrel-66JgvInl.d.mts → dist.commonjs/advancedBarrel-CJAh6sm8.d.ts} +1189 -162
  21. package/dist.commonjs/advancedBarrel-CJAh6sm8.d.ts.map +1 -0
  22. package/dist.commonjs/advancedBarrel.d.ts +1 -1
  23. package/dist.commonjs/advancedBarrel.js +1 -1
  24. package/dist.commonjs/barrel-CbH8-KWr.js +21 -0
  25. package/dist.commonjs/barrel-CbH8-KWr.js.map +1 -0
  26. package/dist.commonjs/barrel.d.ts +2 -2
  27. package/dist.commonjs/barrel.js +1 -1
  28. package/dist.commonjs/index.d.ts +2 -2
  29. package/dist.commonjs/index.js +1 -1
  30. package/dist.commonjs/rolldown-runtime-DWNI8pZO.js +1 -0
  31. package/package.json +4 -3
  32. package/src/ckb/transaction.ts +275 -55
  33. package/src/client/jsonRpc/client.ts +7 -3
  34. package/src/codec/entity.ts +93 -0
  35. package/src/jsonRpc/requestor.ts +9 -4
  36. package/src/jsonRpc/transports/fallback.ts +14 -11
  37. package/src/molecule/codec.ts +25 -5
  38. package/src/signer/btc/signerBtc.ts +1 -1
  39. package/src/signer/ckb/index.ts +1 -0
  40. package/src/signer/ckb/signerCkbAlwaysSuccess.ts +71 -0
  41. package/src/signer/ckb/signerCkbPrivateKey.ts +2 -3
  42. package/src/signer/ckb/signerMultisigCkbReadonly.ts +5 -5
  43. package/src/signer/doge/signerDoge.ts +1 -1
  44. package/src/signer/evm/signerEvm.ts +2 -2
  45. package/src/signer/nostr/signerNostr.ts +1 -1
  46. package/dist/address.advanced-C5UrS2uk.mjs +0 -2
  47. package/dist/address.advanced-C5UrS2uk.mjs.map +0 -1
  48. package/dist/advancedBarrel-66JgvInl.d.mts.map +0 -1
  49. package/dist/barrel-yZm3ICzn.mjs +0 -7
  50. package/dist/barrel-yZm3ICzn.mjs.map +0 -1
  51. package/dist.commonjs/address.advanced-CQJ24Iei.js +0 -2
  52. package/dist.commonjs/address.advanced-CQJ24Iei.js.map +0 -1
  53. package/dist.commonjs/advancedBarrel-CiJnF8me.d.ts.map +0 -1
  54. package/dist.commonjs/barrel-CddEgDBp.js +0 -21
  55. package/dist.commonjs/barrel-CddEgDBp.js.map +0 -1
@@ -32,6 +32,30 @@ import {
32
32
  } from "./transactionErrors.js";
33
33
  import type { LumosTransactionSkeletonType } from "./transactionLumos.js";
34
34
 
35
+ const MAX_ARRAY_INDEX = 2n ** 32n - 2n;
36
+
37
+ function transactionIndexFrom(index: NumLike): number | undefined {
38
+ try {
39
+ const value = numFrom(index);
40
+ if (value < 0n || value > MAX_ARRAY_INDEX) {
41
+ return;
42
+ }
43
+ return Number(value);
44
+ } catch (_) {
45
+ return;
46
+ }
47
+ }
48
+
49
+ function transactionIndexOrThrow(index: number): number {
50
+ const value = transactionIndexFrom(index);
51
+ if (value === undefined) {
52
+ throw new Error(
53
+ `Index ${index} must be a valid array index between 0 and ${MAX_ARRAY_INDEX}`,
54
+ );
55
+ }
56
+ return value;
57
+ }
58
+
35
59
  export const DepTypeCodec: Codec<DepTypeLike, DepType> = Codec.from({
36
60
  byteLength: 1,
37
61
  encode: depTypeToBytes,
@@ -1169,6 +1193,10 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
1169
1193
  * ```
1170
1194
  */
1171
1195
  copy(txLike: TransactionLike) {
1196
+ if (txLike === this) {
1197
+ return;
1198
+ }
1199
+
1172
1200
  const tx = Transaction.from(txLike);
1173
1201
  this.version = tx.version;
1174
1202
  this.cellDeps = tx.cellDeps;
@@ -1620,41 +1648,75 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
1620
1648
  }
1621
1649
 
1622
1650
  /**
1623
- * Set output data at index.
1651
+ * Sets output data at an index.
1652
+ *
1653
+ * @deprecated Use {@link setOutputData} instead.
1654
+ */
1655
+ setOutputDataAt(index: number, data: HexLike): void {
1656
+ this.setOutputData(index, data);
1657
+ }
1658
+
1659
+ /**
1660
+ * Sets output data at an index, filling any skipped positions with empty data.
1624
1661
  *
1625
1662
  * @param index - The index of the output data.
1626
1663
  * @param data - The data to set.
1664
+ * @throws If `index` is invalid or does not identify an existing output.
1627
1665
  *
1628
1666
  * @example
1629
1667
  * ```typescript
1630
- * tx.setOutputDataAt(0, "0x00");
1668
+ * tx.setOutputData(0, "0x00");
1631
1669
  * ```
1632
1670
  */
1633
- setOutputDataAt(index: number, data: HexLike): void {
1634
- if (this.outputsData.length < index) {
1671
+ setOutputData(index: number, data: HexLike): void {
1672
+ const i = transactionIndexOrThrow(index);
1673
+ if (i >= this.outputs.length) {
1674
+ throw new Error(
1675
+ `Index ${index} is out of bounds for outputs of length ${this.outputs.length}`,
1676
+ );
1677
+ }
1678
+
1679
+ if (this.outputsData.length < i) {
1635
1680
  this.outputsData.push(
1636
- ...Array.from(
1637
- new Array(index - this.outputsData.length),
1638
- (): Hex => "0x",
1639
- ),
1681
+ ...Array.from(new Array(i - this.outputsData.length), (): Hex => "0x"),
1640
1682
  );
1641
1683
  }
1642
1684
 
1643
- this.outputsData[index] = hexFrom(data);
1685
+ this.outputsData[i] = hexFrom(data);
1686
+ }
1687
+
1688
+ /**
1689
+ * Gets the data associated with an output.
1690
+ *
1691
+ * @param index - The output index.
1692
+ * @returns The output data, or `undefined` if the index is invalid or out of bounds.
1693
+ *
1694
+ * @example
1695
+ * ```typescript
1696
+ * const data = tx.getOutputData(0);
1697
+ * ```
1698
+ */
1699
+ getOutputData(index: NumLike): Hex | undefined {
1700
+ const i = transactionIndexFrom(index);
1701
+ return i !== undefined && i < this.outputs.length
1702
+ ? (this.outputsData[i] ?? "0x")
1703
+ : undefined;
1644
1704
  }
1645
1705
 
1646
1706
  /**
1647
- * get input
1707
+ * Gets an input.
1648
1708
  *
1649
- * @param index - The cell input index
1709
+ * @param index - The cell input index.
1710
+ * @returns The input, or `undefined` if the index is invalid or out of bounds.
1650
1711
  *
1651
1712
  * @example
1652
1713
  * ```typescript
1653
- * await tx.getInput(0);
1714
+ * tx.getInput(0);
1654
1715
  * ```
1655
1716
  */
1656
1717
  getInput(index: NumLike): CellInput | undefined {
1657
- return this.inputs[Number(numFrom(index))];
1718
+ const i = transactionIndexFrom(index);
1719
+ return i === undefined ? undefined : this.inputs[i];
1658
1720
  }
1659
1721
  /**
1660
1722
  * add input
@@ -1675,18 +1737,19 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
1675
1737
  }
1676
1738
 
1677
1739
  /**
1678
- * get output
1740
+ * Gets an output cell together with its data.
1679
1741
  *
1680
- * @param index - The cell output index
1742
+ * @param index - The cell output index.
1743
+ * @returns The output cell, or `undefined` if the index is invalid or out of bounds.
1681
1744
  *
1682
1745
  * @example
1683
1746
  * ```typescript
1684
- * await tx.getOutput(0);
1747
+ * tx.getOutput(0);
1685
1748
  * ```
1686
1749
  */
1687
1750
  getOutput(index: NumLike): CellAny | undefined {
1688
- const i = Number(numFrom(index));
1689
- if (i >= this.outputs.length) {
1751
+ const i = transactionIndexFrom(index);
1752
+ if (i === undefined || i >= this.outputs.length) {
1690
1753
  return;
1691
1754
  }
1692
1755
  return CellAny.from({
@@ -1769,85 +1832,238 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
1769
1832
  });
1770
1833
 
1771
1834
  const len = this.outputs.push(cell.cellOutput);
1772
- this.setOutputDataAt(len - 1, cell.outputData);
1835
+ this.setOutputData(len - 1, cell.outputData);
1773
1836
 
1774
1837
  return len;
1775
1838
  }
1776
1839
 
1777
1840
  /**
1778
- * Get witness at index as WitnessArgs
1841
+ * Sets or appends an output cell.
1842
+ *
1843
+ * An index equal to the current output count appends the cell. Existing
1844
+ * outputs are replaced together with their corresponding output data.
1845
+ *
1846
+ * @param index - The output index to set, from zero through the current output count.
1847
+ * @param cellLike - A cell-like object containing both the output and its data.
1848
+ * @throws If `index` is not a valid array index or is greater than the output count.
1849
+ *
1850
+ * @example
1851
+ * ```typescript
1852
+ * tx.setOutput(0, {
1853
+ * cellOutput: { lock: recipientLock },
1854
+ * outputData: "0x1234",
1855
+ * });
1856
+ * ```
1857
+ */
1858
+ setOutput(index: number, cellLike: CellAnyLike): void;
1859
+
1860
+ /**
1861
+ * Sets or appends an output and its data.
1862
+ *
1863
+ * @param index - The output index to set, from zero through the current output count.
1864
+ * @param outputLike - The cell output to set.
1865
+ * @param outputDataLike - The output data, defaulting to `"0x"`.
1866
+ * @throws If `index` is not a valid array index or is greater than the output count.
1867
+ *
1868
+ * @example
1869
+ * ```typescript
1870
+ * tx.setOutput(0, { lock: recipientLock }, "0x1234");
1871
+ * ```
1872
+ */
1873
+ setOutput(
1874
+ index: number,
1875
+ outputLike: CellOutputLike,
1876
+ outputDataLike?: HexLike | null,
1877
+ ): void;
1878
+ setOutput(
1879
+ index: number,
1880
+ cellOrOutputLike: CellAnyLike | CellOutputLike,
1881
+ outputDataLike?: HexLike | null,
1882
+ ): void {
1883
+ const i = transactionIndexOrThrow(index);
1884
+ if (i > this.outputs.length) {
1885
+ throw new Error(
1886
+ `Index ${index} is out of bounds for outputs of length ${this.outputs.length}`,
1887
+ );
1888
+ }
1889
+
1890
+ const cell =
1891
+ "cellOutput" in cellOrOutputLike
1892
+ ? CellAny.from(cellOrOutputLike)
1893
+ : CellAny.from({
1894
+ cellOutput: cellOrOutputLike,
1895
+ outputData: outputDataLike,
1896
+ });
1897
+
1898
+ if (i === this.outputs.length) {
1899
+ this.addOutput(cell);
1900
+ } else {
1901
+ this.outputs[i] = cell.cellOutput;
1902
+ this.setOutputData(i, cell.outputData);
1903
+ }
1904
+ }
1905
+
1906
+ /**
1907
+ * Sets or appends an input.
1908
+ *
1909
+ * An index equal to the current input count appends the input using
1910
+ * {@link addInput}, preserving witness alignment. An existing index replaces
1911
+ * only that input.
1912
+ *
1913
+ * @param index - The input index to set, from zero through the current input count.
1914
+ * @param inputLike - The cell input to set.
1915
+ * @throws If `index` is not a valid array index or is greater than the input count.
1916
+ *
1917
+ * @example
1918
+ * ```typescript
1919
+ * tx.setInput(0, {
1920
+ * previousOutput: { txHash: "0x...", index: 0 },
1921
+ * });
1922
+ * ```
1923
+ */
1924
+ setInput(index: number, inputLike: CellInputLike): void {
1925
+ const i = transactionIndexOrThrow(index);
1926
+ if (i > this.inputs.length) {
1927
+ throw new Error(
1928
+ `Index ${index} is out of bounds for inputs of length ${this.inputs.length}`,
1929
+ );
1930
+ }
1931
+
1932
+ if (i === this.inputs.length) {
1933
+ this.addInput(inputLike);
1934
+ } else {
1935
+ this.inputs[i] = CellInput.from(inputLike);
1936
+ }
1937
+ }
1938
+
1939
+ /**
1940
+ * Gets a raw witness.
1941
+ *
1942
+ * @param index - The witness index.
1943
+ * @returns The witness, or `undefined` if the index is invalid or out of bounds.
1944
+ *
1945
+ * @example
1946
+ * ```typescript
1947
+ * const witness = tx.getWitness(0);
1948
+ * ```
1949
+ */
1950
+ getWitness(index: NumLike): Hex | undefined {
1951
+ const i = transactionIndexFrom(index);
1952
+ return i === undefined ? undefined : this.witnesses[i];
1953
+ }
1954
+
1955
+ /**
1956
+ * Gets the witness at an index as `WitnessArgs`.
1779
1957
  *
1780
1958
  * @param index - The index of the witness.
1781
1959
  * @returns The witness parsed as WitnessArgs.
1782
1960
  *
1783
1961
  * @example
1784
1962
  * ```typescript
1785
- * const witnessArgs = tx.getWitnessArgsAt(0);
1963
+ * const witnessArgs = tx.getWitnessArgs(0);
1786
1964
  * ```
1787
1965
  */
1788
- getWitnessArgsAt(index: number): WitnessArgs | undefined {
1966
+ getWitnessArgs(index: NumLike): WitnessArgs | undefined {
1789
1967
  try {
1790
- return this.getWitnessArgsAtUnsafe(index);
1968
+ return this.getWitnessArgsUnsafe(index);
1791
1969
  } catch (_) {
1792
1970
  return undefined;
1793
1971
  }
1794
1972
  }
1795
1973
 
1796
1974
  /**
1797
- * Get witness at index as WitnessArgs, throw if failed to decode
1975
+ * Gets the witness at an index as `WitnessArgs`, throwing if decoding fails.
1798
1976
  *
1799
1977
  * @param index - The index of the witness.
1800
1978
  * @returns The witness parsed as WitnessArgs.
1979
+ * @throws If the witness cannot be decoded as `WitnessArgs`.
1801
1980
  *
1802
1981
  * @example
1803
1982
  * ```typescript
1804
- * const witnessArgs = tx.getWitnessArgsAtUnsafe(0);
1983
+ * const witnessArgs = tx.getWitnessArgsUnsafe(0);
1805
1984
  * ```
1806
1985
  */
1807
- getWitnessArgsAtUnsafe(index: number): WitnessArgs | undefined {
1808
- const rawWitness = this.witnesses[index];
1809
- return (rawWitness ?? "0x") !== "0x"
1986
+ getWitnessArgsUnsafe(index: NumLike): WitnessArgs | undefined {
1987
+ const rawWitness = this.getWitness(index);
1988
+ return rawWitness && rawWitness !== "0x"
1810
1989
  ? WitnessArgs.fromBytes(rawWitness)
1811
1990
  : undefined;
1812
1991
  }
1813
1992
 
1814
1993
  /**
1815
- * Set witness at index by WitnessArgs
1994
+ * Sets a witness at an index from `WitnessArgs`.
1816
1995
  *
1817
1996
  * @param index - The index of the witness.
1818
- * @param witness - The WitnessArgs to set.
1997
+ * @param witnessLike - The WitnessArgs-like value to set.
1998
+ * @throws If `index` is not a valid array index.
1819
1999
  *
1820
2000
  * @example
1821
2001
  * ```typescript
1822
- * await tx.setWitnessArgsAt(0, witnessArgs);
2002
+ * tx.setWitnessArgs(0, witnessArgs);
1823
2003
  * ```
1824
2004
  */
1825
- setWitnessArgsAt(index: number, witness: WitnessArgs): void {
1826
- this.setWitnessAt(index, witness.toBytes());
2005
+ setWitnessArgs(index: number, witnessLike: WitnessArgsLike): void {
2006
+ this.setWitness(index, WitnessArgs.from(witnessLike).toBytes());
1827
2007
  }
1828
2008
 
1829
2009
  /**
1830
- * Set witness at index
2010
+ * Sets a witness at an index, filling any skipped positions with empty witnesses.
1831
2011
  *
1832
2012
  * @param index - The index of the witness.
1833
2013
  * @param witness - The witness to set.
2014
+ * @throws If `index` is not a valid array index.
1834
2015
  *
1835
2016
  * @example
1836
2017
  * ```typescript
1837
- * await tx.setWitnessAt(0, witness);
2018
+ * tx.setWitness(0, witness);
1838
2019
  * ```
1839
2020
  */
1840
- setWitnessAt(index: number, witness: HexLike): void {
1841
- if (this.witnesses.length < index) {
2021
+ setWitness(index: number, witness: HexLike): void {
2022
+ const i = transactionIndexOrThrow(index);
2023
+
2024
+ if (this.witnesses.length < i) {
1842
2025
  this.witnesses.push(
1843
- ...Array.from(
1844
- new Array(index - this.witnesses.length),
1845
- (): Hex => "0x",
1846
- ),
2026
+ ...Array.from(new Array(i - this.witnesses.length), (): Hex => "0x"),
1847
2027
  );
1848
2028
  }
1849
2029
 
1850
- this.witnesses[index] = hexFrom(witness);
2030
+ this.witnesses[i] = hexFrom(witness);
2031
+ }
2032
+
2033
+ /**
2034
+ * Gets the witness at an index as `WitnessArgs`.
2035
+ *
2036
+ * @deprecated Use {@link getWitnessArgs} instead.
2037
+ */
2038
+ getWitnessArgsAt(index: NumLike): WitnessArgs | undefined {
2039
+ return this.getWitnessArgs(index);
2040
+ }
2041
+
2042
+ /**
2043
+ * Gets the witness at an index as `WitnessArgs`, throwing if decoding fails.
2044
+ *
2045
+ * @deprecated Use {@link getWitnessArgsUnsafe} instead.
2046
+ */
2047
+ getWitnessArgsAtUnsafe(index: NumLike): WitnessArgs | undefined {
2048
+ return this.getWitnessArgsUnsafe(index);
2049
+ }
2050
+
2051
+ /**
2052
+ * Sets a witness at an index from `WitnessArgs`.
2053
+ *
2054
+ * @deprecated Use {@link setWitnessArgs} instead.
2055
+ */
2056
+ setWitnessArgsAt(index: number, witnessLike: WitnessArgsLike): void {
2057
+ this.setWitnessArgs(index, witnessLike);
2058
+ }
2059
+
2060
+ /**
2061
+ * Sets a witness at an index.
2062
+ *
2063
+ * @deprecated Use {@link setWitness} instead.
2064
+ */
2065
+ setWitnessAt(index: number, witness: HexLike): void {
2066
+ this.setWitness(index, witness);
1851
2067
  }
1852
2068
 
1853
2069
  /**
@@ -1873,9 +2089,9 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
1873
2089
  return;
1874
2090
  }
1875
2091
 
1876
- const witness = this.getWitnessArgsAt(position) ?? WitnessArgs.from({});
2092
+ const witness = this.getWitnessArgs(position) ?? WitnessArgs.from({});
1877
2093
  witness.lock = hexFrom(Array.from(new Array(lockLen), () => 0));
1878
- this.setWitnessArgsAt(position, witness);
2094
+ this.setWitnessArgs(position, witness);
1879
2095
  }
1880
2096
 
1881
2097
  async getInputsCapacityExtra(client: Client): Promise<Num> {
@@ -2210,6 +2426,8 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
2210
2426
  let leastFee = Zero;
2211
2427
  let leastExtraCapacity = Zero;
2212
2428
  let collected = 0;
2429
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
2430
+ let tx: Transaction = this;
2213
2431
 
2214
2432
  // ===
2215
2433
  // Usually, for the worst situation, three iterations are needed
@@ -2224,7 +2442,7 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
2224
2442
  }
2225
2443
 
2226
2444
  try {
2227
- return await this.completeInputsByCapacity(
2445
+ return await tx.completeInputsByCapacity(
2228
2446
  from,
2229
2447
  leastFee + leastExtraCapacity,
2230
2448
  filter,
@@ -2243,7 +2461,8 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
2243
2461
  }
2244
2462
  })();
2245
2463
 
2246
- const fee = await this.getFee(from.client);
2464
+ tx = await from.prepareTransaction(tx);
2465
+ const fee = await tx.getFee(from.client);
2247
2466
  if (fee < leastFee + leastExtraCapacity) {
2248
2467
  // Not enough capacity are collected, it should only happens when shouldAddInputs is false
2249
2468
  throw new ErrorTransactionInsufficientCapacity(
@@ -2251,44 +2470,45 @@ export class Transaction extends Entity.Base<TransactionLike, Transaction>() {
2251
2470
  { isForChange: leastExtraCapacity !== Zero },
2252
2471
  );
2253
2472
  }
2254
-
2255
- await from.prepareTransaction(this);
2256
2473
  if (leastFee === Zero) {
2257
2474
  // The initial fee is calculated based on prepared transaction
2258
2475
  // This should only happens during the first iteration
2259
- leastFee = this.estimateFee(feeRate);
2476
+ leastFee = tx.estimateFee(feeRate);
2260
2477
  }
2261
2478
  // The extra capacity paid the fee without a change
2262
2479
  // leastExtraCapacity should be 0 here, otherwise we should failed in the previous check
2263
2480
  // So this only happens in the first iteration
2264
2481
  if (fee === leastFee) {
2482
+ this.copy(tx);
2265
2483
  return [collected, false];
2266
2484
  }
2267
2485
 
2268
2486
  // Invoke the change function on a transaction multiple times may cause problems, so we clone it
2269
- const tx = this.clone();
2270
- const needed = numFrom(await Promise.resolve(change(tx, fee - leastFee)));
2487
+ let changedTx = tx.clone();
2488
+ const needed = numFrom(
2489
+ await Promise.resolve(change(changedTx, fee - leastFee)),
2490
+ );
2271
2491
  if (needed > Zero) {
2272
2492
  // No enough extra capacity to create new cells for change, collect inputs again
2273
2493
  leastExtraCapacity = needed;
2274
2494
  continue;
2275
2495
  }
2276
2496
 
2277
- if ((await tx.getFee(from.client)) !== leastFee) {
2497
+ if ((await changedTx.getFee(from.client)) !== leastFee) {
2278
2498
  throw new Error(
2279
2499
  "The change function doesn't use all available capacity",
2280
2500
  );
2281
2501
  }
2282
2502
 
2283
2503
  // New change cells created, update the fee
2284
- await from.prepareTransaction(tx);
2285
- const changedFee = tx.estimateFee(feeRate);
2504
+ changedTx = await from.prepareTransaction(changedTx);
2505
+ const changedFee = changedTx.estimateFee(feeRate);
2286
2506
  if (leastFee > changedFee) {
2287
2507
  throw new Error("The change function removed existed transaction data");
2288
2508
  }
2289
2509
  // The fee has been paid
2290
2510
  if (leastFee === changedFee) {
2291
- this.copy(tx);
2511
+ this.copy(changedTx);
2292
2512
  return [collected, true];
2293
2513
  }
2294
2514
 
@@ -13,6 +13,7 @@ import { Num, NumLike, numFrom, numToHex } from "../../num/index.js";
13
13
  import { apply } from "../../utils/index.js";
14
14
  import { ClientCache } from "../cache/index.js";
15
15
  import { Client } from "../client.js";
16
+ import { DEFAULT_MIN_FEE_RATE } from "../clientTypes.advanced.js";
16
17
  import {
17
18
  ClientFindCellsResponse,
18
19
  ClientIndexerSearchKeyLike,
@@ -131,9 +132,12 @@ export abstract class ClientJsonRpc extends Client {
131
132
  getFeeRateStatistics = this.buildSender(
132
133
  "get_fee_rate_statistics",
133
134
  [(n: NumLike) => apply(numFrom, n)],
134
- ({ mean, median }: { mean: NumLike; median: NumLike }) => ({
135
- mean: numFrom(mean),
136
- median: numFrom(median),
135
+ // TODO: This is a temporary workaround for the fact that the node may return null for fee rate statistics.
136
+ // We should change the method signature to return null instead of falling back to the default value,
137
+ // but this would be a breaking change.
138
+ (res: { mean: NumLike; median: NumLike } | null) => ({
139
+ mean: numFrom(res?.mean ?? DEFAULT_MIN_FEE_RATE),
140
+ median: numFrom(res?.median ?? DEFAULT_MIN_FEE_RATE),
137
141
  }),
138
142
  ) as Client["getFeeRateStatistics"];
139
143
 
@@ -1,7 +1,9 @@
1
1
  import { Bytes, bytesEq, bytesFrom, BytesLike } from "../bytes/index.js";
2
2
  import { hashCkb } from "../hasher/index.js";
3
3
  import { Hex, hexFrom } from "../hex/index.js";
4
+ import type { UnionDecoded, UnionMatchHandlers } from "../molecule/codec.js";
4
5
  import { Constructor } from "../utils/index.js";
6
+ import type { CodecLike, DecodedType } from "./codec.js";
5
7
 
6
8
  /**
7
9
  * The base class of CCC to create a serializable instance. This should be used with the {@link codec} decorator.
@@ -144,10 +146,101 @@ export abstract class Entity {
144
146
  return Impl;
145
147
  }
146
148
 
149
+ /**
150
+ * Convert the entity to bytes.
151
+ * @public
152
+ * @returns The bytes representation of the entity.
153
+ */
147
154
  abstract toBytes(): Bytes;
155
+
156
+ /**
157
+ * Calculate the hash of the entity.
158
+ * @public
159
+ * @returns The hash of the entity.
160
+ */
148
161
  abstract hash(): Hex;
162
+
163
+ /**
164
+ * Convert the entity to a full-byte untrimmed Hex representation.
165
+ * @public
166
+ * @returns The entity full-byte untrimmed hexadecimal representation.
167
+ */
149
168
  abstract toHex(): Hex;
169
+
170
+ /**
171
+ * Create a clone of the entity.
172
+ * @public
173
+ * @returns A clone of the entity.
174
+ */
150
175
  abstract clone(): Entity;
176
+
177
+ /**
178
+ * Generate a base class of CCC to create a serializable Union instance.
179
+ * This should be used with the {@link codec} decorator.
180
+ * @public
181
+ */
182
+ static BaseUnion<
183
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
184
+ CodecType extends CodecLike<any, UnionDecoded<any, any>>,
185
+ SubTypeLike,
186
+ SubType = SubTypeLike,
187
+ >() {
188
+ type Handlers<Result> = UnionMatchHandlers<CodecType, Result>;
189
+ type Decoded = DecodedType<CodecType>;
190
+
191
+ abstract class Impl extends Entity.Base<SubTypeLike, SubType>() {
192
+ /**
193
+ * The inner decoded object representing the union value.
194
+ * @public
195
+ */
196
+ constructor(public readonly inner: Decoded) {
197
+ super();
198
+ }
199
+
200
+ /**
201
+ * Match the union type with a set of handlers.
202
+ * @public
203
+ * @param handlers - A map of type-specific handlers, all variants must be handled if no default handler `_` is provided.
204
+ * @returns The result returned by the matching handler.
205
+ */
206
+ match<Result>(
207
+ handlers: Handlers<Result> & {
208
+ _?: undefined | null;
209
+ },
210
+ ): Result;
211
+ /**
212
+ * Match the union type with a set of handlers.
213
+ * @public
214
+ * @param handlers - A map of type-specific handlers, unmatched variants will be handled by the default handler `_`.
215
+ * @returns The result returned by the matching handler.
216
+ */
217
+ match<Result>(
218
+ handlers: Partial<Handlers<Result>> & {
219
+ _: (inner: Decoded) => Result;
220
+ },
221
+ ): Result;
222
+ match<Result>(
223
+ handlers:
224
+ | (Handlers<Result> & {
225
+ _?: undefined | null;
226
+ })
227
+ | (Partial<Handlers<Result>> & {
228
+ _: (inner: Decoded) => Result;
229
+ }),
230
+ ): Result {
231
+ const type = this.inner.type as Decoded["type"];
232
+ const handler = (handlers as Partial<Handlers<Result>>)[type];
233
+
234
+ if (handler != undefined) {
235
+ return handler(this.inner.value);
236
+ }
237
+
238
+ return (handlers._ as (inner: Decoded) => Result)(this.inner);
239
+ }
240
+ }
241
+
242
+ return Impl;
243
+ }
151
244
  }
152
245
 
153
246
  /**
@@ -129,14 +129,19 @@ export class RequestorJsonRpc {
129
129
  await pending;
130
130
  }
131
131
 
132
- this.concurrent += 1;
133
- const res = (await this.transport.request(payload)) as {
132
+ const res = (await (async () => {
133
+ this.concurrent += 1;
134
+ try {
135
+ return await this.transport.request(payload);
136
+ } finally {
137
+ this.concurrent -= 1;
138
+ this.pending.shift()?.();
139
+ }
140
+ })()) as {
134
141
  id: number;
135
142
  error: unknown;
136
143
  result: unknown;
137
144
  };
138
- this.concurrent -= 1;
139
- this.pending.shift()?.();
140
145
 
141
146
  if (res.id !== payload.id) {
142
147
  throw new Error(`Id mismatched, got ${res.id}, expected ${payload.id}`);