Skip to content

Commit

Permalink
fix: refactor code and fix signer to support impersonated signers
Browse files Browse the repository at this point in the history
  • Loading branch information
kiriyaga committed Aug 20, 2024
1 parent 4355690 commit 3511fb3
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 64 deletions.
9 changes: 4 additions & 5 deletions packages/hardhat-zksync-ethers/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
export const PLUGIN_NAME = '@matterlabs/hardhat-zksync-ethers';

export const LOCAL_CHAIN_IDS = [
'0x104', // era-node
'0x10e', // local-setup
];

// eslint-disable-next-line @typescript-eslint/naming-convention
export enum LOCAL_CHAIN_IDS_ENUM {
ERA_NODE = '0x104',
LOCAL_SETUP = '0x10e',
}

export const LOCAL_CHAIN_IDS = [LOCAL_CHAIN_IDS_ENUM.ERA_NODE, LOCAL_CHAIN_IDS_ENUM.LOCAL_SETUP];

export const LOCAL_CHAINS_WITH_IMPERSONATION = [LOCAL_CHAIN_IDS_ENUM.ERA_NODE];

export const ZKSOLC_ARTIFACT_FORMAT_VERSION = 'hh-zksolc-artifact-1';
export const ZKVYPER_ARTIFACT_FORMAT_VERSION = 'hh-zkvyper-artifact-1';

Expand Down
50 changes: 2 additions & 48 deletions packages/hardhat-zksync-ethers/src/extension-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,9 @@ import {
makeGetContractFactory,
} from './helpers';
import { HardhatZksyncSignerOrWallet, HardhatZksyncSignerOrWalletOrFactoryOptions, ZkSyncArtifact } from './types';
import { Generator } from './generator';

export class ExtensionGenerator {
constructor(private _hre: HardhatRuntimeEnvironment) {}

public populatedExtension(): any {
if (this._hre.network.zksync) {
const zkSyncGenerator = new ZkSyncGenerator(this._hre);
return zkSyncGenerator.populateExtension();
}

const ethersGenerators = new EthersGenerator(this._hre);
return ethersGenerators.populateExtension();
}
}

interface Generator {
populateExtension(): any;
}

class ZkSyncGenerator implements Generator {
export class ZkSyncGenerator implements Generator {
constructor(private _hre: HardhatRuntimeEnvironment) {}

public populateExtension(): any {
Expand Down Expand Up @@ -90,32 +73,3 @@ class ZkSyncGenerator implements Generator {
});
}
}

class EthersGenerator implements Generator {
constructor(private _hre: HardhatRuntimeEnvironment) {}

public populateExtension(): any {
return lazyObject(() => {
const hardhatEthersHelpers = require('@nomiclabs/hardhat-ethers/internal/helpers');
const { createProviderProxy } = require('@nomiclabs/hardhat-ethers/internal/provider-proxy');
const { ethers } = require('ethers');
const provider = new createProviderProxy(this._hre.network.provider);
return {
...ethers,
provider,
getSigner: (address: string) => hardhatEthersHelpers.getSigner(this._hre, address),
getSigners: () => hardhatEthersHelpers.getSigners(this._hre),
getImpersonatedSigner: (address: string) =>
hardhatEthersHelpers.getImpersonatedSigner(this._hre, address),
getContractFactory: hardhatEthersHelpers.getContractFactory.bind(null, this._hre) as any,
getContractFactoryFromArtifact: hardhatEthersHelpers.getContractFactoryFromArtifact.bind(
null,
this._hre,
),
getContractAt: hardhatEthersHelpers.getContractAt.bind(null, this._hre),
getContractAtFromArtifact: hardhatEthersHelpers.getContractAtFromArtifact.bind(null, this._hre),
deployContract: hardhatEthersHelpers.deployContract.bind(null, this._hre) as any,
};
});
}
}
21 changes: 21 additions & 0 deletions packages/hardhat-zksync-ethers/src/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { ZkSyncGenerator } from './extension-generator';
import { EthersGenerator } from './hardhat-ethers/extension-generator';

export class ExtensionGenerator {
constructor(private _hre: HardhatRuntimeEnvironment) {}

public populatedExtension(): any {
if (this._hre.network.zksync) {
const zkSyncGenerator = new ZkSyncGenerator(this._hre);
return zkSyncGenerator.populateExtension();
}

const ethersGenerators = new EthersGenerator(this._hre);
return ethersGenerators.populateExtension();
}
}

