'Testing error for function call to a non-contract account
While testing our VRF getRandomNumber(s) with test-helpers, we keep on getting Error: Transaction reverted: function call to a non-contract account at:
require(LINK.balanceOf(address(this)) > fee, "Not enough LINK to initialte function call");
LINK seems to be used correctly here. What's the meaning/issue with the non-contract account?
Other tests on the same RandomNumberConsumer object are successful.
contract RandomNumberConsumer is VRFConsumerBase {
[...]
function getRandomNumber(uint256 userProvidedSeed) public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
return requestRandomness(keyHash, fee, userProvidedSeed);
}
describe("getRandomNumber()", function() {
it("Should return a requestID", async function() {
const requestId = await randomNumberConsumer.getRandomNumber(12);
// checks on requestId
});
});
Solution 1:[1]
Any LINK.xxx() call refers to the external LINK contract which is not existent in your code. It's a contract already deployed on the network - that's why you're most probably passing the LINK address to the constructor of your contract.
To make it work in the test, you need to mock something so that your test doesn't end up calling the real LINK interface. One of the ways would be to mock your getRandomNumber function. Since it's public, that should be easily doable with Waffle's mocking utils: https://ethereum-waffle.readthedocs.io/en/latest/mock-contract.html.
Alternatively (probably more legit, but longer) you can mock the entire LINK contract:
- Have some
Mocks.solcontract:pragma solidity ^0.8.7; import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; abstract contract LinkMock is LinkTokenInterface {} - Initialize it as a mock in your test and pass its address to your mock contract as the first argument, as per the VRF documentation:
import { waffle } from 'hardhat' import { abi as linkMockAbi } from '../artifacts/contracts/Mocks.sol/LinkMock.json' const [deployer, vrfCoordinatorMock, ...actors] = waffle.provider.getWallets() const getContract = async ({ mockedLinkBalance }: { mockedLinkBalance: string }) => { const linkMockContract = await waffle.deployMockContract(deployer, linkMockAbi) // Mocks the external LINK contract that we don't have access to during tests await linkMockContract.mock.balanceOf.returns(ethers.utils.parseEther(mockedLinkBalance)) await linkMockContract.mock.transferAndCall.returns(true) return waffle.deployContract(deployer, ContractJson, [ vrfCoordinatorMock.address, linkMockContract.address, '0x0000000000000000000000000000000000000000000000000000000000000000', '100000000000000000', ]) } - Call
rawFulfillRandomness()which the VRF calls itself, whenever you want to mock the VRF generating the randomness:const contract = await getContract() await contract.connect(vrfCoordinatorMock).rawFulfillRandomness('0x0000000000000000000000000000000000000000000000000000000000000000', mockedRandomnessValue)
Note I hardcoded requestId for brevity in the above example. You'll have to come up with a way of stubbing it if you rely on its value in your contract.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
