Skip to content

Commit

Permalink
Fix behaviour of connect method in Ethers (#75)
Browse files Browse the repository at this point in the history
* Fixed behaviour of connect method in Ethers

* Fixed behaviour of connect method in Ethers

* Added return to _insertAddressGetter

* Updated CI (matrix:node)
  • Loading branch information
KyrylR authored Mar 18, 2024
1 parent 5407225 commit 78f3024
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 60 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jobs:
strategy:
matrix:
node:
- 16
- 18
- 20
steps:
- uses: actions/setup-node@v3
with:
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Version 2.1.5

* Fixed behaviour of connect method in Ethers Adapter.

## Version 2.1.4

* Deleted `pinst` package.

## Version 2.1.3

* Added the `verificationDelay` parameter, which defines the time in milliseconds that must pass before the verification process starts.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,4 +296,4 @@ If verification fails, the `attempts` parameter indicates how many additional re

- This plugin, as well as the [Hardhat Toolbox](https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-toolbox) plugin, use the [@nomicfoundation/hardhat-verify](https://www.npmjs.com/package/@nomicfoundation/hardhat-verify) plugin internally, so both of these plugins cannot be imported at the same time. A quick fix is to manually import the needed plugins that ToolBox imports.
- Adding, removing, moving or renaming new contracts to the hardhat project or reorganizing the directory structure of contracts after deployment may alter the resulting bytecode in some solc versions. See this [Solidity issue](https://github.com/ethereum/solidity/issues/9573) for further information.
- This plugin does not function properly with native Truffle methods, such as in `contract.deployed()`, unless otherwise specified above at the instance level. For example, instead of using `contract.deployed()`, it is necessary to use the `deployer.deploy()` method.
- This plugin does not function properly with native Truffle or Ethers factories methods, such as `contract.deployed()`, `factory.at()`, or in case of ethers `factory.attach()`. So, instead of using mentioned methods, it is necessary to use the `deployer.deployed()`.
18 changes: 16 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.4",
"version": "2.1.5",
"description": "Automatic deployment and verification of smart contracts",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
Expand Down
34 changes: 22 additions & 12 deletions src/deployer/adapters/AbstractEthersAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
BaseContract,
BaseContractMethod,
ContractFactory,
ContractRunner,
ContractTransaction,
ContractTransactionReceipt,
ContractTransactionResponse,
Expand Down Expand Up @@ -31,8 +32,6 @@ import { TransactionProcessor } from "../../tools/storage/TransactionProcessor";
type Factory<A, I> = EthersContract<A, I> | BytecodeFactory | ContractFactory;

export abstract class AbstractEthersAdapter extends Adapter {
private static _processedClasses = new Set<string>();

public getRawBytecode<A, I>(instance: Factory<A, I>): string {
return bytecodeToString(instance.bytecode);
}
Expand All @@ -48,18 +47,13 @@ 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 contract = new BaseContract(address, this.getInterface(instance), signer);

const contractName = this.getContractName(instance, parameters);

if (!AbstractEthersAdapter._processedClasses.has(contractName)) {
AbstractEthersAdapter._processedClasses.add(contractName);
let contract = new BaseContract(address, this.getInterface(instance), signer);

await this._overrideConnectMethod(instance, contractName);
}
contract = this._insertAddressGetter(contract, address);
contract = await this._overrideConnectMethod(contract, this.getInterface(instance), contractName);

this._insertAddressGetter(contract, address);
return this._insertHandlers(contract, contractName) as unknown as I;
}

Expand Down Expand Up @@ -103,10 +97,26 @@ export abstract class AbstractEthersAdapter extends Adapter {
return contract;
}

protected abstract _overrideConnectMethod<A, I>(instance: Factory<A, I>, contractName: string): Promise<void>;
protected async _overrideConnectMethod(
contract: BaseContract,
contractInterface: Interface,
contractName: string,
): Promise<BaseContract> {
const defaultRunner = await getSignerHelper();

contract.connect = (runner: ContractRunner | null): BaseContract => {
const newContract = new BaseContract(contract.target, contractInterface, runner ?? defaultRunner);

private _insertAddressGetter(contract: BaseContract, contractAddress: string): void {
return this._insertHandlers(newContract, contractName) as any;
};

return contract;
}

private _insertAddressGetter(contract: BaseContract, contractAddress: string): BaseContract {
(contract as any).address = contractAddress;

return contract;
}

private _getContractFunctionFragments(contractInterface: Interface): FunctionFragment[] {
Expand Down
5 changes: 0 additions & 5 deletions src/deployer/adapters/BytecodeAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,4 @@ export class BytecodeAdapter extends AbstractEthersAdapter {
public getContractName(instance: BytecodeFactory): string {
return instance.contractName;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected async _overrideConnectMethod(_instance: BytecodeFactory, _contractName: string) {
return;
}
}
16 changes: 2 additions & 14 deletions src/deployer/adapters/EthersContractAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { BaseContract, ContractRunner, Interface } from "ethers";
import { Interface } from "ethers";

import { AbstractEthersAdapter } from "./AbstractEthersAdapter";

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

import { EthersContract } from "../../types/adapter";
import { OverridesAndName } from "../../types/deployer";
Expand Down Expand Up @@ -34,16 +34,4 @@ export class EthersContractAdapter extends AbstractEthersAdapter {
return getInstanceNameFromClass(instance);
}
}

protected async _overrideConnectMethod<A, I>(instance: EthersContract<A, I>, contractName: string) {
const connectMethod = instance.connect;

const defaultRunner = await getSignerHelper();

instance.connect = (address: string, runner?: ContractRunner): I => {
const contract = connectMethod(address, runner ?? defaultRunner) as BaseContract;

return this._insertHandlers(contract, contractName) as unknown as I;
};
}
}
12 changes: 1 addition & 11 deletions src/deployer/adapters/EthersFactoryAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Addressable, ContractFactory, Interface } from "ethers";
import { ContractFactory, Interface } from "ethers";

import { AbstractEthersAdapter } from "./AbstractEthersAdapter";

Expand Down Expand Up @@ -33,14 +33,4 @@ export class EthersFactoryAdapter extends AbstractEthersAdapter {
return getInstanceNameFromClass(instance);
}
}

protected async _overrideConnectMethod(instance: ContractFactory, contractName: string) {
const attachMethod = instance.attach;

instance.attach = (target: string | Addressable): any => {
const contract = attachMethod(target);

return this._insertHandlers(contract, contractName);
};
}
}
14 changes: 2 additions & 12 deletions src/deployer/adapters/TruffleAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export class TruffleAdapter extends Adapter {
public async toInstance<I>(instance: TruffleFactory<I>, address: string, _: OverridesAndName): Promise<I> {
const contract = this._hre.artifacts.require(instance.contractName!);

await this._overrideConnectMethod(contract);

const contractInstance = await contract.at(address);
(instance as any).setAsDeployed(contractInstance);

this._insertHandlers(instance, contractInstance, address);

return contractInstance;
}

Expand Down Expand Up @@ -93,16 +93,6 @@ export class TruffleAdapter extends Adapter {
}
}

protected async _overrideConnectMethod<I>(instance: TruffleFactory<I>) {
const atMethod = instance.at;

instance.at = async (address: string): Promise<I> => {
const contract = await atMethod(address);

return this._insertHandlers(instance, contract, address);
};
}

protected _insertHandlers<I>(instance: TruffleFactory<I>, contract: I, address: string): I {
const contractName = this.getContractName(instance, {});
const ethersBaseContract: BaseContract = new BaseContract(address, this.getInterface(instance));
Expand Down
24 changes: 23 additions & 1 deletion test/integration/deployer/base-contract-interaction.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { HardhatRuntimeEnvironment } from "hardhat/types";

import { expect } from "chai";

import { useEnvironment } from "../../helpers";
Expand All @@ -17,14 +19,18 @@ import { TransactionStorage } from "../../../src/tools/storage/MigrateStorage";
describe("deployer", () => {
let deployer: Deployer;

describe("default interaction with contracts", () => {
describe("default interaction with contracts (Ethers)", () => {
useEnvironment("typechain-ethers");

let hre: HardhatRuntimeEnvironment;

beforeEach("setup", async function () {
await Migrator.buildMigrateTaskDeps(this.hre);

deployer = new Deployer(this.hre);

hre = this.hre;

TransactionStorage.clear();
});

Expand Down Expand Up @@ -65,5 +71,21 @@ describe("deployer", () => {

expect(await ethersProvider!.provider.getBalance(contract.getAddress())).to.equal(toPay);
});

it("should connect to different signer and send transaction", async function () {
const [signer1, signer2] = await hre.ethers.getSigners();

const contract = await deployer.deploy(PayableReceive__factory);

let tx = await contract.pay({ value: 100n });
let receipt = await tx.wait();

expect(receipt!.from).to.equal(signer1.address);

tx = await contract.connect(signer2).pay({ value: 110n });
receipt = await tx.wait();

expect(receipt!.from).to.equal(signer2.address);
});
});
});

0 comments on commit 78f3024

Please sign in to comment.