Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ jobs:
fail-fast: false
matrix:
node: [ 16, 18 ]
ethers: [ 5, 6 ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
Expand All @@ -25,4 +26,4 @@ jobs:
npm install
npm run build
- name: Run tests
run: npm run test
run: ETHERSJS_VERSION=${{ matrix.ethers }} npm run test
10 changes: 0 additions & 10 deletions jest.config.js

This file was deleted.

3,963 changes: 2,057 additions & 1,906 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
],
"scripts": {
"build": "lerna run build",
"test": "lerna run test",
"test": "lerna run --stream test",
"docs": "typedoc --plugin nlfurniss-typedoc-plugin-sourcefile-url --plugin typedoc-plugin-extras --sourcefile-url-prefix 'https://github.com/spruceid/siwe' --favicon ./favicon.svg --out docs packages/siwe/lib/siwe.ts"
},
"author": "Spruce Systems Inc.",
"license": "Apache-2.0",
"devDependencies": {
"@types/jest": "^27.0.2",
"ethers": "^5.5.1",
"jest": "^27.3.1",
"@types/jest": "^29.4.0",
"ethers5": "npm:ethers@^5.6.8",
"ethers6": "npm:ethers@^6.0.8",
"jest": "^29.4.3",
"lerna": "^6.5.1",
"nlfurniss-typedoc-plugin-sourcefile-url": "^2.0.0",
"ts-jest": "^27.0.7",
"ts-jest": "^29.0.5",
"typedoc": "^0.23.12",
"typedoc-plugin-extras": "^2.2.1",
"typescript": "^4.4.4"
Expand Down
2 changes: 1 addition & 1 deletion packages/siwe-parser/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
modulePathIgnorePatterns: ["<rootDir>/dist/"]
modulePathIgnorePatterns: ['<rootDir>/dist/'],
};
12 changes: 2 additions & 10 deletions packages/siwe-parser/lib/parsers.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { ParsedMessage } from "./abnf";

const parsingPositive: Object = require("../../../test/parsing_positive.json");
const parsingNegative: Object = require("../../../test/parsing_negative.json");

