VRFCoordinatorV2Mock's fulfillRandomWords not being called in hardhat local deployment - ethereum

I'm building a smart contract that generates 3 random numbers using Chainlink VRF. My contract (SlotMachine.sol) implements the VRFConsumerBaseV2 and the constructor has 2 parameters: _subscriptionId and _vrfCoordinator:
constructor(
uint64 _subscriptionId,
address _vrfCoordinator
) payable VRFConsumerBaseV2(_vrfCoordinator)
I have a play() function which calls the requestRandonWords through the vrfCoordinator and I overrided the fulfillRandomWord function to use the generated random words.
I want to do 2 things: 1) create the unit tests in slotmachine.js and 2) test my contract in the hardhat network (chainId = 31337) through deploy.js
For this I'm using the VRFCoordinatorV2Mock which helps me mock an oracle's behavior (as I understand).
My unit tests in slotmachine.js are working well. The fulfillRandomWords is called and then my tests are passing. However when I add a similar logic to the deploy.js file (deploy the mock contract and then the slot machine contract) the fulfillRandomWords is not being called when I call the play() function (which has the requestRandomWords inside it) from my deployed contract.
slotmachine.js
const { ethers } = require("hardhat");
const { expect } = require("chai");
const { BigNumber } = require("ethers");
const provider = ethers.getDefaultProvider();
describe("Decentralized Slot Machine", async function () {
let myContract;
let hardhatVrfCoordinatorV2Mock;
describe("Testing Decentralized Slot Machine", function () {
//1. Contract deployment
it("Should deploy Slot Machine Contract", async function () {
const SlotMachine = await ethers.getContractFactory("SlotMachine");
let vrfCoordinatorV2Mock = await ethers.getContractFactory(
"VRFCoordinatorV2Mock"
);
hardhatVrfCoordinatorV2Mock = await vrfCoordinatorV2Mock.deploy(0, 0);
await hardhatVrfCoordinatorV2Mock.createSubscription();
await hardhatVrfCoordinatorV2Mock.fundSubscription(
1,
ethers.utils.parseEther("7")
);
myContract = await SlotMachine.deploy(
1,
hardhatVrfCoordinatorV2Mock.address,
{
value: ethers.utils.parseEther("100"),
}
);
await hardhatVrfCoordinatorV2Mock.addConsumer(1, myContract.address);
});
//2. Play
describe("First Play - First Player", function () {
it("Contract should receive random numbers", async () => {
const [account1, account2] = await ethers.getSigners();
let tx = await myContract.play(ethers.constants.AddressZero, {
value: ethers.utils.parseEther("1"),
});
let { events } = await tx.wait();
let [reqId] = events.filter((x) => x.event === "RequestedRandomness")[0]
.args;
await expect(
hardhatVrfCoordinatorV2Mock.fulfillRandomWords(
reqId,
myContract.address
)
).to.emit(myContract, "ReceivedRandomness");
let round = await myContract.rounds(reqId);
expect(round.userAddress).to.be.equal(account1.address);
expect(round.number1).to.be.equal(1);
expect(round.number2).to.be.equal(9);
expect(round.number3).to.be.equal(6);
});
});
});
});
deploy.js
const { ethers } = require("hardhat");
const localChainId = "31337";
module.exports = async ({ getNamedAccounts, deployments, getChainId }) => {
const { deploy } = deployments;
const { deployer } = await getNamedAccounts();
const chainId = await getChainId();
await deploy("VRFCoordinatorV2Mock", {
from: deployer,
args: [0, 0],
log: true,
waitConfirmations: 5,
});
const hardhatVrfCoordinatorV2Mock = await ethers.getContract(
"VRFCoordinatorV2Mock",
deployer
);
await hardhatVrfCoordinatorV2Mock.createSubscription();
await hardhatVrfCoordinatorV2Mock.fundSubscription(
1,
ethers.utils.parseEther("7")
);
const myContract = await deploy("SlotMachine", {
from: deployer,
args: [1, hardhatVrfCoordinatorV2Mock.address],
log: true,
waitConfirmations: 5,
});
await hardhatVrfCoordinatorV2Mock.addConsumer(1, myContract.address);
console.log("Contract address: ", myContract.address);
};
module.exports.tags = ["SlotMachine"];
Question
Can I deploy my contract in the hardhat network with the expected behavior of the VRFCoordinatorV2Mock which is to call automatically fulfillRandomWords? If so, how can I do this? What do I have to change in my code? If the answer is no, is there another alternative than deploying to a testnet?

