@ckb-ccc/core 1.0.0 → 1.1.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 (74) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/address/address.advanced.d.ts.map +1 -1
  3. package/dist/address/address.advanced.js +6 -2
  4. package/dist/bytes/index.d.ts +21 -0
  5. package/dist/bytes/index.d.ts.map +1 -1
  6. package/dist/bytes/index.js +31 -4
  7. package/dist/ckb/script.d.ts +1 -1
  8. package/dist/ckb/script.d.ts.map +1 -1
  9. package/dist/ckb/script.js +2 -4
  10. package/dist/ckb/transaction.d.ts +19 -12
  11. package/dist/ckb/transaction.d.ts.map +1 -1
  12. package/dist/ckb/transaction.js +23 -38
  13. package/dist/client/clientPublicMainnet.d.ts +4 -4
  14. package/dist/client/clientPublicMainnet.d.ts.map +1 -1
  15. package/dist/client/clientPublicTestnet.d.ts +4 -4
  16. package/dist/client/clientPublicTestnet.d.ts.map +1 -1
  17. package/dist/client/clientPublicTestnet.js +3 -0
  18. package/dist/client/jsonRpc/index.d.ts +4 -0
  19. package/dist/client/jsonRpc/index.d.ts.map +1 -1
  20. package/dist/client/jsonRpc/index.js +11 -0
  21. package/dist/molecule/codec.d.ts +2 -0
  22. package/dist/molecule/codec.d.ts.map +1 -1
  23. package/dist/molecule/codec.js +51 -41
  24. package/dist/molecule/entity.d.ts +25 -4
  25. package/dist/molecule/entity.d.ts.map +1 -1
  26. package/dist/molecule/entity.js +22 -1
  27. package/dist/num/index.d.ts.map +1 -1
  28. package/dist/num/index.js +2 -2
  29. package/dist/signer/btc/verify.js +1 -1
  30. package/dist/signer/doge/verify.d.ts.map +1 -1
  31. package/dist/signer/doge/verify.js +3 -1
  32. package/dist.commonjs/address/address.advanced.d.ts.map +1 -1
  33. package/dist.commonjs/address/address.advanced.js +6 -2
  34. package/dist.commonjs/bytes/index.d.ts +21 -0
  35. package/dist.commonjs/bytes/index.d.ts.map +1 -1
  36. package/dist.commonjs/bytes/index.js +32 -4
  37. package/dist.commonjs/ckb/script.d.ts +1 -1
  38. package/dist.commonjs/ckb/script.d.ts.map +1 -1
  39. package/dist.commonjs/ckb/script.js +2 -4
  40. package/dist.commonjs/ckb/transaction.d.ts +19 -12
  41. package/dist.commonjs/ckb/transaction.d.ts.map +1 -1
  42. package/dist.commonjs/ckb/transaction.js +23 -38
  43. package/dist.commonjs/client/clientPublicMainnet.d.ts +4 -4
  44. package/dist.commonjs/client/clientPublicMainnet.d.ts.map +1 -1
  45. package/dist.commonjs/client/clientPublicTestnet.d.ts +4 -4
  46. package/dist.commonjs/client/clientPublicTestnet.d.ts.map +1 -1
  47. package/dist.commonjs/client/clientPublicTestnet.js +3 -0
  48. package/dist.commonjs/client/jsonRpc/index.d.ts +4 -0
  49. package/dist.commonjs/client/jsonRpc/index.d.ts.map +1 -1
  50. package/dist.commonjs/client/jsonRpc/index.js +11 -0
  51. package/dist.commonjs/molecule/codec.d.ts +2 -0
  52. package/dist.commonjs/molecule/codec.d.ts.map +1 -1
  53. package/dist.commonjs/molecule/codec.js +50 -40
  54. package/dist.commonjs/molecule/entity.d.ts +25 -4
  55. package/dist.commonjs/molecule/entity.d.ts.map +1 -1
  56. package/dist.commonjs/molecule/entity.js +22 -1
  57. package/dist.commonjs/num/index.d.ts.map +1 -1
  58. package/dist.commonjs/num/index.js +2 -2
  59. package/dist.commonjs/signer/btc/verify.js +1 -1
  60. package/dist.commonjs/signer/doge/verify.d.ts.map +1 -1
  61. package/dist.commonjs/signer/doge/verify.js +3 -1
  62. package/package.json +1 -1
  63. package/src/address/address.advanced.ts +6 -2
  64. package/src/bytes/index.ts +36 -6
  65. package/src/ckb/script.ts +5 -7
  66. package/src/ckb/transaction.ts +43 -58
  67. package/src/client/clientPublicMainnet.ts +4 -3
  68. package/src/client/clientPublicTestnet.ts +9 -3
  69. package/src/client/jsonRpc/index.ts +24 -1
  70. package/src/molecule/codec.ts +73 -61
  71. package/src/molecule/entity.ts +25 -5
  72. package/src/num/index.ts +2 -5
  73. package/src/signer/btc/verify.ts +1 -1
  74. package/src/signer/doge/verify.ts +3 -1
