Estimate gas prices with Ethers JS - javascript

I am trying to make a simple js bot that checks every block for eth(or the main token of the chain) and sends it to another wallet.
I have a working bot:
const { ethers } = require('ethers')
const provider = new ethers.providers.JsonRpcProvider("")
const addressReceiver = ''
const privateKeys = [""]
const bot = async =>{
provider.on('block', async () => {
console.log('Listening to new block, waiting ;)');
for (let i = 0; i < privateKeys.length; i++){
const _target = new ethers.Wallet(privateKeys[i]);
const target = _target.connect(provider);
const balance = await provider.getBalance(target.address);
const txBuffer = ethers.utils.parseEther('0.005');
if (balance.sub(txBuffer) > 0){
console.log("New Account with Eth!");
const amount = balance.sub(txBuffer);
try {
await target.sendTransaction({
to: addressReceiver,
value: amount
});
console.log(`Success! transferred -->${ethers.utils.formatEther(balance)}`);
} catch(e){
console.log(`error: ${e}`);
}
}
}
})
}
bot();
But this has a set transaction buffer that ends up leaving some eth in the wallet after it the bot runs. I want to estimate fees and then subtract those fees from the total taken out. Something like this :
const {
ethers
} = require('ethers')
const provider = new ethers.providers.JsonRpcProvider("")
const addressReceiver = ''
const privateKeys = [""]
const bot = async =>{
provider.on('block', async () => {
console.log('Listening to new block, waiting ;)');
for (let i = 0; i < privateKeys.length; i++) {
const _target = new ethers.Wallet(privateKeys[i]);
const target = _target.connect(provider);
const balance = await provider.getBalance(target.address);
const gasLimit = await provider.estimateGas({
to: addressReceiver,
value: await provider.getBalance(target.address),
gasLimit: 21000,
gasPrice: ethers.utils.parseUnits('10', 'gwei'),
nonce: await provider.getTransactionCount(privateKeys[i])
})
if (balance.sub(gasLimit) > 0) {
console.log("New Account with Eth!");
const amount = balance.sub(gasLimit);
try {
await target.sendTransaction({
to: addressReceiver,
value: amount
});
console.log(`Success! transferred -->${ethers.utils.formatEther(balance)}`);
} catch (e) {
console.log(`error: ${e}`);
}
}
}
})
}
bot();
But this throws an ENS name not configured error.

Few issues here:
ENS name not figured is probably because addressReceiver was an ENS name (ends with .eth), not an address (starts with 0x). Use an address.
amount = balance.sub(gasLimit) is not right. gasLimit is the amount of gas used, not in eth. You'll need to multiply that by fee per gas to get eth. The easiest way to figure out the exact fee is to set tx.maxFeePerGas and tx.maxPriorityFeePerGas to be the same values. That will cause you to overpay most of the time. Then the new code will be amount = balance.sub(tx.maxFeePerGas.mul(gasLimit))

Related

Investigating an issue with my Uniswap tokens scraper that errors out after 7 requests to the Graph API