export interface Generator {
populateExtension(): any;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { lazyObject } from 'hardhat/plugins';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { Generator } from '../generator';

export class EthersGenerator implements Generator {
constructor(private _hre: HardhatRuntimeEnvironment) {}

public populateExtension(): any {
return lazyObject(() => {
const hardhatEthersHelpers = require('@nomiclabs/hardhat-ethers/internal/helpers');
const { createProviderProxy } = require('@nomiclabs/hardhat-ethers/internal/provider-proxy');
const { ethers } = require('ethers');
const provider = new createProviderProxy(this._hre.network.provider);
return {
...ethers,
provider,
getSigner: (address: string) => hardhatEthersHelpers.getSigner(this._hre, address),
getSigners: () => hardhatEthersHelpers.getSigners(this._hre),
getImpersonatedSigner: (address: string) =>
hardhatEthersHelpers.getImpersonatedSigner(this._hre, address),
getContractFactory: hardhatEthersHelpers.getContractFactory.bind(null, this._hre) as any,
getContractFactoryFromArtifact: hardhatEthersHelpers.getContractFactoryFromArtifact.bind(
null,
this._hre,
),
getContractAt: hardhatEthersHelpers.getContractAt.bind(null, this._hre),
getContractAtFromArtifact: hardhatEthersHelpers.getContractAtFromArtifact.bind(null, this._hre),
deployContract: hardhatEthersHelpers.deployContract.bind(null, this._hre) as any,
};
});
}
}
62 changes: 56 additions & 6 deletions packages/hardhat-zksync-ethers/src/hardhat-zksync-signer.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Provider, Signer, Wallet } from 'zksync-ethers';
import { EIP712Signer, Provider, Signer, Wallet } from 'zksync-ethers';
import { TransactionRequest, TransactionResponse } from 'zksync-ethers/build/types';
import { isAddressEq } from 'zksync-ethers/build/utils';
import { EIP712_TX_TYPE, isAddressEq, serialize } from 'zksync-ethers/build/utils';
import { ethers } from 'ethers';
import { findWalletFromAddress } from './utils';
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { findWalletFromAddress, isImpersonatedSigner } from './utils';
import { HardhatZksyncEthersProvider } from './hardhat-zksync-provider';
import { richWallets } from './rich-wallets';
import { LOCAL_CHAIN_IDS_ENUM } from './constants';