@@ -10,6 +10,41 @@ export type Bytes = Uint8Array;
10
10
  */
11
11
  export type BytesLike = string | Uint8Array | ArrayBuffer | ArrayLike<number>;
12
12
 
13
+ /**
14
+ * Concatenates multiple byte-like arrays to the first number array.
15
+ * @public
16
+ *
17
+ * @param result - The number array as result
18
+ * @param args - The byte-like arrays to concatenate.
19
+ * @returns The first number array
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const concatenatedBytes = [1, 2];
24
+ * bytesConcatTo(
25
+ * concatenatedBytes
26
+ * new Uint8Array([3, 4]),
27
+ * "hello",
28
+ * [5, 6, 7]
29
+ * );
30
+ * console.log(concatenatedBytes); // Outputs [1, 2, 3, 4, /* bytes of "hello" *\/, 5, 6, 7]
31
+ * ```
32
+ */
33
+
34
+ export function bytesConcatTo(
35
+ result: number[],
36
+ ...args: BytesLike[]
37
+ ): number[] {
38
+ return args.reduce((acc: number[], v) => {
39
+ const bytes = bytesFrom(v);
40
+ // Spread operator will cause call stack size exceeded
41
+ for (const byte of bytes) {
42
+ result.push(byte);
43
+ }
44
+ return acc;
45
+ }, result);
46
+ }
47
+
13
48
  /**
14
49
  * Concatenates multiple byte-like arrays into a single byte array.
15
50
  * @public
@@ -30,12 +65,7 @@ export type BytesLike = string | Uint8Array | ArrayBuffer | ArrayLike<number>;
30
65
  */
31
66
 
32
67
  export function bytesConcat(...args: BytesLike[]): Bytes {
33
- return new Uint8Array(
34
- args.reduce((acc: number[], v) => {
35
- acc.push(...bytesFrom(v));
36
- return acc;
37
- }, []),
38
- );
68
+ return new Uint8Array(bytesConcatTo([], ...args));
39
69
  }
40
70
 
41
71
  /**
package/src/ckb/script.ts CHANGED
@@ -108,13 +108,11 @@ export type ScriptLike = {
108
108
  * @public
109
109
  */
110
110
  @mol.codec(
111
- mol
112
- .table({
113
- codeHash: mol.Byte32,
114
- hashType: HashTypeCodec,
115
- args: mol.Bytes,
116
- })
117
- .map({ outMap: (decoded) => Script.from(decoded) }),
111
+ mol.table({
112
+ codeHash: mol.Byte32,
113
+ hashType: HashTypeCodec,
114
+ args: mol.Bytes,
115
+ }),
118
116
  )