I'm making a scraper that will grab every Uniswap pair and save it to an array using the Graph API.
My problem occurs when I make my 7th request to the API.
Initially, I thought I was being rate limited because I was fetching 1000 tokens at a time, but after adding a 10 second wait between calls and decreasing the fetched tokens from 1000 to 10, it still stops on the 7th loop.
The script works perfectly until this point.
const axios = require('axios');
const fs = require('fs');
async function getTokens(skip) {
try {
const query = `
query tokens($skip: Int!) {
tokens(first: 10, skip: $skip) {
id
name
symbol
}
}
`;
const variables = {
skip: skip
};
const headers = {
"Content-Type": "application/json"
};
const { data } = await axios.post("https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3", {
query,
variables
}, {
headers
});
return data.data.tokens;
} catch (err) {
console.error(err);
return []
}
}
async function saveTokens(tokens) {
try {
await fs.promises.writeFile("uniTokens.json", JSON.stringify(tokens), { flag: "w" });
} catch (err) {
console.error(err);
}
}
async function main() {
let skip = 0;
let tokens = [];
const retrievedIds = new Set();
while (true) {
const newTokens = await getTokens(skip);
if (newTokens.length === 0) {
console.log("Reached end of tokens, finishing up...");
break;
}
// Only save tokens that haven't been retrieved before
const newIds = new Set(newTokens.map(token => token.id));
newIds.forEach(id => {
if (!retrievedIds.has(id)) {
tokens.push(newTokens.find(token => token.id === id));
retrievedIds.add(id);
}
});
console.log(`Retrieved ${tokens.length} tokens`);
await saveTokens(tokens);
skip += 1000;
// delay the next request by 10 seconds
//await new Promise(resolve => setTimeout(resolve, 10000));
}
}
main();
This is the error that it produces:
TypeError: Cannot read properties of undefined (reading 'tokens')
at getTokens (/root/unipairs/uni:31:26)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async main (/root/unipairs/uni:52:27)
Reached end of tokens, finishing up...

Firebase Cloud Functions Async

I am making a function for firebase cloud functions, I want a function to be called every time a new document is created in "posts". I want this function to perform the tasks that I put inside the "onCeatePost" function.
The problem I have is that I'm not sure if this is the correct way to structure such a function.
In several firebase examples I have seen that it is always called return _; or return null; at the end of a task, but I don't know how to structure the function so that all the tasks are carried out, could someone help me to restructure my function or tell me what is wrong please.
There are several if statements in the function, if the created publication does not comply with them, I would like it to skip them but continue with the other tasks that I put inside the function.
I don't know if it's too much to ask, but I'm new to this language and I haven't been able to find the answer I'm looking for. Thank you!
exports.onPostCreate = functions.firestore.document("/posts/{postId}").onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db.collection("users").doc(uid).update({"stats.posts": admin.firestore.FieldValue.increment(1)});
if (topic) {
await db.collection("topics").doc(topic.id).collection("user-authors").doc(uid).set({"date": snap.createTime});
}
if (contentForFeed == true) {
const userPath = db.collection("users").doc(uid);
await userPath.update({"stats.lastUpdate": snap.createTime});
}
if (previous) {
const previousId = previous.id;
const previousUid = previous.uid;
const refPrev = db.collection("posts").doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(priority.seconds + 120, priority.nanoseconds);
await db.collection("posts").doc(previousId).update({"newDate": newDate});
});
if (previousUid != uid) {
const path = db.collection("users").doc(uid).collection("user-posts");
const dataToSet = {"timestamp": snap.createTime, "uid": uid, "postId": onReplyToPostId};
await path(dataToSet);
}
}
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});
You'll find below the adapted code (untested) with 4 corrections.
Here are explanations for the two most important ones:
(Correction 2) In a transaction you need to use the transaction's update() method and not the "standard one"
(Correction 4) When all the asynchronous work is complete you need to return a value or a Promise. See this documntation page for more details.
exports.onPostCreate = functions.firestore
.document('/posts/{postId}')
.onCreate(async (snap) => {
const post = snap.data();
if (post) {
try {
const topic = post.topic;
const contentForFeed = post.contentForFeed;
const uid = post.uid;
const previous = post.prev;
await db
.collection('users')
.doc(uid)
.update({
'stats.posts': admin.firestore.FieldValue.increment(1),
});
if (topic) {
await db
.collection('topics')
.doc(topic.id)
.collection('user-authors')
.doc(uid)
.set({ date: snap.createTime });
}
if (contentForFeed == true) {
const userPath = db.collection('users').doc(uid);
await userPath.update({ 'stats.lastUpdate': snap.createTime });
}
let previousUid; // <= Correction 1
if (previous) {
const previousId = previous.id;
previousUid = previous.uid; // <= Correction 1
const refPrev = db.collection('posts').doc(previousId);
await db.runTransaction(async (t) => {
const doc = await t.get(refPrev);
const priority = doc.data().stats.date;
const newDate = new admin.firestore.Timestamp(
priority.seconds + 120,
priority.nanoseconds
);
t.update(refPrev, { newDate: newDate }); // <= Correction 2
});
if (previousUid != uid) {
const path = db
.collection('users')
.doc(uid)
.collection('user-posts');
const dataToSet = {
timestamp: snap.createTime,
uid: uid,
postId: onReplyToPostId,
};
await path.add(dataToSet); // <= Correction 3
}
}
return null; // <= Correction 4
} catch (err) {
functions.logger.log(err);
}
} else {
return null;
}
});

