Skip to content

Commit

Permalink
perf: don't import * as ethers
Browse files Browse the repository at this point in the history
  • Loading branch information
gomesalexandre committed Oct 25, 2023
1 parent 11727c2 commit 4e011d5
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 64 deletions.
8 changes: 4 additions & 4 deletions packages/hdwallet-keepkey/src/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as Types from "@keepkey/device-protocol/lib/types_pb";
import * as core from "@shapeshiftoss/hdwallet-core";
import { getMessage, getTypeHash } from "eip-712";
import * as eip55 from "eip55";
import * as ethers from "ethers";
import { arrayify, isBytes, isHexString } from "ethers/lib/utils.js";

import { Transport } from "./transport";
import { toUTF8Array } from "./utils";
Expand Down Expand Up @@ -173,10 +173,10 @@ export async function ethGetAddress(transport: Transport, msg: core.ETHGetAddres

export async function ethSignMessage(transport: Transport, msg: core.ETHSignMessage): Promise<core.ETHSignedMessage> {
const { addressNList, message } = msg;
if (!ethers.utils.isHexString(message)) throw new Error("data is not an hex string");
if (!isHexString(message)) throw new Error("data is not an hex string");
const m = new Ethereum.EthereumSignMessage();
m.setAddressNList(addressNList);
const messageBytes = ethers.utils.arrayify(message);
const messageBytes = arrayify(message);
m.setMessage(messageBytes);
const response = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMSIGNMESSAGE, m, {
msgTimeout: core.LONG_TIMEOUT,
Expand Down Expand Up @@ -272,7 +272,7 @@ export async function ethVerifyMessage(transport: Transport, msg: core.ETHVerify
const m = new Ethereum.EthereumVerifyMessage();
m.setAddress(core.arrayify(msg.address));
m.setSignature(core.arrayify(msg.signature));
m.setMessage(ethers.utils.isBytes(msg.message) ? ethers.utils.arrayify(msg.message) : toUTF8Array(msg.message));
m.setMessage(isBytes(msg.message) ? arrayify(msg.message) : toUTF8Array(msg.message));
let event: core.Event;
try {
event = await transport.call(Messages.MessageType.MESSAGETYPE_ETHEREUMVERIFYMESSAGE, m, {
Expand Down
6 changes: 2 additions & 4 deletions packages/hdwallet-ledger/src/ethereum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import EthereumTx from "ethereumjs-tx";
// @ts-ignore
// TODO: fix ts-ignore
import * as ethereumUtil from "ethereumjs-util";
import * as ethers from "ethers";
import { arrayify, isBytes } from "ethers/lib/utils.js";
import { isHexString } from "ethjs-util";

import { LedgerTransport } from "./transport";
Expand Down Expand Up @@ -189,9 +189,7 @@ export async function ethVerifyMessage(msg: core.ETHVerifyMessage): Promise<bool
return false;
}
sigb[64] = sigb[64] === 0 || sigb[64] === 1 ? sigb[64] + 27 : sigb[64];
const buffer = ethers.utils.isBytes(msg.message)
? Buffer.from(ethers.utils.arrayify(msg.message))
: Buffer.from(msg.message);
const buffer = isBytes(msg.message) ? Buffer.from(arrayify(msg.message)) : Buffer.from(msg.message);
const hash = ethereumUtil.hashPersonalMessage(buffer);
const pubKey = ethereumUtil.ecrecover(hash, sigb[64], sigb.slice(0, 32), sigb.slice(32, 64));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import detectEthereumProvider from "@metamask/detect-provider";
import MetaMaskOnboarding from "@metamask/onboarding";
import * as core from "@shapeshiftoss/hdwallet-core";
import { enableShapeShiftSnap, shapeShiftSnapInstalled } from "@shapeshiftoss/metamask-snaps-adapter";
import * as ethers from "ethers";
import { providers } from "ethers";

import { SNAP_ID } from "./common";
import { MetaMaskShapeShiftMultiChainHDWallet } from "./shapeshift-multichain";
Expand All @@ -29,7 +29,7 @@ export class MetaMaskAdapter {
mustBeMetaMask: true,
silent: false,
timeout: 3000,
})) as ethers.providers.ExternalProvider | null;
})) as providers.ExternalProvider | null;
if (!provider) {
const onboarding = new MetaMaskOnboarding();
onboarding.startOnboarding();
Expand Down
4 changes: 2 additions & 2 deletions packages/hdwallet-metamask/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import detectEthereumProvider from "@metamask/detect-provider";
import MetaMaskOnboarding from "@metamask/onboarding";
import * as core from "@shapeshiftoss/hdwallet-core";
import * as ethers from "ethers";
import { providers } from "ethers";

import { MetaMaskHDWallet } from "./metamask";

Expand All @@ -27,7 +27,7 @@ export class MetaMaskAdapter {
mustBeMetaMask: true,
silent: false,
timeout: 3000,
})) as ethers.providers.ExternalProvider | null;
})) as providers.ExternalProvider | null;
if (!provider) {
const onboarding = new MetaMaskOnboarding();
onboarding.startOnboarding();
Expand Down
50 changes: 30 additions & 20 deletions packages/hdwallet-native/src/crypto/isolation/adapters/ethereum.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import * as core from "@shapeshiftoss/hdwallet-core";
import { getMessage, TypedData } from "eip-712";
import * as ethers from "ethers";
import { BigNumber, BytesLike, providers, Signature, UnsignedTransaction } from "ethers";
import {
arrayify,
computeAddress,
Deferrable,
getAddress,
joinSignature,
resolveProperties,
serializeTransaction,
splitSignature,
} from "ethers/lib/utils.js";

import { buildMessage } from "../../../util";
import { Isolation } from "../..";
import { SecP256K1 } from "../core";

function ethSigFromRecoverableSig(x: SecP256K1.RecoverableSignature): ethers.Signature {
function ethSigFromRecoverableSig(x: SecP256K1.RecoverableSignature): Signature {
const sig = SecP256K1.RecoverableSignature.sig(x);
const recoveryParam = SecP256K1.RecoverableSignature.recoveryParam(x);
return ethers.utils.splitSignature(core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])]));
return splitSignature(core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])]));
}

