@aztec/foundation 0.65.0 → 0.65.1

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 (78) hide show
  1. package/dest/abi/abi.d.ts +9 -9
  2. package/dest/abi/abi.js +2 -2
  3. package/dest/abi/event_selector.d.ts +2 -4
  4. package/dest/abi/event_selector.d.ts.map +1 -1
  5. package/dest/abi/event_selector.js +6 -5
  6. package/dest/abi/function_selector.d.ts +2 -4
  7. package/dest/abi/function_selector.d.ts.map +1 -1
  8. package/dest/abi/function_selector.js +6 -5
  9. package/dest/abi/note_selector.d.ts +2 -5
  10. package/dest/abi/note_selector.d.ts.map +1 -1
  11. package/dest/abi/note_selector.js +5 -7
  12. package/dest/abi/selector.d.ts.map +1 -1
  13. package/dest/abi/selector.js +3 -2
  14. package/dest/aztec-address/index.d.ts +2 -4
  15. package/dest/aztec-address/index.d.ts.map +1 -1
  16. package/dest/aztec-address/index.js +6 -5
  17. package/dest/buffer/buffer32.d.ts +2 -12
  18. package/dest/buffer/buffer32.d.ts.map +1 -1
  19. package/dest/buffer/buffer32.js +3 -16
  20. package/dest/config/env_var.d.ts +1 -1
  21. package/dest/config/env_var.d.ts.map +1 -1
  22. package/dest/crypto/poseidon/index.d.ts +1 -0
  23. package/dest/crypto/poseidon/index.d.ts.map +1 -1
  24. package/dest/crypto/poseidon/index.js +7 -1
  25. package/dest/eth-address/index.d.ts +2 -9
  26. package/dest/eth-address/index.d.ts.map +1 -1
  27. package/dest/eth-address/index.js +7 -12
  28. package/dest/eth-signature/eth_signature.d.ts +5 -3
  29. package/dest/eth-signature/eth_signature.d.ts.map +1 -1
  30. package/dest/eth-signature/eth_signature.js +18 -9
  31. package/dest/fields/fields.d.ts +4 -8
  32. package/dest/fields/fields.d.ts.map +1 -1
  33. package/dest/fields/fields.js +10 -10
  34. package/dest/fields/point.d.ts +6 -4
  35. package/dest/fields/point.d.ts.map +1 -1
  36. package/dest/fields/point.js +14 -6
  37. package/dest/json-rpc/client/fetch.d.ts.map +1 -1
  38. package/dest/json-rpc/client/fetch.js +20 -14
  39. package/dest/json-rpc/convert.d.ts.map +1 -1
  40. package/dest/json-rpc/convert.js +5 -2
  41. package/dest/json-rpc/index.d.ts +1 -1
  42. package/dest/json-rpc/index.d.ts.map +1 -1
  43. package/dest/json-rpc/index.js +2 -2
  44. package/dest/noir/noir_package_config.d.ts +2 -2
  45. package/dest/schemas/schemas.d.ts +24 -43
  46. package/dest/schemas/schemas.d.ts.map +1 -1
  47. package/dest/schemas/schemas.js +29 -48
  48. package/dest/schemas/utils.d.ts +18 -7
  49. package/dest/schemas/utils.d.ts.map +1 -1
  50. package/dest/schemas/utils.js +20 -8
  51. package/dest/serialize/type_registry.d.ts.map +1 -1
  52. package/dest/serialize/type_registry.js +29 -2
  53. package/dest/string/index.d.ts +1 -0
  54. package/dest/string/index.d.ts.map +1 -1
  55. package/dest/string/index.js +4 -1
  56. package/dest/testing/test_data.js +2 -2
  57. package/package.json +2 -2
  58. package/src/abi/abi.ts +1 -1
  59. package/src/abi/event_selector.ts +6 -4
  60. package/src/abi/function_selector.ts +6 -4
  61. package/src/abi/note_selector.ts +4 -6
  62. package/src/abi/selector.ts +2 -1
  63. package/src/aztec-address/index.ts +6 -4
  64. package/src/buffer/buffer32.ts +3 -17
  65. package/src/config/env_var.ts +1 -0
  66. package/src/crypto/poseidon/index.ts +11 -0
  67. package/src/eth-address/index.ts +6 -11
  68. package/src/eth-signature/eth_signature.ts +20 -9
  69. package/src/fields/fields.ts +11 -9
  70. package/src/fields/point.ts +15 -5
  71. package/src/json-rpc/client/fetch.ts +18 -13
  72. package/src/json-rpc/convert.ts +3 -1
  73. package/src/json-rpc/index.ts +1 -1
  74. package/src/schemas/schemas.ts +28 -51
  75. package/src/schemas/utils.ts +29 -10
  76. package/src/serialize/type_registry.ts +33 -1
  77. package/src/string/index.ts +4 -0
  78. package/src/testing/test_data.ts +1 -1