Mongoose data Writing issue in Javascript

Just started with a javascript couple of days back. I am trying to use MongoDB with mongoose to write the data but it is not writing even though the connection is established.
I would really appreciate it if you can help me point out what I am missing here.
dbtest.js - module to create connection
require("dotenv").config();
const mongoose = require("mongoose");
const Block = require("./model/blockSchema");
const connectDB = async () => {
try {
await mongoose.connect(process.env.DATABASE_URI, {
useUnifiedTopology: true,
useNewUrlParser: true,
});
console.log("CONNECTED to MONGODB DATABASE");
} catch (err) {
console.error(err);
}
};
module.exports = connectDB;
blockchain.js
Even though I have verified the connection before calling the main method, it looks like the connection is not available to class methods.
require("dotenv").config();
const { hash256 } = require("../util/util");
const block = require("./block");
const blockchain = require("./blockHeader");
const Block = require("../database/model/blockSchema");
const { mongoose } = require("mongoose");
const connect = require("../database/dbtest");
VERSION = 1;
const ZERO_HASH = String("0").padStart(64, "0");
// Create connection
connect();
class Blockchain {
GenesisBlock() {
try {
const BlockHeight = 0;
const prevBlockHash = ZERO_HASH;
this.addBlock(BlockHeight, prevBlockHash);
} catch (err) {
console.log(`Error in Genesis Blockchain Function \n ${err}`);
}
}
addBlock(BlockHeight, prevBlockHash) {
let timestamp = Date.now();
let Transaction = `Codies Alert sent ${BlockHeight} to Anni`;
let merkleRoot = hash256(Transaction);
let bits = "ffff001f";
let blockHeader = new blockchain.BlockHeader(
VERSION,
prevBlockHash,
merkleRoot,
timestamp,
bits
);
//Mine a Block
blockHeader.mine();
// Create Schema Instance to Write the data
let BlockObj = new Block({
Height: BlockHeight,
BlockSize: 1,
blockHeader: {
version: 1,
prevBlockHash: "00000",
timestamp: timestamp,
bits: bits,
nonce: blockHeader.nonce,
blockHash: blockHeader.blockhash,
},
TxCount: 1,
Transactions: Transaction,
});
// Mongoose Schema, Write data
BlockObj.save((err) => {
if (err) return console.log(`Error while Writing the Block ${err}`);
console.log(`Block Written Successfully!!!!!!!`);
});
this.chain = new block.Block(BlockHeight, 1, blockHeader, 1, Transaction);
console.log(BlockObj);
}
// Main Function to trigger the process
main() {
this.chain = "";
this.GenesisBlock();
while (true) {
let lastBlock = this.chain;
let Blockheight = lastBlock.Height + 1;
let prevBlockHash = lastBlock.BlockHeader.blockhash;
this.addBlock(Blockheight, prevBlockHash);
}
}
}
mongoose.connection.once("open", async () => {
console.log("Connection Verified and ready to write data");
// Create an instance and call the main method
const blockchain = new Blockchain();
blockchain.main();
});
Issue was due to async/await. Here is the updated code that works.
require("dotenv").config();
const { hash256 } = require("../util/util");
const block = require("./block");
const blockchain = require("./blockHeader");
const Block = require("../database/model/blockSchema");
const connect = require("../database/dbtest");
const getLastBlock = require("../database/read");
VERSION = 1;
const ZERO_HASH = String("0").padStart(64, "0");
let mongoose = "";
class Blockchain {
async GenesisBlock() {
try {
console.log(mongoose.connection.readyState);
const BlockHeight = 0;
const prevBlockHash = ZERO_HASH;
await this.addBlock(BlockHeight, prevBlockHash);
} catch (err) {
console.log(`Error in Genesis Blockchain Function \n ${err}`);
}
}
async addBlock(BlockHeight, prevBlockHash) {
let timestamp = Date.now();
let Transaction = `Codies Alert sent ${BlockHeight} to Anni Maan`;
let merkleRoot = hash256(Transaction);
let bits = "ffff001f";
let blockHeader = new blockchain.BlockHeader(
VERSION,
prevBlockHash,
merkleRoot,
timestamp,
bits
);
blockHeader.mine();
let BlockObj = {
Height: BlockHeight,
BlockSize: 1,
blockHeader: {
version: 1,
prevBlockHash: blockHeader.prevBlockhash,
merkleroot: merkleRoot,
timestamp: timestamp,
bits: bits,
nonce: blockHeader.nonce,
blockhash: blockHeader.blockhash,
},
TxCount: 1,
Transactions: Transaction,
};
// Mongoose Schema, Write data
try {
await new Block(BlockObj).save();
console.log(BlockObj);
console.log("Block Written Successfully");
this.chain = new block.Block(BlockHeight, 1, blockHeader, 1, Transaction);
} catch (err) {
console.log(`Error in addBlock Function \n ${err}`);
}
}
// Main Function to trigger the process
async main() {
const lastBlock = await getLastBlock.main(true);
console.log(lastBlock[0]);
this.chain = lastBlock[0];
if (!this.chain) {
await this.GenesisBlock();
}
while (true) {
console.log(mongoose.connection.readyState);
let lastBlock = this.chain;
let Blockheight = lastBlock.Height + 1;
let prevBlockHash = lastBlock.blockHeader.blockhash;
await this.addBlock(Blockheight, prevBlockHash);
}
}
}
const createConnection = async () => {
try {
mongoose = await connect();
const blockchain = new Blockchain();
blockchain.main();
} catch (err) {
console.log("Error while con", err);
}
};
createConnection();
The issue in your code is due to the asynchronous programming, whenever you make a db call it is an asynchronous request and you will need to use async-await or Promises to make it work. In your previous code you haven't used async await thats why your data is not getting written into the db.
You can learn about async await here link and about promises here.
Please go through it, promises are the core concept of js and you will definitely need it if you are using node js.
Also try to learn about synchronous and asynchronous from here, these are really necessary and base of node js.
All these db calls needs to call with promises or async await to make it work.

