I am using ethers.js in hardhat (buidler) testing and cannot call Write Methods. I can access contracts Properties and Read-Only Methods, but when I call Write Methods I keep getting "... is not a function" error:
In my sample is an ERC20 Token. I can see the method also when calling contract.interface:
const { expect } = require("chai");
describe("Test", function () {
let FZBAT, AddressProvider, ZBAT, owner, addr1, addr2;
const mintAmount = (50 * (10 ** 18)).toString(); // 50 Tokens
before(async function () {
const FZBATArt = await ethers.getContractFactory("FZBAT");
FZBAT = await upgrades.deployProxy(FZBATArt, ["FZBAT", "FZ BA Token", 16]);
await FZBAT.deployed();
console.log('vDeployed FZBAT', FZBAT.address);
});
it("Should mint tokens from FZBAT", async () => {
let result = await FZBAT.mint(addr1.address, mintAmount);
});
});
console:
vDeployed FZBAT 0xCBF508609D841D2dA4E31cFAA504e2922e625e31
1) Should mint tokens from FZBAT
0 passing (5s)
1 failing
1) Test
Should mint tokens from FZBAT:
TypeError: FZBAT.mint is not a function
UPDATE: The issue was not with the Write Methods but with methods with the same name but with a different number of parameters. I have two methods for mint:
function mint(uint256 amount) public returns (bool) {
_mint(_msgSender(), amount);
return true;
}
function mint(address account, uint256 amount) public returns (bool) {
_mint(account, amount);
return true;
}
~Can you console log FZBAT just before calling the method? It should be a [Contract] object.~
Edit: I didn't notice this was an overloading problem, sorry for that.
If you have two methods with the same name, you must specify the fully qualified signature to access it. You can use either of the following, depending on which you want:
contract["mint(uint256)"](amount)
// or
contract["mint(address,uint256)"](account, amount)
If you only use one of those methods, you can simply drop the unused one from the ABI you pass into the Contract constructor, then you can use the name mint to access it.
For more details, see https://github.com/ethers-io/ethers.js/issues/950#issuecomment-657803378. :)
@ricmoo Is the way to to select the function signature which can be called "normally" (which should be the one first in the ABI) deterministic?
How do I find out which one it is and/or how can I define it myself ?
@SvenMeyer
If the result is ambiguous, you must specify the signature. It is not, in general, safe to simply use the "first" one, since various compilers, versions and tools manipulate and output the ABI in different orders. Orders could be alphabetical, ordered by the selector, argument count, dependency order, etc. You won't want switching the compiler version to cause your JavaScript to break. :)
In general I highly recommend that people only include the fragments they use, and to use the Human-Readable ABI (which is much smaller than the equivalent JSON). If there is a method that is completely unused, excluding it keeps the code cleaner and the bundle size smaller. An ABI can easily be 10kb-500kb, while most things only require 1-10 signatures, which fits in about 50-600 bytes. :)
const ABI = [
"function mint(address owner, uint amount) payable",
"function balanceOf(address owner) view returns (uint)",
"event Transfer(address indexed from, address indexed to, uint amount)"
];
const contract = new Contract(tokenAddressOrEnsName, ABI, signer);
const balance = await contract.balanceOf(signer.getAddress());
const tx = await contract.mint(owner, amount);
contract.on("Transfer", (from, to, amount, event) => {
// Tra la la...
});
Does that make sense?
Most helpful comment
If you have two methods with the same name, you must specify the fully qualified signature to access it. You can use either of the following, depending on which you want:
If you only use one of those methods, you can simply drop the unused one from the ABI you pass into the Contract constructor, then you can use the name
mintto access it.For more details, see https://github.com/ethers-io/ethers.js/issues/950#issuecomment-657803378. :)