Error creating contract after getting contract ABI from Etherscan API - blockchain

I am trying to get the contract ABI using the Etherscan API, and then create a contract instance and call a method. I am able to get the ABI from Etherscan but when creating the contract object I am getting this error: "You must provide the json interface of the contract when instantiating a contract object."
This is what my code looks like
let url = 'https://api.etherscan.io/api?module=contract&action=getabi&address=0x672C1f1C978b8FD1E9AE18e25D0E55176824989c&apikey=<api-key>';
request(url, (err, res, body) => {
if (err) {
console.log(err);
}
let data = JSON.parse(body);
let contract_abi = data.result;
console.log(contract_abi)
let contract_address = '0x672C1f1C978b8FD1E9AE18e25D0E55176824989';
const contract = new web3.eth.Contract(contract_abi);
const contract_instance = contract.at(contract_address);
// Call contract method
})
When I console.log the contract_abi I see the ABI data. I've also tried creating the contract by doing
const contract = new web3.eth.Contract(contract_abi, contract_address)
Thanks!

data.result contains the JSON ABI as a string. You need to decode it as well to an object.
let contract_abi = JSON.parse(data.result);
Also, it's possible that you're using a deprecated version of Web3 that supports the contract.at() syntax.
But if you're using the current version, you'd get the contract.at is not a function error. In that case, you need to pass the address as the second argument of the Contract constructor.
const contract = new web3.eth.Contract(contract_abi, contract_address);

Related

How to correctly set up a WebSocketProvider in ethers.js?

I'm trying to switch my dapp from Web3Provider to WebSocketProvider,
form this:
const provider = new ethers.providers.Web3Provider(window.ethereum)
const accounts = await window.ethereum.request({ method: "eth_accounts" })
const account = accounts[0]
const signer = provider.getSigner()
to this:
const provider = new ethers.providers.WebSocketProvider("ws://localhost:8545") <-
const accounts = await window.ethereum.request({ method: "eth_accounts" })
const account = accounts[0]
const signer = provider.getSigner()
With this change I can interact with the Contract only with account that creates and deploy the smart contract, also, the transactions have no confirmation from the user. However, when I try to call some Contract function with another address I get this error:
On the fourth line, the value of the "from" key is different from the address actually selected in the metamask, in fact it is the address of the creator of the Smart Contract. There seems to be some problem with the signer or what? With Web3Provider everything works fine.
Can you help me in any way or tell me more about WebSocketProvider?
Thanks in advance

Uncaught (in promise) Error: call revert exception

Uncaught (in promise) Error: call revert exception [ See: https://links.ethers.org/v5-errors-CALL_EXCEPTION ] (method="name()", data="0x", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.7.0)
const provider = new ethers.providers.Web3Provider(window.ethereum);
const address = '0x6B175474E89094C44Da98b954EedeAC495271d0F'
const abi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function totalSupply() view returns (uint256)"
]
const connectWallet = (async()=>{
await provider.send("eth_requestAccounts",[]);
})
const contract = new ethers.Contract(address,abi,provider);
const getInfo = (async()=>{
const n = await contract.name();
console.log(n)
})
I am trying to read the contract but why I am getting this error ? and how can i solve it??
There is a contract deployed on the specified address only on the Ethereum mainnet. But this address doesn't hold any contract on other networks.
Since you're using the window.ethereum provider published by MetaMask or another browser wallet extension, the call is sent on the network that is currently selected in the wallet.
So if you select the Ethereum mainnet, the call succeeds. In all other cases, the call fails because there is no contract on the selected network that could respond to the call.

web3.eth.getAccounts() returns empty array when using Infura provider. Why?

