Pancake Sniper Bot - node.js

I have a demo sniper bot that work only with BNB.My question is can someone help me to make it work with other tokens?I think that most changes of the code must be done in network.js file.I want the sniper bot to work with different tokens like BUSD,USDT and more.
Full code:https://github.com/zookyy/crypto-sniper
network.js
const chalk = require('chalk');
const ethers = require('ethers');
const msg = require('./msg.js');
const config = require('./config.js');
const cache = require('./cache.js');
const args = require('minimist')(process.argv.slice(2));
require('./init.js');
class Network {
async load() {
try {
// initialize stuff
this.node = new ethers.providers.WebSocketProvider(config.cfg.wallet.wss_node);
// initialize account
this.wallet = new ethers.Wallet(config.cfg.wallet.secret_key);
this.account = this.wallet.connect(this.node);
// get network id for later use
this.network = await this.node.getNetwork();
// pcs stuff for later use
this.factory = new ethers.Contract(
'0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73',
[
'event PairCreated(address indexed token0, address indexed token1, address pair, uint)',
'function getPair(address tokenA, address tokenB) external view returns (address pair)'
],
this.account // pass connected account to pcs factory
);
this.router = new ethers.Contract(
'0x10ED43C718714eb63d5aA57B78B54704E256024E',
[
'function getAmountsOut(uint amountIn, address[] memory path) public view returns (uint[] memory amounts)',
'function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)',
'function swapExactTokensForTokensSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)',
'function swapExactETHForTokensSupportingFeeOnTransferTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable',
'function swapExactTokensForETHSupportingFeeOnTransferTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external',
'function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts)'
],
this.account // pass connected account to pcs router
);
this.contract_in = new ethers.Contract(
config.cfg.contracts.input = '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56',
[
{ "constant": true, "inputs": [{ "name": "_owner", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "balance", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": false, "inputs": [{ "name": "guy", "type": "address" }, { "name": "wad", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "approved", "type": "bool" }], "payable": false, "type": "function" },
{ "constant": true, "inputs": [{ "name": "sender", "type": "address" }, { "name": "guy", "type": "address" }], "name": "allowance", "outputs": [{ "name": "allowed", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": true, "inputs":[],"name":"symbol","outputs":[{"name":"outname","type":"string"}], "payable": false, "type":"function"},
{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},
{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}
],
this.account // pass connected account to bnb smart contract
);
this.contract_out = new ethers.Contract(
config.cfg.contracts.output,
[
{ "constant": true, "inputs": [{ "name": "_owner", "type": "address" }], "name": "balanceOf", "outputs": [{ "name": "balance", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": false, "inputs": [{ "name": "guy", "type": "address" }, { "name": "wad", "type": "uint256" }], "name": "approve", "outputs": [{ "name": "approved", "type": "bool" }], "payable": false, "type": "function" },
{ "constant": true, "inputs": [{ "name": "sender", "type": "address" }, { "name": "guy", "type": "address" }], "name": "allowance", "outputs": [{ "name": "allowed", "type": "uint256" }], "payable": false, "type": "function" },
{ "constant": true, "inputs":[],"name":"symbol","outputs":[{"name":"outname","type":"string"}], "payable": false, "type":"function"},
{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},
{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}
],
this.account // pass connected account to bnb smart contract
);
// load user balances (for later use)
this.bnb_balance = parseInt(await this.account.getBalance());
this.input_balance = parseInt(this.bnb_balance,(this.isETH(config.cfg.contracts.input) ? this.bnb_balance : await this.contract_in.balanceOf(this.account.address )));
this.output_balance = parseInt((this.isETH(config.cfg.contracts.output) ? this.bnb_balance : await this.contract_out.balanceOf(this.account.address)));
// load some more variables
this.base_nonce = parseInt(await this.node.getTransactionCount(this.account.address));
this.nonce_offset = 0;
this.first_block = -1;
} catch(e) {
msg.error(`[error::network] ${e}`);
process.exit();
}
}
async prepare() {
msg.primary(`[debug::network] Preparing network..`);
// format maxInt.
const maxInt = (ethers.BigNumber.from("2").pow(ethers.BigNumber.from("256").sub(ethers.BigNumber.from("1")))).toString();
// cache & prepare contracts
if(!cache.isAddressCached(config.cfg.contracts.input)) {
cache.createAddress(config.cfg.contracts.input);
cache.setAddressArtifacts(config.cfg.contracts.input, (await this.contract_in.decimals()), await this.contract_in.symbol());
msg.primary(`[debug::network] Approving balance for ${cache.data.addresses[config.cfg.contracts.input].symbol}.`);
// approve output (for later)
const inTx = await this.contract_in.approve(
this.router.address,
maxInt,
{
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price,
'nonce': (this.getNonce())
}
);
let inReceipt = await inTx.wait();
if(!inReceipt.logs[0].transactionHash) {
msg.error(`[error::network] Could not approve ${cache.data.addresses[config.cfg.contracts.input].symbol}. (cache)`);
process.exit();
}
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.input].symbol} has been approved. (cache)`);
await cache.save();
} else {
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.input].symbol} has already been approved. (cache)`);
}
if(!cache.isAddressCached(config.cfg.contracts.output)) {
cache.createAddress(config.cfg.contracts.output);
cache.setAddressArtifacts(config.cfg.contracts.output, (await this.contract_out.decimals()), await this.contract_out.symbol());
msg.primary(`[debug::network] Approving balance for ${cache.data.addresses[config.cfg.contracts.output].symbol}.`);
// approve output (for later)
const outTx = await this.contract_out.approve(
this.router.address,
maxInt,
{
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price,
'nonce': (this.getNonce())
}
);
let outReceipt = await outTx.wait();
if(!outReceipt.logs[0].transactionHash) {
msg.error(`[error::network] Could not approve ${cache.data.addresses[config.cfg.contracts.output].symbol}. (cache)`);
process.exit();
}
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.output].symbol} has been approved. (cache)`);
await cache.save();
} else {
msg.success(`[debug::network] ${cache.data.addresses[config.cfg.contracts.output].symbol} has already been approved. (cache)`);
}
// now that the cache is done, restructure variables
config.cfg.transaction.amount_in_formatted = ethers.utils.parseUnits(`${config.cfg.transaction.amount_in}`, cache.data.addresses[config.cfg.contracts.input].decimals);
}
// wrapper function for swapping
async swapFromTokenToToken(amountIn, amountOutMin, contracts) {
try {
return this.router.swapExactETHForTokensSupportingFeeOnTransferTokens(
amountOutMin,
contracts,
this.account.address,
(Date.now() + 1000 * 60 * 10),
{
'value': amountIn,
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price,
'nonce': (this.getNonce())
}
);
} catch(e) {
console.log(`[error::swap] ${e.error}`);
process.exit();
}
}
async estimateTransaction(amountIn, amountOutMin, contracts) {
try {
let gas = await this.router.estimateGas.swapExactETHForTokensSupportingFeeOnTransferTokens(
amountOutMin,
contracts,
this.account.address,
(Date.now() + 1000 * 60 * 10),
{
'value': amountIn,
'gasLimit': config.cfg.transaction.gas_limit,
'gasPrice': config.cfg.transaction.gas_price
}
);
// check if is using enough gas.
if(gas > parseInt(config.cfg.transaction.gas_limit) || gas > parseInt(config.cfg.transaction.gas_limit)) {
msg.error(`[error::simulate] The transaction requires at least ${gas} gas, your limit is ${config.cfg.transaction.gas_limit}.`);
process.exit();
}
return true;
} catch(e) {
console.log(`[error::estimategas] ${e.error}`);
return this.estimateTransaction(amountIn, amountOutMin, contracts);
}
}
async transactToken(from, to) {
try {
let inputTokenAmount = config.cfg.transaction.amount_in_formatted;
// get output amounts
let amounts = await this.router.getAmountsOut(inputTokenAmount, [from, to]);
// calculate min output with current slippage in bnb
let amountOutMin = amounts[1].sub(amounts[1].div(100).mul(config.cfg.transaction.buy_slippage));
// simulate transaction to verify outputs.
let estimationPassed = await this.estimateTransaction(inputTokenAmount, amountOutMin, [from, to]);
// if simulation passed, notify, else, exit
if(estimationPassed) {
msg.success(`[debug::transact] Estimation passed successfully. proceeding with transaction.`);
} else {
msg.error(`[error::transact] Estimation did not pass checks. exiting..`);
process.exit();
}
let tx = await this.swapFromTokenToToken(
inputTokenAmount,
amountOutMin,
[from, to]
);
msg.success(`[debug::transact] TX has been submitted. Waiting for response..\n`);
let receipt = await tx.wait();
// get current ballance from output contract.
let currentOutBalance = await this.contract_out.balanceOf(this.account.address);
this.amount_bought_unformatted = ethers.utils.formatUnits(`${(currentOutBalance - this.output_balance)}`, cache.data.addresses[config.cfg.contracts.output].decimals);
return receipt;
} catch(err) {
if(err.error && err.error.message){
msg.error(`[error::transact] ${err.error.message}`);
}
else
console.log(err);
return this.transactToken(from, to);
}
return null;
}
isETH(token) {
return (token.toLowerCase() = ('0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c').toLowerCase())
}
async getLiquidity(pair) {
const bnbValue = await this.contract_in.balanceOf(pair);
const formattedbnbValue = await ethers.utils.formatEther(bnbValue);
// testing
if (formattedbnbValue < 1) {
msg.warning("[debug::liquidity] There is not enough liquidity yet.");
return this.getLiquidity(pair);
}
return formattedbnbValue;
}
async getPair(contract_in, contract_out) {
// get pair address
let pairAddress = await this.factory.getPair(contract_in, contract_out);
// no pair found, re-launch
if (!pairAddress || (pairAddress.toString().indexOf('0x0000000000000') > -1)) {
msg.warning("[debug::pair] Could not find pair for specified contracts. retrying..");
return this.getPair(contract_in, contract_out);
}
return pairAddress;
}
getNonce() {
let nonce = (this.base_nonce + this.nonce_offset);
this.nonce_offset ++;
return nonce;
}
}
module.exports = new Network();

Related

Unable to retrive ordered job list from Google Transcoder API

i'm using the node.js client library of google transcoder api. I'm able to retrive a paginated list of some jobs, but i'm not able to order elements by start date. Here my codes:
const { TranscoderServiceClient } = require('#google-cloud/video-transcoder').v1;
class TranscoderApiController {
constructor() {
this.projectId = process.env.GOOGLE_CLOUD_PROJECT;
this.location = process.env.TASK_LOCATION;
}
async getEntries(req, res, next) {
const params = {
pageSize: req.query.pageSize ? parseInt(req.query.pageSize) : 10,
pageToken: req.query.pageToken,
filter: req.query.filter,
orderBy: req.query.orderBy
}
const client = new TranscoderServiceClient();
const result = await client.listJobs({
parent: client.locationPath(this.projectId, this.location),
pageSize: params.pageSize,
orderBy: 'createTime.seconds'
}, {
autoPaginate: false
});
if (result.length == 3 && result[2] != undefined) {
return result[2];
} else {
return result[1];
}
}
}
module.exports = new TranscoderApiController();
When i call the getEntries method i receive the following error:
"3 INVALID_ARGUMENT: The request was invalid: sort order \"createTime.seconds\" is unsupported"
If i remove the orderBy: 'createTime.seconds' line then the api works but is not ordered as i want. The result is something like that (i abbreviate the json):
{
"jobs": [
{
"labels": {},
"name": "projects/<id>/locations/europe-west1/jobs/<uuid>",
"inputUri": "",
"outputUri": "",
"state": "SUCCEEDED",
"createTime": {
"seconds": "1656602896",
"nanos": 386772728
},
"startTime": {
"seconds": "1656602900",
"nanos": 755000000
},
"endTime": {
"seconds": "1656603062",
"nanos": 428000000
},
"ttlAfterCompletionDays": 30,
"error": null,
"config": {
"inputs": [
{
"key": "input0",
"uri": "gs://<url>/render_md.mp4",
"preprocessingConfig": null
}
],
"editList": [...],
"elementaryStreams": [...],
"muxStreams": [...],
"manifests": [],
"adBreaks": [],
"spriteSheets": [],
"overlays": [],
"output": {
"uri": "gs://<url>/md.mp4/"
},
"pubsubDestination": {
"topic": "projects/<id>/topics/transcoder_api"
}
},
"jobConfig": "config"
},
...
],
"unreachable": [],
"nextPageToken": "Co8BCjgKDGV1cm9wZS13ZXN0MRIZdHJhbnNjb2Rlci5nb29nbGVhcGlzLmNvbRgBII..."
}
As you can see each job have the startTime.seconds property. I follow the syntax described here:
https://google.aip.dev/132#ordering
Any support to solve the ordered issue is appreciated.

Crypto tradingbot (Pancakeswap) using node.js

I am using node.js in VSCode. I know another programming language decently enough that I could find relevant code snippets and create the below code. However, my JavaScript knowledge is very limited. The below code crashes at line 52, but states "No debugger available, can not send 'variables'". The breakpoints are simply ignored. I initially had most code segments in separate files and would often get unknown errors for "ethers.Contract". This is very frustrating because I have scraped the blow code from 15h+ google searches and no pointers to why things are not working. While they seem to work for others, they don't seem to work for me. I would very much appreciate it if someone with more experience could point out any newbie mistakes I am making!
// THE BOT:
const ethers = require('ethers');
const Web3 = require('web3');
const web3 = new Web3("https://bsc-dataseed1.binance.org:443");
//1. Snipe details
const includesBNB = "bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c"; // WBNW CONTRACT
const amountIn = ethers.utils.parseUnits('0.015', 'ether') // HOW MUCH I BUY
//2. Wallet / connection details
const WSS = "wss://XXX XXX XXX" // MY QUICKNODE NODE CONNECTION
const Seed = 'XXX XXX XXX' // WALLET SEEDPHRASE
const recipientaddress = '0xXXX XXX XXX' // WALLET ADDRESS
// OTHER SETTINGS
const bnbAddress = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c' // contract for WBNB
const routeraddress = '0x10ed43c718714eb63d5aa57b78b54704e256024e' // PANCAKESWAP ROUTER
const minBuy = ethers.utils.parseUnits('0', 'ether')
const MethodID = "0xf305d719" // Liquidity event
const MethodID2 = "0xe8e33700"
const provider = new ethers.providers.WebSocketProvider(WSS);
const wallet = ethers.Wallet.fromMnemonic(Seed);
const account = wallet.connect(provider);
provider.removeAllListeners()
const router = new ethers.Contract(
routeraddress,
['function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)'],
account
);
console.log(`Connecting to the blockchain`),
console.log(`Starting to scan the network for a matching transaction based on the config entered`),
console.log(`As soon as a matching transaction has been found, it will be displayed here`),
// CHECK FOR LIQUIDITY EVENTS USING WBNB
provider.on("pending", async (tx) => {
provider.getTransaction(tx).then(function (transaction){
if (transaction != null && transaction['data'].includes(MethodID2) && transaction['data'].includes(includesBNB) || transaction != null && transaction['data'].includes(MethodID) && transaction['data'].includes(includesBNB))
console.log(transaction),
console.log(transaction['data']),
console.log(`Matching liquidity add transaction found!`),
// EXTRACT THE TOKEN ADDRESS FROM transaction['data].
// This seems to depend on the Method used.
let buyAddress // THIS PRODUCES AN ERROR
if (transaction['data'].includes(MethodID)) {
buyAddress = transaction['data'].substring(38:78);
} else {
buyAddress = transaction['data'].substring(98,138);
};
// CHECK LIQUIDITY
// (1) FIND THE PAIR ADDRESS
var liqABI = [{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"getPair","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint112","name":"_reserve0","type":"uint112"},{"internalType":"uint112","name":"_reserve1","type":"uint112"},{"internalType":"uint32","name":"_blockTimestampLast","type":"uint32"}],"payable":false,"stateMutability":"view","type":"function"}];
var CAKE_FACTORY_V2 = web3.eth.contract(liqABI, routeraddress);
var pairAddress = await (await (new web3.eth.Contract(liqABI, CAKE_FACTORY_V2))).methods.getPair(buyAddress, bnbAddress).call(); // GET THE PAIR ADDRESS
// (2) FIND THE RESERVES
const pairABI = [
'function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast)'
]
let pairContract = new ethers.Contract(pairAddress, pairABI, provider);
let reserves = await pairContract.getReserves();
if (reserves(1) < 1) {
continue // NOT ENOUGH BNB LIQUIDITY
}
// EXTRACT CURRENT NONCE (INCLUDING ANY PENDING TRANSACTIONS)
var curNonce = web3.eth.getTransactionCount(
recipientaddress,
"pending"
);
// BUYING TOKEN
router.swapExactTokensForTokens(
amountIn,
minBuy, // set to 0
[bnbAddress, buyAddress], // THIS IS NOT ALWAYS THE RIGHT ADDRESS, BUT YOU GET THE IDEA
recipientaddress,
Date.now() + 1000 * 60 * 10,
{ gasLimit: transaction.gasLimit, gasPrice: transaction.gasPrice, nonce: curNonce}
),
setTimeout(function () {console.log(`Attempting to place a buy order...`)}, 500),
// CHECK AMOUNT BOUGHT
// Is there an easier way? Like an output from the buying TX? e.g. tx = router.swapExactTokensForTokens and then receipt = tx.wait()? I saw that in another snipped, but never got the code to work, so no idea if amount bought is included here
let minABI = [ // The minimum ABI to get ERC20 Token balance
// balanceOf
{
"constant":true,
"inputs":[{"name":"_owner","type":"address"}],
"name":"balanceOf",
"outputs":[{"name":"balance","type":"uint256"}],
"type":"function"
},
// decimals
{
"constant":true,
"inputs":[],
"name":"decimals",
"outputs":[{"name":"","type":"uint8"}],
"type":"function"
}
];
const contract = new provider.eth.Contract(minABI, buyAddress); // ERROR
// HOW MANY TOKENS HAVE BEEN BOUGHT?
async function getBalance() {
const result = await contract.methods.balanceOf(recipientaddress).call();
}
// NOT ALL TOKENS HAVE 18 DECIMALS
async function getDecimals() {
const resultDec = await contract.methods.decimals(recipientaddress).call()
}
var balance = getBalance().div(10**getDecimals())
// SELL BACK TO BNB AFTER TWO MINUTES. THE BOT SHOULD CONTINUE BUYING AT LIQUIDITY EVENTS DURING THE setTimeout
setTimeout(function () {
router.swapExactTokensForTokens(
balance,
minBuy,
[buyAddress, bnbAddress],
recipientaddress,
Date.now() + 1000 * 60 * 5, // valid for 5 minutes
{ gasLimit: transaction.gasLimit, gasPrice: transaction.gasPrice, nonce: curNonce + 1} // THIS IS BEING CALLED WHILE THE BUY TRANSACTION IS STILL PENDING, THEREFORE THE NONCE MUST BE + 1
);
}, 1000*60*2); // set to 2mins
return;
})})
the line 53 should be buyAddress = transaction['data'].substring(38,78);
Don't know exactly about your trading algorithm or the full functionality of libraries used but as a node.js code there were several issues related to puntiation (',', ';') at the end of lines, some missing block curly brackets {} and so on.
I corrected and beautify the code, but I didn't run it so any error prompted should be about missing or not found parameter data, functions or dependencies. A "happier linter" version could be as follow:
// THE BOT:
const ethers = require('ethers');
const Web3 = require('web3');
const web3 = new Web3("https://bsc-dataseed1.binance.org:443");
//1. Snipe details
const includesBNB = "bb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c"; // WBNW CONTRACT
const amountIn = ethers.utils.parseUnits('0.015', 'ether'); // HOW MUCH I BUY
//2. Wallet / connection details
const WSS = "wss://XXX XXX XXX"; // MY QUICKNODE NODE CONNECTION
const Seed = 'XXX XXX XXX'; // WALLET SEEDPHRASE
const recipientaddress = '0xXXX XXX XXX'; // WALLET ADDRESS
// OTHER SETTINGS
const bnbAddress = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'; // contract for WBNB
const routeraddress = '0x10ed43c718714eb63d5aa57b78b54704e256024e'; // PANCAKESWAP ROUTER
const minBuy = ethers.utils.parseUnits('0', 'ether');
const MethodID = "0xf305d719"; // Liquidity event
const MethodID2 = "0xe8e33700";
const provider = new ethers.providers.WebSocketProvider(WSS);
const wallet = ethers.Wallet.fromMnemonic(Seed);
const account = wallet.connect(provider);
provider.removeAllListeners();
const router = new ethers.Contract(
routeraddress,
['function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts)'],
account
);
console.log(`Connecting to the blockchain`);
console.log(`Starting to scan the network for a matching transaction based on the config entered`);
console.log(`As soon as a matching transaction has been found, it will be displayed here`);
// CHECK FOR LIQUIDITY EVENTS USING WBNB
provider.on("pending", async (tx) => {
provider.getTransaction(tx).then(function (transaction) {
if (transaction != null && transaction['data'].includes(MethodID2) && transaction['data'].includes(includesBNB) || transaction != null && transaction['data'].includes(MethodID) && transaction['data'].includes(includesBNB)) {
console.log(transaction)
console.log(transaction['data'])
console.log(`Matching liquidity add transaction found!`)
// EXTRACT THE TOKEN ADDRESS FROM transaction['data].
// This seems to depend on the Method used.
let buyAddress // THIS PRODUCES AN ERROR
if (transaction['data'].includes(MethodID)) {
buyAddress = transaction['data'].substring(38, 78);
} else {
buyAddress = transaction['data'].substring(98, 138);
};
// CHECK LIQUIDITY
// (1) FIND THE PAIR ADDRESS
var liqABI = [{
"inputs": [],
"name": "decimals",
"outputs": [{
"internalType": "uint256",
"name": "",
"type": "uint256"
}],
"stateMutability": "view",
"type": "function"
}, {
"constant": true,
"inputs": [],
"name": "token0",
"outputs": [{
"internalType": "address",
"name": "",
"type": "address"
}],
"payable": false,
"stateMutability": "view",
"type": "function"
}, {
"inputs": [],
"name": "factory",
"outputs": [{
"internalType": "address",
"name": "",
"type": "address"
}],
"stateMutability": "view",
"type": "function"
}, {
"constant": true,
"inputs": [{
"internalType": "address",
"name": "",
"type": "address"
}, {
"internalType": "address",
"name": "",
"type": "address"
}],
"name": "getPair",
"outputs": [{
"internalType": "address",
"name": "",
"type": "address"
}],
"payable": false,
"stateMutability": "view",
"type": "function"
}, {
"constant": true,
"inputs": [],
"name": "getReserves",
"outputs": [{
"internalType": "uint112",
"name": "_reserve0",
"type": "uint112"
}, {
"internalType": "uint112",
"name": "_reserve1",
"type": "uint112"
}, {
"internalType": "uint32",
"name": "_blockTimestampLast",
"type": "uint32"
}],
"payable": false,
"stateMutability": "view",
"type": "function"
}];
var CAKE_FACTORY_V2 = web3.eth.contract(liqABI, routeraddress);
var pairAddress = await (await (new web3.eth.Contract(liqABI, CAKE_FACTORY_V2))).methods.getPair(buyAddress, bnbAddress).call(); // GET THE PAIR ADDRESS
// (2) FIND THE RESERVES
const pairABI = [
'function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast)'
]
let pairContract = new ethers.Contract(pairAddress, pairABI, provider);
let reserves = await pairContract.getReserves();
if (reserves(1) < 1) {
continue // NOT ENOUGH BNB LIQUIDITY
}
// EXTRACT CURRENT NONCE (INCLUDING ANY PENDING TRANSACTIONS)
var curNonce = web3.eth.getTransactionCount(
recipientaddress,
"pending"
);
// BUYING TOKEN
router.swapExactTokensForTokens(
amountIn,
minBuy, // set to 0
[bnbAddress, buyAddress], // THIS IS NOT ALWAYS THE RIGHT ADDRESS, BUT YOU GET THE IDEA
recipientaddress,
Date.now() + 1000 * 60 * 10, {
gasLimit: transaction.gasLimit,
gasPrice: transaction.gasPrice,
nonce: curNonce
}
),
setTimeout(function () {
console.log(`Attempting to place a buy order...`)
}, 500);
// CHECK AMOUNT BOUGHT
// Is there an easier way? Like an output from the buying TX? e.g. tx = router.swapExactTokensForTokens and then receipt = tx.wait()? I saw that in another snipped, but never got the code to work, so no idea if amount bought is included here
let minABI = [ // The minimum ABI to get ERC20 Token balance
// balanceOf
{
"constant": true,
"inputs": [{
"name": "_owner",
"type": "address"
}],
"name": "balanceOf",
"outputs": [{
"name": "balance",
"type": "uint256"
}],
"type": "function"
},
// decimals
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{
"name": "",
"type": "uint8"
}],
"type": "function"
}
];
const contract = new provider.eth.Contract(minABI, buyAddress); // ERROR
// HOW MANY TOKENS HAVE BEEN BOUGHT?
async function getBalance() {
return await contract.methods.balanceOf(recipientaddress).call();
}
// NOT ALL TOKENS HAVE 18 DECIMALS
async function getDecimals() {
return await contract.methods.decimals(recipientaddress).call()
}
var balance = getBalance().div(10 ** getDecimals())
// SELL BACK TO BNB AFTER TWO MINUTES. THE BOT SHOULD CONTINUE BUYING AT LIQUIDITY EVENTS DURING THE setTimeout
setTimeout(function () {
router.swapExactTokensForTokens(
balance,
minBuy,
[buyAddress, bnbAddress],
recipientaddress,
Date.now() + 1000 * 60 * 5, // valid for 5 minutes
{
gasLimit: transaction.gasLimit,
gasPrice: transaction.gasPrice,
nonce: curNonce + 1
} // THIS IS BEING CALLED WHILE THE BUY TRANSACTION IS STILL PENDING, THEREFORE THE NONCE MUST BE + 1
);
}, 1000 * 60 * 2); // set to 2mins
return;
}
})
})

Stream.io Chat API - Member state omitted from queryChannels response

When I do the following:
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ state: true },
This does not include the members. How am I able to get the member information?
I am writing a webhook and I want it to send a push notification (I am currently sending these myself via expo) to all offline users.
I am migrating from pusher chatkit which is now being discontinued. They had a new_message_users_offline hook for this purpose.
In the message.new webhook payload in the documentation the members are present but they are not present in the request body:
{
"type": "message.new",
"cid": "messaging:394f36fd-d512-4f2b-a785-ab8dfe82af49",
"message": {
"id": "f73ee1a8-f6fd-450b-bc64-0840b4df8fd9-2b4908ad-e267-4c48-8f41-8c26c8f769ce",
"text": "Ffddf",
"html": "<p>Ffddf</p>\n",
"type": "regular",
"user": {
"id": "f73ee1a8-f6fd-450b-bc64-0840b4df8fd9",
"role": "user",
"created_at": "2020-04-06T14:06:37.979584Z",
"updated_at": "2020-04-06T19:45:39.556842Z",
"last_active": "2020-04-06T19:45:39.54939Z",
"banned": false,
"online": true,
"name": "Mark Everett",
"image": "https://8dc-user-files-dev.s3.eu-west-1.amazonaws.com/MEMBER_PROFILE_IMAGE-f73ee1a8-f6fd-450b-bc64-0840b4df8fd9.png?v=6"
},
"attachments": [],
"latest_reactions": [],
"own_reactions": [],
"reaction_counts": null,
"reaction_scores": {},
"reply_count": 0,
"created_at": "2020-04-06T19:51:14.114803Z",
"updated_at": "2020-04-06T19:51:14.114803Z",
"mentioned_users": []
},
"user": {
"id": "f73ee1a8-f6fd-450b-bc64-0840b4df8fd9",
"role": "user",
"created_at": "2020-04-06T14:06:37.979584Z",
"updated_at": "2020-04-06T19:45:39.556842Z",
"last_active": "2020-04-06T19:45:39.54939Z",
"banned": false,
"online": true,
"channel_unread_count": 0,
"channel_last_read_at": "1970-01-01T00:00:00Z",
"total_unread_count": 0,
"unread_channels": 0,
"unread_count": 0,
"image": "https://8dc-user-files-dev.s3.eu-west-1.amazonaws.com/MEMBER_PROFILE_IMAGE-f73ee1a8-f6fd-450b-bc64-0840b4df8fd9.png?v=6",
"name": "Mark Everett"
},
"watcher_count": 1,
"created_at": "2020-04-06T19:51:14.121213459Z",
"channel_type": "messaging",
"channel_id": "394f36fd-d512-4f2b-a785-ab8dfe82af49"
}
My plan is do do something like this:
public async getOfflineUserIds(channelId: string): Promise<string[]> {
try {
// Get the channel
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ message_limit: 0, limit: 1, state: true},
)
const channel = queryChannelsResponse[0]
console.log('channel: ', channel)
// Get the channels members
const userIds: string[] = []
// tslint:disable-next-line: forin
for (const index in channel.state.members) {
userIds.push(channel.state.members[index].user_id)
}
console.log('userIds:', userIds)
const queryUsersResponse = await this.client.queryUsers(
{ id: { $in: userIds } },
{ last_active: -1 },
{},
)
console.log('queryUsersResponse:', queryUsersResponse)
// Work out who is offline/online
const offlineUserIds = queryUsersResponse.users
.filter(u => !u.online)
.map(u => u.id)
return offlineUserIds
} catch (err) {
throw new InternalServerErrorException(
'Error getting offline users for channel.',
err,
)
}
}
This is now resolved.
I did not add the members to the channel with channel.addMembers. I create and add members on the server as this works perfectly for my use case.
If it helps anyone I ended up with these two methods:
public async getChannelUserIds(channelId: string): Promise<string[]> {
try {
const queryChannelsResponse = await this.client.queryChannels(
{ id: channelId },
{ last_updated_at: -1 },
{ message_limit: 0, limit: 1, state: true },
)
const channel = queryChannelsResponse[0]
const userIds = Object.keys(channel.state.members)
console.log('userIds:', userIds)
return userIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting user ids for channel ('${channelId}').`,
err,
)
}
}
public async getOfflineUserIds(userIds: string[]): Promise<string[]> {
try {
const queryUsersResponse = await this.client.queryUsers(
{ id: { $in: userIds } },
{ last_active: -1 },
{},
)
console.log('queryUsersResponse:', queryUsersResponse)
const offlineUserIds = queryUsersResponse.users
.filter(u => !u.online)
.map(u => u.user_id)
return offlineUserIds
} catch (err) {
throw new InternalServerErrorException(
`Error getting offline user ids from ('${JSON.stringify(
userIds,
null,
2,
)}').`,
err,
)
}
}
And then in my webhook I:
#Post('stream/messages')
public async onReceive(
#Req() req: Request,
#Headers('x-signature') signature: string,
#Body() body: any,
) {
try {
console.debug('webhooks-stream.messages.onReceive')
this.chatService.verifyWebhook((req as any).rawBody, signature)
console.log('DEBUG WEBHOOK BODY', JSON.stringify(body, null, 2))
switch (body.type) {
case 'message.new': {
const offlineMemberIds = await this.chatService.getOfflineUserIds(
body.members.map(member => member.user_id),
)
...

How to properly handle context.sendActivity?

I just want to ask two simple questions and then show the card. Problem is, in the second "sendActivity" keeps on repeating "please give password" just forever. I tried to place another onTurn after and even inside the function, with worst or same results. Dont want to implement a whole waterfall just for 2 questions. Which ActivityHandler fits better what am trying to achieve?
async processLogin(context, next, res) {
await context.sendActivity({
text: 'please give username'
})
const SelectedCard2 = CARDS2[0];
this.onTurn(async (context, next, res) => {
let txt = `"${context.activity.text}"`;
if (txt) {
var name = JSON.parse(txt);
console.log(name)
}
await context.sendActivity({
text: 'please give password'
})
let txt2 = `"${context.activity.text}"`;
if (txt2) {
var password = JSON.parse(txt2);
console.log(password)
res = password;
}
await next();
});
}
enter link description hereIf you just want to collect some info from user by an easy , you can use adaptive card in one step, try the code below :
const { ActivityHandler,CardFactory } = require('botbuilder');
class EchoBot extends ActivityHandler {
constructor() {
super();
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
var adaptiveCard = {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": 2,
"items": [
{
"type": "TextBlock",
"text": "Pls type your info here . Don't worry, we'll never share or sell your information.",
"isSubtle": true,
"wrap": true,
"size": "Small"
},
{
"type": "TextBlock",
"text": "Username",
"wrap": true
},
{
"type": "Input.Text",
"id": "username",
"placeholder": "your user name here"
},
{
"type": "TextBlock",
"text": "Password",
"wrap": true
},
{
"type": "Input.Text",
"id": "password",
"placeholder": "makre sure no one is around you ..."
}
]
}
]
}
],
"actions": [
{
"type": "Action.Submit",
"title": "Submit"
}
]
};
this.onMessage(async (context, next) => {
if(context.activity.text==="login"){
await context.sendActivity({ attachments: [CardFactory.adaptiveCard(adaptiveCard)] });
}else if(context.activity.value != undefined){
var user = context.activity.value;
await context.sendActivity("hello , your username : " + user.username + ",password :" + user.password);
}else {
await context.sendActivity("send login to do test");
}
await next();
});
this.onMembersAdded(async (context, next) => {
const membersAdded = context.activity.membersAdded;
for (let cnt = 0; cnt < membersAdded.length; ++cnt) {
if (membersAdded[cnt].id !== context.activity.recipient.id) {
await context.sendActivity('Hello and welcome!');
}
}
// By calling next() you ensure that the next BotHandler is run.
await next();
});
}
}
module.exports.EchoBot = EchoBot;
This code is based on official nodejs echo bot , just cover the content of bot.js file to test it :
Hope it helps .

How to call Web3js function from Nodejs file

I have the following working web3js code, Calling App.createContract() on button click works well on a webpage, however I want to call App.createContract() or similar from another Nodejs controller. Infact what I am thinking of is making an API in Node which could call the web3js function and returns a JSON result back to the caller. Can you please help me how to import my web3js file and call the function from Node Controller? Thanks
import "../stylesheets/app.css";
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract';
import { default as CryptoJS} from 'crypto-js';
var accounts;
var account;
var shLogABI;
var shLogContract;
var shLogCode;
var shLogSource;
window.App = {
start: function() {
var self = this;
web3.eth.getAccounts(function(err, accs) {
if (err != null) {
alert("There was an error fetching your accounts.");
return;
}
if (accs.length == 0) {
alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.");
return;
}
accounts = accs;
console.log(accounts);
account = accounts[0];
web3.eth.defaultAccount= account;
shLogSource= "pragma solidity ^0.4.6; contract SHLog { struct LogData{ string FileName; uint UploadTimeStamp; string AttestationDate; } mapping(uint => LogData) Trail; uint8 TrailCount=0; function AddNewLog(string FileName, uint UploadTimeStamp, string AttestationDate) { LogData memory newLog; newLog.FileName = FileName; newLog.UploadTimeStamp= UploadTimeStamp; newLog.AttestationDate= AttestationDate; Trail[TrailCount] = newLog; TrailCount++; } function GetTrailCount() returns(uint8){ return TrailCount; } function GetLog(uint8 TrailNo) returns (string,uint,string) { return (Trail[TrailNo].FileName, Trail[TrailNo].UploadTimeStamp, Trail[TrailNo].AttestationDate); } }";
web3.eth.compile.solidity(shLogSource, function(error, shLogCompiled){
shLogABI = JSON.parse(' [ { "constant": false, "inputs": [ { "name": "TrailNo", "type": "uint8" } ], "name": "GetLog", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" }, { "name": "", "type": "string" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "FileName", "type": "string" }, { "name": "UploadTimeStamp", "type": "uint256" }, { "name": "AttestationDate", "type": "string" } ], "name": "AddNewLog", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "GetTrailCount", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "type": "function" } ]');
shLogContract = web3.eth.contract(shLogABI);
});
});
},
createContract: function()
{
shLogContract.new("", {from:account, gas: 3000000}, function (error, deployedContract){
if(deployedContract.address)
{
document.getElementById("contractAddress").value=deployedContract.address;
document.getElementById("fileName").value = '';
document.getElementById("uploadTimeStamp").value = '';
document.getElementById("attestationDate").value = '';
}
})
},
addNewLog: function()
{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
var fileName = document.getElementById("fileName").value;
var uploadTimeStamp = document.getElementById("uploadTimeStamp").value;
var attestationDate = document.getElementById("attestationDate").value;
deployedshLog.AddNewLog(fileName, uploadTimeStamp, attestationDate, function(error){
console.log(error);
})
},
getLog: function()
{
try{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
deployedshLog.GetTrailCount.call(function (error, trailCount){
deployedshLog.GetLog.call(trailCount-1, function(error, returnValues){
document.getElementById("fileName").value= returnValues[0];
document.getElementById("uploadTimeStamp").value = returnValues[1];
document.getElementById("attestationDate").value=returnValues[2];
})
})
}
catch (error) {
document.getElementById("fileName").value= error.message;
document.getElementById("uploadTimeStamp").value = error.message;
document.getElementById("attestationDate").value= error.message;
}
}
};
window.addEventListener('load', function() {
if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")
window.web3 = new Web3(web3.currentProvider);
} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
App.start();
});
Simple example for web3js 1.0.0.beta*
1) Add web3js to your package.json on a server side:
"web3": "^1.0.0-beta.27"
2) Require and init web3 with some provider:
const Web3 = require('web3');
const web3SocketProvider = new Web3.providers.WebsocketProvider('ws://0.0.0.0:8546');
const web3Obj = new Web3(web3Provider);
Now you can use your web3 object as usual:
async function getAccounts() {
let accounts = await web3Obj.eth.getAccounts();
}

Resources