1

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.

1
  • I ended up creating a Solidity contract specific for test purposes, that impersonates a Product and wrap itself around a Market. Its method being actual proxies to Market's methods, thus ensuring they are called from the proper invoker at runtime when testing using ethers.js.
    – Davy
    Sep 8 at 21:50
0

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

0

You can impersonate any account you want if you use the fork function of ganache.

With this, you can fork the Mainnet at a block and from there you can manipulate the blockchain as you like and send transactions as if you were another address by simply unlocking the desired account.

you can check their documentation on forking (it's relatively easy). It requires that you fork from an archive node.

Ethers.js would plug in to the new ganache node as any other geth node.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.