How to send ETH to a contract function with ethers.js? - ethereum

I am trying to send ETH to a contract function from a web app via metamask and ethers.js. So far I have tried:
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
const splitterManager = new ethers.Contract(contract.address, contract.abi, signer);
var overrides = {value: 5}
const result = await splitterManager.newSplitter(addresses, shares, erc20, overrides);
console.log(result);
But I keep getting 'Error: resolver or addr is not configured for ENS name (argument="name", value="", code=INVALID_ARGUMENT, version=contracts/5.2.0)'.

You can call the contracts function and pass it an object containing a value key.
contractInstance.testFunction(<any function args>, { value: ethers.utils.parseUnits("1", "ether") });
This would call your contract's function and send that amount of wei to the contract.
function testFunction() public payable {
// contract code
}

If the contract has implemented the receive function, you can send ether to a contract same as sending ether to any other account. Here's a short example:
const accounts = await provider.listAccounts();
const signer = provider.getSigner(accounts[0]);
tx = {
to: **CONTRACT_ADDRESS**,
value: ethers.utils.parseEther('2', 'ether')
};
const transaction = await signer.sendTransaction(tx);

await contractInstance
.connect(rpcProvider)
.function({
value: ethers.utils.parseUnits("1","ether")
});
this should work

some address is an invalid address it could be the contract.address, the addresses, or some other address

Related

sending a transaction requires a signer

I have an NFT Contract and I need to mint NFTs. Also I have a second contract
for handling buying and selling NFTs into this contract.
I am using the ethers library.
this is my code for config:
static async Initial(): Promise<any> {
let provider = new ethers.providers.JsonRpcProvider(config.contractConfig.url);
const signer = provider.getSigner();
this.tokenContract = new ethers.Contract(config.contractConfig.nftAddress, NFT.abi, provider);
this.dNFT = new ethers.Contract(config.contractConfig.dortajNftAddress, DNFT.abi, signer);
}
config address:
export default {
nftAddress: '0x444F15B115ED9663DAE46786a34AA3F6E8c0B57D',
dortajNftAddress: '0x8e4bE2a3BD1169596c38952D8e837b20D419Bcd1',
url : 'HTTP://127.0.0.1:7545'
}
now i need to use this function for mint nft :
let transaction = await this.tokenContract.mintToken();
const tx = await transaction.wait();
but it show me this error:
Error: sending a transaction requires a signer (operation="sendTransaction", code=UNSUPPORTED_OPERATION, version=contracts/5.5.0)
How can I solve this problem?
In this line
this.tokenContract = new ethers.Contract(config.contractConfig.nftAddress, NFT.abi, provider);
try to pass signer instead of provider.

Check balance of ERC20 token in Hardhat using Ethers.js

I'm writing unit tests using Hardhat's mainnet fork, and for a test I want to check the owner account's initial balance of an ERC20 token, in particular DAI. Here's my test so far:
const { ethers } = require("hardhat");
describe("Simple Test", function () {
it("Check balance of DAI", async function () {
const provider = ethers.provider;
const [owner] = await ethers.getSigners();
// Want to get initial DAI balance here so it can be compared later
});
});
What's a simple way to do this?
Found what I was looking for. First thing I did was save the DAI contract address at the top:
const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f";
Then I had to use the ERC20 token ABI, which I placed in the same folder as my test:
const ERC20ABI = require('./ERC20.json');
Then to get the owner account's DAI balance I just called balanceOf() from the DAI contract:
const DAI = new ethers.Contract(DAI_ADDRESS, ERC20ABI, provider);
DAIBalance = await DAI.balanceOf(owner.address);
This should work for any ERC20 token as well.

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;
}
}

Calling the set() function (with web3js) of a solidity contract is creating a new contract address. Why?