confusing contract's balance with externalContract's balance

The total code is here in my gist
https://gist.github.com/supersuperrookie/0cd4584c691c88af9aaa43d67fb730cd
I created a smart contract which has the below function,
function execute() public checkExternalStatus checkDeadline(false) {
uint256 balanceAmount = address(this).balance;
require(balanceAmount > threshold, "threshold is not reached");
(bool sent, ) = address(exampleExternalContract).call{
value: balanceAmount
}(abi.encodeWithSignature("complete()"));
require(sent, "exampleExternalContract.complete failed");
}
and when I test this method like this, i do not know why stakerContractbalance is 0 (★)
it('external contract sucessfully completed', async() => {
const amount = ethers.utils.parseEther('1.1');
await stakerContract.connect(addr1).stake({
value :amount,
});
await stakerContract.connect(addr1).execute();
const competed = await exampleExternalContract.completed();
expect(competed).to.equal(true);
const externalBalance = await ethers.provider.getBalance(exampleExternalContract.address);
expect(externalBalance).to.equal(amount);
const stakerContractbalance = await ethers.provider.getBalance(stakerContract.address);
--(★)confuing witch this line(why stakerContractbalance is 0?)
expect(stakerContractbalance).to.equal(0);
});

How would I set a manual gas limit in web3? To get the smart contract to execute?

