Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions packages/siwe/lib/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
Wallet,
} from 'ethers';
import { SiweMessage } from './client';
import { checkContractWalletSignature } from './utils';
import { SiweErrorType } from './types';

describe(`Message Generation`, () => {
Expand Down Expand Up @@ -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');
});
});
12 changes: 12 additions & 0 deletions packages/siwe/lib/ethersCompat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
4 changes: 2 additions & 2 deletions packages/siwe/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export const VerifyParamsKeys: Array<keyof VerifyParams> = [
];

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;
Expand Down
9 changes: 6 additions & 3 deletions packages/siwe/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
Expand All @@ -22,13 +22,16 @@ const ISO8601 =
export const checkContractWalletSignature = async (
message: SiweMessage,
signature: string,
provider?: providers.Provider | Signer
provider?: providers.Provider | Signer | string
): Promise<boolean> => {
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;
Expand Down