I'm new to Solidity dev and writing tests for a dapp (Hardhat, Chai, ethers.js, Smockit) where two contracts, Product
and Market
, coexist. I'm trying to run a test for a function in Market
that can only be called from a Product
instance.
Here's the Market
contract. It holds a reference to a Product
, and has a foo
method that will revert if not called from that said Product
.
pragma solidity ^0.8.4;
contract Market {
public address productAddr;
constructor(address addr) {
productAddr = addr;
}
function foo() external {
require(msg.sender == productAddr, "nope!");
// do something...
}
}
Accordingly, while testing the foo
method, I'd need to ensure the call is made from a Product
contract instance, otherwise, it'll revert. How do I do that with ethers.js?
I have tried using .connect
, which expects a Signer or Provider as parameter, like so…
const signers = await ethers.getSigners();
const ProductFactory = await ethers.getContractFactory('Product', signers[0]);
const MarketFactory = await ethers.getContractFactory('Market', signers[0]);
const product = await ProductFactory.deploy();
const market = await MarketFactory.deploy(product.address); // store product address
market.connect(product.signer).foo(); // not working :-(
…but it reverts, as product.signer
returns a reference to signers[0]
, not a reference to the contract I'd like to present as caller, obviously.
How should I proceed? Any help would be greatly appreciated and would broaden my knowledge.
Best Answer
contract.signer
returns the signer of the contract deployer account, not the signer of the contract's own address. It's going to be really difficult to impersonate an account address, as the impersonated account would need to contain ETH to pay for the any transaction generated through it. So I would imaging you would first need to expect what the contract address would be (maybe using CREATE2), and then send it some ETH, then create the contract at that address you sent ETH to and impersonate it to generate that transaction. If you do it the other way around, the contract address most likely would have a fallback function to refuse accepting any sent ETH. You'll need to use Hardhat to be able to impersonate an account, here's how to do it: https://hardhat.org/hardhat-network/reference/#hardhat-impersonateaccount