React, Hardhat frontend smart contract method calling, how to do so? - ethereum

I'm using hardhat locally and have a react frontend up and running but I can't call the methods without errors.
I've tried both ethers.js and web3.
Here's my code and attempts. Please let me know if you see what I'm doing wrong.
I'm trying to interact with contracts that are deployed in the local hardhat env through web3
I'm unable to get back the data from the contract, here's the info
I have:
var list = await contract.methods.getList();
console.log("list ", list );
which gets me
list {arguments: Array(0), call: ƒ, send: ƒ, encodeABI: ƒ, estimateGas: ƒ, …}
When I do
var list = await contract.methods.getList().call();
console.log("list ", list );
I get this error in the browser:
Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.
I do:
Setup in console:
npx hardhat node
>Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
>Accounts
>========
>...
npx hardhat compile
> Nothing to compile
npx hardhat run scripts/deploy.js --network hardhat
Note: In the deploy.js file, I do a
const list = await contract.getList();
console.log("list", list ); // correctly outputs ["string", "string"]
The method:
mapping(uint256 => address) internal list;
uint256 internal listCount;
function getList() public override view returns (address[] memory) {
address[] memory assets = new address[](listCount);
for (uint256 i = 0; i < listCount; i++) {
assets[i] = list[i];
}
return assets;
}
In react App.js:
import Contract_from './data/abi/Contract_.json'; // Contract_ is a placer
var contract = new web3.eth.Contract(Contract_, address_given_on_deploy);
var contractAddress = await contract .options.address; // correctly outputs
var list= await contract.methods.getList().call();
console.log("list", list);
As you see, this doesn't return the values from the method. What am I doing wrong here?
For any reason and may be likely the issue, here's my config:
require("#nomiclabs/hardhat-waffle");
// openzeppelin adds
require("#nomiclabs/hardhat-ethers");
require('#openzeppelin/hardhat-upgrades');
//abi
require('hardhat-abi-exporter');
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async () => {
const accounts = await ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
/**
* #type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
networks: {
hardhat: {
gas: 12000000,
blockGasLimit: 0x1fffffffffffff,
allowUnlimitedContractSize: true,
timeout: 1800000,
chainId: 1337
}
},
solidity: {
compilers: [
{
version: "0.8.0",
settings: {
optimizer: {
enabled: true,
runs: 1000
}
}
},
{
version: "0.8.2",
settings: {
optimizer: {
enabled: true,
runs: 1000
}
}
},
],
},
abiExporter: {
path: './frontend/src/data/abi',
clear: true,
flat: true,
only: [],
spacing: 2
}
}
__
I thought maybe i would try ethers.js since that is what i do my testing in but same issue.
For whatever reason, I can "get" the contracts, print the methods that belong to them, but I can't actually call the methods.
Here's my ethers.js brevity:
provider = new ethers.providers.Web3Provider(window.ethereum);
if(provider != null){
const _contract = new ethers.Contract(address, _Contract, provider);
var list= await _contract.getList().call();
console.log("list", list);
}
The error i get from this is:
Error: call revert exception (method="getList()", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.4.0)
I've tried numerous contracts in the protocol and same thing for each

Related

Not able to bridge deployed ERC20 token through POS Bridge using Maticjs, getting this error "execution reverted: ERC20: approve to the zero address"

Here is my code, I have also added the options as well in the approve function which contains the from, to, gasLimit but that also doesn't work for me
const { POSClient, use } = require('#maticnetwork/maticjs');
const { Web3ClientPlugin } = require('#maticnetwork/maticjs-web3');
const HDWalletProvider = require('#truffle/hdwallet-provider');
require('dotenv').config();
use(Web3ClientPlugin);
async function getPOSClient() {
const posClient = new POSClient();
return await posClient.init({
network: 'testnet',
version: 'mumbai',
parent: {
provider: new HDWalletProvider(
process.env.PVT_KEY,
process.env.GOERLI_RPC
),
defaultConfig: {
from: process.env.FROM_ADDRESS,
},
},
child: {
provider: new HDWalletProvider(
process.env.PVT_KEY,
process.env.MUMBAI_RPC
),
defaultConfig: {
from: process.env.FROM_ADDRESS,
},
},
});
}
async function approveToken() {
const posClient = await getPOSClient();
// console.log('🚀 ~ file: index.js:36 ~ approveToken ~ posClient', posClient);
const erc20Token = posClient.erc20(process.env.ROOT_TOKEN, true);
const result = await erc20Token.approve('1000');
const txHash = await result.getTransactionHash();
console.log('txHash', txHash);
const receipt = await result.getReceipt();
console.log('receipt', receipt);
}
approveToken();
The error which I'm getting while approving the token,
Error: execution reverted: ERC20: approve to the zero address
0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002245524332303a20617070726f766520746f20746865207a65726f2061646472657373000000000000000000000000000000000000000000000000000000000000
I have also mapped the token from polygon token mapper portal also. Please give your feedback
This error occurs when you are trying to approve an ERC20 token to the zero address.
Assuming the approve interface is approve(spender, amount)
I believe that if you add the approved address to this call const result = await erc20Token.approve(ADDRESS,'1000'); this issue will be resolved.

how to integrate lighthouse with testcafe?

I need to pass the connection argument while calling lighthouse
https://github.com/GoogleChrome/lighthouse/blob/master/lighthouse-core/index.js#L41
async function lighthouse(url, flags = {}, configJSON, connection) {
// verify the url is valid and that protocol is allowed
if (url && (!URL.isValid(url) || !URL.isProtocolAllowed(url))) {
throw new LHError(LHError.errors.INVALID_URL);
}
// set logging preferences, assume quiet
flags.logLevel = flags.logLevel || 'error';
log.setLevel(flags.logLevel);
const config = generateConfig(configJSON, flags);
connection = connection || new ChromeProtocol(flags.port, flags.hostname);
// kick off a lighthouse run
return Runner.run(connection, {url, config});
}
And in my testcafe my tests look like
test('Run lighthouse, async t => {
lighthouse('https://www.youtube.com', {}, {}, ????)
})
I am unable to retrieve the connection of the chrome instance that testcafe had opened up, instead of spawning a new chromeRunner
there is an npm library called testcafe-lighthouse which helps to audit web pages using TestCafe. It also has the capability to produce an HTML detailed report.
Install the plugin by:
$ yarn add -D testcafe-lighthouse
# or
$ npm install --save-dev testcafe-lighthouse
Audit with default threshold
import { testcafeLighthouseAudit } from 'testcafe-lighthouse';
fixture(`Audit Test`).page('http://localhost:3000/login');
test('user performs lighthouse audit', async () => {
const currentURL = await t.eval(() => document.documentURI);
await testcafeLighthouseAudit({
url: currentURL,
cdpPort: 9222,
});
});
Audit with custom Thresold:
import { testcafeLighthouseAudit } from 'testcafe-lighthouse';
fixture(`Audit Test`).page('http://localhost:3000/login');
test('user page performance with specific thresholds', async () => {
const currentURL = await t.eval(() => document.documentURI);
await testcafeLighthouseAudit({
url: currentURL,
thresholds: {
performance: 50,
accessibility: 50,
'best-practices': 50,
seo: 50,
pwa: 50,
},
cdpPort: 9222,
});
});
you need to kick start the test like below:
# headless mode, preferable for CI
npx testcafe chrome:headless:cdpPort=9222 test.js
# non headless mode
npx testcafe chrome:emulation:cdpPort=9222 test.js
I hope it will help your automation journey.
I did something similar, I launch ligthouse with google chrome on a specific port using CLI
npm run testcafe -- chrome:headless:cdpPort=1234
Then I make the lighthouse function to get port as an argument
export default async function lighthouseAudit(url, browser_port){
let result = await lighthouse(url, {
port: browser_port, // Google Chrome port Number
output: 'json',
logLevel: 'info',
});
return result;
};
Then you can simply run the audit like
test(`Generate Light House Result `, async t => {
auditResult = await lighthouseAudit('https://www.youtube.com',1234);
});
Hopefully It helps

is not contract address during truffle test

I have a stupid smart contract like this:
pragma solidity ^0.4.24;
contract ProdottoFactory {
function foo() view returns(string nome){
return "foo";
}
}
And I want to test it with chai
var Prodotto = artifacts.require("ProdottoFactory");
expect = require("chai").expect;
contract("descrizione primo test", function () {
describe("test 2", function () {
it("blablabla", function () {
return Prodotto.new().then(
istance => {
prodottoContract = istance;
}
)
})
})
})
contract("descrizione primo test2", function () {
describe("test 2 2", function () {
it("blablabla2",function () {
return prodottoContract.foo().then(function (res) {
expect(res.toString()).to.be.equal("foo")
})
})
})
})
When I run the command
truffle test
I have this error
Error: Attempting to run transaction which calls a contract function, but recipient address 0xe8f29e5c4ca41c5b40ed989439ddeae4d9384984 is not a contract address
truffle.js
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545, // Ganache GUI
network_id: "*" // Match any network id
}
}
};
contracts/ProdottoFactory.sol
pragma solidity ^0.4.24;
contract ProdottoFactory {
function foo() pure public returns(string nome){
return "foo";
}
}
test/ProdottoFactory.js
var pf = artifacts.require("ProdottoFactory");
contract('ProdottoFactory', function(accounts) {
var pfInstance;
before(function() {
return pf.new()
.then(function(instance) {
pfInstance = instance;
});
});
it("should return foo", function() {
return pfInstance.foo.call()
.then(function(str) {
assert.equal(str, "foo");
});
});
});
I made 2 small changes in your contract:
I added public keyword. It's good practice to always define the visibility of your function.
I replaced view to pure. When you are not reading from blockchain/state variable, use pure. More info can be found inside the docs here.
FYI, you don't have to require chai or mocha library. It's already there when you init a Truffle project using truffle init command. The before keyword is part of Mocha library. You can read more about it here.
Lastly, if you want to know the differences between new and deployed keyword in Truffle, read my thread here.

Send signed transactions to Ropsten or Truffle develop network with Trezor (Hardware Wallet)

Im trying to integrate web3js with Trezor in a truffle dev network or using ropsten test network.
The idea is to sign the transactions using the hardware wallet and then send a raw transaction using web3js
Im getting that we dont have balance to make the transaction, probably because web3js isnt taking one of the 10 truffle accounts and is using the trezor address that isnt in my local network..
On ropsten i have some ethers and i get "invalid address"
Is there a way to send a signed transactions (with trezor) using web3js into a truffle develop network? i mean, is there a way to include the trezor address into the truffle network?
The situation in truffle is explained more in details here, but the question could be generalized to "is there a way to include hardware wallets into truffle development network?" : https://github.com/trufflesuite/truffle/issues/973
Using ropsten I have managed to send a transaction and receive a transaction hash in the callback, but if we query for that transaction we get that the transaction doesnt exists.. so.. how is that possible?
I tried deploying a contract into Ropsten too and now im getting "Invalid address" when invoking a smart contract function. Maybe the signing function is wrong? anyone could integrate Trezor transaction signining with web3js?
Do you guys see anything wrong in the signing and sending process that we have followed? Maybe is there something wrong on the R, V and S parameters handling
..
Another important thing is that i am using https://github.com/ethereumjs/ethereumjs-tx for creating the raw transactions
Issues published in web3js, truffle and trezzor connect with more information:
https://github.com/trufflesuite/truffle/issues/973
https://github.com/ethereum/web3.js/issues/1669
https://github.com/trezor/connect/issues/130
kind regards
trezorLogin = async()=> {
let trezor= await this.getTrezor();
// site icon, optional. at least 48x48px
let hosticon = 'https://doc.satoshilabs.com/trezor-apps/_images/copay_logo.png';
// server-side generated and randomized challenges
let challenge_hidden = '';
let challenge_visual = '';
//use anonimous functions on callback otherwise returns cross origin errors
trezor.requestLogin(hosticon, challenge_hidden, challenge_visual, function (result){
if (result.success) {
console.log('Public key:', result.public_key); // pubkey in hex
console.log('Signature:', result.signature); // signature in hex
console.log('Version 2:', result.version === 2); // version field
console.log(result);
}else {
console.error('Error:', result.error);
}
});}
trezorSignTx= async(transaction)=> {
let trezor= await this.getTrezor();
// spend one change output
let address_n = "m/44'/60'/0'/0/0"
// let address_n = [44 | 0x80000000,
// 60 | 0x80000000,
// 0 | 0x80000000 ,
// 0 ]; // same, in raw form
let nonce = transaction.nonce.substring(2); // note - it is hex, not number!!!
let gas_price = transaction.gasPrice.substring(2);
let gas_limit = transaction.gasLimit.substring(2);
let to = transaction.to.substring(2);
// let value = '01'; // in hexadecimal, in wei - this is 1 wei
let value = transaction.value.substring(2); // in hexadecimal, in wei - this is about 18 ETC
let data = transaction.data.substring(2); // some contract data
// let data = null // for no data
let chain_id = 5777; // 1 for ETH, 61 for ETC
return new Promise (function (resolve,reject) {
trezor.ethereumSignTx(
address_n,
nonce,
gas_price,
gas_limit,
to,
value,
data,
chain_id,
function (response) {
if (response.success) {
console.log('Signature V (recovery parameter):', response.v); // number
console.log('Signature R component:', response.r); // bytes
console.log('Signature S component:', response.s); // bytes
resolve(response);
} else {
console.error('Error:', response.error); // error message
resolve(null);
}
});
})
}
getTrezorAddress = async() => {
let trezor= await this.getTrezor();
// spend one change output
let address_n = "m/44'/60'/0'/0/0";
trezor.ethereumGetAddress(address_n, function (result) {
if (result.success) { // success
console.log('Address: ', result.address);
} else {
console.error('Error:', result.error); // error message
}
});
}
getTrezor = async() => {
let trezorC;
await getTrezorConnect
.then(trezorConnect => {
trezorC= trezorConnect;
})
.catch((error) => {
console.log(error)
})
return trezorC;
}
sendTransaction= async(address, amount, id)=>{
let tokenInstance = this.props.smartContractInstance;
let getData = tokenInstance.mint.getData(address, amount);
let tx = {
nonce: '0x00',
gasPrice: '0x09184e72a000',
gasLimit: '0x2710',
to: CONTRACT_ADDRESS,
value: '0x00',
from:CONTRACT_OWNER_ADDRESS,
data: getData
};
let response = await this.trezorSignTx(tx);
let web3;
let _this = this;
if (response!=null){
getWeb3
.then(results => {
web3= results.web3;
let v = response.v.toString();
if (v.length % 2 != 0){
v="0"+v;
}
tx.r=Buffer.from(response.r,'hex');
tx.v=Buffer.from(v,'hex');
tx.s=Buffer.from(response.s,'hex');
let ethtx = new ethereumjs(tx);
console.dir(ethtx.getSenderAddress().toString('hex'), );
const serializedTx = ethtx.serialize();
const rawTx = '0x' + serializedTx.toString('hex');
console.log(rawTx);
//finally pass this data parameter to send Transaction
web3.eth.sendRawTransaction(rawTx, function (error, result) {
if(!error){
_this.props.addTokens(id)
.then(()=>{
_this.setState({modalOpen: true});
_this.props.getAllTransactions();
}
);
}else{
alert(error)
}
});
})
.catch((error) => {
console.log(error)
})
}else{
alert("There was an error signing with trezor hardware wallet")
}
}
The getTrezorConnect function is just get window.trezorConnect asynchronously because the object is injected as script
<script src="https://connect.trezor.io/4/connect.js"></script>
let getTrezorConnect = new Promise(function(resolve, reject) {
// Wait for loading completion
window.addEventListener('load', function() {
let trezorConnect = window.TrezorConnect
return resolve(trezorConnect)
})});
export default getTrezorConnect
Well, after a lot of trying we have managed to send a raw transaction signed with Trezor to Ropsten, Truffle (see the edit on the bottom of the answer) and also to a local private Geth network, so, the code is ok and there is no problem with Trezor integration on those environments
https://ropsten.etherscan.io/address/0x89e2c46b22881f747797cf67310aad1a831d50b7
This are the things that i had changed in order to make it possible to send signed transactions to the Ropsten testnet.
This assumes that you have your contract deployed into Ropsten and you have the contract address.
1) Get the address of your Trezor account
getTrezorAddress = async() => {
let trezor= await this.getTrezor();
// spend one change output
let address_n = "m/44'/1'/0'/0/0";
trezor.ethereumGetAddress(address_n, function (result) {
if (result.success) { // success
console.log('Address: ', result.address);
} else {
console.error('Error:', result.error); // error message
}
});
}
2) Put the trezor address into the from field of your raw transaction, get the nonce of the transaction by getting the transaction count for that address. Important: use the "pending" optional parameter on getTransactionCount to get all the transactions of the account, otherwise you will be overriting pending transactions.
getNonce = async(address) => {
let web3 = await this.getWeb3();
return new Promise (function (resolve,reject) {
web3.eth.getTransactionCount(address, "pending", function (error,result){
console.log("Nonce "+result);
resolve(result);
});
});
}
let count = null;
await this.getNonce("0xedff546ac229317df81ef9e6cb3b67c0e6425fa7").then(result => {
if(result.length % 2 !==0){
result = "0"+result;
}
count = "0x"+result;
});
let tx = {
nonce: count ,
gasPrice: web3.toHex(gasPriceGwei*1e9),
gasLimit: web3.toHex(gasLimit),
to: CONTRACT_ADDRESS,
value: '0x00',
data: getData,
chainId:chainId,
from:"yourTrezzorAddress"
};
3) The r, s, v parameters were incorrect, the right way to handle them is take that values for the trezor response and just convert it to hexa:
// response is the Trezor sign response
tx.v= response.v;
tx.r="0x"+response.r;
tx.s="0x"+response.s;
let ethtx = new ethereumjs(tx);.
const serializedTx = ethtx.serialize();
const rawTx = '0x' + serializedTx.toString('hex');
//finally pass this data parameter to send Transaction
web3.eth.sendRawTransaction(rawTx, someCallbackFunction);
Important: the mining time in ropsten will be between 15 and 30 secs so if in your someCallbackFunction you check for the transaction receipt, using the hash, you will get null as result, because the transaction is in a pending state.
4) To test it at ropsten we use Infura, so we change the web3 provider:
import Web3 from 'web3'
import HDWalletProvider from "truffle-hdwallet-provider";
let getWeb3 = new Promise(function(resolve, reject) {
// Wait for loading completion to avoid race conditions with web3 injection timing.
window.addEventListener('load', function() {
let results
let web3 = window.web3
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider.
web3 = new Web3(web3.currentProvider)
results = {
web3: web3
}
console.log('Injected web3 detected.');
return resolve(results)
} else {
// Fallback to localhost if no web3 injection. We've configured this to
// use the development console's port by default.
// let provider = new Web3.providers.HttpProvider("https://ropsten.infura.io/your_infura_api_key")
let mnemonic = "infura mnemonic"
let provider = new HDWalletProvider(mnemonic, "https://ropsten.infura.io/your_infura_api_key")
web3 = new Web3(provider)
results = {
web3: web3
}
console.log('No web3 instance injected, using Local web3.');
return resolve(results)
}
})
})
export default getWeb3
EDIT:
This also works on Truffle! check the last comments of this issue https://github.com/trufflesuite/truffle/issues/973
We developed a small library to use Truffle with Trezor hardware wallets: https://github.com/rarible/trezor-provider
It can be used like this:
const { createProvider } = require('#rarible/trezor-provider')
module.exports = {
networks: {
ropsten: {
provider: function() {
//websocket and http urls are supported
return createProvider({ url: "{infura or other}", path: "m/44'/60'/0'/0/0", chainId: 3 })
},
network_id: 3
}
}
};

How to response a plain text in feathersjs websocket api?

I define a feathers service api as below:
class Monitor {
find(_) {
const metrics = prom.register.metrics();
log.info(metrics);
return new Promise((resolve) => {
resolve({text: metrics});
});
}
}
function restFormatter(req, res) {
res.format({
'text/plain': function() {
log('xxxx:', res);
res.end(`The Message is: "${res.data}"`);
}
});
}
module.exports = function () {
const app = this;
// Initialize our service with any options it requires
const service = new Monitor();
app.configure(rest(restFormatter)).use('/metrics', service);
// Get our initialize service to that we can bind hooks
const monitorService = app.service('/metrics');
// Set up our before hooks
monitorService.before(hooks.before);
// Set up our after hooks
monitorService.after(hooks.after);
return service;
};
module.exports.Monitor = Monitor;
when call this API from browser, I get below response:
"# HELP nodejs_gc_runs_total Count of total garbage collections.\n# TYPE nodejs_gc_runs_total counter\n\n# HELP nodejs_gc_pause_seconds_total Time spent in GC Pause in seconds.\n# TYPE nodejs_gc_pause_seconds_total counter\n\n# HELP nodejs_gc_reclaimed_bytes_total Total number of bytes reclaimed by GC.\n# TYPE nodejs_gc_reclaimed_bytes_total counter\n"
from above output you can see that feathersjs doesn't return the data in plain text format. It transpile my response text into a string. Below is the output from express service shown in the browser:
# HELP nodejs_gc_runs_total Count of total garbage collections.
# TYPE nodejs_gc_runs_total counter
# HELP nodejs_gc_pause_seconds_total Time spent in GC Pause in seconds.
# TYPE nodejs_gc_pause_seconds_total counter
# HELP nodejs_gc_reclaimed_bytes_total Total number of bytes reclaimed by GC.
# TYPE nodejs_gc_reclaimed_bytes_total counter
# HELP newConnection The number of requests served
# TYPE newConnection counter
this output is what I really want. How can I make them feathersjs service return above output?
Below is my feathersjs configuration part:
app
.use(compress())
.options('*', cors())
.use(cors())
.use('/', serveStatic(app.get('public')))
.use(bodyParser.json())
.use(bodyParser.urlencoded({extended: true}))
.configure(hooks())
.configure(rest())
.configure(
swagger({
docsPath: '/docs',
uiIndex: path.join(__dirname, '../public/docs.html'),
info: {
title: process.env.npm_package_fullName,
description: process.env.npm_package_description
}
})
)
.configure(
primus(
{
transformer: 'websockets',
timeout: false
},
(primus) => {
primus.library();
primus.save(path.join(__dirname, '../public/dist/primus.js'));
}
)
)
.configure(services)
.configure(middleware);
You are configuring feathers-rest twice which is why you still get the old output. Remove the app.configure(rest(restFormatter)) from your service file and then either change .configure(rest()) to .configure(rest(restFormatter)) in the main file to use the formatter to apply to all services or register a custom middleware for the service that does the formatting just for that service:
app.use('/metrics', service, function(req, res) {
res.format({
'text/plain': function() {
log('xxxx:', res);
res.end(`The Message is: "${res.data}"`);
}
});
});