export class SignerAdapter {
protected readonly nodeAdapter: Isolation.Adapters.BIP32;
readonly provider?: ethers.providers.Provider;
readonly provider?: providers.Provider;

constructor(nodeAdapter: Isolation.Adapters.BIP32, provider?: ethers.providers.Provider) {
constructor(nodeAdapter: Isolation.Adapters.BIP32, provider?: providers.Provider) {
this.nodeAdapter = nodeAdapter;
this.provider = provider;
}
Expand All @@ -26,62 +36,62 @@ export class SignerAdapter {
// wrapper that deferred its initialization and awaited it before calling through to a "real" method, but that's
// a lot of complexity just to implement this one method we don't actually use.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
connect(_provider: ethers.providers.Provider): never {
connect(_provider: providers.Provider): never {
throw new Error("changing providers on a SignerAdapter is unsupported");
}

async getAddress(addressNList: core.BIP32Path): Promise<string> {
const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList));
return ethers.utils.computeAddress(SecP256K1.UncompressedPoint.from(nodeAdapter.getPublicKey()));
return computeAddress(SecP256K1.UncompressedPoint.from(nodeAdapter.getPublicKey()));
}

async signDigest(digest: ethers.BytesLike, addressNList: core.BIP32Path): Promise<ethers.Signature> {
async signDigest(digest: BytesLike, addressNList: core.BIP32Path): Promise<Signature> {
const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList));
const recoverableSig = await SecP256K1.RecoverableSignature.signCanonically(
nodeAdapter.node,
null,
digest instanceof Uint8Array ? digest : ethers.utils.arrayify(digest)
digest instanceof Uint8Array ? digest : arrayify(digest)
);
const sig = SecP256K1.RecoverableSignature.sig(recoverableSig);
const recoveryParam = SecP256K1.RecoverableSignature.recoveryParam(recoverableSig);
return ethers.utils.splitSignature(core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])]));
return splitSignature(core.compatibleBufferConcat([sig, Buffer.from([recoveryParam])]));
}

