viem 2.55.13 → 2.55.16

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 (64) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/_cjs/actions/index.js +4 -2
  3. package/_cjs/actions/index.js.map +1 -1
  4. package/_cjs/actions/public/simulateCalls.js +213 -142
  5. package/_cjs/actions/public/simulateCalls.js.map +1 -1
  6. package/_cjs/actions/public/watchBlockHeaders.js +61 -0
  7. package/_cjs/actions/public/watchBlockHeaders.js.map +1 -0
  8. package/_cjs/clients/decorators/public.js +2 -0
  9. package/_cjs/clients/decorators/public.js.map +1 -1
  10. package/_cjs/errors/version.js +1 -1
  11. package/_cjs/index.js.map +1 -1
  12. package/_cjs/tempo/actions/earn.js +2 -0
  13. package/_cjs/tempo/actions/earn.js.map +1 -1
  14. package/_cjs/tempo/actions/zone.js +18 -8
  15. package/_cjs/tempo/actions/zone.js.map +1 -1
  16. package/_cjs/utils/siwe/parseSiweMessage.js +16 -3
  17. package/_cjs/utils/siwe/parseSiweMessage.js.map +1 -1
  18. package/_cjs/utils/siwe/validateSiweMessage.js +13 -3
  19. package/_cjs/utils/siwe/validateSiweMessage.js.map +1 -1
  20. package/_esm/actions/index.js +1 -0
  21. package/_esm/actions/index.js.map +1 -1
  22. package/_esm/actions/public/simulateCalls.js +238 -150
  23. package/_esm/actions/public/simulateCalls.js.map +1 -1
  24. package/_esm/actions/public/watchBlockHeaders.js +81 -0
  25. package/_esm/actions/public/watchBlockHeaders.js.map +1 -0
  26. package/_esm/clients/decorators/public.js +2 -0
  27. package/_esm/clients/decorators/public.js.map +1 -1
  28. package/_esm/errors/version.js +1 -1
  29. package/_esm/index.js.map +1 -1
  30. package/_esm/tempo/actions/earn.js +2 -0
  31. package/_esm/tempo/actions/earn.js.map +1 -1
  32. package/_esm/tempo/actions/zone.js +20 -8
  33. package/_esm/tempo/actions/zone.js.map +1 -1
  34. package/_esm/utils/siwe/parseSiweMessage.js +18 -3
  35. package/_esm/utils/siwe/parseSiweMessage.js.map +1 -1
  36. package/_esm/utils/siwe/validateSiweMessage.js +16 -3
  37. package/_esm/utils/siwe/validateSiweMessage.js.map +1 -1
  38. package/_types/actions/index.d.ts +1 -0
  39. package/_types/actions/index.d.ts.map +1 -1
  40. package/_types/actions/public/simulateCalls.d.ts +5 -3
  41. package/_types/actions/public/simulateCalls.d.ts.map +1 -1
  42. package/_types/actions/public/watchBlockHeaders.d.ts +45 -0
  43. package/_types/actions/public/watchBlockHeaders.d.ts.map +1 -0
  44. package/_types/clients/decorators/public.d.ts +24 -0
  45. package/_types/clients/decorators/public.d.ts.map +1 -1
  46. package/_types/errors/version.d.ts +1 -1
  47. package/_types/index.d.ts +1 -0
  48. package/_types/index.d.ts.map +1 -1
  49. package/_types/tempo/actions/earn.d.ts.map +1 -1
  50. package/_types/tempo/actions/zone.d.ts +21 -5
  51. package/_types/tempo/actions/zone.d.ts.map +1 -1
  52. package/_types/utils/siwe/parseSiweMessage.d.ts.map +1 -1
  53. package/_types/utils/siwe/validateSiweMessage.d.ts.map +1 -1
  54. package/actions/index.ts +9 -0
  55. package/actions/public/simulateCalls.ts +324 -181
  56. package/actions/public/watchBlockHeaders.ts +131 -0
  57. package/clients/decorators/public.ts +33 -0
  58. package/errors/version.ts +1 -1
  59. package/index.ts +8 -0
  60. package/package.json +1 -1
  61. package/tempo/actions/earn.ts +2 -0
  62. package/tempo/actions/zone.ts +56 -5
  63. package/utils/siwe/parseSiweMessage.ts +20 -3
  64. package/utils/siwe/validateSiweMessage.ts +13 -2