@@ -2,6 +2,7 @@ import { inspect } from 'util';
2
2
 
3
3
  import { toBufferBE } from '../bigint-buffer/index.js';
4
4
  import { Fr } from '../fields/index.js';
5
+ import { bufferToHex } from '../string/index.js';
5
6
 
6
7
  /** A selector is the first 4 bytes of the hash of a signature. */
7
8
  export abstract class Selector {
@@ -36,7 +37,7 @@ export abstract class Selector {
36
37
  * @returns The string.
37
38
  */
38
39
  toString(): string {
39
- return '0x' + this.toBuffer().toString('hex');
40
+ return bufferToHex(this.toBuffer());
40
41
  }
41
42
 
42
43
  [inspect.custom]() {
@@ -2,6 +2,7 @@
2
2
  import { inspect } from 'util';
3
3
 
4
4
  import { Fr, Point, fromBuffer } from '../fields/index.js';
5
+ import { hexSchemaFor } from '../schemas/utils.js';
5
6
  import { type BufferReader, FieldReader } from '../serialize/index.js';
6
7
  import { TypeRegistry } from '../serialize/type_registry.js';
7
8
  import { hexToBuffer } from '../string/index.js';
@@ -133,10 +134,11 @@ export class AztecAddress {
133
134
  }
134
135
 
135
136
  toJSON() {
136
- return {
137
- type: 'AztecAddress',
138
- value: this.toString(),
139
- };
137
+ return this.toString();
138
+ }
139
+
140
+ static get schema() {
141
+ return hexSchemaFor(AztecAddress, AztecAddress.isAddress);
140
142
  }
141
143
  }
142
144
 
@@ -2,6 +2,8 @@ import { randomBytes } from '@aztec/foundation/crypto';
2
2
  import { type Fr } from '@aztec/foundation/fields';
3
3
  import { BufferReader, deserializeBigInt, serializeBigInt } from '@aztec/foundation/serialize';
4
4
 
5
+ import { bufferToHex } from '../string/index.js';
6
+
5
7
  /**
6
8
  * A class representing a 32 byte Buffer.
7
9
  */
@@ -67,17 +69,13 @@ export class Buffer32 {
67
69
  * @returns The hex string.
68
70
  */
69
71
  public toString() {
70
- return this.buffer.toString('hex');
72
+ return bufferToHex(this.buffer);
71
73
  }
72
74
 
73
75
  toJSON() {
74
76
  return this.toString();
75
77
  }
76
78
 
77
- public to0xString(): `0x${string}` {
78
- return `0x${this.buffer.toString('hex')}`;
79
- }
80
-
81
79
  /**
82
80
  * Convert this hash to a big int.
83
81
  * @returns The big int.
@@ -117,18 +115,6 @@ export class Buffer32 {
117
115
  * @param str - The TX hash in string format.
118
116
  * @returns A new Buffer32 object.
119
117
  */
120
- public static fromStringUnchecked(str: string): Buffer32 {
121
- return new Buffer32(Buffer.from(str, 'hex'));
122
- }
123
-
124
- /**
125
- * Converts a string into a Buffer32 object.
126
- * NOTE: this method includes checks for the 0x prefix and the length of the string.
127
- * if you dont need this checks, use fromStringUnchecked instead.
128
- *
129
- * @param str - The TX hash in string format.
130
- * @returns A new Buffer32 object.
131
- */
132
118
  public static fromString(str: string): Buffer32 {
133
119
  if (str.startsWith('0x')) {
134
120
  str = str.slice(2);
@@ -155,6 +155,7 @@ export type EnvVar =
155
155
  | 'SEQ_VIEM_POLLING_INTERVAL_MS'
156
156
  | 'WS_DB_MAP_SIZE_KB'
157
157
  | 'WS_DATA_DIRECTORY'
158
+ | 'WS_NUM_HISTORIC_BLOCKS'
158
159
  | 'ETHEREUM_SLOT_DURATION'
159
160
  | 'AZTEC_SLOT_DURATION'
160
161
  | 'AZTEC_EPOCH_DURATION'
@@ -41,6 +41,17 @@ export function poseidon2HashWithSeparator(input: Fieldable[], separator: number
41
41
  );
42
42
  }
43
43
 
44
+ export function poseidon2HashAccumulate(input: Fieldable[]): Fr {
45
+ const inputFields = serializeToFields(input);
46
+ return Fr.fromBuffer(
47
+ Buffer.from(
48
+ BarretenbergSync.getSingleton()
49
+ .poseidon2HashAccumulate(inputFields.map(i => new FrBarretenberg(i.toBuffer())))
50
+ .toBuffer(),
51
+ ),
52
+ );
53
+ }
54
+
44
55
  /**
45
56
  * Runs a Poseidon2 permutation.
46
57
  * @param input the input state. Expected to be of size 4.
@@ -3,8 +3,10 @@ import { inspect } from 'util';
3
3
  import { keccak256String } from '../crypto/keccak/index.js';
4
4
  import { randomBytes } from '../crypto/random/index.js';
5
5
  import { Fr } from '../fields/index.js';
6
+ import { hexSchemaFor } from '../schemas/utils.js';
6
7
  import { BufferReader, FieldReader } from '../serialize/index.js';
7
8
  import { TypeRegistry } from '../serialize/type_registry.js';
9
+ import { bufferToHex } from '../string/index.js';
8
10
 
9
11
  /**
10
12
  * Represents an Ethereum address as a 20-byte buffer and provides various utility methods
@@ -154,7 +156,7 @@ export class EthAddress {
154
156
  * @returns A hex-encoded string representation of the Ethereum address.
155
157
  */
156
158
  public toString() {
157
- return `0x${this.buffer.toString('hex')}` as `0x${string}`;
159
+ return bufferToHex(this.buffer);
158
160
  }
159
161
 
160
162
  [inspect.custom]() {
@@ -226,19 +228,12 @@ export class EthAddress {
226
228
  return new EthAddress(reader.readBytes(EthAddress.SIZE_IN_BYTES));
227
229
  }
228
230
 
229
- /**
230
- * Friendly representation for debugging purposes.
231
- * @returns A hex string representing the address.
232
- */
233
- toFriendlyJSON() {
231
+ toJSON() {
234
232
  return this.toString();
235
233
  }
236
234
 
237
- toJSON() {
238
- return {
239
- type: 'EthAddress',
240
- value: this.toString(),
241
- };
235
+ static get schema() {
236
+ return hexSchemaFor(EthAddress, EthAddress.isAddress);
242
237
  }
243
238
  }
244
239
 
@@ -1,6 +1,10 @@
1
1
  import { Buffer32 } from '@aztec/foundation/buffer';
2
2
  import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
3
3
 
4
+ import { z } from 'zod';
5
+
6
+ import { hasHexPrefix, hexToBuffer } from '../string/index.js';
7
+
4
8
  /**Viem Signature
5
9
  *
6
10
  * A version of the Signature class that uses `0x${string}` values for r and s rather than
@@ -45,7 +49,7 @@ export class Signature {
45
49
  return new Signature(r, s, v, isEmpty);
46
50
  }
47
51
 
48
- static isValid0xString(sig: `0x${string}`): boolean {
52
+ static isValidString(sig: `0x${string}`): boolean {
49
53
  return /^0x[0-9a-f]{129,}$/i.test(sig);
50
54
  }
51
55
 
@@ -54,10 +58,9 @@ export class Signature {
54
58
  * parsing from viem, we can expect the v value to be a u8, rather than our
55
59
  * default serialization of u32
56
60
  */
57
- static from0xString(sig: `0x${string}`): Signature {
58
- const buf = Buffer.from(sig.slice(2), 'hex');
61
+ static fromString(sig: `0x${string}`): Signature {
62
+ const buf = hexToBuffer(sig);
59
63
  const reader = BufferReader.asReader(buf);
60
-
61
64
  const r = reader.readObject(Buffer32);
62
65
  const s = reader.readObject(Buffer32);
63
66
  const v = parseInt(sig.slice(2 + 64 * 2), 16);
@@ -95,8 +98,8 @@ export class Signature {
95
98
  return this.size;
96
99
  }
97
100
 
98
- to0xString(): `0x${string}` {
99
- return `0x${this.r.toString()}${this.s.toString()}${this.v.toString(16)}`;
101
+ toString(): `0x${string}` {
102
+ return `0x${this.r.buffer.toString('hex')}${this.s.buffer.toString('hex')}${this.v.toString(16)}`;
100
103
  }
101
104
 
102
105
  /**
@@ -104,14 +107,22 @@ export class Signature {
104
107
  */
105
108
  toViemSignature(): ViemSignature {
106
109
  return {
107
- r: this.r.to0xString(),
108
- s: this.s.to0xString(),
110
+ r: this.r.toString(),
111
+ s: this.s.toString(),
109
112
  v: this.v,
110
113
  isEmpty: this.isEmpty,
111
114
  };
112
115
  }
113
116
 
114
117
  toJSON() {
115
- return this.to0xString();
118
+ return this.toString();
119
+ }
120
+
121
+ static get schema() {
122
+ return z
123
+ .string()
124
+ .refine(hasHexPrefix, 'No hex prefix')
125
+ .refine(Signature.isValidString, 'Not a valid Ethereum signature')
126
+ .transform(Signature.fromString);
116
127
  }
117
128
  }
@@ -4,6 +4,7 @@ import { inspect } from 'util';
4
4
 
5
5
  import { toBigIntBE, toBufferBE } from '../bigint-buffer/index.js';
6
6
  import { randomBytes } from '../crypto/random/index.js';
7
+ import { hexSchemaFor } from '../schemas/utils.js';
7
8
  import { BufferReader } from '../serialize/buffer_reader.js';
8
9
  import { TypeRegistry } from '../serialize/type_registry.js';
9
10
 
@@ -300,12 +301,12 @@ export class Fr extends BaseField {
300
301
  return Fr.fromBuffer(rootBuf);
301
302
  }
302
303
 
303
- // TODO(palla/schemas): Use toString instead of structured type
304
304
  toJSON() {
305
- return {
306
- type: 'Fr',
307
- value: this.toString(),
308
- };
305
+ return this.toString();
306
+ }
307
+
308
+ static get schema() {
309
+ return hexSchemaFor(Fr);
309
310
  }
310
311
  }
311
312
 
@@ -385,10 +386,11 @@ export class Fq extends BaseField {
385
386
  }
386
387
 
387
388
  toJSON() {
388
- return {
389
- type: 'Fq',
390
- value: this.toString(),
391
- };
389
+ return this.toString();
390
+ }
391
+
392
+ static get schema() {
393
+ return hexSchemaFor(Fq);
392
394
  }
393
395
  }
394
396
 
@@ -1,7 +1,9 @@
1
1
  import { toBigIntBE } from '../bigint-buffer/index.js';
2
2
  import { poseidon2Hash } from '../crypto/poseidon/index.js';
3
3
  import { randomBoolean } from '../crypto/random/index.js';
4
+ import { hexSchemaFor } from '../schemas/utils.js';
4
5
  import { BufferReader, FieldReader, serializeToBuffer } from '../serialize/index.js';
6
+ import { bufferToHex, hexToBuffer } from '../string/index.js';
5
7
  import { Fr } from './fields.js';
6
8
 
7
9
  /**
@@ -34,6 +36,14 @@ export class Point {
34
36
  // TODO(#7386): check if on curve
35
37
  }
36
38
 
39
+ toJSON() {
40
+ return this.toString();
41
+ }
42
+
43
+ static get schema() {
44
+ return hexSchemaFor(Point);
45
+ }
46
+
37
47
  /**
38
48
  * Generate a random Point instance.
39
49
  *
@@ -84,14 +94,14 @@ export class Point {
84
94
 
85
95
  /**
86
96
  * Create a Point instance from a hex-encoded string.
87
- * The input 'address' should be prefixed with '0x' or not, and have exactly 128 hex characters representing the x and y coordinates.
97
+ * The input should be prefixed with '0x' or not, and have exactly 128 hex characters representing the x and y coordinates.
88
98
  * Throws an error if the input length is invalid or coordinate values are out of range.
89
99
  *
90
- * @param address - The hex-encoded string representing the Point coordinates.
100
+ * @param str - The hex-encoded string representing the Point coordinates.
91
101
  * @returns A Point instance.
92
102
  */
93
- static fromString(address: string) {
94
- return this.fromBuffer(Buffer.from(address.replace(/^0x/i, ''), 'hex'));
103
+ static fromString(str: string) {
104
+ return this.fromBuffer(hexToBuffer(str));
95
105
  }
96
106
 
97
107
  /**
@@ -211,7 +221,7 @@ export class Point {
211
221
  * @returns A hex-encoded string representing the Point instance.
212
222
  */
213
223
  toString() {
214
- return '0x' + this.toBuffer().toString('hex');
224
+ return bufferToHex(this.toBuffer());
215
225
  }
216
226
 
217
227
  /**
@@ -1,4 +1,4 @@
1
- import { format } from 'util';
1
+ import { format, inspect } from 'util';
2
2
 
3
3
  import { type DebugLogger, createDebugLogger } from '../../log/index.js';
4
4
  import { NoRetryError, makeBackoff, retry } from '../../retry/index.js';
@@ -25,18 +25,23 @@ export async function defaultFetch(
25
25
  ) {
26
26
  log.debug(format(`JsonRpcClient.fetch`, host, rpcMethod, '->', body));
27
27
  let resp: Response;
28
- if (useApiEndpoints) {
29
- resp = await fetch(`${host}/${rpcMethod}`, {
30
- method: 'POST',
31
- body: jsonStringify(body),
32
- headers: { 'content-type': 'application/json' },
33
- });
34
- } else {
35
- resp = await fetch(host, {
36
- method: 'POST',
37
- body: jsonStringify({ ...body, method: rpcMethod }),
38
- headers: { 'content-type': 'application/json' },
39
- });
28
+ try {
29
+ if (useApiEndpoints) {
30
+ resp = await fetch(`${host}/${rpcMethod}`, {
31
+ method: 'POST',
32
+ body: jsonStringify(body),
33
+ headers: { 'content-type': 'application/json' },
34
+ });
35
+ } else {
36
+ resp = await fetch(host, {
37
+ method: 'POST',
38
+ body: jsonStringify({ ...body, method: rpcMethod }),
39
+ headers: { 'content-type': 'application/json' },
40
+ });
41
+ }
42
+ } catch (err) {
43
+ const errorMessage = `Error fetching from host ${host} with method ${rpcMethod}: ${inspect(err)}`;
44
+ throw new Error(errorMessage);
40
45
  }
41
46
 
42
47
  let responseJson;
@@ -23,7 +23,9 @@ export function jsonStringify(obj: object, prettify?: boolean): string {
23
23
  (_key, value) => {
24
24
  if (typeof value === 'bigint') {
25
25
  return value.toString();
26
- } else if (typeof value === 'object' && Buffer.isBuffer(value)) {
26
+ } else if (typeof value === 'object' && value && value.type === 'Buffer' && Array.isArray(value.data)) {
27
+ return Buffer.from(value.data).toString('base64');
28
+ } else if (typeof value === 'object' && value && Buffer.isBuffer(value)) {
27
29
  return value.toString('base64');
28
30
  } else if (typeof value === 'object' && value instanceof Map) {
29
31
  return Array.from(value.entries());
@@ -1 +1 @@
1
- export { jsonStringify } from './convert.js';
1
+ export { jsonStringify, jsonParseWithSchema, tryJsonStringify } from './convert.js';
@@ -7,65 +7,45 @@ import { NoteSelector } from '../abi/note_selector.js';
7
7
  import { AztecAddress } from '../aztec-address/index.js';
8
8
  import { Buffer32 } from '../buffer/buffer32.js';
9
9
  import { EthAddress } from '../eth-address/index.js';
10
- import { Signature } from '../eth-signature/eth_signature.js';
11
10
  import { Fq, Fr } from '../fields/fields.js';
12
11
  import { Point } from '../fields/point.js';
13
- import { hasHexPrefix, isHex, withoutHexPrefix } from '../string/index.js';
12
+ import { isHex, withoutHexPrefix } from '../string/index.js';
14
13
  import { type ZodFor } from './types.js';
15
- import { hexSchema, maybeStructuredStringSchemaFor } from './utils.js';
16
-
17
- const FrSchema = maybeStructuredStringSchemaFor('Fr', Fr, isHex);
18
- const FqSchema = maybeStructuredStringSchemaFor('Fq', Fq, isHex);
14
+ import { bufferSchema, hexSchema } from './utils.js';
19
15
 
20
16
  /** Validation schemas for common types. Every schema must match its toJSON. */
21
17
  export const schemas = {
22
- /** Accepts both a 0x string and a structured `{ type: EthAddress, value: '0x...' }` */
23
- EthAddress: maybeStructuredStringSchemaFor('EthAddress', EthAddress, EthAddress.isAddress),
24
-
25
- /** Accepts both a 0x string and a structured `{ type: AztecAddress, value: '0x...' }` */
26
- AztecAddress: maybeStructuredStringSchemaFor('AztecAddress', AztecAddress, AztecAddress.isAddress),
18
+ /** Accepts a hex string. */
19
+ EthAddress: EthAddress.schema,
27
20
 
28
- /** Accepts both a 0x string and a structured type. */
29
- FunctionSelector: maybeStructuredStringSchemaFor('FunctionSelector', FunctionSelector),
21
+ /** Accepts a hex string. */
22
+ AztecAddress: AztecAddress.schema,
30
23
 
31
- /** Accepts both a 0x string and a structured type. */
32
- NoteSelector: maybeStructuredStringSchemaFor('NoteSelector', NoteSelector),
24
+ /** Accepts a hex string. */
25
+ FunctionSelector: FunctionSelector.schema,
33
26
 
34
- /** Accepts both a 0x string and a structured type. */
35
- EventSelector: maybeStructuredStringSchemaFor('EventSelector', EventSelector),
27
+ /** Accepts a hex string. */
28
+ NoteSelector: NoteSelector.schema,
36
29
 
37
- /** Field element. Accepts a 0x prefixed hex string or a structured type. */
38
- Fr: FrSchema,
30
+ /** Accepts a hex string. */
31
+ EventSelector: EventSelector.schema,
39
32
 
40
- /** Field element. Accepts a 0x prefixed hex string or a structured type. */
41
- Fq: FqSchema,
33
+ /** Accepts a hex string. */
34
+ Fr: Fr.schema,
42
35
 
43
- /** Point. Serialized as 0x prefixed string or a type. */
44
- Point: z
45
- .object({
46
- x: FrSchema,
47
- y: FrSchema,
48
- isInfinite: z.boolean().optional(),
49
- })
50
- .or(hexSchema)
51
- .transform(value =>
52
- typeof value === 'string' ? Point.fromString(value) : new Point(value.x, value.y, value.isInfinite ?? false),
53
- ),
36
+ /** Accepts a hex string. */
37
+ Fq: Fq.schema,
54
38
 
55
- /** Accepts a 0x string */
56
- Signature: z
57
- .string()
58
- .refine(hasHexPrefix, 'No hex prefix')
59
- .refine(Signature.isValid0xString, 'Not a valid Ethereum signature')
60
- .transform(Signature.from0xString),
39
+ /** Point. Serialized as a hex string. */
40
+ Point: Point.schema,
61
41
 
62
- /** Coerces any input to bigint */
42
+ /** Coerces any input to bigint. */
63
43
  BigInt: z.union([z.bigint(), z.number(), z.string()]).pipe(z.coerce.bigint()),
64
44
 
65
- /** Coerces any input to integer number */
45
+ /** Coerces any input to integer number. */
66
46
  Integer: z.union([z.bigint(), z.number(), z.string()]).pipe(z.coerce.number().int()),
67
47
 
68
- /** Coerces input to UInt32 */
48
+ /** Coerces input to UInt32. */
69
49
  UInt32: z.union([z.bigint(), z.number(), z.string()]).pipe(
70
50
  z.coerce
71
51
  .number()
@@ -74,31 +54,28 @@ export const schemas = {
74
54
  .max(2 ** 32 - 1),
75
55
  ),
76
56
 
77
- /** Accepts a hex string as a Buffer32 type */
57
+ /** Accepts a hex string as a Buffer32 type. */
78
58
  Buffer32: z.string().refine(isHex, 'Not a valid hex string').transform(Buffer32.fromString),
79
59
 
80
- /** Accepts a base64 string or a structured `{ type: 'Buffer', data: [byte, byte...] }` as a buffer */
81
- BufferB64: z.union([
82
- z
83
- .string()
84
- .base64()
85
- .transform(data => Buffer.from(data, 'base64')),
60
+ /** Accepts a base64 string or an object `{ type: 'Buffer', data: [byte, byte...] }` as a buffer. */
61
+ Buffer: z.union([
62
+ bufferSchema,
86
63
  z
87
64
  .object({
88
65
  type: z.literal('Buffer'),
89
- data: z.array(z.number().int().max(255)),
66
+ data: z.array(z.number().int().min(0).max(255)),
90
67
  })
91
68
  .transform(({ data }) => Buffer.from(data)),
92
69
  ]),
93
70
 
94
- /** Accepts a hex string with optional 0x prefix as a buffer */
71
+ /** Accepts a hex string as a buffer. */
95
72
  BufferHex: z
96
73
  .string()
97
74
  .refine(isHex, 'Not a valid hex string')
98
75
  .transform(withoutHexPrefix)
99
76
  .transform(data => Buffer.from(data, 'hex')),
100
77
 
101
- /** Hex string with an optional 0x prefix, which gets removed as part of the parsing */
78
+ /** Hex string with an optional 0x prefix which gets removed as part of the parsing. */
102
79
  HexString: hexSchema,
103
80
  };
104
81
 
@@ -15,6 +15,17 @@ import { type ZodFor } from './types.js';
15
15
 
16
16
  export const hexSchema = z.string().refine(isHex, 'Not a valid hex string').transform(withoutHexPrefix);
17
17
 
18
+ // Copied from zod internals, which was copied from https://stackoverflow.com/questions/7860392/determine-if-string-is-in-base64-using-javascript
19
+ const base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
20
+
21
+ /** Schema for a buffer represented as a base64 string. */
22
+ export const bufferSchema = z
23
+ .string()
24
+ // We only test the str for base64 if it's shorter than 1024 bytes, otherwise we've run into maximum
25
+ // stack size exceeded errors when trying to validate excessively long strings (such as contract bytecode).
26
+ .refine(str => str.length > 1024 || base64Regex.test(str), 'Not a valid base64 string')
27
+ .transform(data => Buffer.from(data, 'base64'));
28
+
18
29
  export class ZodNullableOptional<T extends ZodTypeAny> extends ZodOptional<T> {
19
30
  _isNullableOptional = true;
20
31
 
@@ -43,6 +54,8 @@ export function optional<T extends ZodTypeAny>(schema: T) {
43
54
  return ZodNullableOptional.create(schema);
44
55
  }
45
56
 
57
+ type ToJsonIs<T, TRet> = T extends { toJSON(): TRet } ? T : never;
58
+
46
59
  /**
47
60
  * Creates a schema that accepts a hex string and uses it to hydrate an instance.
48
61
  * @param klazz - Class that implements either fromString or fromBuffer.
@@ -50,28 +63,34 @@ export function optional<T extends ZodTypeAny>(schema: T) {
50
63
  */
51
64
  export function hexSchemaFor<TClass extends { fromString(str: string): any } | { fromBuffer(buf: Buffer): any }>(
52
65
  klazz: TClass,
66
+ refinement?: (input: string) => boolean,
53
67
  ): ZodType<
54
68
  TClass extends { fromString(str: string): infer TInstance } | { fromBuffer(buf: Buffer): infer TInstance }
55
- ? TInstance
69
+ ? ToJsonIs<TInstance, string>
56
70
  : never,
57
71
  any,
58
72
  string
59
73
  > {
74
+ const stringSchema = refinement ? z.string().refine(refinement, `Not a valid instance`) : z.string();
75
+ const hexSchema = stringSchema.refine(isHex, 'Not a valid hex string').transform(withoutHexPrefix);
60
76
  return 'fromString' in klazz
61
77
  ? hexSchema.transform(klazz.fromString.bind(klazz))
62
78
  : hexSchema.transform(str => Buffer.from(str, 'hex')).transform(klazz.fromBuffer.bind(klazz));
63
79
  }
64
80
 
65
- // TODO(palla/schemas): Delete this class once all serialization of the type { type: string, value: string } are removed.
66
- export function maybeStructuredStringSchemaFor<TClass extends { fromString(str: string): any }>(
67
- name: string,
81
+ /**
82
+ * Creates a schema that accepts a base64 string and uses it to hydrate an instance.
83
+ * @param klazz - Class that implements fromBuffer.
84
+ * @returns A schema for the class.
85
+ */
86
+ export function bufferSchemaFor<TClass extends { fromBuffer(buf: Buffer): any }>(
68
87
  klazz: TClass,
69
- refinement?: (input: string) => boolean,
70
- ): ZodFor<TClass extends { fromString(str: string): infer TInstance } ? TInstance : never> {
71
- const stringSchema = refinement ? z.string().refine(refinement, `Not a valid ${name}`) : z.string();
72
- return z
73
- .union([stringSchema, z.object({ type: z.literal(name), value: stringSchema })])
74
- .transform(input => klazz.fromString(typeof input === 'string' ? input : input.value));
88
+ ): ZodType<
89
+ TClass extends { fromBuffer(buf: Buffer): infer TInstance } ? ToJsonIs<TInstance, Buffer> : never,
90
+ any,
91
+ string
92
+ > {
93
+ return bufferSchema.transform(klazz.fromBuffer.bind(klazz));
75
94
  }
76
95
 
77
96
  /** Creates a schema for a js Map type that matches the serialization used in jsonStringify. */
@@ -1,3 +1,5 @@
1
+ import { mapValues } from '../collection/object.js';
2
+
1
3
  type Deserializable = { fromString(str: string): object };
2
4
 
3
5
  /**
@@ -23,9 +25,39 @@ export class TypeRegistry {
23
25
  }
24
26
  }
25
27
 
28
+ function replace<T>(value: T) {
29
+ if (
30
+ value &&
31
+ typeof value === 'object' &&
32
+ 'toString' in value &&
33
+ TypeRegistry.getConstructor(value.constructor.name)
34
+ ) {
35
+ return {
36
+ type: value.constructor.name,
37
+ value: value.toString(),
38
+ };
39
+ }
40
+
41
+ return value;
42
+ }
43
+
26
44
  // Resolver function that enables JSON serialization of BigInts.
27
45
  export function resolver(_: any, value: any) {
28
- return typeof value === 'bigint' ? value.toString() + 'n' : value;
46
+ if (typeof value === 'bigint') {
47
+ return value.toString() + 'n';
48
+ }
49
+
50
+ if (typeof value === 'object' && value) {
51
+ if (Array.isArray(value)) {
52
+ return value.map(replace);
53
+ } else if (Buffer.isBuffer(value)) {
54
+ return { type: 'buffer', value: value.toString('hex') };
55
+ } else {
56
+ return mapValues(value, replace);
57
+ }
58
+ }
59
+
60
+ return value;
29
61
  }
30
62
 
31
63
  // Reviver function that uses TypeRegistry to instantiate objects.
@@ -14,6 +14,10 @@ export function hexToBuffer(str: string): Buffer {
14
14
  return Buffer.from(withoutHexPrefix(str), 'hex');
15
15
  }
16
16
 
17
+ export function bufferToHex(buffer: Buffer): `0x${string}` {
18
+ return `0x${buffer.toString('hex')}`;
19
+ }
20
+
17
21
  export function pluralize(str: string, count: number | bigint, plural?: string): string {
18
22
  return count === 1 || count === 1n ? str : plural ?? `${str}s`;
19
23
  }
@@ -66,7 +66,7 @@ export function updateInlineTestData(targetFileFromRepoRoot: string, itemName: s
66
66
  const logger = createConsoleLogger('aztec:testing:test_data');
67
67
  const targetFile = getPathToFile(targetFileFromRepoRoot);
68
68
  const contents = readFileSync(targetFile, 'utf8').toString();
69
- const regex = new RegExp(`let ${itemName} = [\\s\\S]*?;`, 'g');
69
+ const regex = new RegExp(`let ${itemName} =[\\s\\S]*?;`, 'g');
70
70
  if (!regex.exec(contents)) {
71
71
  throw new Error(`Test data marker for ${itemName} not found in ${targetFile}`);
72
72
  }