ox 1.8.9 → 1.8.10

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.
@@ -242,6 +242,54 @@ describe('fromHttp', () => {
242
242
 
243
243
  await server.close()
244
244
  })
245
+
246
+ test('behavior: caller signal without `AbortSignal.any`', async () => {
247
+ const server = await createHttpServer((_, res) => {
248
+ res.end(JSON.stringify({ id: 0, jsonrpc: '2.0', result: 'wagmi' }))
249
+ })
250
+
251
+ const transport = RpcTransport.fromHttp(server.url, {
252
+ timeout: 50,
253
+ fetchOptions: {
254
+ signal: new AbortController().signal,
255
+ },
256
+ })
257
+
258
+ const descriptor = Object.getOwnPropertyDescriptor(AbortSignal, 'any')!
259
+ Object.defineProperty(AbortSignal, 'any', {
260
+ configurable: true,
261
+ value: undefined,
262
+ })
263
+ try {
264
+ await expect(
265
+ transport.request({ method: 'eth_accounts' }),
266
+ ).resolves.toMatchInlineSnapshot(`"wagmi"`)
267
+ } finally {
268
+ Object.defineProperty(AbortSignal, 'any', descriptor)
269
+ await server.close()
270
+ }
271
+ })
272
+
273
+ test('behavior: timeout covers a response body that stalls after its headers', async () => {
274
+ const server = await createHttpServer(async (_, res) => {
275
+ res.writeHead(200, { 'Content-Type': 'application/json' })
276
+ res.write('{"id":0,"jsonrpc":"2.0",')
277
+ await setTimeout(200)
278
+ res.end('"result":"wagmi"}')
279
+ })
280
+
281
+ const transport = RpcTransport.fromHttp(server.url, {
282
+ timeout: 50,
283
+ })
284
+
285
+ await expect(() =>
286
+ transport.request({ method: 'eth_accounts' }),
287
+ ).rejects.toThrowErrorMatchingInlineSnapshot(
288
+ `[Promise.TimeoutError: Operation timed out.]`,
289
+ )
290
+
291
+ await server.close()
292
+ })
245
293
  })
246
294
 
