Deploy contract to local hardhat node with forked Kovan chain - ethereum

I am trying to write tests for my contract on Kovan network. In order to do so I am using the fork feature of hardhat and added the following to the hardhat.config.js file:
module.exports = {
solidity: "0.8.0",
defaultNetwork: "hardhat",
networks: {
hardhat: {
forking: {
url: INFURA_URL,
accounts: [`0x${PRIVATE_KEY}`]
}
}
}
};
INFURA_URL points to node on Kovan. PRIVATE_KEY is the key of an account on Kovan I would like to deploy with. This variables work well when I deploy to Kovan directly but not to forked node.
In my deployment script I do the following:
const [deployer] = await ethers.getSigners();
But my deployer is not the account that corresponds to the private key from config. It is a correct account when I deploy directly to Kovan.
Not sure why does this happen are forks of Kovan not supported on hardhat?

The way it worked for me was to forget Hardhat and rely entirely on ethers, for that I created a Signer specifying a private key and a provider like:
import { providers, Wallet } from 'ethers';
require('dotenv').config();
const main = async() => {
const providerLocal = new
providers.JsonRpcProvider("http://127.0.0.1:8545");
const owner = new Wallet(process.env.DEPLOYER_PRIVATE_KEY as string, providerLocal);
console.log(`The wallet ${owner.address} will deploy this contract`)
const MyContract = await ethers.getContractFactory('MyContract');
const mycontract = await MayContract.connect(owner).deploy();
}
main();

Related

How to correctly pass --network parameter to Hardhat scripts?