I have a simple solidity contract with a set() function. When I call the contract's set() function, the resulting transaction is at a newly created contract address instead of the contract address of where the solidity code lives.
If I use the UI in Remix, the new transactions (with the updated string values) are associated with the original contract. When I attempt the same thing with web3js, brand new contracts are being created.
I am wanting all new get() calls with web3js to be associated with the original contract.
Solidity Code
pragma solidity ^0.4.0;
contract HashRecord {
string public hashValue;
function setHashValue(string newHashValue) public {
hashValue = newHashValue;
}
function getHashValue() public view returns (string) {
return hashValue;
}
}
web3js Code
var Tx = require('ethereumjs-tx')
const Web3 = require('web3')
const web3 = new Web3('https://ropsten.infura.io/v3/d55489f8ea264a1484c293b05ed7eb85')
const abi = [ABI_CODE]
const contractAddress = '0x6c716feb775d5e7b34856edf75048a13fe0c16b0'
const myAccount = '0x59f568176e21EF86017EfED3660625F4397A2ecE'
const privateKey1 = new Buffer('PRIVATE_KEY', 'hex')
hashValue = 'newly updated value'
const contract = new web3.eth.Contract(abi, contractAddress,{
from: myAccount,
web3.eth.getTransactionCount(myAccount, (err, txCount) => {
//Smart contract data
const data = contract.methods.setHashValue(hashValue).encodeABI()
// Build the transaction
const txObject = {
nonce: web3.utils.toHex(txCount),
gasLimit: web3.utils.toHex(1000000),
gasPrice: '5000',
data: data,
from: myAccount,
}
// Sign the transaction
const tx = new Tx(txObject)
tx.sign(privateKey1)
const serializedTx = tx.serialize()
// Broadcast the transaction
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).
on('receipt', console.log)
})
My guess is this has to do with const contract = new web3.eth.Contractcreating a new contract. I can not figure out another way to do it though.
Again, I would like new values stored under the variable hashValue to be associated with the original contract address of const contractAddress = '0x6c716feb775d5e7b34856edf75048a13fe0c16b0'
Thanks!!!
Adding to: contractAddress,
in the following code block
const txObject = {
nonce: web3.utils.toHex(txCount),
// value: web3.utils.toHex(web3.utils.toWei('0.1', 'ether')),
gasLimit: web3.utils.toHex(1000000),
gasPrice: '5000',
// gasPrice: '0x' + estimatedGas,
data: data,
from: myAccount,
to: contractAddress,
solves the problem.
Thank you #smarx!

Call contract methods with web3 from newly created account

I need to call methods from my contract in Ethereum without using MetaMask. I use Infura API and try to call my methods from account, recently created with web3.eth.create() method. This method returns object like this:
{
address: "0xb8CE9ab6943e0eCED004cG5834Hfn7d",
privateKey: "0x348ce564d427a3311b6536bbcff9390d69395b06ed6",
signTransaction: function(tx){...},
sign: function(data){...},
encrypt: function(password){...}
}
I also using infura provider:
const web3 = new Web3(new Web3.providers.HttpProvider(
"https://rinkeby.infura.io/5555666777888"
))
So, when I try to write smth like that:
contract.methods.contribute().send({
from: '0xb8CE9ab6943e0eCED004cG5834Hfn7d', // here I paste recently created address
value: web3.utils.toWei("0.5", "ether")
});
I have this error:
Error: No "from" address specified in neither the given options, nor
the default options.
How it could be no from address if I write it in from option??
P.S. With Metamask my application works fine. But when I logout from MetaMask and try to create new account and use it, I have that issue.
In fact, we can't just send transactions from newly created address. We must sign this transaction with our private key. For example, we can use ethereumjs-tx module for NodeJS.
const Web3 = require('web3')
const Tx = require('ethereumjs-tx')
let web3 = new Web3(
new Web3.providers.HttpProvider(
"https://ropsten.infura.io/---your api key-----"
)
)
const account = '0x46fC1600b1869b3b4F9097185...'; //Your account address
const privateKey = Buffer.from('6e4702be2aa6b2c96ca22df40a004c2c944...', 'hex');
const contractAddress = '0x2b622616e3f338266a4becb32...'; // Deployed manually
const abi = [Your ABI from contract]
const contract = new web3.eth.Contract(abi, contractAddress, {
from: account,
gasLimit: 3000000,
});
const contractFunction = contract.methods.createCampaign(0.1); // Here you can call your contract functions
const functionAbi = contractFunction.encodeABI();
let estimatedGas;
let nonce;
console.log("Getting gas estimate");
contractFunction.estimateGas({from: account}).then((gasAmount) => {
estimatedGas = gasAmount.toString(16);
console.log("Estimated gas: " + estimatedGas);
web3.eth.getTransactionCount(account).then(_nonce => {
nonce = _nonce.toString(16);
console.log("Nonce: " + nonce);
const txParams = {
gasPrice: 100000,
gasLimit: 3000000,
to: contractAddress,
data: functionAbi,
from: account,
nonce: '0x' + nonce
};
const tx = new Tx(txParams);
tx.sign(privateKey); // Transaction Signing here
const serializedTx = tx.serialize();
web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex')).on('receipt', receipt => {
console.log(receipt);
})
});
});
Transaction time is about 20-30 seconds so you should wait quite a bit.