Skip to content

Commit

Permalink
Add an ability to change signer during the migration (#79)
Browse files Browse the repository at this point in the history
* Added an ability to change signer during the migration

* Updated CHANGELOG.md

* Updated package version

* Updated README.md
  • Loading branch information
KyrylR authored Jun 21, 2024
1 parent b83f35c commit aa1fa88
Show file tree
Hide file tree
Showing 14 changed files with 127 additions and 36 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Version 2.1.10

* Added an ability to change signer during the migration

## Version 2.1.9

* Updated dependencies
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,14 @@ Facilitates sending native assets to a specified address, primarily for the reco

---

- **setSigner(from <- optional)**:

Sets the signer for the following transactions and deployments.

If the `from` parameter is not specified, the signer is reset to the default.

---

- **getSigner(from <- optional)**:

Retrieves an ethers signer for use in migrations.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@solarity/hardhat-migrate",
"version": "2.1.9",
"version": "2.1.10",
"description": "Automatic deployment and verification of smart contracts",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
Expand Down
13 changes: 9 additions & 4 deletions src/deployer/Deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { isAddress, Signer } from "ethers";

import { HardhatRuntimeEnvironment } from "hardhat/types";

import { catchError, getChainId, getSignerHelper, isDeployedContractAddress } from "../utils";
import { catchError, getChainId, isDeployedContractAddress } from "../utils";

import { MigrateError } from "../errors";

Expand All @@ -21,6 +21,7 @@ import { isContractFactory, isEthersContract, isBytecodeFactory, isTruffleFactor

import { Stats } from "../tools/Stats";
import { Reporter } from "../tools/reporters/Reporter";
import { networkManager } from "../tools/network/NetworkManager";
import { TransactionRunner } from "../tools/runners/TransactionRunner";
import { TransactionProcessor } from "../tools/storage/TransactionProcessor";

Expand Down Expand Up @@ -103,7 +104,7 @@ export class Deployer {
value: bigint,
name: string = SEND_NATIVE_TX_NAME,
): Promise<TransactionFieldsToSave> {
const signer = await getSignerHelper();
const signer = await networkManager!.getSigner();

const tx = await this._buildSendTransaction(to, value, name);

Expand Down Expand Up @@ -138,8 +139,12 @@ export class Deployer {
return savedTx!;
}

public async setSigner(from?: string) {
await networkManager!.setSigner(from);
}

public async getSigner(from?: string): Promise<Signer> {
return getSignerHelper(from);
return networkManager!.getSigner(from);
}

public async getChainId(): Promise<bigint> {
Expand All @@ -152,7 +157,7 @@ export class Deployer {
value,
chainId: await getChainId(),
data: "0x",
from: (await getSignerHelper()).address,
from: (await networkManager!.getSigner()).address,
name,
};
}
Expand Down
7 changes: 4 additions & 3 deletions src/deployer/MinimalContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isFullyQualifiedName } from "hardhat/utils/contract-names";

import { Linker } from "./Linker";

import { catchError, fillParameters, getChainId, getInterfaceOnlyWithConstructor, getSignerHelper } from "../utils";
import { catchError, fillParameters, getChainId, getInterfaceOnlyWithConstructor } from "../utils";

import { MigrateError } from "../errors";

Expand All @@ -15,6 +15,7 @@ import { ContractDeployTxWithName, OverridesAndLibs } from "../types/deployer";

import { Stats } from "../tools/Stats";
import { Reporter } from "../tools/reporters/Reporter";
import { networkManager } from "../tools/network/NetworkManager";
import { TransactionRunner } from "../tools/runners/TransactionRunner";
import { ArtifactProcessor } from "../tools/storage/ArtifactProcessor";
import { TransactionProcessor } from "../tools/storage/TransactionProcessor";
Expand Down Expand Up @@ -77,7 +78,7 @@ export class MinimalContract {
return {
contractName: this._contractName,
chainId: await getChainId(),
from: (await getSignerHelper(txOverrides.from)).address,
from: (await networkManager!.getSigner(txOverrides.from)).address,
...(await factory.getDeployTransaction(...args, txOverrides)),
};
}
Expand All @@ -101,7 +102,7 @@ export class MinimalContract {
}

private async _processContractDeploymentTransaction(tx: ContractDeployTxWithName, args: any[]): Promise<string> {
const signer: Signer = await getSignerHelper(tx.from);
const signer: Signer = await networkManager!.getSigner(tx.from);

const txResponse = await signer.sendTransaction(tx);

Expand Down
7 changes: 4 additions & 3 deletions src/deployer/adapters/AbstractEthersAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ import "../../type-extensions";

import { UNKNOWN_TRANSACTION_NAME } from "../../constants";

import { bytecodeToString, fillParameters, getMethodString, getSignerHelper } from "../../utils";
import { bytecodeToString, fillParameters, getMethodString } from "../../utils";

import { OverridesAndLibs, OverridesAndName } from "../../types/deployer";
import { EthersContract, BytecodeFactory } from "../../types/adapter";
import { KeyTransactionFields, MigrationMetadata, TransactionFieldsToSave } from "../../types/tools";

import { Stats } from "../../tools/Stats";
import { Reporter } from "../../tools/reporters/Reporter";
import { networkManager } from "../../tools/network/NetworkManager";
import { TransactionRunner } from "../../tools/runners/TransactionRunner";
import { TransactionProcessor } from "../../tools/storage/TransactionProcessor";

Expand All @@ -46,7 +47,7 @@ export abstract class AbstractEthersAdapter extends Adapter {
}

public async toInstance<A, I>(instance: Factory<A, I>, address: string, parameters: OverridesAndLibs): Promise<I> {
const signer = await getSignerHelper(parameters.from);
const signer = await networkManager!.getSigner(parameters.from);
const contractName = this.getContractName(instance, parameters);

let contract = new BaseContract(address, this.getInterface(instance), signer);
Expand Down Expand Up @@ -102,7 +103,7 @@ export abstract class AbstractEthersAdapter extends Adapter {
contractInterface: Interface,
contractName: string,
): Promise<BaseContract> {
const defaultRunner = await getSignerHelper();
const defaultRunner = await networkManager!.getSigner();

contract.connect = (runner: ContractRunner | null): BaseContract => {
const newContract = new BaseContract(contract.target, contractInterface, runner ?? defaultRunner);
Expand Down
14 changes: 4 additions & 10 deletions src/deployer/adapters/TruffleAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,7 @@ import { Adapter } from "./Adapter";

import { MinimalContract } from "../MinimalContract";

import {
bytecodeToString,
catchError,
fillParameters,
getInstanceNameFromClass,
getMethodString,
getSignerHelper,
} from "../../utils";
import { bytecodeToString, catchError, fillParameters, getInstanceNameFromClass, getMethodString } from "../../utils";

import { UNKNOWN_TRANSACTION_NAME } from "../../constants";

Expand All @@ -31,6 +24,7 @@ import { OverridesAndName, TruffleTransactionResponse } from "../../types/deploy

import { Stats } from "../../tools/Stats";
import { Reporter } from "../../tools/reporters/Reporter";
import { networkManager } from "../../tools/network/NetworkManager";
import { TransactionRunner } from "../../tools/runners/TransactionRunner";
import { ArtifactProcessor } from "../../tools/storage/ArtifactProcessor";
import { TransactionProcessor } from "../../tools/storage/TransactionProcessor";
Expand Down Expand Up @@ -133,14 +127,14 @@ export class TruffleAdapter extends Adapter {
const tx = await contractMethod.populateTransaction(...args);

// In case if the `from` field is not specified, it should be filled with the default signer.
tx.from = tx.from ?? (await getSignerHelper()).address;
tx.from = tx.from ?? (await networkManager!.getSigner()).address;

await fillParameters(tx);

const keyFields = this._getKeyFieldsFromTransaction(tx, contractMethod, args);

// Connect to signer and get method again with signer
contractMethod = ethersBaseContract.connect(await getSignerHelper(tx.from)).getFunction(methodName);
contractMethod = ethersBaseContract.connect(await networkManager!.getSigner(tx.from)).getFunction(methodName);

const methodString = getMethodString(contractName, methodName, contractMethod.fragment, args);

Expand Down
18 changes: 18 additions & 0 deletions src/tools/network/NetworkManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios, { Axios } from "axios";
import { AddressLike, ethers } from "ethers";

import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import type { HardhatEthersProvider as HardhatEthersProviderT } from "@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider";

import { HardhatRuntimeEnvironment } from "hardhat/types";
Expand Down Expand Up @@ -61,11 +63,27 @@ class NetworkManager {
public axios: Axios;
public provider: HardhatEthersProviderT;

private currentFrom: string | undefined = undefined;

constructor() {
this.axios = this.withRetry(axios);
this.provider = this.withRetry(ethersProvider!);
}

public async getSigner(from?: null | AddressLike): Promise<HardhatEthersSigner> {
if (!from) {
return this.provider.getSigner(this.currentFrom);
}

const address = await ethers.resolveAddress(from, this.provider);

return this.provider.getSigner(address);
}

public async setSigner(from?: AddressLike): Promise<void> {
this.currentFrom = from ? await ethers.resolveAddress(from, this.provider) : undefined;
}

public withRetry<T extends { [key: string]: any }>(instance: T): T {
return new Proxy(instance, {
get(target, propKey, receiver) {
Expand Down
14 changes: 1 addition & 13 deletions src/utils/network.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { AddressLike, ethers, toBigInt } from "ethers";

import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers";
import { toBigInt } from "ethers";

import { networkManager } from "../tools/network/NetworkManager";

Expand All @@ -11,13 +9,3 @@ export async function getChainId(): Promise<bigint> {
export async function isDeployedContractAddress(address: string): Promise<boolean> {
return (await networkManager!.provider.getCode(address)) !== "0x";
}

export async function getSignerHelper(from?: null | AddressLike): Promise<HardhatEthersSigner> {
if (!from) {
return networkManager!.provider.getSigner();
}

const address = await ethers.resolveAddress(from, networkManager!.provider);

return networkManager!.provider.getSigner(address);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Deployer } from "../../../../src/deployer/Deployer";

import { GovToken__factory } from "../typechain-types";

export = async (deployer: Deployer) => {
await deployer.setSigner("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");

await deployer.deploy(GovToken__factory, ["Token", "TKN"], { name: "Governance Token #12" });

const govToken5 = await deployer.deployed(GovToken__factory, "Governance Token #12");

if ((await govToken5.owner()) !== "0x70997970C51812dc3A010C7d01b50e0d17dc79C8") {
console.error("Owner is not set correctly");
process.exit(1);
}

await deployer.setSigner();

const newToken = await deployer.deploy(GovToken__factory, ["Token", "TKN"], { name: "Governance Token #13" });

if ((await newToken.owner()) !== "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") {
console.error("Owner is not set correctly");
process.exit(1);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Deployer } from "../../../../src/deployer/Deployer";

export = async (deployer: Deployer) => {
// Moved here for testing reasons, before it was initialized globally once and failed on the second test on the .link function.
// Because Truffle throws an error if attempting to link the same object twice to the same library.
const GovToken = artifacts.require("GovToken");

await deployer.setSigner("0x70997970C51812dc3A010C7d01b50e0d17dc79C8");

await deployer.deploy(GovToken, ["Token", "TKN"], { name: "Governance Token #12" });

const govToken5 = await deployer.deployed(GovToken, "Governance Token #12");

if ((await govToken5.owner()) !== "0x70997970C51812dc3A010C7d01b50e0d17dc79C8") {
console.error("Owner is not set correctly");
process.exit(1);
}

await deployer.setSigner();

const newToken = await deployer.deploy(GovToken, ["Token", "TKN"], { name: "Governance Token #13" });

if ((await newToken.owner()) !== "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266") {
console.error("Owner is not set correctly");
process.exit(1);
}
};
10 changes: 10 additions & 0 deletions test/integration/migration/typechain-ethers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,14 @@ describe("typechain-ethers", () => {
await runWithContinue(hre, 3);
});
});

describe("migration flow for edge cases", () => {
it("should run migration successfully", async function () {
await runWithoutContinue(hre, 4);
});

it("should recover migration successfully", async function () {
await runWithContinue(hre, 4);
});
});
});
10 changes: 10 additions & 0 deletions test/integration/migration/typechain-truffle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,14 @@ describe("typechain-truffle", () => {
await runWithContinue(hre, 3);
});
});

describe("migration flow for different signers", () => {
it("should run migration successfully", async function () {
await runWithoutContinue(hre, 4);
});

it("should recover migration successfully", async function () {
await runWithContinue(hre, 4);
});
});
});

0 comments on commit aa1fa88

Please sign in to comment.