viem 2.11.0 → 2.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +3 -3
  3. package/_cjs/actions/public/verifyMessage.js.map +1 -1
  4. package/_cjs/actions/public/waitForTransactionReceipt.js +9 -3
  5. package/_cjs/actions/public/waitForTransactionReceipt.js.map +1 -1
  6. package/_cjs/actions/siwe/verifySiweMessage.js +32 -0
  7. package/_cjs/actions/siwe/verifySiweMessage.js.map +1 -0
  8. package/_cjs/chains/definitions/flowPreviewnet.js +6 -0
  9. package/_cjs/chains/definitions/flowPreviewnet.js.map +1 -1
  10. package/_cjs/chains/definitions/jbcTestnet.js +0 -4
  11. package/_cjs/chains/definitions/jbcTestnet.js.map +1 -1
  12. package/_cjs/chains/definitions/l3x.js +24 -0
  13. package/_cjs/chains/definitions/l3x.js.map +1 -0
  14. package/_cjs/chains/definitions/l3xTestnet.js +24 -0
  15. package/_cjs/chains/definitions/l3xTestnet.js.map +1 -0
  16. package/_cjs/chains/definitions/metis.js +5 -0
  17. package/_cjs/chains/definitions/metis.js.map +1 -1
  18. package/_cjs/chains/definitions/thaiChain.js +29 -0
  19. package/_cjs/chains/definitions/thaiChain.js.map +1 -0
  20. package/_cjs/chains/index.js +10 -4
  21. package/_cjs/chains/index.js.map +1 -1
  22. package/_cjs/clients/decorators/public.js +2 -0
  23. package/_cjs/clients/decorators/public.js.map +1 -1
  24. package/_cjs/errors/siwe.js +22 -0
  25. package/_cjs/errors/siwe.js.map +1 -0
  26. package/_cjs/errors/version.js +1 -1
  27. package/_cjs/siwe/index.js +16 -0
  28. package/_cjs/siwe/index.js.map +1 -0
  29. package/_cjs/utils/siwe/createSiweMessage.js +122 -0
  30. package/_cjs/utils/siwe/createSiweMessage.js.map +1 -0
  31. package/_cjs/utils/siwe/generateSiweNonce.js +9 -0
  32. package/_cjs/utils/siwe/generateSiweNonce.js.map +1 -0
  33. package/_cjs/utils/siwe/parseSiweMessage.js +25 -0
  34. package/_cjs/utils/siwe/parseSiweMessage.js.map +1 -0
  35. package/_cjs/utils/siwe/types.js +3 -0
  36. package/_cjs/utils/siwe/types.js.map +1 -0
  37. package/_cjs/utils/siwe/utils.js +44 -0
  38. package/_cjs/utils/siwe/utils.js.map +1 -0
  39. package/_cjs/utils/siwe/validateSiweMessage.js +29 -0
  40. package/_cjs/utils/siwe/validateSiweMessage.js.map +1 -0
  41. package/_esm/actions/public/verifyMessage.js.map +1 -1
  42. package/_esm/actions/public/waitForTransactionReceipt.js +9 -3
  43. package/_esm/actions/public/waitForTransactionReceipt.js.map +1 -1
  44. package/_esm/actions/siwe/verifySiweMessage.js +39 -0
  45. package/_esm/actions/siwe/verifySiweMessage.js.map +1 -0
  46. package/_esm/chains/definitions/flowPreviewnet.js +6 -0
  47. package/_esm/chains/definitions/flowPreviewnet.js.map +1 -1
  48. package/_esm/chains/definitions/jbcTestnet.js +0 -4
  49. package/_esm/chains/definitions/jbcTestnet.js.map +1 -1
  50. package/_esm/chains/definitions/l3x.js +21 -0
  51. package/_esm/chains/definitions/l3x.js.map +1 -0
  52. package/_esm/chains/definitions/l3xTestnet.js +21 -0
  53. package/_esm/chains/definitions/l3xTestnet.js.map +1 -0
  54. package/_esm/chains/definitions/metis.js +5 -0
  55. package/_esm/chains/definitions/metis.js.map +1 -1
  56. package/_esm/chains/definitions/thaiChain.js +26 -0
  57. package/_esm/chains/definitions/thaiChain.js.map +1 -0
  58. package/_esm/chains/index.js +3 -0
  59. package/_esm/chains/index.js.map +1 -1
  60. package/_esm/clients/decorators/public.js +2 -0
  61. package/_esm/clients/decorators/public.js.map +1 -1
  62. package/_esm/errors/siwe.js +18 -0
  63. package/_esm/errors/siwe.js.map +1 -0
  64. package/_esm/errors/version.js +1 -1
  65. package/_esm/siwe/index.js +7 -0
  66. package/_esm/siwe/index.js.map +1 -0
  67. package/_esm/utils/siwe/createSiweMessage.js +137 -0
  68. package/_esm/utils/siwe/createSiweMessage.js.map +1 -0
  69. package/_esm/utils/siwe/generateSiweNonce.js +15 -0
  70. package/_esm/utils/siwe/generateSiweNonce.js.map +1 -0
  71. package/_esm/utils/siwe/parseSiweMessage.js +30 -0
  72. package/_esm/utils/siwe/parseSiweMessage.js.map +1 -0
  73. package/_esm/utils/siwe/types.js +2 -0
  74. package/_esm/utils/siwe/types.js.map +1 -0
  75. package/_esm/utils/siwe/utils.js +49 -0
  76. package/_esm/utils/siwe/utils.js.map +1 -0
  77. package/_esm/utils/siwe/validateSiweMessage.js +30 -0
  78. package/_esm/utils/siwe/validateSiweMessage.js.map +1 -0
  79. package/_types/actions/public/verifyMessage.d.ts +3 -2
  80. package/_types/actions/public/verifyMessage.d.ts.map +1 -1
  81. package/_types/actions/public/waitForTransactionReceipt.d.ts.map +1 -1
  82. package/_types/actions/siwe/verifySiweMessage.d.ts +34 -0
  83. package/_types/actions/siwe/verifySiweMessage.d.ts.map +1 -0
  84. package/_types/chains/definitions/flowPreviewnet.d.ts +6 -8
  85. package/_types/chains/definitions/flowPreviewnet.d.ts.map +1 -1
  86. package/_types/chains/definitions/jbcTestnet.d.ts +0 -4
  87. package/_types/chains/definitions/jbcTestnet.d.ts.map +1 -1
  88. package/_types/chains/definitions/l3x.d.ts +37 -0
  89. package/_types/chains/definitions/l3x.d.ts.map +1 -0
  90. package/_types/chains/definitions/l3xTestnet.d.ts +37 -0
  91. package/_types/chains/definitions/l3xTestnet.d.ts.map +1 -0
  92. package/_types/chains/definitions/metis.d.ts +5 -0
  93. package/_types/chains/definitions/metis.d.ts.map +1 -1
  94. package/_types/chains/definitions/thaiChain.d.ts +34 -0
  95. package/_types/chains/definitions/thaiChain.d.ts.map +1 -0
  96. package/_types/chains/index.d.ts +3 -0
  97. package/_types/chains/index.d.ts.map +1 -1
  98. package/_types/clients/decorators/public.d.ts +30 -0
  99. package/_types/clients/decorators/public.d.ts.map +1 -1
  100. package/_types/errors/siwe.d.ts +13 -0
  101. package/_types/errors/siwe.d.ts.map +1 -0
  102. package/_types/errors/version.d.ts +1 -1
  103. package/_types/siwe/index.d.ts +8 -0
  104. package/_types/siwe/index.d.ts.map +1 -0
  105. package/_types/utils/siwe/createSiweMessage.d.ts +24 -0
  106. package/_types/utils/siwe/createSiweMessage.d.ts.map +1 -0
  107. package/_types/utils/siwe/generateSiweNonce.d.ts +12 -0
  108. package/_types/utils/siwe/generateSiweNonce.d.ts.map +1 -0
  109. package/_types/utils/siwe/parseSiweMessage.d.ts +11 -0
  110. package/_types/utils/siwe/parseSiweMessage.d.ts.map +1 -0
  111. package/_types/utils/siwe/types.d.ts +61 -0
  112. package/_types/utils/siwe/types.d.ts.map +1 -0
  113. package/_types/utils/siwe/utils.d.ts +2 -0
  114. package/_types/utils/siwe/utils.d.ts.map +1 -0
  115. package/_types/utils/siwe/validateSiweMessage.d.ts +39 -0
  116. package/_types/utils/siwe/validateSiweMessage.d.ts.map +1 -0
  117. package/actions/public/verifyMessage.ts +11 -8
  118. package/actions/public/waitForTransactionReceipt.ts +13 -4
  119. package/actions/siwe/verifySiweMessage.ts +90 -0
  120. package/chains/definitions/flowPreviewnet.ts +6 -0
  121. package/chains/definitions/jbcTestnet.ts +0 -4
  122. package/chains/definitions/l3x.ts +21 -0
  123. package/chains/definitions/l3xTestnet.ts +21 -0
  124. package/chains/definitions/metis.ts +6 -0
  125. package/chains/definitions/thaiChain.ts +26 -0
  126. package/chains/index.ts +3 -0
  127. package/clients/decorators/public.ts +37 -0
  128. package/errors/siwe.ts +20 -0
  129. package/errors/version.ts +1 -1
  130. package/package.json +9 -1
  131. package/siwe/index.ts +29 -0
  132. package/siwe/package.json +6 -0
  133. package/utils/siwe/createSiweMessage.ts +168 -0
  134. package/utils/siwe/generateSiweNonce.ts +15 -0
  135. package/utils/siwe/parseSiweMessage.ts +55 -0
  136. package/utils/siwe/types.ts +61 -0
  137. package/utils/siwe/utils.ts +51 -0
  138. package/utils/siwe/validateSiweMessage.ts +70 -0