@@ -0,0 +1,131 @@
1
+ import type { Client } from '../../clients/createClient.js'
2
+ import type { Transport } from '../../clients/transports/createTransport.js'
3
+ import type { ErrorType } from '../../errors/utils.js'
4
+ import type { Chain } from '../../types/chain.js'
5
+ import type { HasTransportType } from '../../types/transport.js'
6
+ import { formatBlock } from '../../utils/formatters/block.js'
7
+ import { observe } from '../../utils/observe.js'
8
+ import { type StringifyErrorType, stringify } from '../../utils/stringify.js'
9
+
10
+ import type { GetBlockReturnType } from './getBlock.js'
11
+
12
+ const blockFields = [
13
+ 'size',
14
+ 'totalDifficulty',
15
+ 'transactions',
16
+ 'uncles',
17
+ 'withdrawals',
18
+ ] as const
19
+
20
+ export type BlockHeader<chain extends Chain | undefined = Chain> = Omit<
21
+ GetBlockReturnType<chain, false, 'latest'>,
22
+ (typeof blockFields)[number]
23
+ >
24
+
25
+ export type OnBlockHeaderParameter<chain extends Chain | undefined = Chain> =
26
+ BlockHeader<chain>
27
+
28
+ export type OnBlockHeader<chain extends Chain | undefined = Chain> = (
29
+ blockHeader: OnBlockHeaderParameter<chain>,
30
+ prevBlockHeader: OnBlockHeaderParameter<chain> | undefined,
31
+ ) => void
32
+
33
+ export type WatchBlockHeadersParameters<
34
+ chain extends Chain | undefined = Chain,
35
+ > = {
36
+ /** The callback to call when a new block header is received. */
37
+ onBlockHeader: OnBlockHeader<chain>
38
+ /** The callback to call when an error occurs while watching block headers. */
39
+ onError?: ((error: Error) => void) | undefined
40
+ }
41
+
42
+ export type WatchBlockHeadersReturnType = () => void
43
+
44
+ export type WatchBlockHeadersErrorType = StringifyErrorType | ErrorType
45
+
46
+ /**
47
+ * Watches and returns incoming block headers.
48
+ *
49
+ * - Docs: https://viem.sh/docs/actions/public/watchBlockHeaders
50
+ * - JSON-RPC Methods: Uses a WebSocket or IPC subscription via [`eth_subscribe`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_subscribe) and the `"newHeads"` event.
51
+ *
52
+ * @param client - Client to use
53
+ * @param parameters - {@link WatchBlockHeadersParameters}
54
+ * @returns A function that can be invoked to stop watching for new block headers. {@link WatchBlockHeadersReturnType}
55
+ *
56
+ * @example
57
+ * import { createPublicClient, webSocket } from 'viem'
58
+ * import { watchBlockHeaders } from 'viem/actions'
59
+ * import { mainnet } from 'viem/chains'
60
+ *
61
+ * const client = createPublicClient({
62
+ * chain: mainnet,
63
+ * transport: webSocket(),
64
+ * })
65
+ * const unwatch = watchBlockHeaders(client, {
66
+ * onBlockHeader: (blockHeader) => console.log(blockHeader),
67
+ * })
68
+ */
69
+ export function watchBlockHeaders<
70
+ transport extends Transport,
71
+ chain extends Chain | undefined,
72
+ >(
73
+ client: Client<transport, chain>,
74
+ {
75
+ onBlockHeader,
76
+ onError,
77
+ }: HasTransportType<transport, 'webSocket' | 'ipc'> extends true
78
+ ? WatchBlockHeadersParameters<chain>
79
+ : never,
80
+ ): WatchBlockHeadersReturnType {
81
+ let prevBlockHeader: OnBlockHeaderParameter<chain> | undefined
82
+
83
+ const observerId = stringify(['watchBlockHeaders', client.uid])
84
+
85
+ return observe(observerId, { onBlockHeader, onError }, (emit) => {
86
+ let active = true
87
+ let subscribed = false
88
+ let unsubscribe = () => (active = false)
89
+ ;(async () => {
90
+ try {
91
+ const transport = (() => {
92
+ if (client.transport.type === 'fallback') {
93
+ const transport = client.transport.transports.find(
94
+ (transport: ReturnType<Transport>) =>
95
+ transport.config.type === 'webSocket' ||
96
+ transport.config.type === 'ipc',
97
+ )
98
+ if (!transport) return client.transport
99
+ return transport.value
100
+ }
101
+ return client.transport
102
+ })()
103
+
104
+ const { unsubscribe: unsubscribe_ } = await transport.subscribe({
105
+ params: ['newHeads'],
106
+ onData(data: any) {
107
+ if (!active) return
108
+ const blockHeader = (
109
+ client.chain?.formatters?.block?.format || formatBlock
110
+ )(data.result, 'watchBlockHeaders')
111
+ for (const field of blockFields) delete blockHeader[field]
112
+ emit.onBlockHeader(
113
+ blockHeader as OnBlockHeaderParameter<chain>,
114
+ prevBlockHeader,
115
+ )
116
+ prevBlockHeader = blockHeader as OnBlockHeaderParameter<chain>
117
+ },
118
+ onError(error: Error) {
119
+ if (subscribed) emit.onError?.(error)
120
+ },
121
+ })
122
+ subscribed = true
123
+ unsubscribe = unsubscribe_
124
+ if (!active) unsubscribe()
125
+ } catch (err) {
126
+ emit.onError?.(err as Error)
127
+ }
128
+ })()
129
+ return () => unsubscribe()
130
+ })
131
+ }
@@ -240,6 +240,11 @@ import {
240
240
  type WaitForTransactionReceiptReturnType,
241
241
  waitForTransactionReceipt,
242
242
  } from '../../actions/public/waitForTransactionReceipt.js'