I was trying to use Infura api to make an Ethereum web app. First I compiled a solidity contract and then deployed it using infura api on rinkeby network. Here is my deploy script which seems to be running successfully.
const HDWalletProvider = require("truffle-hdwallet-provider");
const Web3 = require('Web3');
const compileFactory = require('./build/CampaignFactory.json');
const provider = new HDWalletProvider(
"MY_SECRET_MNEMONIC",
"https://rinkeby.infura.io/v3/363ea9633bcb40bc8a857d908ee27094"
);
const web3 = new Web3(provider);
console.log("provider info: " + provider);
const deploy = async () => {
const accounts = await web3.eth.getAccounts();
console.log("account used: " + accounts[0]);
result = await new web3.eth.Contract(JSON.parse(compileFactory.interface))
.deploy({data: "0x"+compileFactory.bytecode})
.send({from: accounts[0]});
console.log("deployed to address: " + result.options.address);
};
deploy();
Then I created another script web3.js which creates a web3 provider using Infura api:
import Web3 from 'web3';
let web3;
if (typeof window !== 'undefined' && typeof window.web3!=='undefined') {
// we are in the browser and metamask is running.
web3 = new Web3(window.web3.currentProvider);
console.log("using metamask");
}
else {
// we are in server OR user without metamask.
const provider = new Web3.providers.HttpProvider(
"https://rinkeby.infura.io/v3/363ea9633bcb40bc8a857d908ee27094"
);
web3 = new Web3(provider);
console.log("using infura");
}
export default web3;
but when I import this web3.js file somewhere and then try to use 'web3' object, it returns empty array of accounts. For example:
import web3 from '../../ethereum/web3';
...
const accounts = await web3.eth.getAccounts();
console.log("Account list: "+accounts); // returns empty array.
But ideally it should return the accounts list associated with my mnemonic. What is the problem?
A naive solution is to use the HDWalletProvider in your second script, instead of the HttpProvider.
What exactly do you want to do with the second script? I suspect that the second script is something that you want to deploy with a DApp, so including your mnemonic there is a good way to give away all your ether to the first user who knows how to "view source".
If so, in the first script, display the addresses associated with your mnemonic using: provider.getAddresses() and then hard-code those addresses into the second script, for later usage. Naturally, you won't be able to sign any transactions in the second script, but at least you can read data associated with those addresses.
Put await window.ethereum.enable() before web3.eth.getAccounts(),
Or use requestAccounts() instead of getAccounts() :
await web3.eth.requestAccounts();

Calling a Smart Contract method using Web3 1.0

