@ckb-ccc/core 1.18.1 → 2.0.0-next.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 +42 -0
  2. package/dist/address.advanced-LxntWbev.mjs +2 -0
  3. package/dist/address.advanced-LxntWbev.mjs.map +1 -0
  4. package/dist/advanced.d.mts +1 -1
  5. package/dist/advanced.mjs +1 -1
  6. package/{dist.commonjs/advancedBarrel-CJAh6sm8.d.ts → dist/advancedBarrel-BHwnMJqR.d.mts} +749 -382
  7. package/dist/advancedBarrel-BHwnMJqR.d.mts.map +1 -0
  8. package/dist/advancedBarrel.d.mts +1 -1
  9. package/dist/advancedBarrel.mjs +1 -1
  10. package/dist/barrel-C-BaV9Pe.mjs +7 -0
  11. package/dist/barrel-C-BaV9Pe.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-BqcYOpZn.js +2 -0
  17. package/dist.commonjs/address.advanced-BqcYOpZn.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-BbbHx4z4.d.mts → dist.commonjs/advancedBarrel-DNdhwiU0.d.ts} +750 -381
  21. package/dist.commonjs/advancedBarrel-DNdhwiU0.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-C_zSgXKn.js → barrel-BBOmj4RJ.js} +10 -10
  25. package/dist.commonjs/barrel-BBOmj4RJ.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/package.json +1 -1
  31. package/src/ckb/epoch.ts +44 -26
  32. package/src/ckb/script.ts +23 -46
  33. package/src/ckb/transaction.ts +200 -196
  34. package/src/client/client.ts +2 -2
  35. package/src/client/jsonRpc/client.ts +3 -7
  36. package/src/codec/codec.ts +65 -40
  37. package/src/codec/entity.ts +95 -67
  38. package/src/codec/predefined.ts +11 -0
  39. package/src/molecule/codec.ts +116 -8
  40. package/src/molecule/predefined.ts +1 -0
  41. package/src/signer/ckb/signerMultisigCkbReadonly.ts +36 -24
  42. package/src/signer/ckb/verifyJoyId.ts +39 -11
  43. package/src/signer/index.ts +17 -0
  44. package/src/signer/signer/index.ts +11 -0
  45. package/src/signer/signerFromSignature.ts +65 -0
  46. package/src/utils/index.ts +108 -26
  47. package/dist/address.advanced-Dw4qPDGb.mjs +0 -2
  48. package/dist/address.advanced-Dw4qPDGb.mjs.map +0 -1
  49. package/dist/advancedBarrel-BbbHx4z4.d.mts.map +0 -1
  50. package/dist/barrel-BsC1djHG.mjs +0 -7
  51. package/dist/barrel-BsC1djHG.mjs.map +0 -1
  52. package/dist.commonjs/address.advanced-DG1gpW7t.js +0 -2
  53. package/dist.commonjs/address.advanced-DG1gpW7t.js.map +0 -1
  54. package/dist.commonjs/advancedBarrel-CJAh6sm8.d.ts.map +0 -1
  55. package/dist.commonjs/barrel-C_zSgXKn.js.map +0 -1
@@ -3,12 +3,13 @@
3
3
  import { Bytes, bytesFrom, BytesLike } from "../bytes/index.js";
4
4
 