243
+ import {
244
+ type WatchBlockHeadersParameters,
245
+ type WatchBlockHeadersReturnType,
246
+ watchBlockHeaders,
247
+ } from '../../actions/public/watchBlockHeaders.js'
243
248
  import {
244
249
  type WatchBlockNumberParameters,
245
250
  type WatchBlockNumberReturnType,
@@ -305,6 +310,7 @@ import type {
305
310
  } from '../../types/contract.js'
306
311
  import type { FeeValuesType } from '../../types/fee.js'
307
312
  import type { FilterType } from '../../types/filter.js'
313
+ import type { HasTransportType } from '../../types/transport.js'
308
314
  import { bindActionDecorators, type Client } from '../createClient.js'
309
315
  import type { Transport } from '../transports/createTransport.js'
310
316
 
@@ -1988,6 +1994,32 @@ export type PublicActions<
1988
1994
  watchBlockNumber: (
1989
1995
  args: WatchBlockNumberParameters,
1990
1996
  ) => WatchBlockNumberReturnType
1997
+ /**
1998
+ * Watches and returns incoming block headers without fetching full blocks.
1999
+ *
2000
+ * - Docs: https://viem.sh/docs/actions/public/watchBlockHeaders
2001
+ * - JSON-RPC Methods: Uses a WebSocket or IPC subscription via [`eth_subscribe`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_subscribe) and the `"newHeads"` event.
2002
+ *
2003
+ * @param args - {@link WatchBlockHeadersParameters}
2004
+ * @returns A function that can be invoked to stop watching for new block headers. {@link WatchBlockHeadersReturnType}
2005
+ *
2006
+ * @example
2007
+ * import { createPublicClient, webSocket } from 'viem'
2008
+ * import { mainnet } from 'viem/chains'
2009
+ *
2010
+ * const client = createPublicClient({
2011
+ * chain: mainnet,
2012
+ * transport: webSocket(),
2013
+ * })
2014
+ * const unwatch = client.watchBlockHeaders({
2015
+ * onBlockHeader: (blockHeader) => console.log(blockHeader),
2016
+ * })
2017
+ */
2018
+ watchBlockHeaders: (
2019
+ args: HasTransportType<transport, 'webSocket' | 'ipc'> extends true
2020
+ ? WatchBlockHeadersParameters<chain>
2021
+ : never,
2022
+ ) => WatchBlockHeadersReturnType
1991
2023
  /**
1992
2024
  * Watches and returns information for incoming blocks.
1993
2025
  *
@@ -2337,6 +2369,7 @@ export function publicActions<
2337
2369
  uninstallFilter: (args) => uninstallFilter(client, args),
2338
2370
  waitForTransactionReceipt: (args) =>
2339
2371
  waitForTransactionReceipt(client, args),
2372
+ watchBlockHeaders: (args) => watchBlockHeaders(client, args),
2340
2373
  watchBlocks: (args) => watchBlocks(client, args),
2341
2374
  watchBlockNumber: (args) => watchBlockNumber(client, args),
2342
2375
  watchContractEvent: (args) => watchContractEvent(client, args),
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.55.13'
1
+ export const version = '2.55.16'
package/index.ts CHANGED
@@ -320,6 +320,14 @@ export type {
320
320
  WaitForTransactionReceiptParameters,
321
321
  WaitForTransactionReceiptReturnType,
322
322
  } from './actions/public/waitForTransactionReceipt.js'
323
+ export type {
324
+ BlockHeader,
325
+ OnBlockHeader,
326
+ OnBlockHeaderParameter,
327
+ WatchBlockHeadersErrorType,
328
+ WatchBlockHeadersParameters,
329
+ WatchBlockHeadersReturnType,
330
+ } from './actions/public/watchBlockHeaders.js'
323
331
  export type {
324
332
  OnBlockNumberFn,
325
333
  OnBlockNumberParameter,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "viem",
3
3
  "description": "TypeScript Interface for Ethereum",
4
- "version": "2.55.13",
4
+ "version": "2.55.16",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -1051,6 +1051,7 @@ export namespace privateDeposit {
1051
1051
  memo: returnMemo,
1052
1052
  portalAddress: config.zonePortal,
1053
1053
  recipient,
1054
+ sender: gateway,
1054
1055
  zoneId: config.zoneId,
1055
1056
  })
1056
1057
  const shareAmountMin = resolveMinimumShareAmount(parameters)
@@ -2429,6 +2430,7 @@ export namespace privateRedeem {
2429
2430
  memo: returnMemo,
2430
2431
  portalAddress: config.zonePortal,
2431
2432
  recipient,
2433
+ sender: gateway,
2432
2434
  zoneId: config.zoneId,
2433
2435
  }),
2434
2436
  (async () => {
@@ -28,7 +28,12 @@ import type { Transport } from '../../clients/transports/createTransport.js'
28
28
  import { zeroHash } from '../../constants/bytes.js'
29
29
  import type { BaseErrorType } from '../../errors/base.js'
30
30
  import type { Chain, GetChainParameter } from '../../types/chain.js'
31
- import type { Compute, UnionOmit } from '../../types/utils.js'
31
+ import type {
32
+ Compute,
33
+ IsUndefined,
34
+ MaybeRequired,
35
+ UnionOmit,
36
+ } from '../../types/utils.js'
32
37
  import { parseEventLogs } from '../../utils/abi/parseEventLogs.js'
33
38
  import type { RequestErrorType } from '../../utils/buildRequest.js'
34
39
  import { type ObserveErrorType, observe } from '../../utils/observe.js'
@@ -81,6 +86,8 @@ export type PreparedEncryptedDeposit = {
81
86
  keyIndex: bigint
82
87
  /** Zone portal address on the parent chain. */
83
88
  portalAddress: Address
89
+ /** Address that will call the Zone portal. */
90
+ sender: Address
84
91
  /** Token address or ID to deposit. */
85
92
  token: TokenId.TokenIdOrAddress
86
93
  /** Zone ID (e.g. `7`). */
@@ -96,6 +103,8 @@ export type PreparedEncryptedDepositRecipient = {
96
103
  keyIndex: bigint
97
104
  /** Zone portal address on the parent chain. */
98
105
  portalAddress: Address
106
+ /** Address that will call the Zone portal. */
107
+ sender: Address
99
108
  /** Zone ID (e.g. `7`). */
100
109
  zoneId: number
101
110
  }
@@ -494,6 +503,7 @@ export async function encryptedDeposit<
494
503
  memo: parameters.memo,
495
504
  portalAddress: parameters.portalAddress,
496
505
  recipient,
506
+ sender: account_.address,
497
507
  token: parameters.token,
498
508
  zoneId: parameters.zoneId,
499
509
  })
