67

I can deploy a contract using ethers and Hardhat with the following:

const myContract = await hre.ethers.getContractFactory("SomeContract");
const deployedContract = await myContract.deploy();

I can even interact with the methods from that deployed contract if I use that deployedContract variable. However what if the contract has already been deployed and I want to interact with it? From what I've read, I need to create an instance of that contract. And I've tried to do that like this:

const provider = new ethers.providers.JsonRpcProvider();
const fs = require('fs');
const abi = JSON.parse(fs.readFileSync('./abi/SomeContract.json', 'utf8'));
const contractInstance = new ethers.Contract('contract address goes here', abi, provider);

I believe an instance is created on that contractInstance variable, but if I try to call methods on that instance in the console or from a script, I get a WARNING: Calling an account which is not a contract error.

Maybe I'm misunderstanding something about how contract instances are supposed to work?

14 Answers 14

84

I ran into the same question and stumbled upon a section in the OpenZeppelin docs that may work for you (it did for me):

For example:

const MyContract = await ethers.getContractFactory("MyContract");
const contract = MyContract.attach(
  "0x..." // The deployed contract address
);

// Now you can call functions of the contract
await contract.doTheThing();
2
  • The only one that worked for me! But you can remove the "await" for MyContract.attach function. Thanks Commented Dec 8, 2022 at 16:44
  • that results in an error: "Tried to import a nonexistent artifact. Please double check that your contracts have been compiled and double check your artifact's name."
    – ekkis
    Commented Feb 29 at 7:13
30

hardhat-ethers ^2.0.0 has a special function getContractAt for exactly this purpose:

const contractAddress = "0x...",
const myContract = await hre.ethers.getContractAt("MyContract", contractAddress);
2
  • that also results in [HardhatError: HH700: Artifact for contract "MyContract" not found]. I have to have an "artifact" (whatever that means)
    – ekkis
    Commented Feb 29 at 7:17
  • An "artifact" is the output file of building/compiling a smart contract with hardhat. It contains "the information that is necessary to deploy and interact with the contract." (see: hardhat docs) and is generated when running hardhat compile for all solidity files in your project. The above example assumes that you are working in a hardhat project where you have compiled a contract with the name "MyContract". Your error suggest, that is not the case.
    – Chris
    Commented Apr 2 at 5:53
8

I will give a full example for interaction with the already deployed contract. We call mint method and send some ether as well to the contract

async function main() {
    const contractAddress = "0xC47Fd6ec9bb45115e89E76aC914F6EEe19501c15";
    const myContract = await hre.ethers.getContractAt("NameOfContract", contractAddress);
    
    const mintToken = await myContract.mint(1, { value: ethers.utils.parseEther("0.3") });

    console.log("Trx hash:", mintToken.hash);

}

main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.error(error);
        process.exit(1);
    });

Then we call that main function with async/await pattern. Whole code is called from CLI like

npx hardhat run scripts/interact.js --network  rinkeby
6

Assuming

import { Contract, providers, utils, Wallet } from "ethers";

and

    const rpc =  await new providers.JsonRpcProvider( process.env.RPC_ENDPOINT ) ;
    const wallet = new Wallet( process.env.PRIVATE_KEY, rpc);

Then using the ABI...

import { testTokenAbi } from "./abi/token.js";

where the abi file in the format of:

export const testTokenAbi =[*abi json here*];

...You can also link the json in a .json file, or just assign the abi json for relevant functions to a variable directly...such as

const testTokenAbi = [ *abi json here*];

now we can instantiate the deployed contract with a signer using:

const testTokenContract = new Contract(testTokenAddress, testTokenAbi, wallet);

then use our contract object to do things, like approve a transfer

testTokenContract.approve( exchangeRouterAddress, amountToken);

Now if you deploy to the wrong chain, then this won't work.

When you run the deploy script make sure the --network [network] flag is pointing to the same rpc that you are running your script against..

I just had this come up where I deployed without the flag, which was just creating a temporary node and then deleting it so that all my contract calls failed because I didn't deploy to the forked node I was running my scripts against.

3