Related

Test case for vestingWallet smart contract returning "header not found"

network,
I've been trying to finish a unit test for an investing wallet smart contract on Solidity as mentioned above:
The idea is to create a test cenarium for a vesting wallet smart contract.
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import "#openzeppelin/contracts/finance/VestingWallet.sol";
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract VestingContract is VestingWallet {
constructor(
address beneficiaryAddress,
uint64 startTimestamp,
uint64 durationSeconds
)
public
VestingWallet(beneficiaryAddress, startTimestamp, durationSeconds)
{}
function getBalance() public view returns (uint256) {
return address(this).balance;
}
function getBalanceFromToken(address _token) public view returns (uint256) {
return IERC20(_token).balanceOf(address(this));
}
}
and I also created these tests.
const VestingContract = artifacts.require("VestingContract");
const IoraContract = artifacts.require("Iora");
contract("VestingContract", function (accounts) {
it("should assert true", async function () {
await VestingContract.deployed();
return assert.isTrue(true);
});
it("beneficiary and accounts[0] should be the same", async function () {
const wallet = accounts[0];
const vestingInstance = await VestingContract.deployed();
const walletFromSmartContract = await vestingInstance.beneficiary();
return assert.isTrue(wallet === walletFromSmartContract)
});
it("actual timestamp should be the high or the same when it was deployed", async function () {
const vestingInstance = await VestingContract.deployed();
const vestingStarted = await vestingInstance.start();
const startTimestamp = Math.floor(Date.now() / 1000);
return assert.isTrue(startTimestamp >= vestingStarted.toNumber())
});
it("balance from erc20 token should be the same on this contract balance", async function () {
const vestingInstance = await VestingContract.deployed();
const ioraInstance = await IoraContract.deployed();
await ioraInstance.transfer(vestingInstance.address, 150);
const ioraBalance = await ioraInstance.balanceOf(vestingInstance.address);
const ioraTokenBalance = await vestingInstance.getBalanceFromToken(ioraInstance.address);
return assert.isTrue(ioraBalance.toNumber() === ioraTokenBalance.toNumber())
});
it("total released should be the same as we transfered", async function () {
const amount = 3000;
const vestingInstance = await VestingContract.deployed();
const ioraInstance = await IoraContract.deployed();
await ioraInstance.transfer(vestingInstance.address, amount);
await vestingInstance.release(ioraInstance.address);
const releasedVesting = await vestingInstance.released(vestingInstance.address);
return assert.isTrue(amount === releasedVesting.toNumber())
});
});
but, I'm getting this error:
1) Contract: VestingContract
total released should be the same as we transfered:
Error: Returned error: header not found
at Context.<anonymous> (test/vesting_contract.js:47:51)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
My question is, why my header was not found and also how can I solve it?

Sample APIConsumer contract not receiving ChainlinkFulfilled event in Hardhat

