sending a transaction requires a signer - ethereum

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.

Related

Reading smart contract function - what am I doing wrong?

I'm trying to read total supply from this smart contract, I have checked the ABI and the read does not require arguments. Why am I getting this error?
Imported the ABI from Etherscan with a valid API key
Connected Infura node with valid api.
Please help. I'm new!
Error:
Error: call revert exception [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="TOTAL_SUPPLY()", data="0x", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.7.0)
reason: null,
code: 'CALL_EXCEPTION',
method: 'TOTAL_SUPPLY()',
data: '0x',
errorArgs: null,
errorName: null,
errorSignature: null,
address: '0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155',
args: [],
transaction: {
data: '0x902d55a5',
to: '0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155',
from: '0xa9519a76981489Cf44a6b1023C53EA674B9365fB'
}
}
CODE:
const ethers = require("ethers");
async function main() {
// make an API call to the ABIs endpoint
const response = await fetch('https://api.etherscan.io/api?module=contract&action=getabi&address=0x31Cd7378715174e2c5Bd7EDF9984c9bC2A9209Eb&apikey=xxx');
const data = await response.json();
let abi = data.result;
/*console.log(abi);*/
// creating a new Provider, and passing in our node URL
const node = "https://mainnet.infura.io/v3/xxx";
const provider = new ethers.providers.JsonRpcProvider(node);
// initiating a new Wallet, passing in our private key to sign transactions
let privatekey = "xxx";
let wallet = new ethers.Wallet(privatekey, provider);
// print the wallet address
console.log("Using wallet address " + wallet.address);
// specifying the deployed contract address
let contractaddress = "0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155";
// initiating a new Contract
let contract = new ethers.Contract(contractaddress, abi, wallet);
console.log("Using contract address " + contract.address);
// calling the "retrieve" function to read the stored value
let supply = await contract.TOTAL_SUPPLY();
console.log("Value stored in contract is " + supply.toString());
You're retrieving ABI of address 0x31Cd7378715174e2c5Bd7EDF9984c9bC2A9209Eb on the Ethereum mainnet.
Your ethers provider correctly connects to the Ethereum mainnet.
But then it's trying to interact (call function TOTAL_SUPPLY()) with contract on address 0xFc7a5BD22dFc48565D6f04698E566Dd0C71d3155.
Since there's no contract on the 0xFc... address on the Ethereum mainnet, the call fails.
Have u tried web3client.app.
You can test any abi methods across 500+ chains in any smart contracts.
Identify which network your contract resides and do test it out.
It has code generate feature that lets you to easily get a bug free high quality code

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

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

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

eth_sendTransaction does not exist/is not available

I'm currently using ERC721PresetMinterPauserAutoId for a smart contract and the Web3.js library in the Node.js backend server. When I try to call the mint function using this Web3 API:
var myContract = new web3.eth.Contract(ERC721PresetMinterPauserAutoIdABI, ERC721PresetMinterPauserAutoIdContractAddress, {
from: from,
gasPrice: gasPrice
});
let result;
try {
result = await myContract.methods.mint(receipientAddress).send();
res.status(201).send(result)
} catch (error) {
res.status(201).send(error)
}
I get the following error:
Returned error: The method eth_sendTransaction does not exist/is not available
I'm communicating to the Rinkeby blockchain through the Infura gateway and according to this post, Infura supports only eth_sendRawTransaction, not eth_sendTransaction.
I was able to successfully send Ether using a signed transaction:
const gasPrice = await web3.eth.getGasPrice()
const txCount = await web3.eth.getTransactionCount(from, 'pending')
var rawTx = {
nonce: txCount,
gasPrice:"0x" + gasPrice,
gasLimit: '0x200000',
to: to,
value: "0x1000000000000000",
data: "0x",
chainId: 4
};
var privateKey = new Buffer.from(pk, "hex")
var tx = new Tx(rawTx, {"chain": "rinkeby"});
tx.sign(privateKey);
var serializedTx = tx.serialize();
const signedTx = await web3.eth.sendSignedTransaction("0x" + serializedTx.toString("hex"));
However, I'm unable to call the mint method on the smart contract using the raw transaction. I've tried:
await myContract.methods.mint(receipientAddress).sendSignedTransaction("0x" + serializedTx.toString("hex"));
or
await myContract.methods.mint(receipientAddress).sendRawTransaction("0x" + serializedTx.toString("hex"));
But, I still get the error message eth_sendTransaction does not exist/is not available.
Update
I tried using signing the transaction using a Truffle's library on the advise of #MikkoOhtamaa:
const HDWalletProvider = require("#truffle/hdwallet-provider");
const privateKeys = process.env.PRIVATE_KEYS || ""
const walletAPIUrl = `https://rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}`
const provider = new HDWalletProvider(
privateKeys.split(','),
walletAPIUrl
);
const web3 = new Web3API(provider)
Install a Web3.js middleware layer that signs transactions locally, instead of sending a JSON-RPC method to Infura.
One of the solutions is Truffle HDWallet.

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.