diff --git a/packages/siwe/lib/client.test.ts b/packages/siwe/lib/client.test.ts index 8d2acaef..48f6f9cb 100644 --- a/packages/siwe/lib/client.test.ts +++ b/packages/siwe/lib/client.test.ts @@ -26,6 +26,7 @@ import { Wallet, } from 'ethers'; import { SiweMessage } from './client'; +import { checkContractWalletSignature } from './utils'; import { SiweErrorType } from './types'; describe(`Message Generation`, () => { @@ -285,4 +286,44 @@ describe(`Unit`, () => { ); } }); + + test('Should accept RPC URL string as provider in verify opts.', async () => { + const wallet = Wallet.createRandom(); + const msg = new SiweMessage({ + address: wallet.address, + domain: 'login.xyz', + statement: 'Sign-In With Ethereum Example Statement', + uri: 'https://login.xyz', + version: '1', + nonce: 'bTyXgcQxn2htgkjJn', + issuedAt: '2022-01-27T17:09:38.578Z', + chainId: 1, + expirationTime: '2100-01-07T14:31:43.952Z', + }); + const signature = await wallet.signMessage(msg.toMessage()); + const res = await msg.verify( + { signature }, + { provider: 'https://rpc.example.com' } + ); + expect(res.success).toBeTruthy(); + }); + + test('checkContractWalletSignature accepts string RPC provider', async () => { + const msg = new SiweMessage({ + domain: 'service.org', + address: '0x1234567890123456789012345678901234567890', + statement: 'Sign-In With Ethereum', + uri: 'https://service.org/login', + version: '1', + chainId: 1, + nonce: '32891757', + issuedAt: '2021-09-30T16:25:24.000Z', + }); + const err = await checkContractWalletSignature( + msg, + '0x1234', + 'http://127.0.0.1:8545' + ).catch(e => e); + expect(err.message).not.toContain('contract runner does not support'); + }); }); diff --git a/packages/siwe/lib/ethersCompat.ts b/packages/siwe/lib/ethersCompat.ts index 73c4a200..7c4321be 100644 --- a/packages/siwe/lib/ethersCompat.ts +++ b/packages/siwe/lib/ethersCompat.ts @@ -57,6 +57,18 @@ type ProviderV5 = ethers.providers.Provider; type ProviderV6 = ethers.Provider; export type Provider = ProviderV6 extends undefined ? ProviderV5 : ProviderV6; + +let ethersJsonRpcProvider: any = null; +try { + // @ts-expect-error -- v6 compatibility hack + ethersJsonRpcProvider = ethers.providers.JsonRpcProvider; +} catch { + ethersJsonRpcProvider = (ethers as any).JsonRpcProvider; +} + +export const getJsonRpcProvider = (url: string): Provider => + new ethersJsonRpcProvider(url); + export const verifyMessage = ethersVerifyMessage; export const hashMessage = ethersHashMessage; export const getAddress = ethersGetAddress; diff --git a/packages/siwe/lib/types.ts b/packages/siwe/lib/types.ts index 6583cee3..ead10cbe 100644 --- a/packages/siwe/lib/types.ts +++ b/packages/siwe/lib/types.ts @@ -28,8 +28,8 @@ export const VerifyParamsKeys: Array = [ ]; export interface VerifyOpts { - /** ethers provider to be used for EIP-1271 validation */ - provider?: providers.Provider; + /** ethers provider or RPC URL to be used for EIP-1271 validation */ + provider?: providers.Provider | string; /** If the library should reject promises on errors, defaults to false */ suppressExceptions?: boolean; diff --git a/packages/siwe/lib/utils.ts b/packages/siwe/lib/utils.ts index 6edc70cd..91df1e5e 100644 --- a/packages/siwe/lib/utils.ts +++ b/packages/siwe/lib/utils.ts @@ -3,7 +3,7 @@ import { randomStringForEntropy } from '@stablelib/random'; import { Contract, providers, Signer } from 'ethers'; import type { SiweMessage } from './client'; -import { hashMessage } from './ethersCompat'; +import { getJsonRpcProvider, hashMessage } from './ethersCompat'; const EIP1271_ABI = [ 'function isValidSignature(bytes32 _message, bytes _signature) public view returns (bytes4)', @@ -22,13 +22,16 @@ const ISO8601 = export const checkContractWalletSignature = async ( message: SiweMessage, signature: string, - provider?: providers.Provider | Signer + provider?: providers.Provider | Signer | string ): Promise => { if (!provider) { return false; } - const walletContract = new Contract(message.address, EIP1271_ABI, provider); + const resolvedProvider = + typeof provider === 'string' ? getJsonRpcProvider(provider) : provider; + + const walletContract = new Contract(message.address, EIP1271_ABI, resolvedProvider); const hashedMessage = hashMessage(message.prepareMessage()); const res = await walletContract.isValidSignature(hashedMessage, signature); return res === EIP1271_MAGICVALUE;