I deployed the APIConsumer sample contract (https://docs.chain.link/docs/make-a-http-get-request/) from Remix on Kovan, funded it with LINK, and sent a requestVolumeData transaction from Remix. As can be seen on Etherscan, the ChainlinkFulfilled event was fired: https://kovan.etherscan.io/address/0xde79d39db6b5210f83acf57346d74e5c762ab895#events.
Then I attached to the very same contract in Hardhat using a Kovan fork (with the Alchemy API key), and it never receives the event. Here is my test script:
const { ethers } = require("hardhat");
describe("API", function() {
it("Should make request and get result", async function() {
const ERC20 = await ethers.getContractFactory("ERC20")
const link = await ERC20.attach("0xa36085F69e2889c224210F603D836748e7dC0088"); //Kovan
console.log("name=" + await link.name());
const APIConsumer = await ethers.getContractFactory("APIConsumer");
const apiConsumer = APIConsumer.attach("0xde79d39db6B5210F83acf57346d74E5C762AB895");
//Ensure contract has sufficient LINK
console.log("balanceOf=" + await link.balanceOf(apiConsumer.address));
//Make request
const transaction = await apiConsumer.requestVolumeData()
const tx_receipt = await transaction.wait()
const requestId = tx_receipt.events[0].topics[1]
console.log("requestId=%s", requestId)
//Optionally subscribe to events
// apiConsumer.on("ChainlinkFulfilled", id => {
// console.log("ChainlinkFulfilled: %s", id);
// })
//Wait 30 secs for oracle to callback
await new Promise(resolve => setTimeout(resolve, 30000))
//Now check the result
const result = await apiConsumer.volume()
console.log("API Consumer Volume: %s", result)
});
});
In hardhat.config.js, blockNumber is chosen to be just before the first ChainlinkRequested event, and we can see from the script output that the contract is funded with sufficient LINK.
require("#nomiclabs/hardhat-waffle");
/**
* #type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
solidity: "0.8.7",
networks: {
hardhat: {
forking: {
url: "https://eth-kovan.alchemyapi.io/v2/<API Key>",
blockNumber: 31301193
}
}
},
};
Script output
API
name=ChainLink Token
balanceOf=1000000000000000000
requestId=0x352988e0ddfe5c4349711ed9787069b1ea55bae562f676a08f6103435a874514
API Consumer Volume: 0
Why doesn't ChainlinkFulfilled seem to get fired from the Hardhat test?
Welcome, Peter! There is no oracle listening on your forked network. To test oracles locally you should consider mocking the oracle (hardhat way).

trying to deploy a todolist in blockchain, when adding a new task, I get invalid address error

I am making a simple block chain using the ETH blockchain technology.
I am making a simple todolist, following this tutorial.
My todolist works fine and I can see the tasks in there, how ever when I try to add a new task I get this following error:
Uncaught (in promise) Error: invalid address
v http://localhost:3000/js/web3.min.js:2
l http://localhost:3000/js/web3.min.js:2
formatInput http://localhost:3000/js/web3.min.js:2
formatInput http://localhost:3000/js/web3.min.js:2
toPayload http://localhost:3000/js/web3.min.js:2
e http://localhost:3000/js/web3.min.js:2
sendTransaction http://localhost:3000/js/web3.min.js:2
execute http://localhost:3000/js/web3.min.js:2
synchronizeFunction http://localhost:3000/vendor/truffle-contract/dist/truffle-contract.js:206
synchronizeFunction http://localhost:3000/vendor/truffle-contract/dist/truffle-contract.js:157
promise callback*synchronizeFunction/< http://localhost:3000/vendor/truffle-contract/dist/truffle-contract.js:156
createTask http://localhost:3000/js/app.js:124
onsubmit http://localhost:3000/:1
web3.min.js:2:4288
createTask http://localhost:3000/js/app.js:125
AsyncFunctionThrow self-hosted:696
(Async: async)
onsubmit http://localhost:3000/:1
​
this is my app.js
App = {
loading: false,
contracts: {},
load: async () => {
await App.loadWeb3();
await App.loadAccount();
await App.loadContract();
await App.render();
},
// https://medium.com/metamask/https-medium-com-metamask-breaking-change-injecting-web3-7722797916a8
loadWeb3: async () => {
if (typeof web3 !== "undefined") {
App.web3Provider = web3.currentProvider;
web3 = new Web3(web3.currentProvider);
} else {
window.alert("Please connect to Metamask.");
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum);
try {
// Request account access if needed
await ethereum.enable();
// Acccounts now exposed
web3.eth.sendTransaction({
/* ... */
});
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider;
window.web3 = new Web3(web3.currentProvider);
// Acccounts always exposed
web3.eth.sendTransaction({
/* ... */
});
}
// Non-dapp browsers...
else {
console.log(
"Non-Ethereum browser detected. You should consider trying MetaMask!"
);
}
},
loadAccount: async () => {
// Set the current blockchain account
App.account = web3.eth.accounts[0];
console.log(App.account);
},
loadContract: async () => {
// Create a JavaScript version of the smart contract
const todoList = await $.getJSON("TodoList.json");
App.contracts.TodoList = TruffleContract(todoList);
App.contracts.TodoList.setProvider(App.web3Provider);
// Hydrate the smart contract with values from the blockchain
App.todoList = await App.contracts.TodoList.deployed();
// console.log(todoList);
},
render: async () => {
// Prevent double render
if (App.loading) {
return;
}
// Update app loading state
App.setLoading(true);
// Render Account
$("#account").html(App.account);
// Render Tasks
await App.renderTasks();
// Update loading state
App.setLoading(false);
},
renderTasks: async () => {
// Load the total task count from the blockchain
const taskCount = await App.todoList.taskCount();
const $taskTemplate = $(".taskTemplate");
// Render out each task with a new task template
for (var i = 1; i <= taskCount; i++) {
// Fetch the task data from the blockchain
const task = await App.todoList.tasks(i);
const taskId = task[0].toNumber();
const taskContent = task[1];
const taskCompleted = task[2];
// Create the html for the task
const $newTaskTemplate = $taskTemplate.clone();
$newTaskTemplate.find(".content").html(taskContent);
$newTaskTemplate
.find("input")
.prop("name", taskId)
.prop("checked", taskCompleted)
.on("click", App.toggleCompleted);
// Put the task in the correct list
if (taskCompleted) {
$("#completedTaskList").append($newTaskTemplate);
} else {
$("#taskList").append($newTaskTemplate);
}
// Show the task
$newTaskTemplate.show();
}
},
createTask: async () => {
App.setLoading(true);
const content = $("#newTask").val();
await App.todoList.createTask(content);
window.location.reload();
},
toggleCompleted: async (e) => {
App.setLoading(true);
const taskId = e.target.name;
await App.todoList.toggleCompleted(taskId);
window.location.reload();
},
setLoading: (boolean) => {
App.loading = boolean;
const loader = $("#loader");
const content = $("#content");
if (boolean) {
loader.show();
content.hide();
} else {
loader.hide();
content.show();
}
},
};
$(() => {
$(window).load(() => {
App.load();
});
});
in console it shows me the address to be: 0xc5cfa0a0345f74e26cecfd8ec3a5cfa3843955ac
I am using metamask and genache, and I tried to connect my smart contacrt to my memask wallet, so I know I am connected, but I am not sure why I am getting this error.
SHould I delete my metamask and do it again?
I tried to look for this solution here, but the solutions are mostly old and doesnt make scnese what I need to do.
Help would be really apperciated.
fixed it, in the app.js you have to replace this line:
App.account = web3.eth.accounts[0];
with the following line:
web3.eth.defaultAccount=web3.eth.accounts[0]

