viem 2.11.0 → 2.12.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +22 -0
- package/README.md +3 -3
- package/_cjs/actions/public/verifyMessage.js.map +1 -1
- package/_cjs/actions/public/waitForTransactionReceipt.js +9 -3
- package/_cjs/actions/public/waitForTransactionReceipt.js.map +1 -1
- package/_cjs/actions/siwe/verifySiweMessage.js +32 -0
- package/_cjs/actions/siwe/verifySiweMessage.js.map +1 -0
- package/_cjs/chains/definitions/flowPreviewnet.js +6 -0
- package/_cjs/chains/definitions/flowPreviewnet.js.map +1 -1
- package/_cjs/chains/definitions/jbcTestnet.js +0 -4
- package/_cjs/chains/definitions/jbcTestnet.js.map +1 -1
- package/_cjs/chains/definitions/l3x.js +24 -0
- package/_cjs/chains/definitions/l3x.js.map +1 -0
- package/_cjs/chains/definitions/l3xTestnet.js +24 -0
- package/_cjs/chains/definitions/l3xTestnet.js.map +1 -0
- package/_cjs/chains/definitions/metis.js +5 -0
- package/_cjs/chains/definitions/metis.js.map +1 -1
- package/_cjs/chains/definitions/thaiChain.js +29 -0
- package/_cjs/chains/definitions/thaiChain.js.map +1 -0
- package/_cjs/chains/index.js +10 -4
- package/_cjs/chains/index.js.map +1 -1
- package/_cjs/clients/decorators/public.js +2 -0
- package/_cjs/clients/decorators/public.js.map +1 -1
- package/_cjs/errors/siwe.js +22 -0
- package/_cjs/errors/siwe.js.map +1 -0
- package/_cjs/errors/version.js +1 -1
- package/_cjs/siwe/index.js +16 -0
- package/_cjs/siwe/index.js.map +1 -0
- package/_cjs/utils/siwe/createSiweMessage.js +122 -0
- package/_cjs/utils/siwe/createSiweMessage.js.map +1 -0
- package/_cjs/utils/siwe/generateSiweNonce.js +9 -0
- package/_cjs/utils/siwe/generateSiweNonce.js.map +1 -0
- package/_cjs/utils/siwe/parseSiweMessage.js +25 -0
- package/_cjs/utils/siwe/parseSiweMessage.js.map +1 -0
- package/_cjs/utils/siwe/types.js +3 -0
- package/_cjs/utils/siwe/types.js.map +1 -0
- package/_cjs/utils/siwe/utils.js +44 -0
- package/_cjs/utils/siwe/utils.js.map +1 -0
- package/_cjs/utils/siwe/validateSiweMessage.js +29 -0
- package/_cjs/utils/siwe/validateSiweMessage.js.map +1 -0
- package/_esm/actions/public/verifyMessage.js.map +1 -1
- package/_esm/actions/public/waitForTransactionReceipt.js +9 -3
- package/_esm/actions/public/waitForTransactionReceipt.js.map +1 -1
- package/_esm/actions/siwe/verifySiweMessage.js +39 -0
- package/_esm/actions/siwe/verifySiweMessage.js.map +1 -0
- package/_esm/chains/definitions/flowPreviewnet.js +6 -0
- package/_esm/chains/definitions/flowPreviewnet.js.map +1 -1
- package/_esm/chains/definitions/jbcTestnet.js +0 -4
- package/_esm/chains/definitions/jbcTestnet.js.map +1 -1
- package/_esm/chains/definitions/l3x.js +21 -0
- package/_esm/chains/definitions/l3x.js.map +1 -0
- package/_esm/chains/definitions/l3xTestnet.js +21 -0
- package/_esm/chains/definitions/l3xTestnet.js.map +1 -0
- package/_esm/chains/definitions/metis.js +5 -0
- package/_esm/chains/definitions/metis.js.map +1 -1
- package/_esm/chains/definitions/thaiChain.js +26 -0
- package/_esm/chains/definitions/thaiChain.js.map +1 -0
- package/_esm/chains/index.js +3 -0
- package/_esm/chains/index.js.map +1 -1
- package/_esm/clients/decorators/public.js +2 -0
- package/_esm/clients/decorators/public.js.map +1 -1
- package/_esm/errors/siwe.js +18 -0
- package/_esm/errors/siwe.js.map +1 -0
- package/_esm/errors/version.js +1 -1
- package/_esm/siwe/index.js +7 -0
- package/_esm/siwe/index.js.map +1 -0
- package/_esm/utils/siwe/createSiweMessage.js +137 -0
- package/_esm/utils/siwe/createSiweMessage.js.map +1 -0
- package/_esm/utils/siwe/generateSiweNonce.js +15 -0
- package/_esm/utils/siwe/generateSiweNonce.js.map +1 -0
- package/_esm/utils/siwe/parseSiweMessage.js +30 -0
- package/_esm/utils/siwe/parseSiweMessage.js.map +1 -0
- package/_esm/utils/siwe/types.js +2 -0
- package/_esm/utils/siwe/types.js.map +1 -0
- package/_esm/utils/siwe/utils.js +49 -0
- package/_esm/utils/siwe/utils.js.map +1 -0
- package/_esm/utils/siwe/validateSiweMessage.js +30 -0
- package/_esm/utils/siwe/validateSiweMessage.js.map +1 -0
- package/_types/actions/public/verifyMessage.d.ts +3 -2
- package/_types/actions/public/verifyMessage.d.ts.map +1 -1
- package/_types/actions/public/waitForTransactionReceipt.d.ts.map +1 -1
- package/_types/actions/siwe/verifySiweMessage.d.ts +34 -0
- package/_types/actions/siwe/verifySiweMessage.d.ts.map +1 -0
- package/_types/chains/definitions/flowPreviewnet.d.ts +6 -8
- package/_types/chains/definitions/flowPreviewnet.d.ts.map +1 -1
- package/_types/chains/definitions/jbcTestnet.d.ts +0 -4
- package/_types/chains/definitions/jbcTestnet.d.ts.map +1 -1
- package/_types/chains/definitions/l3x.d.ts +37 -0
- package/_types/chains/definitions/l3x.d.ts.map +1 -0
- package/_types/chains/definitions/l3xTestnet.d.ts +37 -0
- package/_types/chains/definitions/l3xTestnet.d.ts.map +1 -0
- package/_types/chains/definitions/metis.d.ts +5 -0
- package/_types/chains/definitions/metis.d.ts.map +1 -1
- package/_types/chains/definitions/thaiChain.d.ts +34 -0
- package/_types/chains/definitions/thaiChain.d.ts.map +1 -0
- package/_types/chains/index.d.ts +3 -0
- package/_types/chains/index.d.ts.map +1 -1
- package/_types/clients/decorators/public.d.ts +30 -0
- package/_types/clients/decorators/public.d.ts.map +1 -1
- package/_types/errors/siwe.d.ts +13 -0
- package/_types/errors/siwe.d.ts.map +1 -0
- package/_types/errors/version.d.ts +1 -1
- package/_types/siwe/index.d.ts +8 -0
- package/_types/siwe/index.d.ts.map +1 -0
- package/_types/utils/siwe/createSiweMessage.d.ts +24 -0
- package/_types/utils/siwe/createSiweMessage.d.ts.map +1 -0
- package/_types/utils/siwe/generateSiweNonce.d.ts +12 -0
- package/_types/utils/siwe/generateSiweNonce.d.ts.map +1 -0
- package/_types/utils/siwe/parseSiweMessage.d.ts +11 -0
- package/_types/utils/siwe/parseSiweMessage.d.ts.map +1 -0
- package/_types/utils/siwe/types.d.ts +61 -0
- package/_types/utils/siwe/types.d.ts.map +1 -0
- package/_types/utils/siwe/utils.d.ts +2 -0
- package/_types/utils/siwe/utils.d.ts.map +1 -0
- package/_types/utils/siwe/validateSiweMessage.d.ts +39 -0
- package/_types/utils/siwe/validateSiweMessage.d.ts.map +1 -0
- package/actions/public/verifyMessage.ts +11 -8
- package/actions/public/waitForTransactionReceipt.ts +13 -4
- package/actions/siwe/verifySiweMessage.ts +90 -0
- package/chains/definitions/flowPreviewnet.ts +6 -0
- package/chains/definitions/jbcTestnet.ts +0 -4
- package/chains/definitions/l3x.ts +21 -0
- package/chains/definitions/l3xTestnet.ts +21 -0
- package/chains/definitions/metis.ts +6 -0
- package/chains/definitions/thaiChain.ts +26 -0
- package/chains/index.ts +3 -0
- package/clients/decorators/public.ts +37 -0
- package/errors/siwe.ts +20 -0
- package/errors/version.ts +1 -1
- package/package.json +9 -1
- package/siwe/index.ts +29 -0
- package/siwe/package.json +6 -0
- package/utils/siwe/createSiweMessage.ts +168 -0
- package/utils/siwe/generateSiweNonce.ts +15 -0
- package/utils/siwe/parseSiweMessage.ts +55 -0
- package/utils/siwe/types.ts +61 -0
- package/utils/siwe/utils.ts +51 -0
- 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.
|
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.
|
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,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
|
+
}
|