export class HardhatZksyncSigner extends Signer {
private accountWallet?: Wallet;
private accountWallet?: Wallet | EIP712Signer | undefined;

public static from(
signer: ethers.providers.JsonRpcSigner & { provider: HardhatZksyncEthersProvider },
Expand All @@ -22,9 +25,9 @@ export class HardhatZksyncSigner extends Signer {

public async sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
if (!this.accountWallet) {
this.accountWallet = await findWalletFromAddress(
this._address,
this.accountWallet = await HardhatZksyncSigner._getProperSigner(
(this.provider as HardhatZksyncEthersProvider).hre,
this._address,
);
}

Expand All @@ -41,6 +44,53 @@ export class HardhatZksyncSigner extends Signer {
throw new Error(`Account ${from} is not managed by the node you are connected to.`);
}

if (this.accountWallet instanceof EIP712Signer) {
return this._sendTransaction(transaction);
}

return this.accountWallet.sendTransaction(transaction);
}

private async _sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
if (!transaction.customData && !transaction.type) {
// use legacy txs by default
transaction.type = 0;
}
if (!transaction.customData && transaction.type !== EIP712_TX_TYPE) {
return (await super.sendTransaction(transaction)) as TransactionResponse;
} else {
const address = await this.getAddress();
transaction.from ??= address;
if (!isAddressEq(transaction.from, address)) {
throw new Error('Transaction `from` address mismatch!');
}
transaction.type = EIP712_TX_TYPE;
transaction.value ??= 0;
transaction.data ??= '0x';
transaction.nonce ??= await this.getNonce();
transaction.customData = this._fillCustomData(transaction.customData ?? {});
transaction.gasPrice ??= await this.provider.getGasPrice();
transaction.gasLimit ??= await this.provider.estimateGas(transaction);
transaction.chainId ??= (await this.provider.getNetwork()).chainId;
transaction.customData.customSignature = await (this.accountWallet as EIP712Signer).sign(transaction);

const txBytes = serialize(transaction);
return await this.provider.sendTransaction(txBytes);
}
}

private static async _getProperSigner(
hre: HardhatRuntimeEnvironment,
address: string,
): Promise<Wallet | EIP712Signer | undefined> {
let signer: Wallet | EIP712Signer | undefined = await findWalletFromAddress(address, hre);
if (!signer && (await isImpersonatedSigner(hre.ethers.provider, address))) {
signer = new EIP712Signer(
new Wallet(richWallets[LOCAL_CHAIN_IDS_ENUM.ERA_NODE][0].privateKey),
hre.ethers.provider.getNetwork().then((n) => Number(n.chainId)),
);
}

return signer;
}
}
2 changes: 1 addition & 1 deletion packages/hardhat-zksync-ethers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { extendEnvironment } from 'hardhat/config';
import './type-extensions';
import { ExtensionGenerator } from './extension-generator';
import { ExtensionGenerator } from './generator';

extendEnvironment((hre) => {
const extensionGenerator = new ExtensionGenerator(hre);
Expand Down
4 changes: 2 additions & 2 deletions packages/hardhat-zksync-ethers/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type * as ethers from 'ethers';
import type { Artifact } from 'hardhat/types';
import { Contract, ContractFactory, Provider, Signer, Wallet } from 'zksync-ethers';
import { Contract, ContractFactory, Provider, Wallet } from 'zksync-ethers';
import { Address, DeploymentType } from 'zksync-ethers/build/types';
import { HardhatZksyncSigner } from './hardhat-zksync-signer';

Expand Down Expand Up @@ -98,7 +98,7 @@ export interface HardhatZksyncEthersHelpers {
getWallet: (privateKeyOrIndex?: string | number) => Promise<Wallet>;
getContractFactory: typeof getContractFactory;
getContractAt: typeof getContractAt;
getImpersonatedSigner: (address: string) => Promise<Signer>;
getImpersonatedSigner: (address: string) => Promise<HardhatZksyncSigner>;
extractFactoryDeps: (artifact: ZkSyncArtifact) => Promise<string[]>;
loadArtifact: (name: string) => Promise<ZkSyncArtifact>;
deployContract: (
Expand Down
27 changes: 25 additions & 2 deletions packages/hardhat-zksync-ethers/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
HttpNetworkConfig,
NetworkConfig,
} from 'hardhat/types';
import { Signer, Wallet } from 'zksync-ethers';
import { Provider, Signer, Wallet } from 'zksync-ethers';
import { ethers } from 'ethers';
import { isAddressEq } from 'zksync-ethers/build/utils';
import {
Expand All @@ -15,7 +15,13 @@ import {
ZkFactoryOptions,
ZkSyncArtifact,
} from './types';
import { ETH_DEFAULT_NETWORK_RPC_URL, LOCAL_CHAIN_IDS, LOCAL_CHAIN_IDS_ENUM, SUPPORTED_L1_TESTNETS } from './constants';
import {
ETH_DEFAULT_NETWORK_RPC_URL,
LOCAL_CHAIN_IDS,
LOCAL_CHAIN_IDS_ENUM,
LOCAL_CHAINS_WITH_IMPERSONATION,
SUPPORTED_L1_TESTNETS,
} from './constants';
import { richWallets } from './rich-wallets';
import { ZkSyncEthersPluginError } from './errors';
import { HardhatZksyncEthersProvider } from './hardhat-zksync-provider';
Expand Down Expand Up @@ -243,3 +249,20 @@ export function isValidEthNetworkURL(string: string) {
return false;
}
}

export async function isImpersonatedSigner(provider: Provider, address: string): Promise<boolean> {
const chainId = await provider.send('eth_chainId', []);

if (!LOCAL_CHAINS_WITH_IMPERSONATION.includes(chainId)) {
return false;
}

const result = await provider.send('hardhat_stopImpersonatingAccount', [address]);

if (!result) {
return false;
}

await provider.send('hardhat_impersonateAccount', [address]);
return true;
}

0 comments on commit 3511fb3

Please sign in to comment.