Skip to content

Commit

Permalink
Merge pull request #1908 from Web3Auth/feat/remove-eth-sig-util
Browse files Browse the repository at this point in the history
Feat/remove eth sig util
  • Loading branch information
arch1995 authored Aug 30, 2024
2 parents c3128b9 + 2b22069 commit e41f973
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 272 deletions.
31 changes: 4 additions & 27 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { MessageTypes, TypedDataV1, TypedMessage } from "@metamask/eth-sig-util";
import type { ISignClient, SessionTypes } from "@walletconnect/types";
import { getAccountsFromNamespaces, parseAccountId } from "@walletconnect/utils";
import { type JRPCRequest, providerErrors, rpcErrors } from "@web3auth/auth";
Expand Down Expand Up @@ -70,15 +69,7 @@ export function getProviderHandlers({ connector, chainId }: { connector: ISignCl
const methodRes = await sendJrpcRequest<string, string[]>(connector, chainId, "personal_sign", [msgParams.data, msgParams.from]);
return methodRes;
},
processTypedMessage: async (msgParams: MessageParams<TypedDataV1>, _: JRPCRequest<unknown>): Promise<string> => {
const methodRes = await sendJrpcRequest<string, unknown[]>(connector, chainId, "eth_signTypedData", [msgParams.data, msgParams.from]);
return methodRes;
},
processTypedMessageV3: async (msgParams: TypedMessageParams<TypedMessage<MessageTypes>>): Promise<string> => {
const methodRes = await sendJrpcRequest<string, unknown[]>(connector, chainId, "eth_signTypedData_v3", [msgParams.from, msgParams.data]);
return methodRes;
},
processTypedMessageV4: async (msgParams: TypedMessageParams<TypedMessage<MessageTypes>>): Promise<string> => {
processTypedMessageV4: async (msgParams: TypedMessageParams): Promise<string> => {
const methodRes = await sendJrpcRequest<string, unknown[]>(connector, chainId, "eth_signTypedData_v4", [msgParams.from, msgParams.data]);
return methodRes;
},
Expand Down
4 changes: 2 additions & 2 deletions packages/providers/ethereum-mpc-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
"@ethereumjs/common": "^4.3.0",
"@ethereumjs/tx": "^5.3.0",
"@ethereumjs/util": "^9.0.3",
"@metamask/eth-sig-util": "7.0.2",
"@toruslabs/base-controllers": "^6.0.2",
"@web3auth/auth": "^9.1.3",
"@web3auth/base": "^8.12.4",
"@web3auth/base-provider": "^8.12.4",
"@web3auth/ethereum-provider": "^8.12.4"
"@web3auth/ethereum-provider": "^8.12.4",
"ethers": "^6.13.2"
},
"peerDependencies": {
"@babel/runtime": "7.x"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
import { hashPersonalMessage, intToBytes, isHexString, publicToAddress, stripHexPrefix, toBytes } from "@ethereumjs/util";
import {
type MessageTypes,
TypedDataUtils,
type TypedDataV1,
type TypedDataV1Field,
type TypedMessage,
typedSignatureHash,
} from "@metamask/eth-sig-util";
import { concatSig } from "@toruslabs/base-controllers";
import { JRPCRequest, providerErrors, SafeEventEmitterProvider } from "@web3auth/auth";
import { isHexStrict, log } from "@web3auth/base";
import { log } from "@web3auth/base";
import {
IProviderHandlers,
MessageParams,
SignTypedDataMessageV4,
SignTypedDataVersion,
TransactionFormatter,
TransactionParams,
TypedMessageParams,
validateTypedMessageParams,
validateTypedSignMessageDataV4,
} from "@web3auth/ethereum-provider";
import { TypedDataEncoder } from "ethers";

async function signTx(
txParams: TransactionParams & { gas?: string },
Expand Down Expand Up @@ -120,18 +114,16 @@ function validateVersion(version: string, allowedVersions: string[]) {

async function signTypedData(
sign: (msgHash: Buffer, rawMsg?: Buffer) => Promise<{ v: number; r: Buffer; s: Buffer }>,
data: unknown,
data: TypedMessageParams,
version: SignTypedDataVersion
) {
validateVersion(version, undefined); // Note: this is intentional;
if (data === null || data === undefined) {
throw new Error("Missing data parameter");
}
const messageHash =
version === SignTypedDataVersion.V1
? Buffer.from(stripHexPrefix(typedSignatureHash(data as TypedDataV1Field[])), "hex")
: TypedDataUtils.eip712Hash(data as TypedMessage<MessageTypes>, version);
const { v, r, s } = await sign(Buffer.from(messageHash.buffer));
const message: SignTypedDataMessageV4 = typeof data === "string" ? JSON.parse(data) : data;

const { v, r, s } = await sign(Buffer.from(TypedDataEncoder.hash(message.domain, message.types, message.message)));

let modifiedV = v;
if (modifiedV <= 1) {
Expand Down Expand Up @@ -195,41 +187,7 @@ export function getProviderHandlers({
const sig = personalSign(sign, msgParams.data);
return sig;
},
processTypedMessage: async (msgParams: MessageParams<TypedDataV1>, _: JRPCRequest<unknown>): Promise<string> => {
log.debug("processTypedMessage", msgParams);
const providerEngineProxy = getProviderEngineProxy();
if (!providerEngineProxy)
throw providerErrors.custom({
message: "Provider is not initialized",
code: 4902,
});
const chainId = await providerEngineProxy.request<unknown, string>({ method: "eth_chainId" });
const finalChainId = Number.parseInt(chainId, isHexStrict(chainId) ? 16 : 10);
const params = {
...msgParams,
version: SignTypedDataVersion.V1,
};
await validateTypedMessageParams(params, finalChainId);
const data = typeof params.data === "string" ? JSON.parse(params.data) : params.data;
const sig = signTypedData(sign, data, SignTypedDataVersion.V1);
return sig;
},
processTypedMessageV3: async (msgParams: TypedMessageParams<TypedMessage<MessageTypes>>, _: JRPCRequest<unknown>): Promise<string> => {
log.debug("processTypedMessageV3", msgParams);
const providerEngineProxy = getProviderEngineProxy();
if (!providerEngineProxy)
throw providerErrors.custom({
message: "Provider is not initialized",
code: 4902,
});
const chainId = await providerEngineProxy.request<unknown, string>({ method: "eth_chainId" });
const finalChainId = Number.parseInt(chainId, isHexStrict(chainId) ? 16 : 10);
await validateTypedMessageParams(msgParams, finalChainId);
const data = typeof msgParams.data === "string" ? JSON.parse(msgParams.data) : msgParams.data;
const sig = signTypedData(sign, data, SignTypedDataVersion.V3);
return sig;
},
processTypedMessageV4: async (msgParams: TypedMessageParams<TypedMessage<MessageTypes>>, _: JRPCRequest<unknown>): Promise<string> => {
processTypedMessageV4: async (msgParams: TypedMessageParams, _: JRPCRequest<unknown>): Promise<string> => {
log.debug("processTypedMessageV4", msgParams);
const providerEngineProxy = getProviderEngineProxy();
if (!providerEngineProxy)
Expand All @@ -238,8 +196,7 @@ export function getProviderHandlers({
code: 4902,
});
const chainId = await providerEngineProxy.request<unknown, string>({ method: "eth_chainId" });
const finalChainId = Number.parseInt(chainId, isHexStrict(chainId) ? 16 : 10);
await validateTypedMessageParams(msgParams, finalChainId);
await validateTypedSignMessageDataV4(msgParams, chainId);
const data = typeof msgParams.data === "string" ? JSON.parse(msgParams.data) : msgParams.data;
const sig = signTypedData(sign, data, SignTypedDataVersion.V4);
return sig;
Expand Down
2 changes: 1 addition & 1 deletion packages/providers/ethereum-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"@ethereumjs/common": "^4.3.0",
"@ethereumjs/tx": "^5.3.0",
"@ethereumjs/util": "^9.0.3",
"@metamask/eth-sig-util": "7.0.2",
"@toruslabs/base-controllers": "^6.0.2",
"@toruslabs/http-helpers": "^7.0.0",
"@web3auth/auth": "^9.1.3",
Expand All @@ -32,6 +31,7 @@
"assert": "^2.1.0",
"bignumber.js": "^9.1.2",
"bn.js": "^5.2.1",
"ethers": "^6.13.2",
"jsonschema": "^1.4.1"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { isValidAddress } from "@ethereumjs/util";
import { type MessageTypeProperty, TYPED_MESSAGE_SCHEMA, type TypedDataV1Field, type TypedMessage, typedSignatureHash } from "@metamask/eth-sig-util";
import { get } from "@toruslabs/http-helpers";
import { rpcErrors } from "@web3auth/auth";
import { isHexStrict } from "@web3auth/base";
import assert from "assert";
import { BigNumber } from "bignumber.js";
import jsonschema from "jsonschema";

import { TypedMessageParams } from "../../../rpc/interfaces";
import { decGWEIToHexWEI, hexWEIToDecGWEI } from "../../converter";
import { EIP1159GasData, EthereumGasFeeEstimates, LegacyGasData, SignTypedDataVersion } from "./interfaces";
import { EIP1159GasData, EthereumGasFeeEstimates, LegacyGasData } from "./interfaces";

export function normalizeGWEIDecimalNumbers(n: string | BigNumber): string {
const numberAsWEIHex = decGWEIToHexWEI(n);
Expand Down Expand Up @@ -63,66 +58,47 @@ export async function fetchLegacyGasPriceEstimates(url: string): Promise<LegacyG
};
}

export const validateTypedMessageParams = async (parameters: TypedMessageParams<unknown>, activeChainId: number) => {
try {
assert.ok(parameters && typeof parameters === "object", "Params must be an object.");
assert.ok("data" in parameters, 'Params must include a "data" field.');
assert.ok("from" in parameters, 'Params must include a "from" field.');
assert.ok(
typeof parameters.from === "string" && isValidAddress(parameters.from),
'"from" field must be a valid, lowercase, hexadecimal Ethereum address string.'
);
let data: unknown = null;
let chainId = null;
export function validateAddress(address: string, propertyName: string) {
if (!address || typeof address !== "string" || !isValidAddress(address)) {
throw new Error(`Invalid "${propertyName}" address: ${address} must be a valid string.`);
}
}

export async function validateTypedSignMessageDataV4(messageData: TypedMessageParams, currentChainId: string) {
validateAddress(messageData.from, "from");

if (!messageData.data || Array.isArray(messageData.data) || (typeof messageData.data !== "object" && typeof messageData.data !== "string")) {
throw new Error(`Invalid message "data": Must be a valid string or object.`);
}

let data;
if (typeof messageData.data === "object") {
data = messageData.data;
} else {
try {
data = JSON.parse(messageData.data);
} catch (e) {
throw new Error("Data must be passed as a valid JSON string.");
}
}

if (!currentChainId) {
throw new Error("Current chainId cannot be null or undefined.");
}

switch ((parameters as TypedMessageParams<unknown>).version) {
case SignTypedDataVersion.V1:
if (typeof parameters.data === "string") {
assert.doesNotThrow(() => {
data = JSON.parse(parameters.data as string);
}, '"data" must be a valid JSON string.');
} else {
// for backward compatiblity we validate for both string and object type.
data = parameters.data;
}
assert.ok(Array.isArray(data as unknown), "params.data must be an array.");
assert.doesNotThrow(() => {
typedSignatureHash(data as TypedDataV1Field[]);
}, "Signing data must be valid EIP-712 typed data.");
break;
case SignTypedDataVersion.V3:
case SignTypedDataVersion.V4: {
if (typeof parameters.data === "string") {
assert.doesNotThrow(() => {
data = JSON.parse(parameters.data as string);
}, '"data" must be a valid JSON string.');
} else {
// for backward compatiblity we validate for both string and object type.
data = parameters.data;
}
const typedData = data as TypedMessage<{
EIP712Domain: MessageTypeProperty[];
}>;
let { chainId } = data.domain;
if (chainId) {
if (typeof chainId === "string") {
chainId = parseInt(chainId, chainId.startsWith("0x") ? 16 : 10);
}

assert.ok(typedData.primaryType in typedData.types, `Primary type of "${typedData.primaryType}" has no type definition.`);
const validation = jsonschema.validate(typedData, TYPED_MESSAGE_SCHEMA.properties);
assert.strictEqual(validation.errors.length, 0, "Signing data must conform to EIP-712 schema. See https://git.io/fNtcx.");
chainId = typedData.domain?.chainId;
if (chainId) {
assert.ok(!Number.isNaN(activeChainId), `Cannot sign messages for chainId "${chainId}", because Web3Auth is switching networks.`);
if (typeof chainId === "string") {
chainId = Number.parseInt(chainId, isHexStrict(chainId) ? 16 : 10);
}
assert.strictEqual(chainId, activeChainId, `Provided chainId "${chainId}" must match the active chainId "${activeChainId}"`);
}
break;
}
default:
assert.fail(`Unknown typed data version "${(parameters as TypedMessageParams<unknown>).version}"`);
const activeChainId = parseInt(currentChainId, 16);
if (Number.isNaN(activeChainId)) {
throw new Error(`Cannot sign messages for chainId "${chainId}", because MetaMask is switching networks.`);
}

if (chainId !== activeChainId) {
throw new Error(`Provided chainId "${chainId}" must match the active chainId "${activeChainId}"`);
}
} catch (error) {
throw rpcErrors.invalidInput({
message: (error as Error)?.message,
});
}
};
}
Loading

0 comments on commit e41f973

Please sign in to comment.