async signTransaction(
transaction: ethers.utils.Deferrable<ethers.providers.TransactionRequest>,
transaction: Deferrable<providers.TransactionRequest>,
addressNList: core.BIP32Path
): Promise<string> {
const tx = await ethers.utils.resolveProperties(transaction);
const tx = await resolveProperties(transaction);
if (tx.from != null) {
if (ethers.utils.getAddress(tx.from) !== (await this.getAddress(addressNList))) {
if (getAddress(tx.from) !== (await this.getAddress(addressNList))) {
throw new Error("transaction from address mismatch");
}
delete tx.from;
}
const unsignedTx: ethers.UnsignedTransaction = {
const unsignedTx: UnsignedTransaction = {
...tx,
nonce: tx.nonce !== undefined ? ethers.BigNumber.from(tx.nonce).toNumber() : undefined,
nonce: tx.nonce !== undefined ? BigNumber.from(tx.nonce).toNumber() : undefined,
};

const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList));
const txBuf = ethers.utils.arrayify(ethers.utils.serializeTransaction(unsignedTx));
const txBuf = arrayify(serializeTransaction(unsignedTx));
const rawSig = await SecP256K1.RecoverableSignature.signCanonically(nodeAdapter.node, "keccak256", txBuf);
return ethers.utils.serializeTransaction(unsignedTx, ethSigFromRecoverableSig(rawSig));
return serializeTransaction(unsignedTx, ethSigFromRecoverableSig(rawSig));
}

async signMessage(messageData: ethers.BytesLike, addressNList: core.BIP32Path): Promise<string> {
async signMessage(messageData: BytesLike, addressNList: core.BIP32Path): Promise<string> {
const messageBuf = buildMessage(messageData);
const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList));
const rawSig = await SecP256K1.RecoverableSignature.signCanonically(nodeAdapter.node, "keccak256", messageBuf);
return ethers.utils.joinSignature(ethSigFromRecoverableSig(rawSig));
return joinSignature(ethSigFromRecoverableSig(rawSig));
}

async signTypedData(typedData: TypedData, addressNList: core.BIP32Path): Promise<core.ETHSignedTypedData> {
const address = await this.getAddress(addressNList);
const messageArray = getMessage(typedData);
const nodeAdapter = await this.nodeAdapter.derivePath(core.addressNListToBIP32(addressNList));
const rawSig = await SecP256K1.RecoverableSignature.signCanonically(nodeAdapter.node, "keccak256", messageArray);
const signature = ethers.utils.joinSignature(ethSigFromRecoverableSig(rawSig));
const signature = joinSignature(ethSigFromRecoverableSig(rawSig));
return { address, signature };
}
}
Expand Down
6 changes: 3 additions & 3 deletions packages/hdwallet-native/src/ethereum.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as core from "@shapeshiftoss/hdwallet-core";
import * as ethers from "ethers";
import { parseTransaction } from "ethers/lib/utils.js";

import * as native from "./native";

Expand Down Expand Up @@ -143,7 +143,7 @@ describe("NativeETHWallet", () => {
"v": 38,
}
`);*/
expect(ethers.utils.parseTransaction(sig!.serialized).from).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
expect(parseTransaction(sig!.serialized).from).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
});

it("should sign a EIP-1559 transaction correctly", async () => {
Expand Down Expand Up @@ -177,7 +177,7 @@ describe("NativeETHWallet", () => {
"v": 38,
}
`);*/
expect(ethers.utils.parseTransaction(sig!.serialized).from).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
expect(parseTransaction(sig!.serialized).from).toEqual("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
});

describe("sign and verify message", () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/hdwallet-native/src/ethereum.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as core from "@shapeshiftoss/hdwallet-core";
import * as ethers from "ethers";
import { keccak256, parseTransaction, recoverAddress } from "ethers/lib/utils.js";

import * as Isolation from "./crypto/isolation";
import SignerAdapter from "./crypto/isolation/adapters/ethereum";
Expand Down Expand Up @@ -111,7 +111,7 @@ export function MixinNativeETHWallet<TBase extends core.Constructor<NativeHDWall
msg.addressNList
);