//
describe("Successfully parses with ABNF Client", () => {
let ParsedMessage;
beforeEach(
async () => (ParsedMessage = (await import("./abnf")).ParsedMessage)
);

test.concurrent.each(Object.entries(parsingPositive))(
"Parses message successfully: %s",
(test_name, test) => {
Expand All @@ -24,11 +21,6 @@ describe("Successfully parses with ABNF Client", () => {
});

describe("Successfully fails with ABNF Client", () => {
let ParsedMessage;
beforeEach(
async () => (ParsedMessage = (await import("./abnf")).ParsedMessage)
);

test.concurrent.each(Object.entries(parsingNegative))(
"Fails to parse message: %s",
(test_name, test) => {
Expand Down
1 change: 1 addition & 0 deletions packages/siwe/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
modulePathIgnorePatterns: ['<rootDir>/dist/'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
};
16 changes: 16 additions & 0 deletions packages/siwe/jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
jest.mock('ethers', () => {
const packages = {
5: 'ethers5',
6: 'ethers6',
};
const version = process.env.ETHERSJS_VERSION || '6';
console.log('Testing with ethers version', version);

// Require the original module.
const originalModule = jest.requireActual(packages[version]);

return {
__esModule: true,
...originalModule,
};
});
23 changes: 19 additions & 4 deletions packages/siwe/lib/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ const verificationPositive = require('../../../test/verification_positive.json')
const verificationNegative = require('../../../test/verification_negative.json');
const EIP1271 = require('../../../test/eip1271.json');

import { providers, Wallet } from 'ethers';
import {
providers,
// @ts-expect-error -- ethers v6 compatibility hack
InfuraProvider,
Wallet,
} from 'ethers';
import { SiweMessage } from './client';
import { SiweErrorType } from './types';

Expand Down Expand Up @@ -44,7 +49,7 @@ describe(`Message Generation`, () => {

describe(`Message verification without suppressExceptions`, () => {
test.concurrent.each(Object.entries(verificationPositive))(
'Verificates message successfully: %s',
'Verifies message successfully: %s',
async (_, test_fields: any) => {
const msg = new SiweMessage(test_fields);
await expect(
Expand All @@ -65,8 +70,11 @@ describe(`Message verification without suppressExceptions`, () => {
return res === data;
})
).resolves.toBeTruthy();

jest.useRealTimers();
}
);

test.concurrent.each(Object.entries(verificationNegative))(
'Fails to verify message: %s and rejects the promise',
async (n, test_fields: any) => {
Expand Down Expand Up @@ -146,9 +154,16 @@ describe(`Round Trip`, () => {
});

describe(`EIP1271`, () => {
function getProviderCompat(networkId: number | string) {
return typeof providers?.InfuraProvider !== 'undefined'
? new providers.InfuraProvider(networkId)
: new InfuraProvider(networkId);
}

test.concurrent.each(Object.entries(EIP1271))(
'Verificates message successfully: %s',
'Verifies message successfully: %s',
async (_, test_fields: any) => {
const provider = getProviderCompat(1);
const msg = new SiweMessage(test_fields.message);
await expect(
msg
Expand All @@ -157,7 +172,7 @@ describe(`EIP1271`, () => {
signature: test_fields.signature,
},
{
provider: new providers.CloudflareProvider(1),
provider,
}
)
.then(({ success }) => success)
Expand Down
8 changes: 5 additions & 3 deletions packages/siwe/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import {
ParsedMessage,
parseIntegerNumber,
} from '@spruceid/siwe-parser';
import { providers, utils } from 'ethers';
import { providers } from 'ethers';
import * as uri from 'valid-url';

import { getAddress, verifyMessage } from './ethersCompat';
import {
SiweError,
SiweErrorType,
Expand Down Expand Up @@ -290,7 +292,7 @@ export class SiweMessage {
/** Recover address from signature */
let addr;
try {
addr = utils.verifyMessage(EIP4361Message, signature);
addr = verifyMessage(EIP4361Message, signature);
} catch (e) {
console.error(e);
}
Expand Down Expand Up @@ -379,7 +381,7 @@ export class SiweMessage {
if (!isEIP55Address(this.address)) {
throw new SiweError(
SiweErrorType.INVALID_ADDRESS,
utils.getAddress(this.address),
getAddress(this.address),
this.address
);
}
Expand Down
51 changes: 51 additions & 0 deletions packages/siwe/lib/ethersCompat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
utils,
// @ts-expect-error -- ethers v6 compatibility hack
verifyMessage as ethersVerifyMessage,
// @ts-expect-error -- ethers v6 compatibility hack
hashMessage as ethersHashMessage,
// @ts-expect-error -- ethers v6 compatibility hack
getAddress as ethersGetAddress,
} from 'ethers';

type Ethers6BigNumberish = string | number | bigint;

// NB: This compatibility type omits the `Signature` class defined in ethers v6;
// however, a `Signature` instance is compatible with the object type defined.
type Ethers6SignatureLike =
| string
| {
r: string;
s: string;
v: Ethers6BigNumberish;
yParity?: 0 | 1;
yParityAndS?: string;
}
| {
r: string;
yParityAndS: string;
yParity?: 0 | 1;
s?: string;
v?: number;
}
| {
r: string;
s: string;
yParity: 0 | 1;
v?: Ethers6BigNumberish;
yParityAndS?: string;
};

export const verifyMessage =
utils?.verifyMessage ??
(ethersVerifyMessage as (
message: Uint8Array | string,
sig: Ethers6SignatureLike
) => string);

export const hashMessage =
utils?.hashMessage ??
(ethersHashMessage as (message: Uint8Array | string) => string);

export const getAddress =
utils?.getAddress ?? (ethersGetAddress as (address: string) => string);
11 changes: 5 additions & 6 deletions packages/siwe/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { randomStringForEntropy } from '@stablelib/random';
import { Contract, providers, Signer, utils } from 'ethers';
import { Contract, providers, Signer } from 'ethers';

import type { SiweMessage } from './client';
import { hashMessage } from './ethersCompat';

const EIP1271_ABI = ["function isValidSignature(bytes32 _message, bytes _signature) public view returns (bytes4)"];
const EIP1271_MAGICVALUE = "0x1626ba7e";
Expand All @@ -23,11 +25,8 @@ export const checkContractWalletSignature = async (
}

const walletContract = new Contract(message.address, EIP1271_ABI, provider);
const hashMessage = utils.hashMessage(message.prepareMessage());
const res = await walletContract.isValidSignature(
hashMessage,
signature
);
const hashedMessage = hashMessage(message.prepareMessage());
const res = await walletContract.isValidSignature(hashedMessage, signature);
return res == EIP1271_MAGICVALUE;
};

Expand Down
2 changes: 1 addition & 1 deletion packages/siwe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"prettier": "^2.6.2"
},
"peerDependencies": {
"ethers": "^5.5.1"
"ethers": "^5.6.8 || ^6.0.8"
},
"repository": {
"type": "git",
Expand Down