I'm trying to deploy to Goerli, but my deploy script seems to ignore the --network parameter.
Here is my hardhat.config.ts:
import { HardhatUserConfig } from "hardhat/config";
import "#nomicfoundation/hardhat-toolbox";
import "hardhat-gas-reporter"
import "#nomiclabs/hardhat-ethers";
import * as dotenv from 'dotenv'
dotenv.config();
const env:any = process.env;
const config: HardhatUserConfig = {
solidity: {
[...]
},
networks: {
hardhat: {
[...]
},
goerli: {
url: 'https://goerli.infura.io/v3/',
accounts: [env['DEPLOYER_PRIVATE_KEY']]
},
},
[...]
};
export default config;
Then I run:
npx hardhat run scripts/deploy.ts --network goerli
And in my deploy.ts:
async function main() {
const [deployer] = await ethers.getSigners();
console.log('Using RPC ', ethers.provider.connection.url);
console.log('Deploying from address', deployer.address);
[...] // contract deployment code
}
However it fails with error "could not detect network". It makes sense because it also logs (from my code):
Using RPC http://localhost:8545
Deploying from address 0x3a5Bd3fBc2a17f2eECf2Cff44aef38bd7dc4fd7c
My address is correct, the address logged indeed corresponds to the account that I provided with the private key from dotenv, so it's being read from the config correctly. However, the RPC URL is incorrect: it seems that it's trying to connect to my local RPC and failing.
Why isn't Hardhat respecting the url property in the config, and still trying to connect to my local instance?
Change
const config: HardhatUserConfig = {
solidity: {
[...]
},
to
module.exports = {
solidity: "0.8.4",
networks: {
goerli: {
url: 'https://goerli.infura.io/v3/',
accounts: [env['DEPLOYER_PRIVATE_KEY']]
}
}
};
If you want to deploy on Hardhat you can run npx hardhat node and remove goerli and const env:any = process.env;.

Unable to create contract clone on Hardhat. "Error: Transaction reverted: function call to a non-contract account"

I'm trying to create a clone using OpenZeppelin Clones library. However, it seems that hardhat is not able to recognize the created clone contracts address.
The same code works on Remix, so does this have something to with Hardhat? NOTE: I have tried using Ganache as well, however it reverts with the same error.
Here is my factory contract:
contract WhoopyFactory {
address immutable implementationContract;
address[] public allClones;
event NewClone(address indexed _instance);
mapping(address => address) public whoopyList;
constructor() {
implementationContract = address (new Whoopy());
}
function createClone(address _whoopyCreator) payable external returns(address) { address clone = Clones.clone(implementationContract); Whoopy(clone).initialize(_whoopyCreator);
emit NewClone(clone);
return clone;
}
And here is the test I am running:
describe("Whoopy + WhoopyFactory", function () {
it("Initialises contract correctly", async function () {
const provider = new ethers.providers.JsonRpcProvider("HTTP://127.0.0.1:7545")
const deployer = provider.getSigner(0);
const player = provider.getSigner(1);
Whoopy = await ethers.getContractFactory("Whoopy")
whoopy = await Whoopy.deploy()
await whoopy.deployed()
WhoopyFactory = await ethers.getContractFactory("WhoopyFactory")
wf = await WhoopyFactory.deploy()
await wf.deployed()
wf.connect(player)
const tx = await wf.createClone("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
console.log(tx)
const txReceipt = await tx.wait(1)
console.log(txReceipt)
This is the error which reverts:
Error: Transaction reverted: function call to a non-contract account
at Whoopy.initialize (contracts/Whoopy.sol:117)
at <UnrecognizedContract>.<unknown> (0x9f1ac54bef0dd2f6f3462ea0fa94fc62300d3a8e)
As I said before, this code works correctly on Remix. Hope someone can point me in the right direction. Thanks in advance!
From what I understood, you are deploying a smart contract on Ganache, you can use hardhat's own local chain, by doing yarn chain, or npx hardhat node, and change the port to 8545.
You can make it more simpler by replacing the following
const provider = new ethers.providers.JsonRpcProvider("HTTP://127.0.0.1:7545")
const deployer = provider.getSigner(0);
const player = provider.getSigner(1);
with
const accounts = await hre.ethers.getSigners();
const deployer = accounts[0];
const player = accounts[1];
and while deploying use npx hardhat test --network localhost

How to pass an Interface object in a constructor parameter?

I don't know how to deploy this contract whit HARDHAT because there is an Interface object in the constructor.
Please can someone explain to me how to initialize IUniswapV2Pair _pair
IUniswapV2Pair is a Uniswap Library written in Solidity.
const Unitest = await hre.ethers.getContractFactory("Unitest");
const unitest = await Unitest.deploy(int256 .....,
uint256 .... , IUniswapV2Pair(?????));
await unitest.deployed();
console.log("unitest deployed to:", unitest.address);
You get the address of Interface. This is in rinkeby: https://rinkeby.etherscan.io/address/0x6bcd5b1e919277afe06a3f6355265ff3cf7956a4#code
and then when you deploy the contract,
const Uniswap = await hre.ethers.getContractFactory("ContractName");
const contract = await NFTMarket.deploy("0x6BcD5b1E919277AFe06a3f6355265ff3Cf7956A4",period,startTime);

Using local private key with Web3.js

How can I interact with smart contracts and send transactions with Web3.js by having a local private key? The private key is either hardcoded or comes from an environment (.env) file?
This is needed for Node.js and server-side interaction or batch jobs with Ethereum/Polygon/Binance Smart Chain smart contracts.
You may encounter e.g. the error
Error: The method eth_sendTransaction does not exist/is not available
Ethereum node providers like Infura, QuikNode and others require you to sign outgoing transactions locally before you broadcast them through their node.
Web3.js does not have this function built-in. You need to use #truffle/hdwallet-provider package as a middleware for your Ethereum provider.
Example in TypeScript:
const Web3 = require('web3');
const HDWalletProvider = require("#truffle/hdwallet-provider");
import { abi } from "../../build/contracts/AnythingTruffleCompiled.json";
//
// Project secrets are hardcoded here
// - do not do this in real life
//
// No 0x prefix
const myPrivateKeyHex = "123123123";
const infuraProjectId = "123123123";
const provider = new Web3.providers.HttpProvider(`https://mainnet.infura.io/v3/${infuraProjectId}`);
// Create web3.js middleware that signs transactions locally
const localKeyProvider = new HDWalletProvider({
privateKeys: [myPrivateKeyHex],
providerOrUrl: provider,
});
const web3 = new Web3(localKeyProvider);
const myAccount = web3.eth.accounts.privateKeyToAccount(myPrivateKeyHex);
// Interact with existing, already deployed, smart contract on Ethereum mainnet
const address = '0x123123123123123123';
const myContract = new web3.eth.Contract(abi as any, address);
// Some example calls how to read data from the smart contract
const currentDuration = await myContract.methods.stakingTime().call();
const currentAmount = await myContract.methods.stakingAmount().call();
console.log('Transaction signer account is', myAccount.address, ', smart contract is', address);
console.log('Starting transaction now');
// Approve this balance to be used for the token swap
const receipt = await myContract.methods.myMethod(1, 2).send({ from: myAccount.address });
console.log('TX receipt', receipt);
You need to also avoid to commit your private key to any Github repository. A dotenv package is a low entry solution for secrets management.
You can achieve what you want by using ethers.js instead of web3 with no other package needed.
First import the library:
Node.js:
npm install --save ethers
const { ethers } = require("ethers");
Web browser:
<script src="https://cdn.ethers.io/lib/ethers-5.2.umd.min.js"
type="application/javascript"></script>
Define the provider. One way is using the provider URL like this:
const provider = new ethers.providers.JsonRpcProvider(rpcProvider);
Then, in order to interact with the contract without asking for authorization, we will create a wallet using the private key and the provider like this:
const signer = new ethers.Wallet(privateKey,provider)
Now, you can create the contract with the address, ABI, and the signer we created in the previous step:
const contract = new ethers.Contract(contractAddress,ABI, signer);
Now, you can interact with the contract directly. For example, getting the balance of a token:
const tokenBalance = await nftContractReadonly.balanceOf(signer.getAddress(),tokenId);
Don't forget to store the private key in a safe place and never hardcode it in a web page.
Further reading: Provider Signer
There is a better and simple way to sign and execute the smart contract function. Here your function is addBonus.
First of all we'll create the smart contract instance:
const createInstance = () => {
const bscProvider = new Web3(
new Web3.providers.HttpProvider(config.get('bscRpcURL')),
);
const web3BSC = new Web3(bscProvider);
const transactionContractInstance = new web3BSC.eth.Contract(
transactionSmartContractABI,
transactionSmartContractAddress,
);
return { web3BSC, transactionContractInstance };
};
Now we'll create a new function to sign and execute out addBonus Function
const updateSmartContract = async (//parameters you need) => {
try {
const contractInstance = createInstance();
// need to calculate gas fees for the addBonus
const gasFees =
await contractInstance.transactionContractInstance.methods
.addBonus(
// all the parameters
)
.estimateGas({ from: publicAddress_of_your_desired_wallet });
const tx = {
// this is the address responsible for this transaction
from: chainpalsPlatformAddress,
// target address, this could be a smart contract address
to: transactionSmartContractAddress,
// gas fees for the transaction
gas: gasFees,
// this encodes the ABI of the method and the arguments
data: await contractInstance.transactionContractInstance.methods
.addBonus(
// all the parameters
)
.encodeABI(),
};
// sign the transaction with a private key. It'll return messageHash, v, r, s, rawTransaction, transactionHash
const signPromise =
await contractInstance.web3BSC.eth.accounts.signTransaction(
tx,
config.get('WALLET_PRIVATE_KEY'),
);
// the rawTransaction here is already serialized so you don't need to serialize it again
// Send the signed txn
const sendTxn =
await contractInstance.web3BSC.eth.sendSignedTransaction(
signPromise.rawTransaction,
);
return Promise.resolve(sendTxn);
} catch(error) {
throw error;
}
}

Solidity function returning manager address of 0x00000 after contract deployment in Remix IDE

I have recently ran into a bit of an issue with a solidity contract I created and am trying to deploy and interact with inside of the Remix IDE. The function I have written to return the address of the contract manager is as so
function getManager() public view returns(address){
return manager;
}
The way the contract manager is set is as so
function Contest() public {
manager = msg.sender;
}
I have created two JavaScript compile and deploy scripts which I run inside of my terminal. The deploy script is written as this
const HDWalletProvider = require('truffle-hdwallet-provider');
const Web3 = require('web3');
const {interface, bytecode} = require('./compile.js');
const provider = new HDWalletProvider(
'MetaMask Mnemonic',
'Infura endpoint key'
);
const web3 = new Web3(provider);
const deploy = async () => {
const accounts = await web3.eth.getAccounts();
console.log('deploying from ', accounts[0]);
const ABI = interface;
const deployedContract = await new web3.eth.Contract(JSON.parse(ABI))
.deploy({data: bytecode})
.send({
gas: '1000000',
from: accounts[0]
});
console.log('contract deployed to', deployedContract.options.address);
}
deploy();
Once my contract is deployed I receive the deployed address (something like 0xbc0370FbC085FAf053b418e6ff62F26ED2C7Dee1) and then I go over to the Remix IDE where I put in the address and use the Injected web3 option. However when I call the "getManager" function it returns an address of 0x0000000000000000000000000000000000000000 and I can't seem to get it to return the address of the manager. This method was working once before and I cannot seem to figure out why it's not working any longer.