const decoded = ethers.utils.parseTransaction(result);
const decoded = parseTransaction(result);
return {
v: core.mustBeDefined(decoded.v),
r: core.mustBeDefined(decoded.r),
Expand Down Expand Up @@ -142,8 +142,8 @@ export function MixinNativeETHWallet<TBase extends core.Constructor<NativeHDWall

async ethVerifyMessage({ address, message, signature }: core.ETHVerifyMessage): Promise<boolean> {
if (!signature.startsWith("0x")) signature = `0x${signature}`;
const digest = ethers.utils.keccak256(buildMessage(message));
return ethers.utils.recoverAddress(digest, signature) === address;
const digest = keccak256(buildMessage(message));
return recoverAddress(digest, signature) === address;
}
};
}
4 changes: 2 additions & 2 deletions packages/hdwallet-tallyho/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as core from "@shapeshiftoss/hdwallet-core";
import * as ethers from "ethers";
import { providers } from "ethers";
import TallyHoOnboarding from "tallyho-onboarding";

import { TallyHoHDWallet } from "./tallyho";

interface TallyHoEthereumProvider extends ethers.providers.ExternalProvider {
interface TallyHoEthereumProvider extends providers.ExternalProvider {
isTally?: boolean;
}

Expand Down
20 changes: 10 additions & 10 deletions packages/hdwallet-tallyho/src/ethereum.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as core from "@shapeshiftoss/hdwallet-core";
import * as ethers from "ethers";
import { providers } from "ethers";

import * as ethereum from "./ethereum";

describe("Tally Ho - Ethereum Adapter", () => {
it("ethVerifyMessage returns null as its not implemented", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockReturnValue("0x3f2329C9ADFbcCd9A84f52c906E936A42dA18CB8"),
};
expect(
Expand Down Expand Up @@ -36,7 +36,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
expect(paths).toMatchObject([]);
});
it("ethSignTx returns null as its not implemented", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockReturnValue({
r: "0x63db3dd3bf3e1fe7dde1969c0fc8850e34116d0b501c0483a0e08c0f77b8ce0a",
s: "0x28297d012cccf389f6332415e96ee3fc0bbf8474d05f646e029cd281a031464b",
Expand All @@ -63,7 +63,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
).toEqual(null);
});
it("ethSendTx returns a valid hash", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockReturnValue("0x123"),
};

Expand All @@ -85,7 +85,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
expect(hash).toMatchObject({ hash: "0x123" });
});
it("ethSendTx returns a valid hash if maxFeePerGas is present in msg", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockReturnValue("0x123"),
};

Expand All @@ -107,7 +107,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
expect(hash).toMatchObject({ hash: "0x123" });
});
it("ethSendTx returns null on error", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
};

Expand All @@ -130,7 +130,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
});

it("ethSignMessage returns a valid signature object", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockReturnValue(
`Object {
"address": "0x73d0385F4d8E00C5e6504C6030F47BF6212736A8",
Expand Down Expand Up @@ -201,7 +201,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
});

it("ethSignMessage returns null on error", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockRejectedValue(new Error("An Error has occurred")),
};

Expand All @@ -219,7 +219,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
});

it("ethGetAddress returns a valid address", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockReturnValue(["0x73d0385F4d8E00C5e6504C6030F47BF6212736A8"]),
};

Expand All @@ -228,7 +228,7 @@ describe("Tally Ho - Ethereum Adapter", () => {
expect(address).toBe("0x73d0385F4d8E00C5e6504C6030F47BF6212736A8");
});
it("ethGetAddress returns null on error", async () => {
const ethereumProvider: ethers.providers.ExternalProvider = {
const ethereumProvider: providers.ExternalProvider = {
request: jest.fn().mockRejectedValue(new Error("An error has occurred")),
};

Expand Down
4 changes: 2 additions & 2 deletions packages/hdwallet-xdefi/src/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as core from "@shapeshiftoss/hdwallet-core";
import * as ethers from "ethers";
import { providers } from "ethers";

import { XDEFIHDWallet } from "./xdefi";

declare global {
// https://stackoverflow.com/questions/59459312/using-globalthis-in-typescript
// Global declarations require the use of var
// eslint-disable-next-line no-var
var xfi: { ethereum: ethers.providers.ExternalProvider } | null;
var xfi: { ethereum: providers.ExternalProvider } | null;
}

export class XDEFIAdapter {
Expand Down
Loading

0 comments on commit 4e011d5

Please sign in to comment.