cannot connect client app with web3js to metamask

I am a dapp beginner. This is a demo app for a todolist
I am unable to get a connection to the blockchain in web3js. Websocket connection error
localhost is localhost:8545
using web3js CDN : https://cdn.jsdelivr.net/npm/web3#latest/dist/web3.min.js
this is my app.js
App = {
loading: false,
contracts: {},
load: async () => {
console.log('app loading ...')
console.log(web3);
await App.loadWeb3()
await App.loadAccount()
// await App.loadContract()
// await App.render()
},
// https://medium.com/metamask/https-medium-com-metamask-breaking-change-injecting-web3-7722797916a8
loadWeb3: async () => {
let web3 = new Web3('ws://localhost:8545');
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider
web3.setProvider('ws://localhost:8546');
web3.eth.getAccounts().then(console.log);
} else {
window.alert("Please connect to Metamask.")
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum)
try {
// Request account access if needed
await ethereum.enable()
// Acccounts now exposed
web3.eth.sendTransaction({/* ... */})
console.log('MetaMask is installed!');
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider
window.web3 = new Web3(web3.currentProvider)
// Acccounts always exposed
web3.eth.sendTransaction({/* ... */})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
},
loadAccount: async () => {
// Set the current blockchain account
App.account = web3.eth.accounts[0]
console.log(App.account)
// web3 set up by loadWeb3, includes all accounts, loading first one via MetaMask
},
loadContract: async () => {
// Create a JavaScript version of the smart contract
const todoList = await $.getJSON('TodoList.json')
App.contracts.TodoList = TruffleContract(todoList)
App.contracts.TodoList.setProvider(App.web3Provider)
// Hydrate the smart contract with values from the blockchain
App.todoList = await App.contracts.TodoList.deployed()
},
render: async () => {
// Prevent double render
if (App.loading) {
return
}
// Update app loading state
App.setLoading(true)
// Render Account
$('#account').html(App.account)
// Render Tasks
await App.renderTasks()
// Update loading state
App.setLoading(false)
},
renderTasks: async () => {
// Load the total task count from the blockchain
const taskCount = await App.todoList.taskCount()
const $taskTemplate = $('.taskTemplate')
// Render out each task with a new task template
for (var i = 1; i <= taskCount; i++) {
// Fetch the task data from the blockchain
const task = await App.todoList.tasks(i)
const taskId = task[0].toNumber()
const taskContent = task[1]
const taskCompleted = task[2]
// Create the html for the task
const $newTaskTemplate = $taskTemplate.clone()
$newTaskTemplate.find('.content').html(taskContent)
$newTaskTemplate.find('input')
.prop('name', taskId)
.prop('checked', taskCompleted)
.on('click', App.toggleCompleted)
// Put the task in the correct list
if (taskCompleted) {
$('#completedTaskList').append($newTaskTemplate)
} else {
$('#taskList').append($newTaskTemplate)
}
// Show the task
$newTaskTemplate.show()
}
},
createTask: async () => {
App.setLoading(true)
const content = $('#newTask').val()
await App.todoList.createTask(content)
window.location.reload()
},
toggleCompleted: async (e) => {
App.setLoading(true)
const taskId = e.target.name
await App.todoList.toggleCompleted(taskId)
window.location.reload()
},
setLoading: (boolean) => {
App.loading = boolean
const loader = $('#loader')
const content = $('#content')
if (boolean) {
loader.show()
content.hide()
} else {
loader.hide()
content.show()
}
}
}
$(() => {
$(window).load(() => {
App.load()
})
})
I get this error in the console :
Again I am a total newbie, any help is appreciated.
Any source of info did not help
Hey after metamask update , it no longer injects web3 .
You can check the blog below , It has shown how to connect metamask with our project .
https://dapp-world.com/blogs/01/how-to-connect-metamask-with-dapp--1616927367052
Only thing is its web application , you can relate it with your project .
Hope it works !