119
117
  export class Script extends mol.Entity.Base<ScriptLike, Script>() {
120
118
  /**
@@ -1,6 +1,6 @@
1
- import { ClientCollectableSearchKeyFilterLike } from "../advancedBarrel.js";
2
1
  import { Bytes, BytesLike, bytesFrom } from "../bytes/index.js";
3
- import { CellDepInfoLike, Client, KnownScript } from "../client/index.js";
2
+ import type { ClientCollectableSearchKeyFilterLike } from "../client/clientTypes.advanced.js";
3
+ import type { CellDepInfoLike, Client, KnownScript } from "../client/index.js";
4
4
  import {
5
5
  Zero,
6
6
  fixedPointFrom,
@@ -17,11 +17,11 @@ import {
17
17
  numToBytes,
18
18
  numToHex,
19
19
  } from "../num/index.js";
20
- import { Signer } from "../signer/index.js";
20
+ import type { Signer } from "../signer/index.js";
21
21
  import { apply, reduceAsync } from "../utils/index.js";
22
22
  import { Script, ScriptLike, ScriptOpt } from "./script.js";
23
23
  import { DEP_TYPE_TO_NUM, NUM_TO_DEP_TYPE } from "./transaction.advanced.js";
24
- import { LumosTransactionSkeletonType } from "./transactionLumos.js";
24
+ import type { LumosTransactionSkeletonType } from "./transactionLumos.js";
25
25
 
26
26
  export const DepTypeCodec: mol.Codec<DepTypeLike, DepType> = mol.Codec.from({
27
27
  byteLength: 1,
@@ -119,12 +119,10 @@ export type OutPointLike = {
119
119
  * @public
120
120
  */
121
121
  @mol.codec(
122
- mol
123
- .struct({
124
- txHash: mol.Byte32,
125
- index: mol.Uint32,
126
- })
127
- .map({ outMap: (decoded) => OutPoint.from(decoded) }),
122
+ mol.struct({
123
+ txHash: mol.Byte32,
124
+ index: mol.Uint32,
125
+ }),
128
126
  )
129
127
  export class OutPoint extends mol.Entity.Base<OutPointLike, OutPoint>() {
130
128
  /**
@@ -172,13 +170,11 @@ export type CellOutputLike = {
172
170
  * @public
173
171
  */
174
172
  @mol.codec(
175
- mol
176
- .table({
177
- capacity: mol.Uint64,
178
- lock: Script,
179
- type: ScriptOpt,
180
- })
181
- .map({ outMap: (decoded) => CellOutput.from(decoded) }),
173
+ mol.table({
174
+ capacity: mol.Uint64,
175
+ lock: Script,
176
+ type: ScriptOpt,
177
+ }),
182
178
  )
183
179
  export class CellOutput extends mol.Entity.Base<CellOutputLike, CellOutput>() {
184
180
  /**
@@ -347,10 +343,7 @@ export type SinceLike =
347
343
  * @public
348
344
  */
349
345
  @mol.codec(
350
- mol.Uint64.map<SinceLike, Since>({
351
- inMap: (encodable) => Since.from(encodable).toNum(),
352
- outMap: (decoded) => Since.from(decoded),
353
- }),
346
+ mol.Uint64.mapIn((encodable: SinceLike) => Since.from(encodable).toNum()),
354
347
  )
355
348
  export class Since extends mol.Entity.Base<SinceLike, Since>() {
356
349
  /**
@@ -472,10 +465,10 @@ export type CellInputLike = {
472
465
  since: Since,
473
466
  previousOutput: OutPoint,
474
467
  })
475
- .map<CellInputLike, CellInput>({
476
- inMap: (encodable) => ({ ...encodable, since: encodable.since ?? 0 }),
477
- outMap: (decoded) => CellInput.from(decoded),
478
- }),
468
+ .mapIn((encodable: CellInputLike) => ({
469
+ ...encodable,
470
+ since: encodable.since ?? 0,
471
+ })),
479
472
  )
480
473
  export class CellInput extends mol.Entity.Base<CellInputLike, CellInput>() {
481
474
  /**
@@ -560,12 +553,10 @@ export type CellDepLike = {
560
553
  * @public
561
554
  */
562
555
  @mol.codec(
563
- mol
564
- .struct({
565
- outPoint: OutPoint,
566
- depType: DepTypeCodec,
567
- })
568
- .map({ outMap: (decoded) => CellDep.from(decoded) }),
556
+ mol.struct({
557
+ outPoint: OutPoint,
558
+ depType: DepTypeCodec,
559
+ }),
569
560
  )
570
561
  export class CellDep extends mol.Entity.Base<CellDepLike, CellDep>() {
571
562
  /**
@@ -637,13 +628,11 @@ export type WitnessArgsLike = {
637
628
  * @public
638
629
  */
639
630
  @mol.codec(
640
- mol
641
- .table({
642
- lock: mol.BytesOpt,
643
- inputType: mol.BytesOpt,
644
- outputType: mol.BytesOpt,
645
- })
646
- .map({ outMap: (decoded) => WitnessArgs.from(decoded) }),
631
+ mol.table({
632
+ lock: mol.BytesOpt,
633
+ inputType: mol.BytesOpt,
634
+ outputType: mol.BytesOpt,
635
+ }),
647
636
  )
648
637
  export class WitnessArgs extends mol.Entity.Base<
649
638
  WitnessArgsLike,
@@ -706,16 +695,14 @@ export function udtBalanceFrom(dataLike: BytesLike): Num {
706
695
  return numFromBytes(data);
707
696
  }
708
697
 
709
- export const RawTransaction = mol
710
- .table({
711
- version: mol.Uint32,
712
- cellDeps: CellDepVec,
713
- headerDeps: mol.Byte32Vec,
714
- inputs: CellInputVec,
715
- outputs: CellOutputVec,
716
- outputsData: mol.BytesVec,
717
- })
718
- .map({ outMap: (decoded) => Transaction.from(decoded) });
698
+ export const RawTransaction = mol.table({
699
+ version: mol.Uint32,
700
+ cellDeps: CellDepVec,
701
+ headerDeps: mol.Byte32Vec,
702
+ inputs: CellInputVec,
703
+ outputs: CellOutputVec,
704
+ outputsData: mol.BytesVec,
705
+ });
719
706
 
720
707
  /**
721
708
  * @public
@@ -741,16 +728,14 @@ export type TransactionLike = {
741
728
  raw: RawTransaction,
742
729
  witnesses: mol.BytesVec,
743
730
  })
744
- .map<TransactionLike, Transaction>({
745
- inMap: (txLike) => {
746
- const tx = Transaction.from(txLike);
747
- return {
748
- raw: tx,
749
- witnesses: tx.witnesses,
750
- };
751
- },
752
- outMap: (tx) => Transaction.from({ ...tx.raw, witnesses: tx.witnesses }),
753
- }),
731
+ .mapIn((txLike: TransactionLike) => {
732
+ const tx = Transaction.from(txLike);
733
+ return {
734
+ raw: tx,
735
+ witnesses: tx.witnesses,
736
+ };
737
+ })
738
+ .mapOut((tx) => Transaction.from({ ...tx.raw, witnesses: tx.witnesses })),
754
739
  )
755
740
  export class Transaction extends mol.Entity.Base<
756
741
  TransactionLike,
@@ -1,7 +1,7 @@
1
1
  import WebSocket from "isomorphic-ws";
2
2
  import { ClientCache } from "./cache/index.js";
3
3
  import { MAINNET_SCRIPTS } from "./clientPublicMainnet.advanced.js";
4
- import { KnownScript, ScriptInfo } from "./clientTypes.js";
4
+ import { KnownScript, ScriptInfo, ScriptInfoLike } from "./clientTypes.js";
5
5
  import { ClientJsonRpc } from "./jsonRpc/index.js";
6
6
 
7
7
  /**
@@ -12,7 +12,8 @@ export class ClientPublicMainnet extends ClientJsonRpc {
12
12
  private readonly config?: {
13
13
  url?: string;
14
14
  timeout?: number;
15
- scripts?: typeof MAINNET_SCRIPTS;
15
+ maxConcurrent?: number;
16
+ scripts?: Record<KnownScript, ScriptInfoLike | undefined>;
16
17
  cache?: ClientCache;
17
18
  },
18
19
  ) {
@@ -25,7 +26,7 @@ export class ClientPublicMainnet extends ClientJsonRpc {
25
26
  );
26
27
  }
27
28
 
28
- get scripts(): typeof MAINNET_SCRIPTS {
29
+ get scripts(): Record<KnownScript, ScriptInfoLike | undefined> {
29
30
  return this.config?.scripts ?? MAINNET_SCRIPTS;
30
31
  }
31
32
 
@@ -1,7 +1,7 @@
1
1
  import WebSocket from "isomorphic-ws";
2
2
  import { ClientCache } from "./cache/index.js";
3
3
  import { TESTNET_SCRIPTS } from "./clientPublicTestnet.advanced.js";
4
- import { KnownScript, ScriptInfo } from "./clientTypes.js";
4
+ import { KnownScript, ScriptInfo, ScriptInfoLike } from "./clientTypes.js";
5
5
  import { ClientJsonRpc } from "./jsonRpc/index.js";
6
6
 
7
7
  /**
@@ -12,7 +12,8 @@ export class ClientPublicTestnet extends ClientJsonRpc {
12
12
  private readonly config?: {
13
13
  url?: string;
14
14
  timeout?: number;
15
- scripts?: typeof TESTNET_SCRIPTS;
15
+ maxConcurrent?: number;
16
+ scripts?: Record<KnownScript, ScriptInfoLike | undefined>,
16
17
  cache?: ClientCache;
17
18
  },
18
19
  ) {
@@ -25,7 +26,7 @@ export class ClientPublicTestnet extends ClientJsonRpc {
25
26
  );
26
27
  }
27
28
 
28
- get scripts(): typeof TESTNET_SCRIPTS {
29
+ get scripts(): Record<KnownScript, ScriptInfoLike | undefined> {
29
30
  return this.config?.scripts ?? TESTNET_SCRIPTS;
30
31
  }
31
32
 
@@ -35,6 +36,11 @@ export class ClientPublicTestnet extends ClientJsonRpc {
35
36
 
36
37
  async getKnownScript(script: KnownScript): Promise<ScriptInfo> {
37
38
  const found = this.scripts[script];
39
+ if (!found) {
40
+ throw new Error(
41
+ `No script information was found for ${script} on ${this.addressPrefix}`,
42
+ );
43
+ }
38
44
  return ScriptInfo.from(found);
39
45
  }
40
46
  }
@@ -95,6 +95,10 @@ const ERROR_PARSERS: [
95
95
  */
96
96
 
97
97
  export abstract class ClientJsonRpc extends Client {
98
+ private readonly maxConcurrent?: number;
99
+ private concurrent = 0;
100
+ private readonly pending: (() => void)[] = [];
101
+
98
102
  private id = 0;
99
103
  private readonly transport: Transport;
100
104
 
@@ -107,10 +111,15 @@ export abstract class ClientJsonRpc extends Client {
107
111
 
108
112
  constructor(
109
113
  private readonly url_: string,
110
- config?: { timeout?: number; cache?: ClientCache },
114
+ config?: {
115
+ timeout?: number;
116
+ cache?: ClientCache;
117
+ maxConcurrent?: number;
118
+ },
111
119
  ) {
112
120
  super(config);
113
121
 
122
+ this.maxConcurrent = config?.maxConcurrent;
114
123
  this.transport = transportFromUri(url_, config);
115
124
  }
116
125
 
@@ -433,11 +442,25 @@ export abstract class ClientJsonRpc extends Client {
433
442
  * @throws Will throw an error if the response ID does not match the request ID, or if the response contains an error.
434
443
  */
435
444
  async send(payload: JsonRpcPayload): Promise<unknown> {
445
+ if (
446
+ this.maxConcurrent !== undefined &&
447
+ this.concurrent >= this.maxConcurrent
448
+ ) {
449
+ const pending = new Promise<void>((resolve) =>
450
+ this.pending.push(resolve),
451
+ );
452
+ await pending;
453
+ }
454
+
455
+ this.concurrent += 1;
436
456
  const res = (await this.transport.request(payload)) as {
437
457
  id: number;
438
458
  error: unknown;
439
459
  result: unknown;
440
460
  };
461
+ this.concurrent -= 1;
462
+ this.pending.pop()?.();
463
+
441
464
  if (res.id !== payload.id) {
442
465
  throw new Error(`Id mismatched, got ${res.id}, expected ${payload.id}`);
443
466
  }
@@ -1,6 +1,12 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
- import { Bytes, bytesConcat, bytesFrom, BytesLike } from "../bytes/index.js";
3
+ import {
4
+ Bytes,
5
+ bytesConcat,
6
+ bytesConcatTo,
7
+ bytesFrom,
8
+ BytesLike,
9
+ } from "../bytes/index.js";
4
10
  import {
5
11
  Num,
6
12
  numBeFromBytes,
@@ -47,6 +53,18 @@ export class Codec<Encodable, Decoded = Encodable> {
47
53
  : this.decode(buffer)) as NewDecoded,
48
54
  });
49
55
  }
56
+
57
+ mapIn<NewEncodable>(
58
+ map: (encodable: NewEncodable) => Encodable,
59
+ ): Codec<NewEncodable, Decoded> {
60
+ return this.map({ inMap: map });
61
+ }
62
+
63
+ mapOut<NewDecoded>(
64
+ map: (decoded: Decoded) => NewDecoded,
65
+ ): Codec<Encodable, NewDecoded> {
66
+ return this.map({ outMap: map });
67
+ }
50
68
  }
51
69
 
52
70
  export type EncodableType<T extends CodecLike<any, any>> =
@@ -77,10 +95,12 @@ export function fixedItemVec<Encodable, Decoded>(
77
95
  return Codec.from({
78
96
  encode(userDefinedItems) {
79
97
  try {
80
- return userDefinedItems.reduce(
81
- (concatted, item) => bytesConcat(concatted, itemCodec.encode(item)),
82
- uint32To(userDefinedItems.length),
83
- );
98
+ const concatted: number[] = [];
99
+ bytesConcatTo(concatted, uint32To(userDefinedItems.length));
100
+ for (const item of userDefinedItems) {
101
+ bytesConcatTo(concatted, itemCodec.encode(item));
102
+ }
103
+ return bytesFrom(concatted);
84
104
  } catch (e: unknown) {
85
105
  throw new Error(`fixedItemVec(${e?.toString()})`);
86
106
  }
@@ -125,26 +145,19 @@ export function dynItemVec<Encodable, Decoded>(
125
145
  return Codec.from({
126
146
  encode(userDefinedItems) {
127
147
  try {
128
- const encoded = userDefinedItems.reduce(
129
- ({ offset, header, body }, item) => {
130
- const encodedItem = itemCodec.encode(item);
131
- const packedHeader = uint32To(offset);
132
- return {
133
- header: bytesConcat(header, packedHeader),
134
- body: bytesConcat(body, encodedItem),
135
- offset: offset + bytesFrom(encodedItem).byteLength,
136
- };
137
- },
138
- {
139
- header: bytesFrom([]),
140
- body: bytesFrom([]),
141
- offset: 4 + userDefinedItems.length * 4,
142
- },
143
- );
144
- const packedTotalSize = uint32To(
145
- encoded.header.byteLength + encoded.body.byteLength + 4,
146
- );
147
- return bytesConcat(packedTotalSize, encoded.header, encoded.body);
148
+ let offset = 4 + userDefinedItems.length * 4;
149
+ const header: number[] = [];
150
+ const body: number[] = [];
151
+
152
+ for (const item of userDefinedItems) {
153
+ const encoded = itemCodec.encode(item);
154
+ bytesConcatTo(header, uint32To(offset));
155
+ bytesConcatTo(body, encoded);
156
+ offset += encoded.byteLength;
157
+ }
158
+
159
+ const packedTotalSize = uint32To(header.length + body.length + 4);
160
+ return bytesConcat(packedTotalSize, header, body);
148
161
  } catch (e) {
149
162
  throw new Error(`dynItemVec(${e?.toString()})`);
150
163
  }
@@ -317,29 +330,22 @@ export function table<
317
330
 
318
331
  return Codec.from({
319
332
  encode(object) {
320
- const headerLength = 4 + keys.length * 4;
321
-
322
- const { header, body } = keys.reduce(
323
- (result, key) => {
324
- try {
325
- const encodedItem = codecLayout[key].encode((object as any)[key]);
326
- const packedOffset = uint32To(result.offset);
327
- return {
328
- header: bytesConcat(result.header, packedOffset),
329
- body: bytesConcat(result.body, encodedItem),
330
- offset: result.offset + bytesFrom(encodedItem).byteLength,
331
- };
332
- } catch (e: unknown) {
333
- throw new Error(`table.${key}(${e?.toString()})`);
334
- }
335
- },
336
- {
337
- header: bytesFrom([]),
338
- body: bytesFrom([]),
339
- offset: headerLength,
340
- },
341
- );
342
- const packedTotalSize = uint32To(header.byteLength + body.byteLength + 4);
333
+ let offset = 4 + keys.length * 4;
334
+ const header: number[] = [];
335
+ const body: number[] = [];
336
+
337
+ for (const key of keys) {
338
+ try {
339
+ const encoded = codecLayout[key].encode((object as any)[key]);
340
+ bytesConcatTo(header, uint32To(offset));
341
+ bytesConcatTo(body, encoded);
342
+ offset += encoded.byteLength;
343
+ } catch (e: unknown) {
344
+ throw new Error(`table.${key}(${e?.toString()})`);
345
+ }
346
+ }
347
+
348
+ const packedTotalSize = uint32To(header.length + body.length + 4);
343
349
  return bytesConcat(packedTotalSize, header, body);
344
350
  },
345
351
  decode(buffer) {
@@ -482,23 +488,27 @@ export function struct<
482
488
  Decoded extends DecodedRecord<T>,
483
489
  >(codecLayout: T): Codec<Encodable, Decoded> {
484
490
  const codecArray = Object.values(codecLayout);
485
- if (codecArray.some((codec) => codec.byteLength === undefined)) {
486
- throw new Error("struct: all fields must be fixed-size");
487
- }
488
-
489
491
  const keys = Object.keys(codecLayout);
490
492
 
491
493
  return Codec.from({
492
- byteLength: codecArray.reduce((sum, codec) => sum + codec.byteLength!, 0),
494
+ byteLength: codecArray.reduce((acc, codec) => {
495
+ if (codec.byteLength === undefined) {
496
+ throw new Error("struct: all fields must be fixed-size");
497
+ }
498
+ return acc + codec.byteLength;
499
+ }, 0),
493
500
  encode(object) {
494
- return keys.reduce((result, key) => {
501
+ const bytes: number[] = [];
502
+ for (const key of keys) {
495
503
  try {
496
- const encodedItem = codecLayout[key].encode((object as any)[key]);
497
- return bytesConcat(result, encodedItem);
504
+ const encoded = codecLayout[key].encode((object as any)[key]);
505
+ bytesConcatTo(bytes, encoded);
498
506
  } catch (e: unknown) {
499
507
  throw new Error(`struct.${key}(${e?.toString()})`);
500
508
  }
501
- }, bytesFrom([]));
509
+ }
510
+
511
+ return bytesFrom(bytes);
502
512
  },
503
513
  decode(buffer) {
504
514
  const value = bytesFrom(buffer);
@@ -538,10 +548,12 @@ export function array<Encodable, Decoded>(
538
548
  byteLength,
539
549
  encode(items) {
540
550
  try {
541
- return items.reduce(
542
- (concatted, item) => bytesConcat(concatted, itemCodec.encode(item)),
543
- bytesFrom([]),
544
- );
551
+ const bytes: number[] = [];
552
+ for (const item of items) {
553
+ bytesConcatTo(bytes, itemCodec.encode(item));
554
+ }
555
+
556
+ return bytesFrom(bytes);
545
557
  } catch (e: unknown) {
546
558
  throw new Error(`array(${e?.toString()})`);
547
559
  }
@@ -1,13 +1,19 @@
1
1
  import { Bytes, bytesEq, BytesLike } from "../bytes/index.js";
2
2
  import { hashCkb } from "../hasher/index.js";
3
3
  import { Hex } from "../hex/index.js";
4
+ import { Constructor } from "../utils/index.js";
4
5
  import { Codec } from "./codec.js";
5
6
 
6
7
  /**
7
- * The base class of CCC to create a serializable instance
8
+ * The base class of CCC to create a serializable instance. This should be used with the {@link codec} decorator.
8
9
  * @public
9
10
  */
10
11
  export abstract class Entity {
12
+ /**
13
+ * Generate a base class of CCC to create a serializable instance.
14
+ * This should be used with the {@link codec} decorator.
15
+ * @public
16
+ */
11
17
  static Base<SubTypeLike, SubType = SubTypeLike>() {
12
18
  abstract class Impl {
13
19
  /**
@@ -97,7 +103,7 @@ export abstract class Entity {
97
103
  * @param other - The other entity to compare with
98
104
  * @returns True if the entities are equal, false otherwise
99
105
  */
100
- eq(other: SubTypeLike | SubType): boolean {
106
+ eq(other: SubTypeLike): boolean {
101
107
  if (this === (other as unknown as this)) {
102
108
  return true;
103
109
  }
@@ -130,14 +136,28 @@ export abstract class Entity {
130
136
  abstract clone(): Entity;
131
137
  }
132
138
 
139
+ /**
140
+ * A class decorator to add methods implementation on the {@link Entity.Base} class
141
+ * @example
142
+ * ```typescript
143
+ * @mol.codec(
144
+ * mol.table({
145
+ * codeHash: mol.Byte32,
146
+ * hashType: HashTypeCodec,
147
+ * args: mol.Bytes,
148
+ * }),
149
+ * )
150
+ * export class Script extends mol.Entity.Base<ScriptLike, Script>() {
151
+ * from(scriptLike: ScriptLike): Script {}
152
+ * }
153
+ * ```
154
+ */
133
155
  export function codec<
134
156
  Encodable,
135
157
  TypeLike extends Encodable,
136
158
  Decoded extends TypeLike,
137
159
  Type extends object & TypeLike,
138
- ConstructorType extends {
139
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
- new (...args: any[]): Type;
160
+ ConstructorType extends Constructor<Type> & {
141
161
  from(decoded: TypeLike): Type;
142
162
  byteLength?: number;
143
163
  encode(encodable: TypeLike): Bytes;
package/src/num/index.ts CHANGED
@@ -155,10 +155,7 @@ export function numBeToBytes(val: NumLike, bytes?: number): Bytes {
155
155
  return rawBytes;
156
156
  }
157
157
 
158
- return bytesConcat(
159
- Array.from(Array(bytes - rawBytes.length), () => 0),
160
- rawBytes,
161
- );
158
+ return bytesConcat("00".repeat(bytes - rawBytes.length), rawBytes);
162
159
  }
163
160
 
164
161
  /**
@@ -190,7 +187,7 @@ export function numFromBytes(val: BytesLike): Num {
190
187
  * ```
191
188
  */
192
189
  export function numLeFromBytes(val: BytesLike): Num {
193
- return numBeFromBytes([...bytesFrom(val)].reverse());
190
+ return numBeFromBytes(bytesFrom(val).reverse());
194
191
  }
195
192
 
196
193
  /**
@@ -43,7 +43,7 @@ export function verifyMessageBtcEcdsa(
43
43
  const challenge =
44
44
  typeof message === "string" ? message : hexFrom(message).slice(2);
45
45
 
46
- const [_, ...rawSign] = bytesFrom(signature, "base64");
46
+ const rawSign = bytesFrom(signature, "base64").slice(1);
47
47
 
48
48
  return secp256k1.verify(bytesFrom(rawSign), magicHash(challenge), publicKey);
49
49
  }
@@ -17,7 +17,9 @@ export function verifyMessageDogeEcdsa(
17
17
  ): boolean {
18
18
  const challenge =
19
19
  typeof message === "string" ? message : hexFrom(message).slice(2);
20
- const [recoveryBit, ...rawSign] = bytesFrom(signature, "base64");
20
+ const signatureBytes = bytesFrom(signature, "base64");
21
+ const recoveryBit = signatureBytes[0];
22
+ const rawSign = signatureBytes.slice(1);
21
23
 
22
24
  const sig = secp256k1.Signature.fromCompact(
23
25
  hexFrom(rawSign).slice(2),