@@ -573,6 +583,7 @@ export namespace encryptedDeposit {
573
583
  * amount: 1_000_000n,
574
584
  * bouncebackRecipient: '0x...',
575
585
  * recipient: '0x...',
586
+ * sender: '0x...',
576
587
  * zoneId: 7,
577
588
  * })
578
589
  * ```
@@ -586,7 +597,7 @@ export namespace encryptedDeposit {
586
597
  account extends Account | undefined,
587
598
  >(
588
599
  client: Client<Transport, chain, account>,
589
- parameters: prepare.Parameters,
600
+ parameters: prepare.Parameters<account>,
590
601
  ): Promise<prepare.ReturnValue> {
591
602
  const chainId = client.chain?.id
592
603
  if (!chainId) throw new Error('`chain` is required.')
@@ -597,10 +608,13 @@ export namespace encryptedDeposit {
597
608
  memo,
598
609
  portalAddress: portalAddress_,
599
610
  recipient,
611
+ sender: sender_ = client.account?.address,
600
612
  token,
601
613
  zoneId,
602
614
  ...rest
603
615
  } = parameters
616
+ if (!sender_) throw new Error('`sender` is required.')
617
+ const sender = sender_
604
618
  const portalAddress = portalAddress_ ?? getPortalAddress(chainId, zoneId)
605
619
 
606
620
  const { keyIndex, publicKey } = await getEncryptionKey(client, {
@@ -612,6 +626,7 @@ export namespace encryptedDeposit {
612
626
  const encrypted = await encryptDepositPayload(
613
627
  publicKey,
614
628
  recipient,
629
+ sender,
615
630
  portalAddress,
616
631
  keyIndex,
617
632
  memo,
@@ -624,13 +639,24 @@ export namespace encryptedDeposit {
624
639
  encrypted,
625
640
  keyIndex,
626
641
  portalAddress,
642
+ sender,
627
643
  token,
628
644
  zoneId,
629
645
  }
630
646
  }
631
647
 
632
648
  export namespace prepare {
633
- export type Parameters = ReadParameters & Args
649
+ export type Parameters<
650
+ account extends Account | undefined = Account | undefined,
651
+ > = ReadParameters &
652
+ Omit<Args, 'sender'> &
653
+ MaybeRequired<
654
+ {
655
+ /** Address that will call the Zone portal. @default `client.account.address` */
656
+ sender?: Address | undefined
657
+ },
658
+ IsUndefined<account>
659
+ >
634
660
 
635
661
  export type Args = {
636
662
  /** Amount of tokens to deposit. */
@@ -643,6 +669,8 @@ export namespace encryptedDeposit {
643
669
  portalAddress?: Address | undefined
644
670
  /** Recipient address in the zone. */
645
671
  recipient: Address
672
+ /** Address that will call the Zone portal. */
673
+ sender: Address
646
674
  /** Token address or ID to deposit. */
647
675
  token: TokenId.TokenIdOrAddress
648
676
  /** Zone ID (e.g. `7`). */
@@ -674,6 +702,7 @@ export namespace encryptedDeposit {
674
702
  *
675
703
  * const recipient = await Actions.zone.encryptedDeposit.prepareRecipient(client, {
676
704
  * recipient: '0x...',
705
+ * sender: '0x...',
677
706
  * zoneId: 7,
678
707
  * })
679
708
  * ```