@@ -3,15 +3,11 @@ import { defineChain } from '../../utils/chain/defineChain.js'
3
3
  export const jbcTestnet = /*#__PURE__*/ defineChain({
4
4
  id: 88991,
5
5
  name: 'Jibchain Testnet',
6
- network: 'jbcTestnet',
7
6
  nativeCurrency: { name: 'tJBC', symbol: 'tJBC', decimals: 18 },
8
7
  rpcUrls: {
9
8
  default: {
10
9
  http: ['https://rpc.testnet.jibchain.net'],
11
10
  },
12
- public: {
13
- http: ['https://rpc.testnet.jibchain.net'],
14
- },
15
11
  },
16
12
  blockExplorers: {
17
13
  default: {
@@ -0,0 +1,21 @@
1
+ import { defineChain } from '../../utils/chain/defineChain.js'
2
+
3
+ export const l3x = /*#__PURE__*/ defineChain({
4
+ id: 12324,
5
+ name: 'L3X Protocol',
6
+ nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
7
+ rpcUrls: {
8
+ default: {
9
+ http: ['https://rpc-mainnet.l3x.com'],
10
+ webSocket: ['wss://rpc-mainnet.l3x.com'],
11
+ },
12
+ },
13
+ blockExplorers: {
14
+ default: {
15
+ name: 'L3X Mainnet Explorer',
16
+ url: 'https://explorer.l3x.com',
17
+ apiUrl: 'https://explorer.l3x.com/api/v2',
18
+ },
19
+ },
20
+ testnet: false,
21
+ })
@@ -0,0 +1,21 @@
1
+ import { defineChain } from '../../utils/chain/defineChain.js'
2
+
3
+ export const l3xTestnet = /*#__PURE__*/ defineChain({
4
+ id: 12325,
5
+ name: 'L3X Protocol Testnet',
6
+ nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
7
+ rpcUrls: {
8
+ default: {
9
+ http: ['https://rpc-testnet.l3x.com'],
10
+ webSocket: ['wss://rpc-testnet.l3x.com'],
11
+ },
12
+ },
13
+ blockExplorers: {
14
+ default: {
15
+ name: 'L3X Testnet Explorer',
16
+ url: 'https://explorer-testnet.l3x.com',
17
+ apiUrl: 'https://explorer-testnet.l3x.com/api/v2',
18
+ },
19
+ },
20
+ testnet: true,
21
+ })
@@ -13,6 +13,12 @@ export const metis = /*#__PURE__*/ defineChain({
13
13
  },
14
14
  blockExplorers: {
15
15
  default: {
16
+ name: 'Metis Explorer',
17
+ url: 'https://explorer.metis.io',
18
+ apiUrl:
19
+ 'https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api',
20
+ },
21
+ blockscout: {
16
22
  name: 'Andromeda Explorer',
17
23
  url: 'https://andromeda-explorer.metis.io',
18
24
  apiUrl: 'https://andromeda-explorer.metis.io/api',
@@ -0,0 +1,26 @@
1
+ import { defineChain } from '../../utils/chain/defineChain.js'
2
+
3
+ export const thaiChain = /*#__PURE__*/ defineChain({
4
+ id: 7,
5
+ name: 'ThaiChain',
6
+ nativeCurrency: { name: 'TCH', symbol: 'TCH', decimals: 18 },
7
+ rpcUrls: {
8
+ default: {
9
+ http: ['hhttps://rpc.thaichain.org'],
10
+ },
11
+ },
12
+ blockExplorers: {
13
+ default: {
14
+ name: 'Blockscout',
15
+ url: 'https://exp.thaichain.org',
16
+ apiUrl: 'https://exp.thaichain.org/api',
17
+ },
18
+ },
19
+ contracts: {
20
+ multicall3: {
21
+ address: '0x0DaD6130e832c21719C5CE3bae93454E16A84826',
22
+ blockCreated: 4806386,
23
+ },
24
+ },
25
+ testnet: false,
26
+ })
package/chains/index.ts CHANGED
@@ -124,6 +124,8 @@ export { klaytn } from './definitions/klaytn.js'
124
124
  export { klaytnBaobab } from './definitions/klaytnBaobab.js'
125
125
  export { kroma } from './definitions/kroma.js'
126
126
  export { kromaSepolia } from './definitions/kromaSepolia.js'
127
+ export { l3x } from './definitions/l3x.js'
128
+ export { l3xTestnet } from './definitions/l3xTestnet.js'
127
129
  export { lightlinkPegasus } from './definitions/lightlinkPegasus.js'
128
130
  export { lightlinkPhoenix } from './definitions/lightlinkPhoenix.js'
129
131
  export { linea } from './definitions/linea.js'
@@ -246,6 +248,7 @@ export { telcoinTestnet } from './definitions/telcoinTestnet.js'
246
248
  export { telos } from './definitions/telos.js'
247
249
  export { telosTestnet } from './definitions/telosTestnet.js'
248
250
  export { tenet } from './definitions/tenet.js'
251
+ export { thaiChain } from './definitions/thaiChain.js'
249
252
  export { thunderTestnet } from './definitions/thunderTestnet.js'
250
253
  export { vechain } from './definitions/vechain.js'
251
254
  export { wanchain } from './definitions/wanchain.js'
@@ -220,6 +220,11 @@ import {
220
220
  type WatchPendingTransactionsReturnType,
221
221
  watchPendingTransactions,
222
222
  } from '../../actions/public/watchPendingTransactions.js'
223
+ import {
224
+ type VerifySiweMessageParameters,
225
+ type VerifySiweMessageReturnType,
226
+ verifySiweMessage,
227
+ } from '../../actions/siwe/verifySiweMessage.js'
223
228
  import {
224
229
  type PrepareTransactionRequestParameters,
225
230
  type PrepareTransactionRequestRequest,
@@ -1511,9 +1516,40 @@ export type PublicActions<
1511
1516
  accountOverride
1512
1517
  >
1513
1518
  >
1519
+ /**
1520
+ * Verify that a message was signed by the provided address.
1521
+ *
1522
+ * Compatible with Smart Contract Accounts & Externally Owned Accounts via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492).
1523
+ *
1524
+ * - Docs {@link https://viem.sh/docs/actions/public/verifyMessage}
1525
+ *
1526
+ * @param parameters - {@link VerifyMessageParameters}
1527
+ * @returns Whether or not the signature is valid. {@link VerifyMessageReturnType}
1528
+ */
1514
1529
  verifyMessage: (
1515
1530
  args: VerifyMessageParameters,
1516
1531
  ) => Promise<VerifyMessageReturnType>
1532
+ /**
1533
+ * Verifies [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) formatted message was signed.
1534
+ *
1535
+ * Compatible with Smart Contract Accounts & Externally Owned Accounts via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492).
1536
+ *
1537
+ * - Docs {@link https://viem.sh/docs/siwe/actions/verifySiweMessage}
1538
+ *
1539
+ * @param parameters - {@link VerifySiweMessageParameters}
1540
+ * @returns Whether or not the signature is valid. {@link VerifySiweMessageReturnType}
1541
+ */
1542
+ verifySiweMessage: (
1543
+ args: VerifySiweMessageParameters,
1544
+ ) => Promise<VerifySiweMessageReturnType>
1545
+ /**
1546
+ * Verify that typed data was signed by the provided address.
1547
+ *
1548
+ * - Docs {@link https://viem.sh/docs/actions/public/verifyTypedData}
1549
+ *
1550
+ * @param parameters - {@link VerifyTypedDataParameters}
1551
+ * @returns Whether or not the signature is valid. {@link VerifyTypedDataReturnType}
1552
+ */
1517
1553
  verifyTypedData: (
1518
1554
  args: VerifyTypedDataParameters,
1519
1555
  ) => Promise<VerifyTypedDataReturnType>
@@ -1807,6 +1843,7 @@ export function publicActions<
1807
1843
  sendRawTransaction: (args) => sendRawTransaction(client, args),
1808
1844
  simulateContract: (args) => simulateContract(client, args),
1809
1845
  verifyMessage: (args) => verifyMessage(client, args),
1846
+ verifySiweMessage: (args) => verifySiweMessage(client, args),
1810
1847
  verifyTypedData: (args) => verifyTypedData(client, args),
1811
1848
  uninstallFilter: (args) => uninstallFilter(client, args),
1812
1849
  waitForTransactionReceipt: (args) =>
package/errors/siwe.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { BaseError } from './base.js'
2
+
3
+ export type SiweInvalidMessageFieldErrorType = SiweInvalidMessageFieldError & {
4
+ name: 'SiweInvalidMessageFieldError'
5
+ }
6
+ export class SiweInvalidMessageFieldError extends BaseError {
7
+ override name = 'SiweInvalidMessageFieldError'
8
+ constructor(parameters: {
9
+ docsPath?: string | undefined
10
+ field: string
11
+ metaMessages?: string[] | undefined
12
+ }) {
13
+ const { docsPath, field, metaMessages } = parameters
14
+ super(`Invalid Sign-In with Ethereum message field "${field}".`, {
15
+ docsPath,
16
+ docsSlug: 'TODO',
17
+ metaMessages,
18
+ })
19
+ }
20
+ }
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.11.0'
1
+ export const version = '2.12.0'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "viem",
3
3
  "description": "TypeScript Interface for Ethereum",
4
- "version": "2.11.0",
4
+ "version": "2.12.0",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -68,6 +68,11 @@
68
68
  "import": "./_esm/op-stack/index.js",
69
69
  "default": "./_cjs/op-stack/index.js"
70
70
  },
71
+ "./siwe": {
72
+ "types": "./_types/siwe/index.d.ts",
73
+ "import": "./_esm/siwe/index.js",
74
+ "default": "./_cjs/siwe/index.js"
75
+ },
71
76
  "./utils": {
72
77
  "types": "./_types/utils/index.d.ts",
73
78
  "import": "./_esm/utils/index.js",
@@ -114,6 +119,9 @@
114
119
  "op-stack": [
115
120
  "./_types/op-stack/index.d.ts"
116
121
  ],
122
+ "siwe": [
123
+ "./_types/siwe/index.d.ts"
124
+ ],
117
125
  "utils": [
118
126
  "./_types/utils/index.d.ts"
119
127
  ],
package/siwe/index.ts ADDED
@@ -0,0 +1,29 @@
1
+ export {
2
+ verifySiweMessage,
3
+ type VerifySiweMessageParameters,
4
+ type VerifySiweMessageReturnType,
5
+ type VerifySiweMessageErrorType,
6
+ } from '../actions/siwe/verifySiweMessage.js'
7
+
8
+ export {
9
+ createSiweMessage,
10
+ type CreateSiweMessageParameters,
11
+ type CreateSiweMessageReturnType,
12
+ type CreateSiweMessageErrorType,
13
+ } from '../utils/siwe/createSiweMessage.js'
14
+
15
+ export { generateSiweNonce } from '../utils/siwe/generateSiweNonce.js'
16
+ export { parseSiweMessage } from '../utils/siwe/parseSiweMessage.js'
17
+
18
+ export {
19
+ validateSiweMessage,
20
+ type ValidateSiweMessageParameters,
21
+ type ValidateSiweMessageReturnType,
22
+ } from '../utils/siwe/validateSiweMessage.js'
23
+
24
+ export type { SiweMessage } from '../utils/siwe/types.js'
25
+
26
+ export {
27
+ type SiweInvalidMessageFieldErrorType,
28
+ SiweInvalidMessageFieldError,
29
+ } from '../errors/siwe.js'
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "module",
3
+ "types": "../_types/siwe/index.d.ts",
4
+ "module": "../_esm/siwe/index.js",
5
+ "main": "../_cjs/siwe/index.js"
6
+ }
@@ -0,0 +1,168 @@
1
+ import {
2
+ SiweInvalidMessageFieldError,
3
+ type SiweInvalidMessageFieldErrorType,
4
+ } from '../../errors/siwe.js'
5
+ import type { ErrorType } from '../../errors/utils.js'
6
+ import { type GetAddressErrorType, getAddress } from '../address/getAddress.js'
7
+ import type { SiweMessage } from './types.js'
8
+ import { isUri } from './utils.js'
9
+
10
+ export type CreateSiweMessageParameters = SiweMessage
11
+
12
+ export type CreateSiweMessageReturnType = string
13
+
14
+ export type CreateSiweMessageErrorType =
15
+ | GetAddressErrorType
16
+ | SiweInvalidMessageFieldErrorType
17
+ | ErrorType
18
+
19
+ /**
20
+ * @description Creates EIP-4361 formatted message.
21
+ *
22
+ * @example
23
+ * const message = createMessage({
24
+ * address: '0xA0Cf798816D4b9b9866b5330EEa46a18382f251e',
25
+ * chainId: 1,
26
+ * domain: 'example.com',
27
+ * nonce: 'foobarbaz',
28
+ * uri: 'https://example.com/path',
29
+ * version: '1',
30
+ * })
31
+ *
32
+ * @see https://eips.ethereum.org/EIPS/eip-4361
33
+ */
34
+ export function createSiweMessage(
35
+ parameters: CreateSiweMessageParameters,
36
+ ): CreateSiweMessageReturnType {
37
+ const {
38
+ chainId,
39
+ domain,
40
+ expirationTime,
41
+ issuedAt = new Date(),
42
+ nonce,
43
+ notBefore,
44
+ requestId,
45
+ resources,
46
+ scheme,
47
+ uri,
48
+ version,
49
+ } = parameters
50
+
51
+ // Validate fields
52
+ {
53
+ // Required fields
54
+ if (chainId !== Math.floor(chainId))
55
+ throw new SiweInvalidMessageFieldError({
56
+ field: 'chainId',
57
+ metaMessages: [
58
+ '- Chain ID must be a EIP-155 chain ID.',
59
+ '- See https://eips.ethereum.org/EIPS/eip-155',
60
+ '',
61
+ `Provided value: ${chainId}`,
62
+ ],
63
+ })
64
+ if (!domainRegex.test(domain))
65
+ throw new SiweInvalidMessageFieldError({
66
+ field: 'domain',
67
+ metaMessages: [
68
+ '- Domain must be an RFC 3986 authority.',
69
+ '- See https://www.rfc-editor.org/rfc/rfc3986',
70
+ '',
71
+ `Provided value: ${domain}`,
72
+ ],
73
+ })
74
+ if (!nonceRegex.test(nonce))
75
+ throw new SiweInvalidMessageFieldError({
76
+ field: 'nonce',
77
+ metaMessages: [
78
+ '- Nonce must be at least 8 characters.',
79
+ '- Nonce must be alphanumeric.',
80
+ '',
81
+ `Provided value: ${nonce}`,
82
+ ],
83
+ })
84
+ if (!isUri(uri))
85
+ throw new SiweInvalidMessageFieldError({
86
+ field: 'uri',
87
+ metaMessages: [
88
+ '- URI must be a RFC 3986 URI referring to the resource that is the subject of the signing.',
89
+ '- See https://www.rfc-editor.org/rfc/rfc3986',
90
+ '',
91
+ `Provided value: ${uri}`,
92
+ ],
93
+ })
94
+ if (version !== '1')
95
+ throw new SiweInvalidMessageFieldError({
96
+ field: 'version',
97
+ metaMessages: [
98
+ "- Version must be '1'.",
99
+ '',
100
+ `Provided value: ${version}`,
101
+ ],
102
+ })
103
+
104
+ // Optional fields
105
+ if (scheme && !schemeRegex.test(scheme))
106
+ throw new SiweInvalidMessageFieldError({
107
+ field: 'scheme',
108
+ metaMessages: [
109
+ '- Scheme must be an RFC 3986 URI scheme.',
110
+ '- See https://www.rfc-editor.org/rfc/rfc3986#section-3.1',
111
+ '',
112
+ `Provided value: ${scheme}`,
113
+ ],
114
+ })
115
+ const statement = parameters.statement
116
+ if (statement?.includes('\n'))
117
+ throw new SiweInvalidMessageFieldError({
118
+ field: 'statement',
119
+ metaMessages: [
120
+ "- Statement must not include '\\n'.",
121
+ '',
122
+ `Provided value: ${statement}`,
123
+ ],
124
+ })
125
+ }
126
+
127
+ // Construct message
128
+ const address = getAddress(parameters.address)
129
+ const origin = (() => {
130
+ if (scheme) return `${scheme}://${domain}`
131
+ return domain
132
+ })()
133
+ const statement = (() => {
134
+ if (!parameters.statement) return ''
135
+ return `\n${parameters.statement}\n`
136
+ })()
137
+ const prefix = `${origin} wants you to sign in with your Ethereum account:\n${address}\n${statement}`
138
+
139
+ let suffix = `URI: ${uri}\nVersion: ${version}\nChain ID: ${chainId}\nNonce: ${nonce}\nIssued At: ${issuedAt.toISOString()}`
140
+
141
+ if (expirationTime)
142
+ suffix += `\nExpiration Time: ${expirationTime.toISOString()}`
143
+ if (notBefore) suffix += `\nNot Before: ${notBefore.toISOString()}`
144
+ if (requestId) suffix += `\nRequest ID: ${requestId}`
145
+ if (resources) {
146
+ let content = '\nResources:'
147
+ for (const resource of resources) {
148
+ if (!isUri(resource))
149
+ throw new SiweInvalidMessageFieldError({
150
+ field: 'resources',
151
+ metaMessages: [
152
+ '- Every resource must be a RFC 3986 URI.',
153
+ '- See https://www.rfc-editor.org/rfc/rfc3986',
154
+ '',
155
+ `Provided value: ${resource}`,
156
+ ],
157
+ })
158
+ content += `\n- ${resource}`
159
+ }
160
+ suffix += content
161
+ }
162
+
163
+ return `${prefix}\n${suffix}`
164
+ }
165
+
166
+ const domainRegex = /^(?:(?:(?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,63})$/
167
+ const nonceRegex = /^[a-zA-Z0-9]{8,}$/
168
+ const schemeRegex = /^([a-zA-Z][a-zA-Z0-9+-.]*)$/
@@ -0,0 +1,15 @@
1
+ import { uid } from '../../utils/uid.js'
2
+
3
+ /**
4
+ * @description Generates random EIP-4361 nonce.
5
+ *
6
+ * @example
7
+ * const nonce = generateNonce()
8
+ *
9
+ * @see https://eips.ethereum.org/EIPS/eip-4361
10
+ *
11
+ * @returns A randomly generated EIP-4361 nonce.
12
+ */
13
+ export function generateSiweNonce(): string {
14
+ return uid(96)
15
+ }
@@ -0,0 +1,55 @@
1
+ import type { Address } from 'abitype'
2
+
3
+ import type { ExactPartial, Prettify } from '../../types/utils.js'
4
+ import type { SiweMessage } from './types.js'
5
+
6
+ /**
7
+ * @description Parses EIP-4361 formatted message into message fields object.
8
+ *
9
+ * @see https://eips.ethereum.org/EIPS/eip-4361
10
+ *
11
+ * @returns EIP-4361 fields object
12
+ */
13
+ export function parseSiweMessage(
14
+ message: string,
15
+ ): Prettify<ExactPartial<SiweMessage>> {
16
+ const { scheme, statement, ...prefix } = (message.match(prefixRegex)
17
+ ?.groups ?? {}) as {
18
+ address: Address
19
+ domain: string
20
+ scheme?: string
21
+ statement?: string
22
+ }
23
+ const { chainId, expirationTime, issuedAt, notBefore, requestId, ...suffix } =
24
+ (message.match(suffixRegex)?.groups ?? {}) as {
25
+ chainId: string
26
+ expirationTime?: string
27
+ issuedAt?: string
28
+ nonce: string
29
+ notBefore?: string
30
+ requestId?: string
31
+ uri: string
32
+ version: '1'
33
+ }
34
+ const resources = message.split('Resources:')[1]?.split('\n- ').slice(1)
35
+ return {
36
+ ...prefix,
37
+ ...suffix,
38
+ ...(chainId ? { chainId: Number(chainId) } : {}),
39
+ ...(expirationTime ? { expirationTime: new Date(expirationTime) } : {}),
40
+ ...(issuedAt ? { issuedAt: new Date(issuedAt) } : {}),
41
+ ...(notBefore ? { notBefore: new Date(notBefore) } : {}),
42
+ ...(requestId ? { requestId } : {}),
43
+ ...(resources ? { resources } : {}),
44
+ ...(scheme ? { scheme } : {}),
45
+ ...(statement ? { statement } : {}),
46
+ }
47
+ }
48
+
49
+ // https://regexr.com/80gdj
50
+ const prefixRegex =
51
+ /^(?:(?<scheme>[a-zA-Z][a-zA-Z0-9+-.]*):\/\/)?(?<domain>[a-zA-Z0-9+-.]*) (?:wants you to sign in with your Ethereum account:\n)(?<address>0x[a-fA-F0-9]{40})\n\n(?:(?<statement>.*)\n\n)?/
52
+
53
+ // https://regexr.com/80gf9
54
+ const suffixRegex =
55
+ /(?:URI: (?<uri>.+))\n(?:Version: (?<version>.+))\n(?:Chain ID: (?<chainId>\d+))\n(?:Nonce: (?<nonce>[a-zA-Z0-9]+))\n(?:Issued At: (?<issuedAt>.+))(?:\nExpiration Time: (?<expirationTime>.+))?(?:\nNot Before: (?<notBefore>.+))?(?:\nRequest ID: (?<requestId>.+))?/
@@ -0,0 +1,61 @@
1
+ import type { Address } from 'abitype'
2
+
3
+ /**
4
+ * @description EIP-4361 message fields
5
+ *
6
+ * @see https://eips.ethereum.org/EIPS/eip-4361
7
+ */
8
+ export type SiweMessage = {
9
+ /**
10
+ * The Ethereum address performing the signing.
11
+ */
12
+ address: Address
13
+ /**
14
+ * The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) Chain ID to which the session is bound,
15
+ */
16
+ chainId: number
17
+ /**
18
+ * [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) authority that is requesting the signing.
19
+ */
20
+ domain: string
21
+ /**
22
+ * Time when the signed authentication message is no longer valid.
23
+ */
24
+ expirationTime?: Date | undefined
25
+ /**
26
+ * Time when the message was generated, typically the current time.
27
+ */
28
+ issuedAt?: Date | undefined
29
+ /**
30
+ * A random string typically chosen by the relying party and used to prevent replay attacks.
31
+ */
32
+ nonce: string
33
+ /**
34
+ * Time when the signed authentication message will become valid.
35
+ */
36
+ notBefore?: Date | undefined
37
+ /**
38
+ * A system-specific identifier that may be used to uniquely refer to the sign-in request.
39
+ */
40
+ requestId?: string | undefined
41
+ /**
42
+ * A list of information or references to information the user wishes to have resolved as part of authentication by the relying party.
43
+ */
44
+ resources?: string[] | undefined
45
+ /**
46
+ * [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) URI scheme of the origin of the request.
47
+ */
48
+ scheme?: string | undefined
49
+ /**
50
+ * A human-readable ASCII assertion that the user will sign.
51
+ */
52
+ statement?: string | undefined
53
+ /**
54
+ * [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) URI referring to the resource that is the subject of the signing (as in the subject of a claim).
55
+ */
56
+ uri: string
57
+ /**
58
+ * The current version of the SIWE Message.
59
+ */
60
+ version: '1'
61
+ }
@@ -0,0 +1,51 @@
1
+ export function isUri(value: string) {
2
+ // based on https://github.com/ogt/valid-url
3
+
4
+ // check for illegal characters
5
+ if (/[^a-z0-9\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\.\-\_\~\%]/i.test(value))
6
+ return false
7
+
8
+ // check for hex escapes that aren't complete
9
+ if (/%[^0-9a-f]/i.test(value)) return false
10
+ if (/%[0-9a-f](:?[^0-9a-f]|$)/i.test(value)) return false
11
+
12
+ // from RFC 3986
13
+ const splitted = splitUri(value)
14
+ const scheme = splitted[1]
15
+ const authority = splitted[2]
16
+ const path = splitted[3]
17
+ const query = splitted[4]
18
+ const fragment = splitted[5]
19
+
20
+ // scheme and path are required, though the path can be empty
21
+ if (!(scheme?.length && path.length >= 0)) return false
22
+
23
+ // if authority is present, the path must be empty or begin with a /
24
+ if (authority?.length) {
25
+ if (!(path.length === 0 || /^\//.test(path))) return false
26
+ } else {
27
+ // if authority is not present, the path must not start with //
28
+ if (/^\/\//.test(path)) return false
29
+ }
30
+
31
+ // scheme must begin with a letter, then consist of letters, digits, +, ., or -
32
+ if (!/^[a-z][a-z0-9\+\-\.]*$/.test(scheme.toLowerCase())) return false
33
+
34
+ let out = ''
35
+ // re-assemble the URL per section 5.3 in RFC 3986
36
+ out += `${scheme}:`
37
+ if (authority?.length) out += `//${authority}`
38
+
39
+ out += path
40
+
41
+ if (query?.length) out += `?${query}`
42
+ if (fragment?.length) out += `#${fragment}`
43
+
44
+ return out
45
+ }
46
+
47
+ function splitUri(value: string) {
48
+ return value.match(
49
+ /(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*)(?:\?([^#]*))?(?:#(.*))?/,
50
+ )!
51
+ }