5
5
  export type CodecLike<Encodable, Decoded = Encodable> = {
6
- readonly encode: (encodable: Encodable) => Bytes;
7
- readonly decode: (
8
- decodable: BytesLike,
6
+ encode: (encodable: Encodable) => BytesLike;
7
+ decode: (
8
+ decodable: Bytes,
9
9
  config?: { isExtraFieldIgnored?: boolean },
10
10
  ) => Decoded;
11
- readonly byteLength?: number;
11
+ from?: ((encoded: Encodable) => Decoded) | null;
12
+ byteLength?: number;
12
13
  };
13
14
  export class Codec<Encodable, Decoded = Encodable> {
14
15
  constructor(
@@ -17,6 +18,7 @@ export class Codec<Encodable, Decoded = Encodable> {
17
18
  decodable: BytesLike,
18
19
  config?: { isExtraFieldIgnored?: boolean }, // This is equivalent to "compatible" in the Rust implementation of Molecule.
19
20
  ) => Decoded,
21
+ public readonly from: (encodable: Encodable) => Decoded,
20
22
  public readonly byteLength?: number, // if provided, treat codec as fixed length
21
23
  ) {}
22
24
 
@@ -40,53 +42,76 @@ export class Codec<Encodable, Decoded = Encodable> {
40
42
  }
41
43
  }
42
44
 
43
- static from<Encodable, Decoded = Encodable>({
44
- encode,
45
- decode,
46
- byteLength,
47
- }: CodecLike<Encodable, Decoded>): Codec<Encodable, Decoded> {
45
+ static from<Encodable, Decoded = Encodable>(
46
+ codecLike: CodecLike<Encodable, Decoded>,
47
+ ): Codec<Encodable, Decoded> {
48
+ const newEncode = function (encodable: Encodable) {
49
+ const encoded = bytesFrom(codecLike.encode(encodable));
50
+ if (
51
+ codecLike.byteLength !== undefined &&
52
+ encoded.byteLength !== codecLike.byteLength
53
+ ) {
54
+ throw new Error(
55
+ `Codec.encode: expected byte length ${codecLike.byteLength}, got ${encoded.byteLength}`,
56
+ );
57
+ }
58
+ return encoded;
59
+ };
60
+ const newDecode = function (
61
+ decodable: BytesLike,
62
+ config?: { isExtraFieldIgnored?: boolean },
63
+ ) {
64
+ const decodableBytes = bytesFrom(decodable);
65
+ if (
66
+ codecLike.byteLength !== undefined &&
67
+ decodableBytes.byteLength !== codecLike.byteLength
68
+ ) {
69
+ throw new Error(
70
+ `Codec.decode: expected byte length ${codecLike.byteLength}, got ${decodableBytes.byteLength}`,
71
+ );
72
+ }
73
+ return codecLike.decode(decodableBytes, config);
74
+ };
48
75
  return new Codec(
49
- (encodable: Encodable) => {
50
- const encoded = encode(encodable);
51
- if (byteLength !== undefined && encoded.byteLength !== byteLength) {
52
- throw new Error(
53
- `Codec.encode: expected byte length ${byteLength}, got ${encoded.byteLength}`,
54
- );
55
- }
56
- return encoded;
57
- },
58
- (decodable, config) => {
59
- const decodableBytes = bytesFrom(decodable);
60
- if (
61
- byteLength !== undefined &&
62
- decodableBytes.byteLength !== byteLength
63
- ) {
64
- throw new Error(
65
- `Codec.decode: expected byte length ${byteLength}, got ${decodableBytes.byteLength}`,
66
- );
67
- }
68
- return decode(decodable, config);
69
- },
70
- byteLength,
76
+ newEncode,
77
+ newDecode,
78
+ codecLike.from?.bind(codecLike) ??
79
+ function (encodable: Encodable) {
80
+ return newDecode(newEncode(encodable));
81
+ },
82
+ codecLike.byteLength,
71
83
  );
72
84
  }
73
85
 
74
86
  map<NewEncodable = Encodable, NewDecoded = Decoded>({
75
87
  inMap,
76
88
  outMap,
89
+ from,
77
90
  }: {
78
91
  inMap?: (encodable: NewEncodable) => Encodable;
79
92
  outMap?: (decoded: Decoded) => NewDecoded;
93
+ from?: (encodable: NewEncodable) => NewDecoded;
80
94
  }): Codec<NewEncodable, NewDecoded> {
81
- return new Codec(
82
- (encodable) =>
83
- this.encode((inMap ? inMap(encodable) : encodable) as Encodable),
84
- (buffer, config) =>
85
- (outMap
86
- ? outMap(this.decode(buffer, config))
87
- : this.decode(buffer, config)) as NewDecoded,
88
- this.byteLength,
89
- );
95
+ const encode = inMap
96
+ ? (encodable: NewEncodable) => this.encode(inMap(encodable))
97
+ : (this.encode as unknown as (encodable: NewEncodable) => Bytes);
98
+ const decode = outMap
99
+ ? (buffer: BytesLike, config?: { isExtraFieldIgnored?: boolean }) =>
100
+ outMap(this.decode(buffer, config))
101
+ : (this.decode as unknown as (
102
+ buffer: BytesLike,
103
+ config?: { isExtraFieldIgnored?: boolean },
104
+ ) => NewDecoded);
105
+ const newFrom =
106
+ from ??
107
+ ((encodable: NewEncodable): NewDecoded => {
108
+ const toEncode = (inMap ? inMap(encodable) : encodable) as Encodable;
109
+ return (
110
+ outMap ? outMap(this.from(toEncode)) : this.from(toEncode)
111
+ ) as NewDecoded;
112
+ });
113
+
114
+ return new Codec(encode, decode, newFrom, this.byteLength);
90
115
  }
91
116
 
92
117
  mapIn<NewEncodable>(
@@ -3,7 +3,7 @@ import { hashCkb } from "../hasher/index.js";
3
3
  import { Hex, hexFrom } from "../hex/index.js";
4
4
  import type { UnionDecoded, UnionMatchHandlers } from "../molecule/codec.js";
5
5
  import { Constructor } from "../utils/index.js";
6
- import type { CodecLike, DecodedType } from "./codec.js";
6
+ import { Codec, CodecLike, DecodedType } from "./codec.js";
7
7
 
8
8
  /**
9
9
  * The base class of CCC to create a serializable instance. This should be used with the {@link codec} decorator.
@@ -16,6 +16,13 @@ export abstract class Entity {
16
16
  * @public
17
17
  */
18
18
  static Base<SubTypeLike, SubType = SubTypeLike>() {
19
+ // Static methods in this class are written in function syntax not method syntax
20
+ // to enable strictFunctionTypes to catch more errors.
21
+ // This protects when the class is extended and the static methods are overridden with incompatible types.
22
+ // See https://www.typescriptlang.org/tsconfig/#strictFunctionTypes
23
+ // >> During development of this feature, we discovered a large number of inherently unsafe class hierarchies,
24
+ // >> including some in the DOM. Because of this, the setting only applies to functions written in function syntax,
25
+ // >> not to those in method syntax
19
26
  abstract class Impl extends Entity {
20
27
  /**
21
28
  * The bytes length of the entity, if it is fixed, otherwise undefined
@@ -31,38 +38,34 @@ export abstract class Entity {
31
38
  * @returns The encoded bytes
32
39
  * @throws Will throw an error if the entity is not serializable
33
40
  */
34
- static encode(_: SubTypeLike): Bytes {
35
- throw new Error(
36
- "encode not implemented, use @ccc.codec to decorate your type",
37
- );
38
- }
41
+ static encode: (_: SubTypeLike) => Bytes;
39
42
  /**
40
43
  * Decode the entity from bytes
41
44
  * @public
42
45
  * @static
43
46
  * @param _ - The bytes to decode
47
+ * @param _config - The configuration for decoding, including whether to ignore extra fields
44
48
  * @returns The decoded entity
45
49
  * @throws Will throw an error if the entity is not serializable
46
50
  */
47
- static decode(_: BytesLike): SubType {
48
- throw new Error(
49
- "decode not implemented, use @ccc.codec to decorate your type",
50
- );
51
- }
51
+ static decode: (
52
+ _: BytesLike,
53
+ _config?: { isExtraFieldIgnored?: boolean },
54
+ ) => SubType;
52
55
 
53
56
  /**
54
57
  * Create an entity from bytes
55
58
  * @public
56
59
  * @static
57
60
  * @param _ - The bytes to create the entity from
61
+ * @param _config - The configuration for decoding, including whether to ignore extra fields
58
62
  * @returns The created entity
59
63
  * @throws Will throw an error if the entity is not serializable
60
64
  */
61
- static fromBytes(_bytes: BytesLike): SubType {
62
- throw new Error(
63
- "fromBytes not implemented, use @ccc.codec to decorate your type",
64
- );
65
- }
65
+ static fromBytes: (
66
+ _bytes: BytesLike,
67
+ _config?: { isExtraFieldIgnored?: boolean },
68
+ ) => SubType;
66
69
 
67
70
  /**
68
71
  * Create an entity from a serializable object
@@ -72,9 +75,7 @@ export abstract class Entity {
72
75
  * @returns The created entity
73
76
  * @throws Will throw an error if the entity is not serializable
74
77
  */
75
- static from(_: SubTypeLike): SubType {
76
- throw new Error("from not implemented");
77
- }
78
+ static from: (_: SubTypeLike) => SubType;
78
79
 
79
80
  /**
80
81
  * Convert the entity to bytes
@@ -110,11 +111,9 @@ export abstract class Entity {
110
111
 
111
112
  return bytesEq(
112
113
  this.toBytes(),
113
- /* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any */
114
114
  (
115
- ((this.constructor as any)?.from(other) ?? other) as unknown as Impl
115
+ (this.constructor as typeof Impl).from(other) as unknown as Impl
116
116
  ).toBytes(),
117
- /* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any */
118
117
  );
119
118
  }
120
119
 
@@ -137,12 +136,6 @@ export abstract class Entity {
137
136
  }
138
137
  }
139
138
 
140
- /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment */
141
- Impl.encode = undefined as any;
142
- Impl.decode = undefined as any;
143
- Impl.fromBytes = undefined as any;
144
- /* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment */
145
-
146
139
  return Impl;
147
140
  }
148
141
 
@@ -247,57 +240,92 @@ export abstract class Entity {
247
240
  * A class decorator to add methods implementation on the {@link Entity.Base} class
248
241
  * @example
249
242
  * ```typescript
250
- * @codec(
251
- * mol.table({
252
- * codeHash: mol.Byte32,
253
- * hashType: HashTypeCodec,
254
- * args: mol.Bytes,
255
- * }),
256
- * )
243
+ * const ScriptCodec = mol.table({
244
+ * codeHash: mol.Byte32,
245
+ * hashType: HashTypeCodec,
246
+ * args: mol.Bytes,
247
+ * });
248
+ * export type ScriptLike = EncodableType<typeof ScriptCodec>;
249
+ * @codec(ScriptCodec)
257
250
  * export class Script extends Entity.Base<ScriptLike, Script>() {
258
- * from(scriptLike: ScriptLike): Script {}
251
+ * public codeHash: Hex;
252
+ * public hashType: HashType;
253
+ * public args: Hex;
254
+ *
255
+ * constructor({ codeHash, hashType, args }: DecodedType<typeof ScriptCodec>) {
256
+ * super();
257
+ *
258
+ * this.codeHash = codeHash;
259
+ * this.hashType = hashType;
260
+ * this.args = args;
261
+ * }
259
262
  * }
260
263
  * ```
261
264
  */
262
- export function codec<
263
- Encodable,
264
- TypeLike extends Encodable,
265
- Decoded extends TypeLike,
266
- >(codec: {
267
- encode: (encodable: Encodable) => Bytes;
268
- decode: (
269
- decodable: Bytes,
270
- config?: { isExtraFieldIgnored?: boolean },
271
- ) => Decoded;
272
- byteLength?: number;
273
- }) {
265
+ export function codec<Encodable, Decoded>(
266
+ codecLike: CodecLike<Encodable, Decoded>,
267
+ ) {
268
+ const codec = Codec.from(codecLike);
269
+
274
270
  return function <
271
+ TypeLike extends Encodable,
275
272
  Type extends TypeLike,
276
- ConstructorType extends Constructor<Type> & {
277
- from(decoded: TypeLike): Type;
278
- byteLength?: number;
279
- encode(encodable: TypeLike): Bytes;
280
- decode(bytesLike: BytesLike): Type;
281
- fromBytes(bytes: BytesLike): Type;
282
- },
283
- >(Constructor: ConstructorType, ..._: unknown[]) {
284
- Constructor.byteLength = codec.byteLength;
285
- if (Constructor.encode === undefined) {
286
- Constructor.encode = function (encodable: TypeLike) {
287
- return codec.encode(Constructor.from(encodable));
273
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
274
+ ConstructorType extends Constructor<Type, [Decoded, ...any[]]>,
275
+ >(
276
+ Constructor: ConstructorType &
277
+ Omit<CodecLike<TypeLike, Type>, "from"> & {
278
+ // Since Base.from only accepts TypeLike,
279
+ // passing this check actually tells us that Decoded extends TypeLike
280
+ from: (encodable: TypeLike | Decoded) => Type;
281
+ fromBytes: (bytes: BytesLike) => Type;
282
+ },
283
+ ..._: unknown[]
284
+ ): void {
285
+ const Base = Object.getPrototypeOf(Constructor) as Omit<
286
+ CodecLike<TypeLike, Type>,
287
+ "from"
288
+ > & {
289
+ // See above, we already know that Decoded extends TypeLike, so we can safely cast the type here.
290
+ from: (encodable: TypeLike) => Type;
291
+ fromBytes: (bytes: BytesLike) => Type;
292
+ };
293
+
294
+ Base.byteLength = codec.byteLength;
295
+ if (Base.encode === undefined) {
296
+ Base.encode = function (this: typeof Constructor, encodable: TypeLike) {
297
+ // Theoretically, the type allows us to codec.encode(encodable) directly.
298
+ // However, Constructor.from usually does more than just casting the type,
299
+ // so we need to call it to ensure the correct data is passed to codec.encode.
300
+ return codec.encode(this.from(encodable));
288
301
  };
289
302
  }
290
- if (Constructor.decode === undefined) {
291
- Constructor.decode = function (bytesLike: BytesLike) {
292
- return Constructor.from(codec.decode(bytesFrom(bytesLike)));
303
+ if (Base.decode === undefined) {
304
+ Base.decode = function (
305
+ this: typeof Constructor,
306
+ bytesLike: BytesLike,
307
+ config?: { isExtraFieldIgnored?: boolean },
308
+ ) {
309
+ return this.from(codec.decode(bytesFrom(bytesLike), config));
293
310
  };
294
311
  }
295
- if (Constructor.fromBytes === undefined) {
296
- Constructor.fromBytes = function (bytes: BytesLike) {
297
- return Constructor.from(codec.decode(bytesFrom(bytes)));
312
+ if (Base.fromBytes === undefined) {
313
+ Base.fromBytes = function (
314
+ this: typeof Constructor,
315
+ bytes: BytesLike,
316
+ config?: { isExtraFieldIgnored?: boolean },
317
+ ) {
318
+ return this.from(codec.decode(bytesFrom(bytes), config));
298
319
  };
299
320
  }
321
+ if (Base.from === undefined) {
322
+ Base.from = function (encodable: TypeLike) {
323
+ if (encodable instanceof Constructor) {
324
+ return encodable;
325
+ }
300
326
 
301
- return Constructor;
327
+ return new Constructor(codec.from(encodable));
328
+ };
329
+ }
302
330
  };
303
331
  }
@@ -4,6 +4,7 @@ import {
4
4
  Num,
5
5
  numBeFromBytes,
6
6
  numBeToBytes,
7
+ numFrom,
7
8
  numFromBytes,
8
9
  NumLike,
9
10
  numToBytes,
@@ -35,6 +36,7 @@ export function codecUint(
35
36
  return numBeFromBytes(buffer);
36
37
  }
37
38
  },
39
+ from: numFrom,
38
40
  });
39
41
  }
40
42
 
@@ -69,17 +71,20 @@ export function codecPadding(
69
71
  return new Uint8Array(byteLength);
70
72
  },
71
73
  decode: () => {},
74
+ from: () => {},
72
75
  });
73
76
  }
74
77
 
75
78
  export const CodecRaw: Codec<BytesLike, Bytes> = Codec.from({
76
79
  encode: (value) => bytesFrom(value),
77
80
  decode: (buffer) => bytesFrom(buffer),
81
+ from: bytesFrom,
78
82
  });
79
83
 
80
84
  export const CodecBytes: Codec<HexLike, Hex> = Codec.from({
81
85
  encode: (value) => bytesFrom(value),
82
86
  decode: (buffer) => hexFrom(buffer),
87
+ from: hexFrom,
83
88
  });
84
89
 
85
90
  export const CodecUint8 = codecUintNumber(1, true);
@@ -112,34 +117,40 @@ export const CodecBool: Codec<boolean> = Codec.from({
112
117
  byteLength: 1,
113
118
  encode: (value) => bytesFrom(value ? [1] : [0]),
114
119
  decode: (buffer) => bytesFrom(buffer)[0] !== 0,
120
+ from: Boolean,
115
121
  });
116
122
 
117
123
  export const CodecByte: Codec<HexLike, Hex> = Codec.from({
118
124
  byteLength: 1,
119
125
  encode: (value) => bytesFrom(value),
120
126
  decode: (buffer) => hexFrom(buffer),
127
+ from: hexFrom,
121
128
  });
122
129
 
123
130
  export const CodecByte4: Codec<HexLike, Hex> = Codec.from({
124
131
  byteLength: 4,
125
132
  encode: (value) => bytesFrom(value),
126
133
  decode: (buffer) => hexFrom(buffer),
134
+ from: hexFrom,
127
135
  });
128
136
 
129
137
  export const CodecByte8: Codec<HexLike, Hex> = Codec.from({
130
138
  byteLength: 8,
131
139
  encode: (value) => bytesFrom(value),
132
140
  decode: (buffer) => hexFrom(buffer),
141
+ from: hexFrom,
133
142
  });
134
143
 
135
144
  export const CodecByte16: Codec<HexLike, Hex> = Codec.from({
136
145
  byteLength: 16,
137
146
  encode: (value) => bytesFrom(value),
138
147
  decode: (buffer) => hexFrom(buffer),
148
+ from: hexFrom,
139
149
  });
140
150
 
141
151
  export const CodecByte32: Codec<HexLike, Hex> = Codec.from({
142
152
  byteLength: 32,
143
153
  encode: (value) => bytesFrom(value),
144
154
  decode: (buffer) => hexFrom(buffer),
155
+ from: hexFrom,
145
156
  });
@@ -58,8 +58,9 @@ function getMessage(e: unknown): string {
58
58
  * @param itemCodec fixed-size vector item codec
59
59
  */
60
60
  export function fixedItemVec<Encodable, Decoded>(
61
- itemCodec: CodecLike<Encodable, Decoded>,
61
+ itemCodecLike: CodecLike<Encodable, Decoded>,
62
62
  ): Codec<Array<Encodable>, Array<Decoded>> {
63
+ const itemCodec = Codec.from(itemCodecLike);
63
64
  const itemByteLength = itemCodec.byteLength;
64
65
  if (itemByteLength === undefined) {
65
66
  throw new Error("fixedItemVec: itemCodec requires a byte length");
@@ -108,6 +109,13 @@ export function fixedItemVec<Encodable, Decoded>(
108
109
  throw new Error(`fixedItemVec - ${getMessage(e)}`, { cause: e });
109
110
  }
110
111
  },
112
+ from(userDefinedItems) {
113
+ try {
114
+ return userDefinedItems.map((item) => itemCodec.from(item));
115
+ } catch (e: unknown) {
116
+ throw new Error(`fixedItemVec - ${getMessage(e)}`, { cause: e });
117
+ }
118
+ },
111
119
  });
112
120
  }
113
121
 
@@ -116,8 +124,10 @@ export function fixedItemVec<Encodable, Decoded>(
116
124
  * @param itemCodec the vector item codec. It can be fixed-size or dynamic-size.
117
125
  */
118
126
  export function dynItemVec<Encodable, Decoded>(
119
- itemCodec: CodecLike<Encodable, Decoded>,
127
+ itemCodecLike: CodecLike<Encodable, Decoded>,
120
128
  ): Codec<Array<Encodable>, Array<Decoded>> {
129
+ const itemCodec = Codec.from(itemCodecLike);
130
+
121
131
  return Codec.from({
122
132
  encode(userDefinedItems) {
123
133
  try {
@@ -175,6 +185,13 @@ export function dynItemVec<Encodable, Decoded>(
175
185
  throw new Error(`dynItemVec - ${getMessage(e)}`, { cause: e });
176
186
  }
177
187
  },
188
+ from(userDefinedItems) {
189
+ try {
190
+ return userDefinedItems.map((item) => itemCodec.from(item));
191
+ } catch (e: unknown) {
192
+ throw new Error(`dynItemVec - ${getMessage(e)}`, { cause: e });
193
+ }
194
+ },
178
195
  });
179
196
  }
180
197
 
@@ -199,8 +216,10 @@ export function vector<Encodable, Decoded>(
199
216
  * @param innerCodec
200
217
  */
201
218
  export function option<Encodable, Decoded>(
202
- innerCodec: CodecLike<Encodable, Decoded>,
219
+ innerCodecLike: CodecLike<Encodable, Decoded>,
203
220
  ): Codec<Encodable | undefined | null, Decoded | undefined> {
221
+ const innerCodec = Codec.from(innerCodecLike);
222
+
204
223
  return Codec.from({
205
224
  encode(userDefinedOrNull) {
206
225
  if (userDefinedOrNull == null) {
@@ -223,6 +242,16 @@ export function option<Encodable, Decoded>(
223
242
  throw new Error(`option - ${getMessage(e)}`, { cause: e });
224
243
  }
225
244
  },
245
+ from(userDefinedOrNull) {
246
+ try {
247
+ if (userDefinedOrNull == null) {
248
+ return undefined;
249
+ }
250
+ return innerCodec.from(userDefinedOrNull);
251
+ } catch (e: unknown) {
252
+ throw new Error(`option - ${getMessage(e)}`, { cause: e });
253
+ }
254
+ },
226
255
  });
227
256
  }
228
257
 
@@ -231,8 +260,10 @@ export function option<Encodable, Decoded>(
231
260
  * @param codec
232
261
  */
233
262
  export function byteVec<Encodable, Decoded>(
234
- codec: CodecLike<Encodable, Decoded>,
263
+ codecLike: CodecLike<Encodable, Decoded>,
235
264
  ): Codec<Encodable, Decoded> {
265
+ const codec = Codec.from(codecLike);
266
+
236
267
  return Codec.from({
237
268
  encode(userDefined) {
238
269
  try {
@@ -262,6 +293,13 @@ export function byteVec<Encodable, Decoded>(
262
293
  throw new Error(`byteVec - ${getMessage(e)}`, { cause: e });
263
294
  }
264
295
  },
296
+ from(userDefined) {
297
+ try {
298
+ return codec.from(userDefined);
299
+ } catch (e: unknown) {
300
+ throw new Error(`byteVec - ${getMessage(e)}`, { cause: e });
301
+ }
302
+ },
265
303
  });
266
304
  }
267
305
 
@@ -301,7 +339,13 @@ export function table<
301
339
  T extends Record<string, CodecLike<any, any>>,
302
340
  Encodable extends EncodableRecord<T>,
303
341
  Decoded extends DecodedRecord<T>,
304
- >(codecLayout: T): Codec<Encodable, Decoded> {
342
+ >(codecLayoutLike: T): Codec<Encodable, Decoded> {
343
+ const codecLayout = Object.fromEntries(
344
+ Object.entries(codecLayoutLike).map(([key, codec]) => [
345
+ key,
346
+ Codec.from(codec),
347
+ ]),
348
+ );
305
349
  const keys = Object.keys(codecLayout);
306
350
 
307
351
  return Codec.from({
@@ -380,6 +424,18 @@ export function table<
380
424
  }
381
425
  return object as Decoded;
382
426
  },
427
+ from(object) {
428
+ const result: any = {};
429
+ for (const key of keys) {
430
+ try {
431
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
432
+ result[key] = codecLayout[key].from((object as any)[key]);
433
+ } catch (e: unknown) {
434
+ throw new Error(`table.${key} - ${getMessage(e)}`, { cause: e });
435
+ }
436
+ }
437
+ return result as Decoded;
438
+ },
383
439
  });
384
440
  }
385
441
 
@@ -446,9 +502,15 @@ export type UnionMatchHandlers<
446
502
  */
447
503
 
448
504
  export function union<T extends Record<string, CodecLike<any, any>>>(
449
- codecLayout: T,
505
+ codecLayoutLike: T,
450
506
  fields?: Record<keyof T, number | undefined | null>,
451
507
  ): Codec<UnionEncodable<T> | { inner: UnionEncodable<T> }, UnionDecoded<T>> {
508
+ const codecLayout = Object.fromEntries(
509
+ Object.entries(codecLayoutLike).map(([key, codec]) => [
510
+ key,
511
+ Codec.from(codec),
512
+ ]),
513
+ );
452
514
  const entries = Object.entries(codecLayout);
453
515
 
454
516
  // Determine if all variants have a fixed and equal byteLength.
@@ -532,6 +594,25 @@ export function union<T extends Record<string, CodecLike<any, any>>>(
532
594
  value: codecLayout[field].decode(value.slice(4), config),
533
595
  } as UnionDecoded<T>;
534
596
  },
597
+ from(encodable) {
598
+ const { type, value } = extract(encodable);
599
+ const typeStr = type.toString();
600
+ const codec = codecLayout[typeStr];
601
+ if (!codec) {
602
+ throw new Error(
603
+ `union: invalid type, expected ${entries.map((e) => e[0]).toString()}, but got ${typeStr}`,
604
+ );
605
+ }
606
+ try {
607
+ return {
608
+ type,
609
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
610
+ value: codec.from(value),
611
+ } as UnionDecoded<T>;
612
+ } catch (e: unknown) {
613
+ throw new Error(`union.(${typeStr}) - ${getMessage(e)}`, { cause: e });
614
+ }
615
+ },
535
616
  });
536
617
  }
537
618
 
@@ -544,7 +625,13 @@ export function struct<
544
625
  T extends Record<string, CodecLike<any, any>>,
545
626
  Encodable extends EncodableRecord<T>,
546
627
  Decoded extends DecodedRecord<T>,
547
- >(codecLayout: T): Codec<Encodable, Decoded> {
628
+ >(codecLayoutLike: T): Codec<Encodable, Decoded> {
629
+ const codecLayout = Object.fromEntries(
630
+ Object.entries(codecLayoutLike).map(([key, codec]) => [
631
+ key,
632
+ Codec.from(codec),
633
+ ]),
634
+ );
548
635
  const codecArray = Object.values(codecLayout);
549
636
  const keys = Object.keys(codecLayout);
550
637
 
@@ -584,6 +671,18 @@ export function struct<
584
671
  });
585
672
  return object as Decoded;
586
673
  },
674
+ from(object) {
675
+ const result: any = {};
676
+ for (const key of keys) {
677
+ try {
678
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
679
+ result[key] = codecLayout[key].from((object as any)[key]);
680
+ } catch (e: unknown) {
681
+ throw new Error(`struct.${key} - ${getMessage(e)}`, { cause: e });
682
+ }
683
+ }
684
+ return result as Decoded;
685
+ },
587
686
  });
588
687
  }
589
688
 
@@ -594,9 +693,11 @@ export function struct<
594
693
  * @param itemCount
595
694
  */
596
695
  export function array<Encodable, Decoded>(
597
- itemCodec: CodecLike<Encodable, Decoded>,
696
+ itemCodecLike: CodecLike<Encodable, Decoded>,
598
697
  itemCount: number,
599
698
  ): Codec<Array<Encodable>, Array<Decoded>> {
699
+ const itemCodec = Codec.from(itemCodecLike);
700
+
600
701
  if (itemCodec.byteLength === undefined) {
601
702
  throw new Error("array: itemCodec requires a byte length");
602
703
  }
@@ -635,5 +736,12 @@ export function array<Encodable, Decoded>(
635
736
  throw new Error(`array - ${getMessage(e)}`, { cause: e });
636
737
  }
637
738
  },
739
+ from(userDefinedItems) {
740
+ try {
741
+ return userDefinedItems.map((item) => itemCodec.from(item));
742
+ } catch (e: unknown) {
743
+ throw new Error(`array - ${getMessage(e)}`, { cause: e });
744
+ }
745
+ },
638
746
  });
639
747
  }