Interacting with deployed Ethereum/Quorum contract

I'm trying to deploy a contract in a private Ethereum (Quorum with RAFT but executing only public tx) using solc version 0.5.1 and web3 version 1.0.0-beta.36. Number contract is deployed and contractAddress is returned upon receiving receipt.
Though might seem cumbersome, for easier reproducibility, I've written all code here. The problem I'm having is in the last block (interacting with contract).
const solc = require('solc');
const Web3 = require('web3');
const input = {
language: 'Solidity',
sources: {
solContract: {
content: `
pragma solidity ^0.5.1;
contract Number {
uint private num = 100;
function getNum() public view returns(uint) {
return num;
}
function setNum(uint _num) public returns(bool success) {
num = _num;
return true;
}
}`
}
},
settings: {
outputSelection: {
'*': {
'*': ['*']
}
}
}
}
const output = JSON.parse(solc.compile(JSON.stringify(input)))
const abi = output.contracts.solContract.Number.abi
const bytecode = `0x${output.contracts.solContract.Number.evm.bytecode.object}`
const web3 = new Web3('http://127.0.0.1:8555');
const privateKey = '0x82f74b773d7f948153d7eb8c192bd9819e3e94073d8bdc0e03d659aa42cd34ba';
// const account = web3.eth.accounts.privateKeyToAccount(privateKey);
const contract = new web3.eth.Contract(abi);
web3.eth.personal.unlockAccount('0x27f4cD26d7e4eAde2052ec5B61f6594D1481C4A2', 'passwordstring', 600)
.then(
contract.deploy({
data: bytecode,
arguments: []
})
.send({
from: '0x27f4cD26d7e4eAde2052ec5B61f6594D1481C4A2',
gas: 0x47b760
}, (error, transactionHash) => {
if (error) {
console.log(error)
} else if (transactionHash) {
web3.eth.getTransaction(transactionHash)
.then((data) => {
console.log(data)
})
}
})
.on('receipt', (receipt) => {
console.log(receipt.contractAddress)
}))
// Interacting with deployed contract
// contract address returned from above
// 0x101ea6703717Fa3CF70e96FD3C84FE74ddca50eB
contract.options.address = '0x101ea6703717Fa3CF70e96FD3C84FE74ddca50eB'
contract.methods.getNum().call({
from: 0x27f4cD26d7e4eAde2052ec5B61f6594D1481C4A2,
gas: 0x47b760
}, (err, data) => {
err ? console.log(err) : console.log(data)
})
The issue you're having here is that when you execute the last block of code, the contract has actually not yet been deployed.
You need to perform the call to getNum() within the on() block, e.g.:
.on('receipt', (receipt) => {
console.log("CONTRACT CREATED, ADDRESS = ", receipt.contractAddress)
performGetNum(receipt.contractAddress)
})
function performGetNum(contractAddress) {
sampleContract.options.address = contractAddress
sampleContract.methods.getNum().call({
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 0x47b760
}, (err, data) => {
err ? console.log(err) : console.log("getNum() returned value: ", data)
})
}