ox 1.2.0 → 1.3.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 (187) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/core/AbiFunction.d.ts +1 -1
  3. package/dist/core/AbiFunction.d.ts.map +1 -1
  4. package/dist/core/AbiFunction.js +6 -2
  5. package/dist/core/AbiFunction.js.map +1 -1
  6. package/dist/core/AbiParameters.d.ts +1 -1
  7. package/dist/core/AbiParameters.d.ts.map +1 -1
  8. package/dist/core/AbiParameters.js +1 -13
  9. package/dist/core/AbiParameters.js.map +1 -1
  10. package/dist/core/AesGcm.d.ts +36 -1
  11. package/dist/core/AesGcm.d.ts.map +1 -1
  12. package/dist/core/AesGcm.js +44 -0
  13. package/dist/core/AesGcm.js.map +1 -1
  14. package/dist/core/Ed25519.d.ts +46 -1
  15. package/dist/core/Ed25519.d.ts.map +1 -1
  16. package/dist/core/Ed25519.js +43 -0
  17. package/dist/core/Ed25519.js.map +1 -1
  18. package/dist/core/Engine.d.ts +1 -1
  19. package/dist/core/Engine.d.ts.map +1 -1
  20. package/dist/core/Engine.js.map +1 -1
  21. package/dist/core/Hash.d.ts +164 -1
  22. package/dist/core/Hash.d.ts.map +1 -1
  23. package/dist/core/Hash.js +172 -0
  24. package/dist/core/Hash.js.map +1 -1
  25. package/dist/core/Hex.d.ts.map +1 -1
  26. package/dist/core/Hex.js +1 -4
  27. package/dist/core/Hex.js.map +1 -1
  28. package/dist/core/Rlp.d.ts +32 -0
  29. package/dist/core/Rlp.d.ts.map +1 -1
  30. package/dist/core/Rlp.js +146 -0
  31. package/dist/core/Rlp.js.map +1 -1
  32. package/dist/core/Secp256k1.d.ts +46 -1
  33. package/dist/core/Secp256k1.d.ts.map +1 -1
  34. package/dist/core/Secp256k1.js +48 -0
  35. package/dist/core/Secp256k1.js.map +1 -1
  36. package/dist/core/WebAuthn.d.ts +224 -0
  37. package/dist/core/WebAuthn.d.ts.map +1 -0
  38. package/dist/core/WebAuthn.js +343 -0
  39. package/dist/core/WebAuthn.js.map +1 -0
  40. package/dist/core/WebAuthnP256.d.ts +46 -50
  41. package/dist/core/WebAuthnP256.d.ts.map +1 -1
  42. package/dist/core/WebAuthnP256.js +44 -50
  43. package/dist/core/WebAuthnP256.js.map +1 -1
  44. package/dist/core/internal/abiParameters.d.ts +7 -87
  45. package/dist/core/internal/abiParameters.d.ts.map +1 -1
  46. package/dist/core/internal/abiParameters.js +339 -194
  47. package/dist/core/internal/abiParameters.js.map +1 -1
  48. package/dist/core/internal/engine.d.ts +35 -2
  49. package/dist/core/internal/engine.d.ts.map +1 -1
  50. package/dist/core/internal/engine.js +12 -1
  51. package/dist/core/internal/engine.js.map +1 -1
  52. package/dist/core/internal/hash.d.ts +10 -0
  53. package/dist/core/internal/hash.d.ts.map +1 -1
  54. package/dist/core/internal/hash.js +26 -1
  55. package/dist/core/internal/hash.js.map +1 -1
  56. package/dist/index.d.ts +23 -14
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +23 -14
  59. package/dist/index.js.map +1 -1
  60. package/dist/node/Engine.d.ts +1 -1
  61. package/dist/node/Engine.d.ts.map +1 -1
  62. package/dist/node/Engine.js.map +1 -1
  63. package/dist/node/Hash.d.ts +6 -4
  64. package/dist/node/Hash.d.ts.map +1 -1
  65. package/dist/node/Hash.js +35 -3
  66. package/dist/node/Hash.js.map +1 -1
  67. package/dist/tempo/AuthorizationTempo.d.ts +6 -6
  68. package/dist/tempo/AuthorizationTempo.js +6 -6
  69. package/dist/tempo/KeyAuthorization.d.ts +3 -8
  70. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  71. package/dist/tempo/KeyAuthorization.js +3 -8
  72. package/dist/tempo/KeyAuthorization.js.map +1 -1
  73. package/dist/tempo/SignatureEnvelope.d.ts +8 -8
  74. package/dist/tempo/SignatureEnvelope.js +8 -8
  75. package/dist/wasm/Engine.d.ts +1 -1
  76. package/dist/wasm/Engine.d.ts.map +1 -1
  77. package/dist/wasm/Engine.js.map +1 -1
  78. package/dist/wasm/Hash.d.ts +1 -1
  79. package/dist/wasm/Hash.d.ts.map +1 -1
  80. package/dist/wasm/Hash.js +5 -0
  81. package/dist/wasm/Hash.js.map +1 -1
  82. package/dist/wasm/internal/blake3.d.ts +10 -1
  83. package/dist/wasm/internal/blake3.d.ts.map +1 -1
  84. package/dist/wasm/internal/blake3.js +12 -1
  85. package/dist/wasm/internal/blake3.js.map +1 -1
  86. package/dist/wasm/internal/blake3.wasm.d.ts +3 -1
  87. package/dist/wasm/internal/blake3.wasm.d.ts.map +1 -1
  88. package/dist/wasm/internal/blake3.wasm.js +3 -1
  89. package/dist/wasm/internal/blake3.wasm.js.map +1 -1
  90. package/dist/wasm/internal/hash.d.ts +9 -0
  91. package/dist/wasm/internal/hash.d.ts.map +1 -1
  92. package/dist/wasm/internal/hash.js +13 -1
  93. package/dist/wasm/internal/hash.js.map +1 -1
  94. package/dist/wasm/internal/hashState.d.ts +25 -0
  95. package/dist/wasm/internal/hashState.d.ts.map +1 -0
  96. package/dist/wasm/internal/hashState.js +105 -0
  97. package/dist/wasm/internal/hashState.js.map +1 -0
  98. package/dist/wasm/internal/hashes.d.ts +12 -1
  99. package/dist/wasm/internal/hashes.d.ts.map +1 -1
  100. package/dist/wasm/internal/hashes.js +18 -1
  101. package/dist/wasm/internal/hashes.js.map +1 -1
  102. package/dist/wasm/internal/hashes.wasm.d.ts +11 -3
  103. package/dist/wasm/internal/hashes.wasm.d.ts.map +1 -1
  104. package/dist/wasm/internal/hashes.wasm.js +11 -3
  105. package/dist/wasm/internal/hashes.wasm.js.map +1 -1
  106. package/dist/webauthn/Authentication.d.ts +2 -0
  107. package/dist/webauthn/Authentication.d.ts.map +1 -1
  108. package/dist/webauthn/Authentication.js +2 -1
  109. package/dist/webauthn/Authentication.js.map +1 -1
  110. package/dist/webauthn/Registration.d.ts.map +1 -1
  111. package/dist/webauthn/Registration.js +2 -1
  112. package/dist/webauthn/Registration.js.map +1 -1
  113. package/dist/webauthn/Types.d.ts +27 -6
  114. package/dist/webauthn/Types.d.ts.map +1 -1
  115. package/dist/webauthn/internal/utils.d.ts.map +1 -1
  116. package/dist/webauthn/internal/utils.js +35 -9
  117. package/dist/webauthn/internal/utils.js.map +1 -1
  118. package/dist/zod/TypedData.d.ts +2 -2
  119. package/package.json +9 -1
  120. package/src/benchmarks/Ox.bench.ts +6 -0
  121. package/src/benchmarks/OxNode.bench.ts +8 -0
  122. package/src/benchmarks/OxV0.bench.ts +4 -0
  123. package/src/benchmarks/OxWasm.bench.ts +22 -0
  124. package/src/core/AbiFunction.ts +5 -5
  125. package/src/core/AbiParameters.ts +8 -20
  126. package/src/core/AesGcm.ts +86 -1
  127. package/src/core/Ed25519.ts +87 -1
  128. package/src/core/Engine.ts +1 -0
  129. package/src/core/Hash.ts +262 -1
  130. package/src/core/Hex.ts +1 -4
  131. package/src/core/Rlp.ts +172 -5
  132. package/src/core/Secp256k1.ts +92 -1
  133. package/src/core/WebAuthn.ts +579 -0
  134. package/src/core/WebAuthnP256.ts +47 -50
  135. package/src/core/_test/AbiFunction.test.ts +9 -0
  136. package/src/core/_test/AbiParameters.differential.fuzz.ts +156 -0
  137. package/src/core/_test/AbiParameters.encode.test.ts +22 -0
  138. package/src/core/_test/AbiParameters.fuzz.ts +4 -0
  139. package/src/core/_test/AesGcm.test.ts +76 -0
  140. package/src/core/_test/Ed25519.test-d.ts +13 -1
  141. package/src/core/_test/Ed25519.test.ts +120 -1
  142. package/src/core/_test/Engine.test-d.ts +9 -0
  143. package/src/core/_test/Engine.test.ts +1 -1
  144. package/src/core/_test/Hash.test-d.ts +23 -0
  145. package/src/core/_test/Hash.test.ts +179 -1
  146. package/src/core/_test/Hex.test.ts +2 -0
  147. package/src/core/_test/Rlp.test-d.ts +18 -0
  148. package/src/core/_test/Rlp.test.ts +109 -0
  149. package/src/core/_test/Secp256k1.test-d.ts +14 -0
  150. package/src/core/_test/Secp256k1.test.ts +91 -1
  151. package/src/core/_test/WebAuthn.test-d.ts +142 -0
  152. package/src/core/_test/WebAuthn.test.ts +766 -0
  153. package/src/core/_test/index.test.ts +1 -0
  154. package/src/core/internal/abiParameters.ts +466 -326
  155. package/src/core/internal/engine.ts +47 -2
  156. package/src/core/internal/hash.ts +53 -1
  157. package/src/index.ts +24 -14
  158. package/src/node/Engine.ts +1 -0
  159. package/src/node/Hash.ts +43 -6
  160. package/src/node/_test/Engine.test-d.ts +1 -0
  161. package/src/node/_test/Engine.test.ts +2 -0
  162. package/src/node/_test/Hash.test-d.ts +10 -0
  163. package/src/node/_test/Hash.test.ts +46 -0
  164. package/src/tempo/AuthorizationTempo.ts +6 -6
  165. package/src/tempo/KeyAuthorization.ts +3 -8
  166. package/src/tempo/SignatureEnvelope.ts +8 -8
  167. package/src/version.ts +1 -1
  168. package/src/wasm/Engine.ts +1 -0
  169. package/src/wasm/Hash.ts +10 -0
  170. package/src/wasm/_test/Engine.test-d.ts +1 -0
  171. package/src/wasm/_test/Engine.test.ts +5 -0
  172. package/src/wasm/_test/Hash.browser.test.ts +28 -0
  173. package/src/wasm/_test/Hash.test-d.ts +16 -0
  174. package/src/wasm/_test/Hash.test.ts +250 -1
  175. package/src/wasm/internal/blake3.ts +23 -2
  176. package/src/wasm/internal/blake3.wasm.ts +4 -1
  177. package/src/wasm/internal/hash.ts +27 -1
  178. package/src/wasm/internal/hashState.ts +154 -0
  179. package/src/wasm/internal/hashes.ts +56 -15
  180. package/src/wasm/internal/hashes.wasm.ts +15 -3
  181. package/src/webauthn/Authentication.ts +4 -0
  182. package/src/webauthn/Registration.ts +2 -0
  183. package/src/webauthn/Types.ts +32 -2
  184. package/src/webauthn/_test/Authentication.test.ts +123 -1
  185. package/src/webauthn/_test/Registration.test.ts +26 -6
  186. package/src/webauthn/_test/WebAuthn.browser.test.ts +28 -0
  187. package/src/webauthn/internal/utils.ts +41 -8