@@ -687,7 +716,7 @@ export namespace encryptedDeposit {
687
716
  account extends Account | undefined,
688
717
  >(
689
718
  client: Client<Transport, chain, account>,
690
- parameters: prepareRecipient.Parameters,
719
+ parameters: prepareRecipient.Parameters<account>,
691
720
  ): Promise<prepareRecipient.ReturnValue> {
692
721
  const chainId = client.chain?.id
693
722
  if (!chainId) throw new Error('`chain` is required.')
@@ -696,9 +725,12 @@ export namespace encryptedDeposit {
696
725
  memo,
697
726
  portalAddress: portalAddress_,
698
727
  recipient,
728
+ sender: sender_ = client.account?.address,
699
729
  zoneId,
700
730
  ...rest
701
731
  } = parameters
732
+ if (!sender_) throw new Error('`sender` is required.')
733
+ const sender = sender_
702
734
  const portalAddress = portalAddress_ ?? getPortalAddress(chainId, zoneId)
703
735
  const { keyIndex, publicKey } = await getEncryptionKey(client, {
704
736
  ...rest,
@@ -708,6 +740,7 @@ export namespace encryptedDeposit {
708
740
  const encrypted = await encryptDepositPayload(
709
741
  publicKey,
710
742
  recipient,
743
+ sender,
711
744
  portalAddress,
712
745
  keyIndex,
713
746
  memo,
@@ -718,12 +751,23 @@ export namespace encryptedDeposit {
718
751
  encrypted,
719
752
  keyIndex,
720
753
  portalAddress,
754
+ sender,
721
755
  zoneId,
722
756
  }
723
757
  }
724
758
 
725
759
  export namespace prepareRecipient {
726
- export type Parameters = ReadParameters & Args
760
+ export type Parameters<
761
+ account extends Account | undefined = Account | undefined,
762
+ > = ReadParameters &
763
+ Omit<Args, 'sender'> &
764
+ MaybeRequired<
765
+ {
766
+ /** Address that will call the Zone portal. @default `client.account.address` */
767
+ sender?: Address | undefined
768
+ },
769
+ IsUndefined<account>
770
+ >
727
771
 
728
772
  export type Args = {
729
773
  /** Optional deposit memo. @default `0x00...00` */
@@ -732,6 +776,8 @@ export namespace encryptedDeposit {
732
776
  portalAddress?: Address | undefined
733
777
  /** Recipient address in the zone. */
734
778
  recipient: Address
779
+ /** Address that will call the Zone portal. */
780
+ sender: Address
735
781
  /** Zone ID (e.g. `7`). */
736
782
  zoneId: number
737
783
  }
@@ -864,6 +910,7 @@ export async function encryptedDepositSync<
864
910
  memo: parameters.memo,
865
911
  portalAddress: parameters.portalAddress,
866
912
  recipient,
913
+ sender: account_.address,
867
914
  token: parameters.token,
868
915
  zoneId: parameters.zoneId,
869
916
  })
@@ -1883,6 +1930,7 @@ export namespace signAuthorizationToken {
1883
1930
  async function encryptDepositPayload(
1884
1931
  publicKey: { prefix: 2 | 3; x: Hex.Hex },
1885
1932
  recipient: Address,
1933
+ sender: Address,
1886
1934
  portalAddress: Address,
1887
1935
  keyIndex: bigint,
1888
1936
  memo: Hex.Hex = zeroHash,
@@ -1918,6 +1966,7 @@ async function encryptDepositPayload(
1918
1966
  portalAddress,
1919
1967
  keyIndex,
1920
1968
  Hex.fromNumber(compressedEphemeral.x, { size: 32 }),
1969
+ sender,
1921
1970
  ) as BufferSource,
1922
1971
  },
1923
1972
  hkdfKey,
@@ -1961,11 +2010,13 @@ function buildDepositHkdfInfo(
1961
2010
  portalAddress: Address,
1962
2011
  keyIndex: bigint,
1963
2012
  ephemeralPubkeyX: Hex.Hex,
2013
+ sender: Address,
1964
2014
  ): Bytes.Bytes {
1965
2015
  return Bytes.concat(
1966
2016
  Bytes.from(portalAddress),
1967
2017
  Bytes.fromNumber(keyIndex, { size: 32 }),
1968
2018
  Bytes.from(ephemeralPubkeyX),
2019
+ Bytes.from(sender),
1969
2020
  )
1970
2021
  }
1971
2022
 
@@ -3,6 +3,21 @@ import type { Address } from 'abitype'
3
3
  import type { ExactPartial, Prettify } from '../../types/utils.js'
4
4
  import type { SiweMessage } from './types.js'
5
5
 
6
+ const siweDateTimeRegex =
7
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/
8
+
9
+ function isValidSiweDateTime(value: string): boolean {
10
+ if (!siweDateTimeRegex.test(value)) return false
11
+ return !Number.isNaN(new Date(value).getTime())
12
+ }
13
+
14
+ function parseSiweDateTime(value: string): Date {
15
+ // Reject non-RFC-3339 / non-EIP-4361 date-time strings before Date coercion
16
+ // (JS Date accepts many non-SIWE forms that would otherwise look "valid").
17
+ if (!isValidSiweDateTime(value)) return new Date(Number.NaN)
18
+ return new Date(value)
19
+ }
20
+
6
21
  /**
7
22
  * @description Parses EIP-4361 formatted message into message fields object.
8
23
  *
@@ -36,9 +51,11 @@ export function parseSiweMessage(
36
51
  ...prefix,
37
52
  ...suffix,
38
53
  ...(chainId ? { chainId: Number(chainId) } : {}),
39
- ...(expirationTime ? { expirationTime: new Date(expirationTime) } : {}),
40
- ...(issuedAt ? { issuedAt: new Date(issuedAt) } : {}),
41
- ...(notBefore ? { notBefore: new Date(notBefore) } : {}),
54
+ ...(expirationTime
55
+ ? { expirationTime: parseSiweDateTime(expirationTime) }
56
+ : {}),
57
+ ...(issuedAt ? { issuedAt: parseSiweDateTime(issuedAt) } : {}),
58
+ ...(notBefore ? { notBefore: parseSiweDateTime(notBefore) } : {}),
42
59
  ...(requestId ? { requestId } : {}),
43
60
  ...(resources ? { resources } : {}),
44
61
  ...(scheme ? { scheme } : {}),
@@ -57,8 +57,19 @@ export function validateSiweMessage(
57
57
  if (nonce && message.nonce !== nonce) return false
58
58
  if (scheme && message.scheme !== scheme) return false
59
59
 
60
- if (message.expirationTime && time >= message.expirationTime) return false
61
- if (message.notBefore && time < message.notBefore) return false
60
+ // Invalid `time` makes both lifetime comparisons false in JS; reject first.
61
+ if (Number.isNaN(time.getTime())) return false
62
+
63
+ // Invalid Date (e.g. non-RFC-3339 SIWE timestamp coerced at parse) is truthy;
64
+ // comparisons against it are always false and previously skipped checks.
65
+ if (message.expirationTime) {
66
+ if (Number.isNaN(message.expirationTime.getTime())) return false
67
+ if (time >= message.expirationTime) return false
68
+ }
69
+ if (message.notBefore) {
70
+ if (Number.isNaN(message.notBefore.getTime())) return false
71
+ if (time < message.notBefore) return false
72
+ }
62
73
 
63
74
  try {
64
75
  if (!message.address) return false