viem 0.0.0-main.20240521T214213 → 0.0.0-main.20240521T215546
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.
- package/_cjs/actions/public/verifyMessage.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 +1 -1
- package/_cjs/chains/definitions/metis.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/siwe/verifySiweMessage.js +39 -0
- package/_esm/actions/siwe/verifySiweMessage.js.map +1 -0
- package/_esm/chains/definitions/flowPreviewnet.js +1 -1
- package/_esm/chains/definitions/metis.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/siwe/verifySiweMessage.d.ts +34 -0
- package/_types/actions/siwe/verifySiweMessage.d.ts.map +1 -0
- package/_types/chains/definitions/metis.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/siwe/verifySiweMessage.ts +90 -0
- package/chains/definitions/flowPreviewnet.ts +1 -1
- package/chains/definitions/metis.ts +2 -1
- 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
@@ -0,0 +1,61 @@
|
|
1
|
+
import type { Address } from 'abitype';
|
2
|
+
/**
|
3
|
+
* @description EIP-4361 message fields
|
4
|
+
*
|
5
|
+
* @see https://eips.ethereum.org/EIPS/eip-4361
|
6
|
+
*/
|
7
|
+
export type SiweMessage = {
|
8
|
+
/**
|
9
|
+
* The Ethereum address performing the signing.
|
10
|
+
*/
|
11
|
+
address: Address;
|
12
|
+
/**
|
13
|
+
* The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) Chain ID to which the session is bound,
|
14
|
+
*/
|
15
|
+
chainId: number;
|
16
|
+
/**
|
17
|
+
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) authority that is requesting the signing.
|
18
|
+
*/
|
19
|
+
domain: string;
|
20
|
+
/**
|
21
|
+
* Time when the signed authentication message is no longer valid.
|
22
|
+
*/
|
23
|
+
expirationTime?: Date | undefined;
|
24
|
+
/**
|
25
|
+
* Time when the message was generated, typically the current time.
|
26
|
+
*/
|
27
|
+
issuedAt?: Date | undefined;
|
28
|
+
/**
|
29
|
+
* A random string typically chosen by the relying party and used to prevent replay attacks.
|
30
|
+
*/
|
31
|
+
nonce: string;
|
32
|
+
/**
|
33
|
+
* Time when the signed authentication message will become valid.
|
34
|
+
*/
|
35
|
+
notBefore?: Date | undefined;
|
36
|
+
/**
|
37
|
+
* A system-specific identifier that may be used to uniquely refer to the sign-in request.
|
38
|
+
*/
|
39
|
+
requestId?: string | undefined;
|
40
|
+
/**
|
41
|
+
* A list of information or references to information the user wishes to have resolved as part of authentication by the relying party.
|
42
|
+
*/
|
43
|
+
resources?: string[] | undefined;
|
44
|
+
/**
|
45
|
+
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) URI scheme of the origin of the request.
|
46
|
+
*/
|
47
|
+
scheme?: string | undefined;
|
48
|
+
/**
|
49
|
+
* A human-readable ASCII assertion that the user will sign.
|
50
|
+
*/
|
51
|
+
statement?: string | undefined;
|
52
|
+
/**
|
53
|
+
* [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).
|
54
|
+
*/
|
55
|
+
uri: string;
|
56
|
+
/**
|
57
|
+
* The current version of the SIWE Message.
|
58
|
+
*/
|
59
|
+
version: '1';
|
60
|
+
};
|
61
|
+
//# sourceMappingURL=types.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../utils/siwe/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAA;IACf;;OAEG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,cAAc,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;IACjC;;OAEG;IACH,QAAQ,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;IAC3B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAA;IACb;;OAEG;IACH,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;IAC5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IAChC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B;;OAEG;IACH,GAAG,EAAE,MAAM,CAAA;IACX;;OAEG;IACH,OAAO,EAAE,GAAG,CAAA;CACb,CAAA"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../utils/siwe/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,kBA4ClC"}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import type { Address } from 'abitype';
|
2
|
+
import type { ExactPartial } from '../../types/utils.js';
|
3
|
+
import type { SiweMessage } from './types.js';
|
4
|
+
export type ValidateSiweMessageParameters = {
|
5
|
+
/**
|
6
|
+
* Ethereum address to check against.
|
7
|
+
*/
|
8
|
+
address?: Address | undefined;
|
9
|
+
/**
|
10
|
+
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986) authority to check against.
|
11
|
+
*/
|
12
|
+
domain?: string | undefined;
|
13
|
+
/**
|
14
|
+
* EIP-4361 message fields.
|
15
|
+
*/
|
16
|
+
message: ExactPartial<SiweMessage>;
|
17
|
+
/**
|
18
|
+
* Random string to check against.
|
19
|
+
*/
|
20
|
+
nonce?: string | undefined;
|
21
|
+
/**
|
22
|
+
* [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) URI scheme to check against.
|
23
|
+
*/
|
24
|
+
scheme?: string | undefined;
|
25
|
+
/**
|
26
|
+
* Current time to check optional `expirationTime` and `notBefore` fields.
|
27
|
+
*
|
28
|
+
* @default new Date()
|
29
|
+
*/
|
30
|
+
time?: Date | undefined;
|
31
|
+
};
|
32
|
+
export type ValidateSiweMessageReturnType = boolean;
|
33
|
+
/**
|
34
|
+
* @description Validates EIP-4361 message.
|
35
|
+
*
|
36
|
+
* @see https://eips.ethereum.org/EIPS/eip-4361
|
37
|
+
*/
|
38
|
+
export declare function validateSiweMessage(parameters: ValidateSiweMessageParameters): ValidateSiweMessageReturnType;
|
39
|
+
//# sourceMappingURL=validateSiweMessage.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"validateSiweMessage.d.ts","sourceRoot":"","sources":["../../../utils/siwe/validateSiweMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,CAAA;IAClC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CACxB,CAAA;AAED,MAAM,MAAM,6BAA6B,GAAG,OAAO,CAAA;AAEnD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,6BAA6B,GACxC,6BAA6B,CAyB/B"}
|
@@ -10,6 +10,7 @@ import type {
|
|
10
10
|
SignableMessage,
|
11
11
|
Signature,
|
12
12
|
} from '../../types/misc.js'
|
13
|
+
import type { Prettify } from '../../types/utils.js'
|
13
14
|
import { hashMessage } from '../../utils/signature/hashMessage.js'
|
14
15
|
import type { HashMessageErrorType } from '../../utils/signature/hashMessage.js'
|
15
16
|
import {
|
@@ -18,14 +19,16 @@ import {
|
|
18
19
|
verifyHash,
|
19
20
|
} from './verifyHash.js'
|
20
21
|
|
21
|
-
export type VerifyMessageParameters =
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
22
|
+
export type VerifyMessageParameters = Prettify<
|
23
|
+
Omit<VerifyHashParameters, 'hash'> & {
|
24
|
+
/** The address that signed the original message. */
|
25
|
+
address: Address
|
26
|
+
/** The message to be verified. */
|
27
|
+
message: SignableMessage
|
28
|
+
/** The signature that was generated by signing the message with the address's private key. */
|
29
|
+
signature: Hex | ByteArray | Signature
|
30
|
+
}
|
31
|
+
>
|
29
32
|
|
30
33
|
export type VerifyMessageReturnType = boolean
|
31
34
|
|
@@ -0,0 +1,90 @@
|
|
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 { Hex } from '../../types/misc.js'
|
6
|
+
import type { Prettify } from '../../types/utils.js'
|
7
|
+
import { hashMessage } from '../../utils/signature/hashMessage.js'
|
8
|
+
import type { HashMessageErrorType } from '../../utils/signature/hashMessage.js'
|
9
|
+
import { parseSiweMessage } from '../../utils/siwe/parseSiweMessage.js'
|
10
|
+
import {
|
11
|
+
type ValidateSiweMessageParameters,
|
12
|
+
validateSiweMessage,
|
13
|
+
} from '../../utils/siwe/validateSiweMessage.js'
|
14
|
+
import {
|
15
|
+
type VerifyHashErrorType,
|
16
|
+
type VerifyHashParameters,
|
17
|
+
verifyHash,
|
18
|
+
} from '../public/verifyHash.js'
|
19
|
+
|
20
|
+
export type VerifySiweMessageParameters = Prettify<
|
21
|
+
Pick<VerifyHashParameters, 'blockNumber' | 'blockTag'> &
|
22
|
+
Pick<
|
23
|
+
ValidateSiweMessageParameters,
|
24
|
+
'address' | 'domain' | 'nonce' | 'scheme' | 'time'
|
25
|
+
> & {
|
26
|
+
/**
|
27
|
+
* EIP-4361 formatted message.
|
28
|
+
*/
|
29
|
+
message: string
|
30
|
+
/**
|
31
|
+
* Signature to check against.
|
32
|
+
*/
|
33
|
+
signature: Hex
|
34
|
+
}
|
35
|
+
>
|
36
|
+
|
37
|
+
export type VerifySiweMessageReturnType = boolean
|
38
|
+
|
39
|
+
export type VerifySiweMessageErrorType =
|
40
|
+
| HashMessageErrorType
|
41
|
+
| VerifyHashErrorType
|
42
|
+
| ErrorType
|
43
|
+
|
44
|
+
/**
|
45
|
+
* Verifies [EIP-4361](https://eips.ethereum.org/EIPS/eip-4361) formatted message was signed.
|
46
|
+
*
|
47
|
+
* Compatible with Smart Contract Accounts & Externally Owned Accounts via [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492).
|
48
|
+
*
|
49
|
+
* - Docs {@link https://viem.sh/docs/siwe/actions/verifySiweMessage}
|
50
|
+
*
|
51
|
+
* @param client - Client to use.
|
52
|
+
* @param parameters - {@link VerifySiweMessageParameters}
|
53
|
+
* @returns Whether or not the signature is valid. {@link VerifySiweMessageReturnType}
|
54
|
+
*/
|
55
|
+
export async function verifySiweMessage<chain extends Chain | undefined>(
|
56
|
+
client: Client<Transport, chain>,
|
57
|
+
parameters: VerifySiweMessageParameters,
|
58
|
+
): Promise<VerifySiweMessageReturnType> {
|
59
|
+
const {
|
60
|
+
address,
|
61
|
+
domain,
|
62
|
+
message,
|
63
|
+
nonce,
|
64
|
+
scheme,
|
65
|
+
signature,
|
66
|
+
time = new Date(),
|
67
|
+
...callRequest
|
68
|
+
} = parameters
|
69
|
+
|
70
|
+
const parsed = parseSiweMessage(message)
|
71
|
+
if (!parsed.address) return false
|
72
|
+
|
73
|
+
const isValid = validateSiweMessage({
|
74
|
+
address,
|
75
|
+
domain,
|
76
|
+
message: parsed,
|
77
|
+
nonce,
|
78
|
+
scheme,
|
79
|
+
time,
|
80
|
+
})
|
81
|
+
if (!isValid) return false
|
82
|
+
|
83
|
+
const hash = hashMessage(message)
|
84
|
+
return verifyHash(client, {
|
85
|
+
address: parsed.address,
|
86
|
+
hash,
|
87
|
+
signature,
|
88
|
+
...callRequest,
|
89
|
+
})
|
90
|
+
}
|
@@ -15,7 +15,8 @@ export const metis = /*#__PURE__*/ defineChain({
|
|
15
15
|
default: {
|
16
16
|
name: 'Metis Explorer',
|
17
17
|
url: 'https://explorer.metis.io',
|
18
|
-
apiUrl:
|
18
|
+
apiUrl:
|
19
|
+
'https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api',
|
19
20
|
},
|
20
21
|
blockscout: {
|
21
22
|
name: 'Andromeda Explorer',
|
@@ -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 = '0.0.0-main.
|
1
|
+
export const version = '0.0.0-main.20240521T215546'
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "viem",
|
3
3
|
"description": "TypeScript Interface for Ethereum",
|
4
|
-
"version": "0.0.0-main.
|
4
|
+
"version": "0.0.0-main.20240521T215546",
|
5
5
|
"type": "module",
|
6
6
|
"main": "./_cjs/index.js",
|
7
7
|
"module": "./_esm/index.js",
|
@@ -69,6 +69,11 @@
|
|
69
69
|
"import": "./_esm/op-stack/index.js",
|
70
70
|
"default": "./_cjs/op-stack/index.js"
|
71
71
|
},
|
72
|
+
"./siwe": {
|
73
|
+
"types": "./_types/siwe/index.d.ts",
|
74
|
+
"import": "./_esm/siwe/index.js",
|
75
|
+
"default": "./_cjs/siwe/index.js"
|
76
|
+
},
|
72
77
|
"./utils": {
|
73
78
|
"types": "./_types/utils/index.d.ts",
|
74
79
|
"import": "./_esm/utils/index.js",
|
@@ -115,6 +120,9 @@
|
|
115
120
|
"op-stack": [
|
116
121
|
"./_types/op-stack/index.d.ts"
|
117
122
|
],
|
123
|
+
"siwe": [
|
124
|
+
"./_types/siwe/index.d.ts"
|
125
|
+
],
|
118
126
|
"utils": [
|
119
127
|
"./_types/utils/index.d.ts"
|
120
128
|
],
|
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>.+))?/
|