@@ -9,7 +9,8 @@ import * as Address from '../Address.js'
9
9
  import * as Bytes from '../Bytes.js'
10
10
  import * as Errors from '../Errors.js'
11
11
  import * as Hex from '../Hex.js'
12
- import { integerRegex } from '../Solidity.js'
12
+ import { bytesRegex, integerRegex } from '../Solidity.js'
13
+ import { encoder } from './codec/utf8.js'
13
14
  import type * as Cursor from './cursor.js'
14
15
  import type { Compute, IsNarrowable, UnionToIntersection } from './types.js'
15
16
 
@@ -19,8 +20,61 @@ export type ParameterToPrimitiveType<
19
20
  abiParameterKind extends AbiParameterKind = AbiParameterKind,
20
21
  > = AbiParameterToPrimitiveType<abiParameter, abiParameterKind>
21
22
 
22
- /** @internal */
23
- export type PreparedParameter = { dynamic: boolean; encoded: Hex.Hex }
23
+ type CompiledParameterBase = {
24
+ dynamic: boolean
25
+ name: string | undefined
26
+ staticSize: number
27
+ }
28
+
29
+ type CompiledArray = CompiledParameterBase & {
30
+ child: CompiledParameter
31
+ kind: 'array'
32
+ length: number | null
33
+ type: string
34
+ }
35
+
36
+ type CompiledBytes = CompiledParameterBase & {
37
+ kind: 'bytes'
38
+ size: number | null
39
+ }
40
+
41
+ type CompiledInteger = CompiledParameterBase & {
42
+ kind: 'integer'
43
+ max: bigint
44
+ min: bigint
45
+ signed: boolean
46
+ size: number
47
+ }
48
+
49
+ type CompiledTuple = CompiledParameterBase & {
50
+ children: readonly CompiledParameter[]
51
+ kind: 'tuple'
52
+ }
53
+
54
+ type CompiledParameter =
55
+ | CompiledArray
56
+ | CompiledBytes
57
+ | CompiledInteger
58
+ | CompiledTuple
59
+ | (CompiledParameterBase & {
60
+ kind: 'address' | 'boolean' | 'invalid' | 'string'
61
+ type?: string | undefined
62
+ })
63
+
64
+ type MeasureContext = {
65
+ checksumAddress: boolean
66
+ }
67
+
68
+ type HexPatch = {
69
+ offset: number
70
+ value: string
71
+ }
72
+
73
+ type WriteContext = {
74
+ bytes: Bytes.Bytes
75
+ patches: HexPatch[]
76
+ view: DataView
77
+ }
24
78
 