Presently, I have a smart contract successfully deployed to the Rinkeby testnet, I'm having trouble accessing the method in question using web3 version 1.0.
Here's my web3 code, which instantiates a contract instance and calls a contract method:
const contractInstance = new web3.eth.Contract(abiDefinition, contractAddress);
var value = web3.utils.toWei('1', 'ether')
var sentTransaction = contractInstance.methods.initiateScoreRetrieval().send({value: value, from: fromAddress})
console.log('event sent, now set listeners')
sentTransaction.on('confirmation', function(confirmationNumber, receipt){
console.log('method confirmation', confirmationNumber, receipt)
})
sentTransaction.on('error', console.error);
And here is my smart contract, or rather a version of it stripped down to the relevant bits:
contract myContract {
address private txInitiator;
uint256 private amount;
function initiateScoreRetrieval() public payable returns(bool) {
require(msg.value >= coralFeeInEth);
amount = msg.value;
txInitiator = msg.sender;
return true;
}
}
I am not able to get to the console.log that is setting the event listeners on the web3 side, and I am not getting an error of any kind thrown. I'm certainly not getting the consoles from the actual event listeners. I am guessing something is wrong with the way I'm sending the transaction, but I think I am correctly following the pattern documented below: https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#methods-mymethod-send
Does anyone have any insight on how to use web3 1.0 to make contract method calls correctly? Am I doing something wrong with how I'm passing options, etc.?
Thanks!
I believe you forgot to specify your HttpProvider for your web3, thus you're not connecting to live Rinkeby network, and by default web3 is running on you local host, which is why even if you provide the right contract address, there's nothing there.
To connect to live network, I would strongly encourage you to use Infura Node by ConsenSys.
const Web3 = require("web3");
const web3 = new Web3(new Web3.providers.HttpProvider("https://rinkeby.infura.io"));
Then by now, everything should work perfectly fine.
First, you need to generate your transaction ABI using encodeABI(), here is an example:
let tx_builder = contractInstance.methods.myMethod(arg1, arg2, ...);
let encoded_tx = tx_builder.encodeABI();
let transactionObject = {
gas: amountOfGas,
data: encoded_tx,
from: from_address,
to: contract_address
};
Then you have to sign the transaction using signTransaction() using private key of sender. Later you can sendSignedTransaction()
web3.eth.accounts.signTransaction(transactionObject, private_key, function (error, signedTx) {
if (error) {
console.log(error);
// handle error
} else {
web3.eth.sendSignedTransaction(signedTx.rawTransaction)
.on('receipt', function (receipt) {
//do something
});
}

Ethereum Web3.js Invalid JSON RPC response: ""

I am using web3.js module for ethereum. While executing a transaction I am getting error response.
Error:
"Error: Invalid JSON RPC response: ""
at Object.InvalidResponse (/home/akshay/WS/ethereum/node_modules/web3-core-helpers/src/errors.js:42:16)
at XMLHttpRequest.request.onreadystatechange (/home/akshay/WS/ethereum/node_modules/web3-providers-http/src/index.js:73:32)
at XMLHttpRequestEventTarget.dispatchEvent (/home/akshay/WS/ethereum/node_modules/xhr2/lib/xhr2.js:64:18)
at XMLHttpRequest._setReadyState (/home/akshay/WS/ethereum/node_modules/xhr2/lib/xhr2.js:354:12)
at XMLHttpRequest._onHttpResponseEnd (/home/akshay/WS/ethereum/node_modules/xhr2/lib/xhr2.js:509:12)
at IncomingMessage.<anonymous> (/home/akshay/WS/ethereum/node_modules/xhr2/lib/xhr2.js:469:24)
at emitNone (events.js:111:20)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1064:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)"
I am using ropsten test network url for testing my smart contract:
https://ropsten.infura.io/API_KEY_HERE
When I call the balanceOf function, it works fine but when I try to call function transfer it send me this error. The code is mentioned below:
router.post('/transfer', (req, res, next)=>{
contractInstance.methods.transfer(req.body.address, req.body.amount).send({from:ownerAccountAddress})
.on('transactionHash',(hash)=>{
console.log(hash)
}).on('confirmation',(confirmationNumber, receipt)=>{
console.log(confirmationNumber)
console.log(receipt)
}).on('receipt', (receipt)=>{
console.log(receipt)
}).on('error',(err)=>{
console.log(err)
})
})
Please let me know where I am wrong.
EDIT: I am using web3js version "web3": "^1.0.0-beta.34"
To add to what maptuhec said, while calling a "state-changing" function in Web3 or a state-changing transaction, it MUST be SIGNED!
Below is an example of when you're trying to call a public function (or even a public contract variable), which is only reading (or "view"ing) and returning a value from your smart contract and NOT changing its state, in this we don't need to necessarily specify a transaction body and then sign it as a transaction, because it doesn't change the state of our contract.
contractInstance.methods.aPublicFunctionOrVariableName().call().then( (result) => {console.log(result);})
**
State-Changing Transactions
**
Now, consider the example below, here we're trying to invoke a "state-changing" function and hence we'll be specifying a proper transaction structure for it.
web3.eth.getTransactionCount(functioncalleraddress).then( (nonce) => {
let encodedABI = contractInstance.methods.statechangingfunction().encodeABI();
contractInstance.methods.statechangingfunction().estimateGas({ from: calleraddress }, (error, gasEstimate) => {
let tx = {
to: contractAddress,
gas: gasEstimate,
data: encodedABI,
nonce: nonce
};
web3.eth.accounts.signTransaction(tx, privateKey, (err, resp) => {
if (resp == null) {console.log("Error!");
} else {
let tran = web3.eth.sendSignedTransaction(resp.rawTransaction);
tran.on('transactionHash', (txhash) => {console.log("Tx Hash: "+ txhash);});
For more on signTransaction, sendSignedTransaction, getTransactionCount and estimateGas
when using Web3.js you should sign the transactions. When you call functions which are non-constant, like transfer, you should sign the transaction and after that send the signed transaction (there is a method called sendSignedTransaction). This is very hard using web3js, I recommend using ehtersjs, with it everything is a lot easier.
in my case, it's the network problem.
I set the HTTP_PROXY and HTTPS_PROXY in my terminal and can successfully curl google.com, however I got this error.
solution:
I ssh to another server (located in HK where network is good to connect everywhere in the world ) and everything is fine.