This is how I use to instantiate contract at a specific address in Hardhat:

const accounts = await hre.ethers.getSigners()
const MyContract = await ethers.getContractFactory("MyContract");
const myContract = new ethers.Contract(MyContract, MyContract.interface, accounts[0]);

Hope it helps! :)

2
  • 1
    How does hardhat know the previously deployed address? Also I think it might be accounts[0].address
    – NickJ
    Commented Apr 25, 2021 at 23:00
  • ethers.Contract first argument must be an address. So for example const contract = await MyContract.deploy(); contract.address Commented Sep 9, 2021 at 2:55
2

This would seem like a wrong chain issue. Are you sure that Hardhat is pointed at the same chain as the contract? You can change the chain Hardhat runs on by either changing the value of defaultNetwork in hardhat.config or by running with the --network flag (for example --network rinkeby), provided there is a corresponding object in networks in hardhat.config.

Incidentally, you don't need to use fs to import the ABI. You can import it using an import or require.

const someContractAbi = require("./abi/SomeContract.json");

Depending on the setup, you may need to select the abi object from the ABI, though that doesn't seem to be your issue. For importing, you'd likely want a separate file that you copy-paste the abi object into.

const someContractAbi = require("./abi/SomeContract.json").abi;
2

@Chris answer helped me!

const myContract = ethers.getContractAt("MyContract", contractAddress);
console.log("Live Address:\t" + myContract.address);
await myContract.callFunctions();
2

Both getContractAt and attach work for me: I will share the code.

    this.CollectionMinter = await ethers.getContractFactory("CollectionMinter")
    this.CollectionMinter = await this.CollectionMinter.deploy()
    await this.CollectionMinter.deployed()
    console.log(`           CollectionMinter Deployed at:${this.CollectionMinter.address}`)

    await this.CollectionMinter.createNewCollection("CryptoOwl Collection", "COC", "HTTPS://Empire/collections/COC/") 
    const CryptoOwlAddress = await this.CollectionMinter.collectionAddress()
    console.log(`           CryptoOwl Collection deployed at:${CryptoOwlAddress}`)

   //1.using getContractAt
   this.COC = await ethers.getContractAt("LaunchpadCollection",CryptoOwlAddress)
   //2. using attach
   this.COC = await ethers.getContractFactory("LaunchpadCollection"); 
   this.COC = this.COC.attach(CryptoOwlAddress)
1

None of these answers worked for me until I used the following, including the provider, the signer, and getting the contract instance:

const provider = new ethers.providers.JsonRpcProvider() // using default http://localhost:8545
const signer = new ethers.Wallet(privkey, provider)
const myContract = await ethers.getContractAt('MyContract', contractAddress, signer)
const out = await myContract.balanceOf(walletAddress)
console.log(out)
1

Use

deployedContract = await ethers.getContractAt("ContractName", ContractAddress);
1

Get the signers

const [signer1] = await ethers.getSigners()

deploy the contract

// assuming Token contract is avaible in contracts
/* getContractFactory calls this 
      const artifact = await hre.artifacts.readArtifact(nameOrAbi);
*/
Token = await ethers.getContractFactory("Token", signer1);
token = await Token.deploy();

connect the signer and then call the method

await token.connect(signer1).transfer(to,amount)
1

I noticed that it is important to specify --network flag if you're interacting trough tasks.

So the workflow would be something like this:

  • npx hardhat deploy --network localhost
  • npx hardhat run [cmd] --network localhost
1

Make a script that starts a REPL session.

$ touch scripts/console.ts

And simplest version...

import * as repl from 'node:repl'
import { ethers } from "hardhat";

repl.start('> ')

And run it:

$ npx hardhat run scripts/console.ts

You can add command line arguments and parse them in the script, if you were to want to initialize a specific contract like --contact-address SOME_ADDRESS or connect to a specific network. However, this gives you a basic console like experience with access to your imports and environment.

0
const contract = await (await ethers.getContractFactory("MyContract")).attach("0xAddress")
1
  • It will be nicer if you include a little explanation for the code.
    – Ismael
    Commented Nov 30, 2021 at 5:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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