25
79
  /** @internal */
26
80
  export type ToObject<
@@ -423,385 +477,471 @@ export declare namespace decodeString {
423
477
  }
424
478
 
425
479
  /** @internal */
426
- export function prepareParameters<
427
- const parameters extends AbiParameters.AbiParameters,
428
- >({
429
- checksumAddress,
430
- parameters,
431
- values,
432
- }: {
433
- checksumAddress?: boolean | undefined
434
- parameters: parameters
435
- values: parameters extends AbiParameters.AbiParameters
436
- ? ToPrimitiveTypes<parameters>
437
- : never
438
- }) {
439
- const preparedParameters: PreparedParameter[] = []
440
- for (let i = 0; i < parameters.length; i++) {
441
- preparedParameters.push(
442
- prepareParameter({
443
- checksumAddress,
444
- parameter: parameters[i]!,
445
- value: values[i],
446
- }),
447
- )
480
+ export function encodeParameters(
481
+ parameters: readonly AbiParameters.Parameter[],
482
+ values: readonly unknown[],
483
+ options: encodeParameters.Options = {},
484
+ ): Hex.Hex {
485
+ const { checksumAddress = false, prefix } = options
486
+
487
+ if (parameters.length !== values.length)
488
+ throw new AbiParameters.LengthMismatchError({
489
+ expectedLength: parameters.length,
490
+ givenLength: values.length,
491
+ })
492
+
493
+ // Compile the wire shape before walking values so recursive writes only
494
+ // handle offsets and data.
495
+ const compiled = compileParameters(parameters)
496
+ const context: MeasureContext = {
497
+ checksumAddress,
498
+ }
499
+ const size = measureParameters(compiled, values, context)
500
+
501
+ const prefixSize = prefix ? Hex.size(prefix) : 0
502
+ const bytes = new Uint8Array(prefixSize + size)
503
+ const writer: WriteContext = {
504
+ bytes,
505
+ patches: [],
506
+ view: new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength),
448
507
  }
449
- return preparedParameters
508
+
509
+ if (prefix) writeHex(writer, 0, prefix)
510
+ writeParameters(writer, prefixSize, compiled, values)
511
+
512
+ let data = Hex.fromBytes(bytes)
513
+ for (let i = 0; i < writer.patches.length; i++) {
514
+ const patch = writer.patches[i]!
515
+ const start = 2 + patch.offset * 2
516
+ data =
517
+ `${data.slice(0, start)}${patch.value}${data.slice(start + patch.value.length)}` as Hex.Hex
518
+ }
519
+ return data
450
520
  }
451
521
 
452
- /** @internal */
453
- export declare namespace prepareParameters {
454
- type ErrorType = prepareParameter.ErrorType | Errors.GlobalErrorType
522
+ export declare namespace encodeParameters {
523
+ type Options = {
524
+ checksumAddress?: boolean | undefined
525
+ prefix?: Hex.Hex | undefined
526
+ }
527
+
528
+ type ErrorType =
529
+ | AbiParameters.LengthMismatchError
530
+ | AbiParameters.InvalidArrayError
531
+ | AbiParameters.ArrayLengthMismatchError
532
+ | AbiParameters.BytesSizeMismatchError
533
+ | AbiParameters.InvalidTypeError
534
+ | Address.assert.ErrorType
535
+ | Hex.IntegerOutOfRangeError
536
+ | Hex.fromBytes.ErrorType
537
+ | Errors.GlobalErrorType
455
538
  }
456
539
 
457
- /** @internal */
458
- export function prepareParameter<
459
- const parameter extends AbiParameters.Parameter,
460
- >({
461
- checksumAddress = false,
462
- parameter: parameter_,
463
- value,
464
- }: {
465
- parameter: parameter
466
- value: parameter extends AbiParameters.Parameter
467
- ? ParameterToPrimitiveType<parameter>
468
- : never
469
- checksumAddress?: boolean | undefined
470
- }): PreparedParameter {
471
- const parameter = parameter_ as AbiParameters.Parameter
472
-
473
- const arrayComponents = getArrayComponents(parameter.type)
474
- if (arrayComponents) {
475
- const [length, type] = arrayComponents
476
- return encodeArray(value, {
477
- checksumAddress,
540
+ function compileParameters(
541
+ parameters: readonly AbiParameters.Parameter[],
542
+ ): readonly CompiledParameter[] {
543
+ const compiled: CompiledParameter[] = []
544
+ for (let i = 0; i < parameters.length; i++)
545
+ compiled.push(compileParameter(parameters[i]!))
546
+ return compiled
547
+ }
548
+
549
+ function compileParameter(
550
+ parameter: AbiParameters.Parameter,
551
+ ): CompiledParameter {
552
+ const name = parameter.name
553
+ const array = getArrayComponents(parameter.type)
554
+ if (array) {
555
+ const [length, type] = array
556
+ const child = compileParameter({ ...parameter, type })
557
+ const dynamic = length === null || child.dynamic
558
+ return {
559
+ child,
560
+ dynamic,
561
+ kind: 'array',
478
562
  length,
479
- parameter: {
480
- ...parameter,
481
- type,
482
- },
483
- })
563
+ name,
564
+ staticSize: dynamic ? 32 : child.staticSize * length,
565
+ type: parameter.type,
566
+ }
484
567
  }
568
+
485
569
  if (parameter.type === 'tuple') {
486
- return encodeTuple(value as unknown as Tuple, {
487
- checksumAddress,
488
- parameter: parameter as TupleAbiParameter,
489
- })
490
- }
491
- if (parameter.type === 'address') {
492
- return encodeAddress(value as unknown as Hex.Hex, {
493
- checksum: checksumAddress,
494
- })
495
- }
496
- if (parameter.type === 'bool') {
497
- return encodeBoolean(value as unknown as boolean)
570
+ const children = compileParameters(
571
+ (parameter as TupleAbiParameter).components,
572
+ )
573
+ let dynamic = false
574
+ let staticSize = 0
575
+ for (let i = 0; i < children.length; i++) {
576
+ const child = children[i]!
577
+ if (child.dynamic) dynamic = true
578
+ staticSize += child.staticSize
579
+ }
580
+ return {
581
+ children,
582
+ dynamic,
583
+ kind: 'tuple',
584
+ name,
585
+ staticSize: dynamic ? 32 : staticSize,
586
+ }
498
587
  }
588
+
589
+ if (parameter.type === 'address')
590
+ return {
591
+ dynamic: false,
592
+ kind: 'address',
593
+ name,
594
+ staticSize: 32,
595
+ }
596
+
597
+ if (parameter.type === 'bool')
598
+ return {
599
+ dynamic: false,
600
+ kind: 'boolean',
601
+ name,
602
+ staticSize: 32,
603
+ }
604
+
499
605
  if (parameter.type.startsWith('uint') || parameter.type.startsWith('int')) {
500
606
  const signed = parameter.type.startsWith('int')
501
607
  const [, , size = '256'] = integerRegex.exec(parameter.type) ?? []
502
- return encodeNumber(value as unknown as number, {
608
+ const size_ = Number(size)
609
+ const max = 2n ** (BigInt(size_) - (signed ? 1n : 0n)) - 1n
610
+ return {
611
+ dynamic: false,
612
+ kind: 'integer',
613
+ max,
614
+ min: signed ? -max - 1n : 0n,
615
+ name,
503
616
  signed,
504
- size: Number(size),
505
- })
506
- }
507
- if (parameter.type.startsWith('bytes')) {
508
- return encodeBytes(value as unknown as Hex.Hex, { type: parameter.type })
509
- }
510
- if (parameter.type === 'string') {
511
- return encodeString(value as unknown as string)
617
+ size: size_,
618
+ staticSize: 32,
619
+ }
512
620
  }
513
- throw new AbiParameters.InvalidTypeError(parameter.type)
514
- }
515
621
 
516
- /** @internal */
517
- export declare namespace prepareParameter {
518
- type ErrorType =
519
- | encodeArray.ErrorType
520
- | encodeTuple.ErrorType
521
- | encodeAddress.ErrorType
522
- | encodeBoolean.ErrorType
523
- | encodeBytes.ErrorType
524
- | encodeString.ErrorType
525
- | AbiParameters.InvalidTypeError
526
- | Errors.GlobalErrorType
527
- }
528
-
529
- /** @internal */
530
- export function encode(preparedParameters: PreparedParameter[]): Hex.Hex {
531
- // 1. Compute the size of the static part of the parameters.
532
- let staticSize = 0
533
- for (let i = 0; i < preparedParameters.length; i++) {
534
- const { dynamic, encoded } = preparedParameters[i]!
535
- if (dynamic) staticSize += 32
536
- else staticSize += Hex.size(encoded)
537
- }
538
-
539
- // 2. Split the parameters into static and dynamic parts.
540
- const staticParameters: Hex.Hex[] = []
541
- const dynamicParameters: Hex.Hex[] = []
542
- let dynamicSize = 0
543
- for (let i = 0; i < preparedParameters.length; i++) {
544
- const { dynamic, encoded } = preparedParameters[i]!
545
- if (dynamic) {
546
- staticParameters.push(
547
- Hex.fromNumber(staticSize + dynamicSize, { size: 32 }),
548
- )
549
- dynamicParameters.push(encoded)
550
- dynamicSize += Hex.size(encoded)
551
- } else {
552
- staticParameters.push(encoded)
622
+ if (parameter.type.startsWith('bytes')) {
623
+ if (!bytesRegex.test(parameter.type))
624
+ throw new AbiParameters.InvalidTypeError(parameter.type)
625
+ const [, size] = parameter.type.split('bytes')
626
+ return {
627
+ dynamic: !size,
628
+ kind: 'bytes',
629
+ name,
630
+ size: size ? Number.parseInt(size, 10) : null,
631
+ staticSize: 32,
553
632
  }
554
633
  }
555
634
 
556
- // 3. Concatenate static and dynamic parts.
557
- return Hex.concat(...staticParameters, ...dynamicParameters)
558
- }
559
-
560
- /** @internal */
561
- export declare namespace encode {
562
- type ErrorType =
563
- | Hex.concat.ErrorType
564
- | Hex.fromNumber.ErrorType
565
- | Hex.size.ErrorType
566
- | Errors.GlobalErrorType
567
- }
635
+ if (parameter.type === 'string')
636
+ return {
637
+ dynamic: true,
638
+ kind: 'string',
639
+ name,
640
+ staticSize: 32,
641
+ }
568
642
 
569
- /** @internal */
570
- export function encodeAddress(
571
- value: Hex.Hex,
572
- options: { checksum: boolean },
573
- ): PreparedParameter {
574
- const { checksum } = options
575
- Address.assert(value, { strict: checksum })
576
643
  return {
577
644
  dynamic: false,
578
- encoded: Hex.padLeft(value.toLowerCase() as Hex.Hex),
645
+ kind: 'invalid',
646
+ name,
647
+ staticSize: 0,
648
+ type: parameter.type,
579
649
  }
580
650
  }
581
651
 
582
- /** @internal */
583
- export declare namespace encodeAddress {
584
- type ErrorType =
585
- | Address.assert.ErrorType
586
- | Hex.padLeft.ErrorType
587
- | Errors.GlobalErrorType
588
- }
652
+ function measureParameters(
653
+ parameters: readonly CompiledParameter[],
654
+ values: readonly unknown[] | object,
655
+ context: MeasureContext,
656
+ ): number {
657
+ let size = getHeadSize(parameters)
658
+ for (let i = 0; i < parameters.length; i++) {
659
+ const parameter = parameters[i]!
660
+ const parameterSize = measureParameter(
661
+ parameter,
662
+ getParameterValue(parameter, values, i),
663
+ context,
664
+ )
665
+ if (parameter.dynamic) size += parameterSize
666
+ }
667
+ return size
668
+ }
669
+
670
+ function measureParameter(
671
+ parameter: CompiledParameter,
672
+ value: unknown,
673
+ context: MeasureContext,
674
+ ): number {
675
+ if (parameter.kind === 'array') {
676
+ if (!Array.isArray(value)) throw new AbiParameters.InvalidArrayError(value)
677
+ if (parameter.length !== null && value.length !== parameter.length)
678
+ throw new AbiParameters.ArrayLengthMismatchError({
679
+ expectedLength: parameter.length,
680
+ givenLength: value.length,
681
+ type: parameter.type,
682
+ })
589
683
 
590
- /** @internal */
591
- export function encodeArray<const parameter extends AbiParameters.Parameter>(
592
- value: ParameterToPrimitiveType<parameter>,
593
- options: {
594
- checksumAddress?: boolean | undefined
595
- length: number | null
596
- parameter: parameter
597
- },
598
- ): PreparedParameter {
599
- const { checksumAddress, length, parameter } = options
684
+ let size = parameter.length === null ? 32 : 0
685
+ size +=
686
+ value.length * (parameter.child.dynamic ? 32 : parameter.child.staticSize)
687
+ for (let i = 0; i < value.length; i++) {
688
+ const childSize = measureParameter(parameter.child, value[i], context)
689
+ if (parameter.child.dynamic) size += childSize
690
+ }
691
+ return size
692
+ }
600
693
 
601
- const dynamic = length === null
694
+ if (parameter.kind === 'tuple')
695
+ return measureParameters(parameter.children, value as object, context)
602
696
 
603
- if (!Array.isArray(value)) throw new AbiParameters.InvalidArrayError(value)
604
- if (!dynamic && value.length !== length)
605
- throw new AbiParameters.ArrayLengthMismatchError({
606
- expectedLength: length!,
607
- givenLength: value.length,
608
- type: `${parameter.type}[${length}]`,
697
+ if (parameter.kind === 'address') {
698
+ Address.assert(value as Address.Address, {
699
+ strict: context.checksumAddress,
609
700
  })
701
+ return 32
702
+ }
610
703
 
611
- // Zero-length fixed arrays of dynamic types (e.g. `string[0]`) are dynamic
612
- // per the ABI spec, even though they have no elements to inspect.
613
- let dynamicChild = value.length === 0 && hasDynamicChild(parameter)
614
- const preparedParameters: PreparedParameter[] = []
615
- for (let i = 0; i < value.length; i++) {
616
- const preparedParam = prepareParameter({
617
- checksumAddress,
618
- parameter,
619
- value: value[i],
620
- })
621
- if (preparedParam.dynamic) dynamicChild = true
622
- preparedParameters.push(preparedParam)
623
- }
624
-
625
- if (dynamic || dynamicChild) {
626
- const data = encode(preparedParameters)
627
- if (dynamic) {
628
- const length = Hex.fromNumber(preparedParameters.length, { size: 32 })
629
- return {
630
- dynamic: true,
631
- encoded:
632
- preparedParameters.length > 0 ? Hex.concat(length, data) : length,
633
- }
634
- }
635
- if (dynamicChild) return { dynamic: true, encoded: data }
704
+ if (parameter.kind === 'boolean') {
705
+ if (typeof value !== 'boolean')
706
+ throw new Errors.BaseError(
707
+ `Invalid boolean value: "${value}" (type: ${typeof value}). Expected: \`true\` or \`false\`.`,
708
+ )
709
+ return 32
636
710
  }
637
- return {
638
- dynamic: false,
639
- encoded: Hex.concat(...preparedParameters.map(({ encoded }) => encoded)),
711
+
712
+ if (parameter.kind === 'integer') {
713
+ const value_ = value as number
714
+ if (value_ > parameter.max || value_ < parameter.min)
715
+ throw new Hex.IntegerOutOfRangeError({
716
+ max: parameter.max.toString(),
717
+ min: parameter.min.toString(),
718
+ signed: parameter.signed,
719
+ size: parameter.size / 8,
720
+ value: value_.toString(),
721
+ })
722
+ BigInt(value_)
723
+ return 32
640
724
  }
641
- }
642
725
 
643
- /** @internal */
644
- export declare namespace encodeArray {
645
- type ErrorType =
646
- | AbiParameters.InvalidArrayError
647
- | AbiParameters.ArrayLengthMismatchError
648
- | Hex.concat.ErrorType
649
- | Hex.fromNumber.ErrorType
650
- | Errors.GlobalErrorType
726
+ if (parameter.kind === 'bytes') {
727
+ const value_ = value as Hex.Hex
728
+ const size = Hex.size(value_)
729
+ if (parameter.size !== null && size !== parameter.size)
730
+ throw new AbiParameters.BytesSizeMismatchError({
731
+ expectedSize: parameter.size,
732
+ value: value_,
733
+ })
734
+ if (parameter.size !== null) return 32
735
+ return 32 + Math.ceil(size / 32) * 32
736
+ }
737
+
738
+ if (parameter.kind === 'string') {
739
+ const size = getUtf8Size(normalizeString(value))
740
+ return 32 + Math.ceil(size / 32) * 32
741
+ }
742
+
743
+ throw new AbiParameters.InvalidTypeError(parameter.type!)
651
744
  }
652
745
 
653
- /** @internal */
654
- export function encodeBytes(
655
- value: Hex.Hex,
656
- { type }: { type: string },
657
- ): PreparedParameter {
658
- const [, parametersize] = type.split('bytes')
659
- const bytesSize = Hex.size(value)
660
- if (!parametersize) {
661
- let value_ = value
662
- // If the size is not divisible by 32 bytes, pad the end
663
- // with empty bytes to the ceiling 32 bytes.
664
- if (bytesSize % 32 !== 0)
665
- value_ = Hex.padRight(value_, Math.ceil((value.length - 2) / 2 / 32) * 32)
666
- return {
667
- dynamic: true,
668
- encoded: Hex.concat(
669
- Hex.padLeft(Hex.fromNumber(bytesSize, { size: 32 })),
670
- value_,
671
- ),
672
- }
746
+ function getHeadSize(parameters: readonly CompiledParameter[]): number {
747
+ let size = 0
748
+ for (let i = 0; i < parameters.length; i++) {
749
+ const parameter = parameters[i]!
750
+ size += parameter.dynamic ? 32 : parameter.staticSize
673
751
  }
674
- if (bytesSize !== Number.parseInt(parametersize, 10))
675
- throw new AbiParameters.BytesSizeMismatchError({
676
- expectedSize: Number.parseInt(parametersize, 10),
677
- value,
678
- })
679
- return { dynamic: false, encoded: Hex.padRight(value) }
752
+ return size
680
753
  }
681
754
 
682
- /** @internal */
683
- export declare namespace encodeBytes {
684
- type ErrorType =
685
- | Hex.padLeft.ErrorType
686
- | Hex.padRight.ErrorType
687
- | Hex.fromNumber.ErrorType
688
- | Hex.slice.ErrorType
689
- | Errors.GlobalErrorType
755
+ function getParameterValue(
756
+ parameter: CompiledParameter,
757
+ values: readonly unknown[] | object,
758
+ index: number,
759
+ ) {
760
+ if (Array.isArray(values)) return values[index]
761
+ return (values as Record<string, unknown>)[parameter.name!]
690
762
  }
691
763
 
692
- /** @internal */
693
- export function encodeBoolean(value: boolean): PreparedParameter {
694
- if (typeof value !== 'boolean')
695
- throw new Errors.BaseError(
696
- `Invalid boolean value: "${value}" (type: ${typeof value}). Expected: \`true\` or \`false\`.`,
697
- )
698
- return { dynamic: false, encoded: Hex.padLeft(Hex.fromBoolean(value)) }
764
+ function getUtf8Size(value: string): number {
765
+ let size = 0
766
+ for (let i = 0; i < value.length; i++) {
767
+ const code = value.charCodeAt(i)
768
+ if (code < 0x80) size++
769
+ else if (code < 0x800) size += 2
770
+ else if (
771
+ code >= 0xd800 &&
772
+ code <= 0xdbff &&
773
+ value.charCodeAt(i + 1) >= 0xdc00 &&
774
+ value.charCodeAt(i + 1) <= 0xdfff
775
+ ) {
776
+ size += 4
777
+ i++
778
+ } else size += 3
779
+ }
780
+ return size
699
781
  }
700
782
 
701
- /** @internal */
702
- export declare namespace encodeBoolean {
703
- type ErrorType =
704
- | Hex.padLeft.ErrorType
705
- | Hex.fromBoolean.ErrorType
706
- | Errors.GlobalErrorType
783
+ function normalizeString(value: unknown): string {
784
+ if (value === undefined) return ''
785
+ return `${value}`
707
786
  }
708
787
 
709
- /** @internal */
710
- export function encodeNumber(
711
- value: number,
712
- { signed, size }: { signed: boolean; size: number },
713
- ): PreparedParameter {
714
- if (typeof size === 'number') {
715
- const max = 2n ** (BigInt(size) - (signed ? 1n : 0n)) - 1n
716
- const min = signed ? -max - 1n : 0n
717
- if (value > max || value < min)
718
- throw new Hex.IntegerOutOfRangeError({
719
- max: max.toString(),
720
- min: min.toString(),
721
- signed,
722
- size: size / 8,
723
- value: value.toString(),
724
- })
788
+ function isWritableHex(value: Hex.Hex): boolean {
789
+ if (
790
+ typeof value !== 'string' ||
791
+ value.charCodeAt(0) !== 48 ||
792
+ value.charCodeAt(1) !== 120
793
+ )
794
+ return false
795
+ for (let i = 2; i < value.length; i++)
796
+ if (toNibble(value.charCodeAt(i)) === -1) return false
797
+ return true
798
+ }
799
+
800
+ function writeParameters(
801
+ context: WriteContext,
802
+ offset: number,
803
+ parameters: readonly CompiledParameter[],
804
+ values: readonly unknown[] | object,
805
+ ): number {
806
+ let head = offset
807
+ let tail = offset + getHeadSize(parameters)
808
+ for (let i = 0; i < parameters.length; i++) {
809
+ const parameter = parameters[i]!
810
+ const value = getParameterValue(parameter, values, i)
811
+ if (parameter.dynamic) {
812
+ writeWord(context, head, tail - offset)
813
+ head += 32
814
+ tail = writeParameter(context, tail, parameter, value)
815
+ } else head = writeParameter(context, head, parameter, value)
725
816
  }
726
- return {
727
- dynamic: false,
728
- encoded: Hex.fromNumber(value, {
729
- size: 32,
730
- signed,
731
- }),
817
+ return Math.max(head, tail)
818
+ }
819
+
820
+ function writeParameter(
821
+ context: WriteContext,
822
+ offset: number,
823
+ parameter: CompiledParameter,
824
+ value: unknown,
825
+ ): number {
826
+ if (parameter.kind === 'array') {
827
+ const values = value as readonly unknown[]
828
+ let head = offset
829
+ if (parameter.length === null) {
830
+ writeWord(context, head, values.length)
831
+ head += 32
832
+ }
833
+
834
+ let tail =
835
+ head +
836
+ values.length *
837
+ (parameter.child.dynamic ? 32 : parameter.child.staticSize)
838
+ for (let i = 0; i < values.length; i++) {
839
+ if (parameter.child.dynamic) {
840
+ writeWord(
841
+ context,
842
+ head,
843
+ tail - (parameter.length === null ? offset + 32 : offset),
844
+ )
845
+ head += 32
846
+ tail = writeParameter(context, tail, parameter.child, values[i])
847
+ } else head = writeParameter(context, head, parameter.child, values[i])
848
+ }
849
+ return Math.max(head, tail)
732
850
  }
733
- }
734
851
 
735
- /** @internal */
736
- export declare namespace encodeNumber {
737
- type ErrorType = Hex.fromNumber.ErrorType | Errors.GlobalErrorType
738
- }
852
+ if (parameter.kind === 'tuple')
853
+ return writeParameters(context, offset, parameter.children, value as object)
739
854
 
740
- /** @internal */
741
- export function encodeString(value: string): PreparedParameter {
742
- const hexValue = Hex.fromString(value)
743
- const partsLength = Math.ceil(Hex.size(hexValue) / 32)
744
- const parts: Hex.Hex[] = []
745
- for (let i = 0; i < partsLength; i++) {
746
- parts.push(Hex.padRight(Hex.slice(hexValue, i * 32, (i + 1) * 32)))
855
+ if (parameter.kind === 'address') {
856
+ writeHex(context, offset + 12, value as Hex.Hex)
857
+ return offset + 32
747
858
  }
748
- return {
749
- dynamic: true,
750
- encoded: Hex.concat(
751
- Hex.padRight(Hex.fromNumber(Hex.size(hexValue), { size: 32 })),
752
- ...parts,
753
- ),
859
+
860
+ if (parameter.kind === 'boolean') {
861
+ context.bytes[offset + 31] = value ? 1 : 0
862
+ return offset + 32
754
863
  }
755
- }
756
864
 
757
- /** @internal */
758
- export declare namespace encodeString {
759
- type ErrorType =
760
- | Hex.fromNumber.ErrorType
761
- | Hex.padRight.ErrorType
762
- | Hex.slice.ErrorType
763
- | Hex.size.ErrorType
764
- | Errors.GlobalErrorType
865
+ if (parameter.kind === 'integer') {
866
+ writeWord(context, offset, value as number)
867
+ return offset + 32
868
+ }
869
+
870
+ if (parameter.kind === 'bytes') {
871
+ const value_ = value as Hex.Hex
872
+ if (parameter.size === null) {
873
+ const size = Hex.size(value_)
874
+ writeWord(context, offset, size)
875
+ writeHex(context, offset + 32, value_)
876
+ return offset + 32 + Math.ceil(size / 32) * 32
877
+ }
878
+ writeHex(context, offset, value_)
879
+ return offset + 32
880
+ }
881
+
882
+ if (parameter.kind === 'string') {
883
+ const value_ = normalizeString(value)
884
+ const size = getUtf8Size(value_)
885
+ writeWord(context, offset, size)
886
+ const { written } = encoder.encodeInto(
887
+ value_,
888
+ context.bytes.subarray(offset + 32, offset + 32 + size),
889
+ )
890
+ if (written !== size) throw new Error('Failed to encode string.')
891
+ return offset + 32 + Math.ceil(size / 32) * 32
892
+ }
893
+
894
+ return offset
765
895
  }
766
896
 
767
- /** @internal */
768
- export function encodeTuple<
769
- const parameter extends AbiParameters.Parameter & {
770
- components: readonly AbiParameters.Parameter[]
771
- },
772
- >(
773
- value: ParameterToPrimitiveType<parameter>,
774
- options: {
775
- checksumAddress?: boolean | undefined
776
- parameter: parameter
777
- },
778
- ): PreparedParameter {
779
- const { checksumAddress, parameter } = options
780
-
781
- let dynamic = false
782
- const preparedParameters: PreparedParameter[] = []
783
- for (let i = 0; i < parameter.components.length; i++) {
784
- const param_ = parameter.components[i]!
785
- const index = Array.isArray(value) ? i : param_.name
786
- const preparedParam = prepareParameter({
787
- checksumAddress,
788
- parameter: param_,
789
- value: (value as any)[index!] as readonly unknown[],
790
- })
791
- preparedParameters.push(preparedParam)
792
- if (preparedParam.dynamic) dynamic = true
897
+ function writeHex(
898
+ context: WriteContext,
899
+ offset: number,
900
+ value: Hex.Hex,
901
+ ): number {
902
+ if (!isWritableHex(value)) {
903
+ const patch = value.slice(2)
904
+ context.patches.push({ offset, value: patch })
905
+ return offset + Math.ceil(patch.length / 2)
793
906
  }
794
- return {
795
- dynamic,
796
- encoded: dynamic
797
- ? encode(preparedParameters)
798
- : Hex.concat(...preparedParameters.map(({ encoded }) => encoded)),
907
+
908
+ let i = 2
909
+ for (; i + 1 < value.length; i += 2)
910
+ context.bytes[offset++] =
911
+ (toNibble(value.charCodeAt(i)) << 4) | toNibble(value.charCodeAt(i + 1))
912
+ if (i < value.length)
913
+ context.bytes[offset++] = toNibble(value.charCodeAt(i)) << 4
914
+ return offset
915
+ }
916
+
917
+ function writeWord(
918
+ context: WriteContext,
919
+ offset: number,
920
+ value: bigint | number,
921
+ ) {
922
+ let value_ = BigInt(value)
923
+ if (value_ < 0n) value_ = BigInt.asUintN(256, value_)
924
+
925
+ // DataView avoids 32 BigInt shifts for the common word-sized case.
926
+ if (value_ <= 0xffff_ffff_ffff_ffffn) {
927
+ context.view.setBigUint64(offset + 24, value_)
928
+ return
799
929
  }
930
+
931
+ const hex = value_.toString(16)
932
+ let cursor = offset + 32 - Math.ceil(hex.length / 2)
933
+ let i = hex.length % 2
934
+ if (i === 1) context.bytes[cursor++] = toNibble(hex.charCodeAt(0))
935
+ for (; i < hex.length; i += 2)
936
+ context.bytes[cursor++] =
937
+ (toNibble(hex.charCodeAt(i)) << 4) | toNibble(hex.charCodeAt(i + 1))
800
938
  }
801
939
 
802
- /** @internal */
803
- export declare namespace encodeTuple {
804
- type ErrorType = Hex.concat.ErrorType | Errors.GlobalErrorType
940
+ function toNibble(code: number): number {
941
+ if (code >= 48 && code <= 57) return code - 48
942
+ if (code >= 65 && code <= 70) return code - 55
943
+ if (code >= 97 && code <= 102) return code - 87
944
+ return -1
805
945
  }
806
946
 
807
947
  /** @internal */