247
295
  test('exports', () => {
@@ -1,4 +1,11 @@
1
- import { Bytes, Hex, Signature, Solidity, TxEnvelopeEip1559 } from 'ox'
1
+ import {
2
+ Bytes,
3
+ Hex,
4
+ Secp256k1,
5
+ Signature,
6
+ Solidity,
7
+ TxEnvelopeEip1559,
8
+ } from 'ox'
2
9
  import { describe, expect, test } from 'vp/test'
3
10
 
4
11
  describe('assert', () => {
@@ -488,6 +495,40 @@ describe('serialize', () => {
488
495
  `"0x6e100a352ec6ad1b70802290e18aeed190704973570f3b8ed42cb9808e2ea6bf4a90a229a244495b41890987806fcbd2d5d23fc0dbe5f5256c2613c039d76db8"`,
489
496
  )
490
497
  })
498
+
499
+ test('behavior: `r` and `s` shorter than 32 bytes', () => {
500
+ const signature = Signature.toHex({
501
+ r: '0x1cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e',
502
+ s: '0xc3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b',
503
+ yParity: 1,
504
+ })
505
+ expect(signature).toMatchInlineSnapshot(
506
+ `"0x001cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e00c3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b1c"`,
507
+ )
508
+ expect(Hex.size(signature)).toMatchInlineSnapshot(`65`)
509
+ })
510
+
511
+ test('behavior: short `s` round-trips through recovery', () => {
512
+ const payload =
513
+ '0x44caf484852acee09d52b2802dba26d3c8ab3d28c7abcc1398b87898115633ec'
514
+ const signature = Signature.fromHex(
515
+ Signature.toHex({
516
+ r: '0xdf1cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e',
517
+ s: '0xc3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b',
518
+ yParity: 1,
519
+ }),
520
+ )
521
+ expect(signature).toMatchInlineSnapshot(`
522
+ {
523
+ "r": "0xdf1cdb3cbf94c7059e0e16d2ba046daef144d77384995a66f379194b638fc07e",
524
+ "s": "0x00c3d1ccd2c334876b9f8ef0ba3725c30dd5e1def225df36efa406d881ed610b",
525
+ "yParity": 1,
526
+ }
527
+ `)
528
+ expect(
529
+ Secp256k1.recoverAddress({ payload, signature }),
530
+ ).toMatchInlineSnapshot(`"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"`)
531
+ })
491
532
  })
492
533
 
493
534
  describe('toBytes', () => {
@@ -791,6 +791,23 @@ describe('extractEip712DomainTypes', () => {
791
791
  test('empty', () => {
792
792
  expect(TypedData.extractEip712DomainTypes({})).toMatchInlineSnapshot('[]')
793
793
  })
794
+
795
+ test('empty version', () => {
796
+ expect(
797
+ TypedData.extractEip712DomainTypes({ name: 'Ether Mail', version: '' }),
798
+ ).toMatchInlineSnapshot(`
799
+ [
800
+ {
801
+ "name": "name",
802
+ "type": "string",
803
+ },
804
+ {
805
+ "name": "version",
806
+ "type": "string",
807
+ },
808
+ ]
809
+ `)
810
+ })
794
811
  })
795
812
 
796
813
  describe('getSignPayload', () => {
@@ -849,6 +866,18 @@ describe('getSignPayload', () => {
849
866
  )
850
867
  })
851
868
 
869
+ test('domain: empty version', () => {
870
+ expect(
871
+ TypedData.getSignPayload({
872
+ ...typedData.complex,
873
+ domain: { name: 'Ether Mail', version: '' },
874
+ primaryType: 'Mail',
875
+ }),
876
+ ).toMatchInlineSnapshot(
877
+ `"0xa437e12ee2fd4391429d9714be617951b7e69fb61dc520ad6fb8230d03b7508c"`,
878
+ )
879
+ })
880
+
852
881
  test('domain: bigint chainId produces same hash as number chainId', () => {
853
882
  const typedDataWithNumberChainId = {
854
883
  domain: {
@@ -4,6 +4,7 @@ import type * as AbiItem from '../AbiItem.js'
4
4
  import type * as AbiParameters from '../AbiParameters.js'
5
5
  import * as Address from '../Address.js'
6
6
  import * as Errors from '../Errors.js'
7
+ import { getArrayComponents } from './abiParameters.js'
7
8
  import type {
8
9
  Compute,
9
10
  IsNever,
@@ -570,20 +571,6 @@ const integerTypeRegex =
570
571
  */
571
572
  const fixedBytesTypeRegex = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/
572
573
 
573
- /**
574
- * Fixed-length (`<type>[M]`) / dynamic (`<type>[]`) array regex.
575
- *
576
- * @internal
577
- */
578
- const arrayTypeRegex = /[a-z]+[1-9]{0,3}(\[[0-9]{0,}\])+$/
579
-
580
- /**
581
- * Strips a single trailing `[...]` array suffix from a type.
582
- *
583
- * @internal
584
- */
585
- const arraySuffixStripRegex = /(\[[0-9]{0,}\])$/
586
-
587
574
  /** @internal */
588
575
  export function isArgOfType(
589
576
  arg: unknown,
@@ -604,9 +591,12 @@ export function isArgOfType(
604
591
  if (abiParameterType === 'tuple' && 'components' in abiParameter)
605
592
  return Object.values(abiParameter.components).every(
606
593
  (component, index) => {
607
- return isArgOfType(
608
- Object.values(arg as unknown[] | Record<string, unknown>)[index],
609
- component as AbiParameters.Parameter,
594
+ return (
595
+ argType === 'object' &&
596
+ isArgOfType(
597
+ getComponentValue(arg, component, index),
598
+ component as AbiParameters.Parameter,
599
+ )
610
600
  )
611
601
  },
612
602
  )
@@ -617,14 +607,17 @@ export function isArgOfType(
617
607
  if (fixedBytesTypeRegex.test(abiParameterType))
618
608
  return argType === 'string' || arg instanceof Uint8Array
619
609
 
620
- if (arrayTypeRegex.test(abiParameterType)) {
610
+ // Fixed-length (`<type>[M]`) and dynamic (`<type>[]`) arrays.
611
+ const arrayComponents = getArrayComponents(abiParameterType)
612
+ if (arrayComponents) {
613
+ const [length, type] = arrayComponents
621
614
  return (
622
615
  Array.isArray(arg) &&
616
+ (length === null || arg.length === length) &&
623
617
  arg.every((x: unknown) =>
624
618
  isArgOfType(x, {
625
619
  ...abiParameter,
626
- // Pop off `[]` or `[M]` from end of type
627
- type: abiParameterType.replace(arraySuffixStripRegex, ''),
620
+ type,
628
621
  } as AbiParameters.Parameter),
629
622
  )
630
623
  )
@@ -649,19 +642,49 @@ export function getAmbiguousTypes(
649
642
  parameterIndex++
650
643
  ) {
651
644
  const sourceParameter = sourceParameters[parameterIndex]!
652
- const targetParameter = targetParameters[parameterIndex]!
645
+ const targetParameter = targetParameters[parameterIndex]
646
+ if (!targetParameter) continue
653
647
 
654
648
  if (
655
649
  sourceParameter.type === 'tuple' &&
656
650
  targetParameter.type === 'tuple' &&
657
651
  'components' in sourceParameter &&
658
652
  'components' in targetParameter
659
- )
660
- return getAmbiguousTypes(
653
+ ) {
654
+ const value = args[parameterIndex]
655
+ const types = getAmbiguousTypes(
661
656
  sourceParameter.components,
662
657
  targetParameter.components,
663
- (args as any)[parameterIndex],
658
+ sourceParameter.components.map((component, index) =>
659
+ getComponentValue(value, component, index),
660
+ ),
661
+ )
662
+ if (types) return types
663
+ continue
664
+ }
665
+
666
+ const sourceArray = getArrayComponents(sourceParameter.type)
667
+ const targetArray = getArrayComponents(targetParameter.type)
668
+ if (sourceArray && targetArray) {
669
+ // Every item matches both element types, so the first item (or `undefined` for an empty array) decides.
670
+ const types = getAmbiguousTypes(
671
+ [
672
+ {
673
+ ...sourceParameter,
674
+ type: sourceArray[1],
675
+ } as AbiParameters.Parameter,
676
+ ],
677
+ [
678
+ {
679
+ ...targetParameter,
680
+ type: targetArray[1],
681
+ } as AbiParameters.Parameter,
682
+ ],
683
+ [(args[parameterIndex] as readonly unknown[] | undefined)?.[0]],
664
684
  )
685
+ if (types) return types
686
+ continue
687
+ }
665
688
 
666
689
  const sourceType = sourceParameter.type
667
690
  const targetType = targetParameter.type
@@ -679,10 +702,21 @@ export function getAmbiguousTypes(
679
702
  (sourceType === 'address' && targetType === 'bytes') ||
680
703
  (sourceType === 'bytes' && targetType === 'address')
681
704
  )
682
- ambiguous = Address.validate(arg, { strict: false })
705
+ // An `undefined` value comes from an empty array, which matches both types.
706
+ ambiguous = arg === undefined || Address.validate(arg, { strict: false })
683
707
 
684
708
  if (ambiguous) return [sourceType, targetType]
685
709
  }
686
710
 
687
711
  return
688
712
  }
713
+
714
+ // Reads a tuple component's value by index from arrays and by name from objects, like `AbiParameters.encode`.
715
+ function getComponentValue(
716
+ arg: unknown,
717
+ component: AbiParameters.Parameter,
718
+ index: number,
719
+ ): unknown {
720
+ if (Array.isArray(arg)) return arg[index]
721
+ return (arg as Record<string, unknown> | undefined)?.[component.name!]
722
+ }
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** @internal */
2
- export const version = '1.8.9'
2
+ export const version = '1.8.10'