I'm having a problem executing my smart contract. From the error code, I figured it is must be a gas limit problem, although I don't know where I would place the code in order for it to execute properly. Any suggestions?
Solidity code:
pragma solidity ^0.8.0;
import 'hardhat/console.sol';
contract WavePort {
//TrackWaves
uint totalWaves; //State Variable, Permanetly Stored In Contract Storage
constructor() {
console.log("Yo, I am I contract");
}
//Function Increments Waves On The Blockchain/ Immutable Never Decreasing
function wave() public {
totalWaves += 1;
console.log("%s has waved!", msg.sender);
}
//Function Returns Us the The Total Waves To Be Viewed
function getTotalWaves()public view returns(uint256) {
console.log("We have %d total waves", totalWaves);
return totalWaves;
}
}
JavaScript code:
const App = () => {
const [currentAccount, setCurrentAccount] = useState("")
const contractAddress = "contractAddress"
const contractABI = abi.abi
const checkIfWalletConnected = async () => {
let web3
try {
const {ethereum} = window;
if(!ethereum) {
window.alert("Make sure you have metamask !");
return;
}else {
web3 = new Web3(window.ethereum)
console.log("We have the ethereum object", ethereum)}
const accounts = await ethereum.request({method: 'eth_accounts'})
if(accounts.length !== 0) {
const account = accounts[0]
console.log("Found an Authorized account:", account, );
setCurrentAccount(account)
} else {
window.alert("NO authorized account found")
}
const EthBalance = await web3.eth.getBalance(accounts[0])
console.log(EthBalance)
}catch(err) {console.log(err)}
}
const connectWallet = async () => {
try {
const {ethereum} = window;
if(!ethereum) {alert("Get Metamask extenstion")
return
} else {
const accounts = await ethereum.request({method: 'eth_accounts'})
console.log("Connected", accounts[0])
window.alert("Wallet is Connected")
setCurrentAccount(accounts[0])
const ethBlance = await Web3.eth.getBalance(currentAccount)
console.log(ethBlance)
}
}catch(err) {
console.log(err)
}
}
const wave = async () => {
try {
const {ethereum} = window;
if(ethereum) {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const wavePortalContract = new ethers.Contract(contractAddress, contractABI, signer);
let count = await wavePortalContract.getTotalWaves( );
const waveTxn = await wavePortalContract.wave({gas:10000000 }, {gasPrice:80000000000});
console.log("Mining....", waveTxn.hash)
await waveTxn.wait( );
console.log("Mined-- ", waveTxn.hash)
count = await await wavePortalContract.getTotalWaves( );
console.log("Retrieved total wave count.. ", count.toNumber())
}else {
console.log("Eth Object doesn't exist!")
}
} catch(err) {console.log(`${err} hello world`) }
}
useEffect(() => {
checkIfWalletConnected();
}, [])
The error code I get:
Error: cannot estimate gas; transaction may fail or may require manual gas limit (error={"code":-32000,"message":"execution reverted"}, method="call", transaction={"from":"0xD49a9a33F180D1e35A30F0ae2Dbfe5716a740Ebc","to":"0x5FbDB2315678afecb367f032d93F642f64180aa3","data":"0x9a2cdc08","accessList":null}, code=UNPREDICTABLE_GAS_LIMIT, version=providers/5.5.1)
While calling a contract Abi method, you can pass an object of additional gas-related params in the second argument (or first if you don't have any arguments in your method).
So calling of wave function should looks like following:
const waveTxn = await wavePortalContract.wave({gasLimit:30000});
Check all additional params in the below documentation.
https://docs.ethers.io/v5/api/contract/contract/#contract-functionsSend

Categories

Resources