Crypto tradingbot (Pancakeswap) using node.js - 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;
}
})
})

Related

Pancake Sniper Bot

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();

How to retrieve metadata from Stripe session object NodeJS

I am trying to pass a UID and purchase ID with Stripe Checkout session object (using metadata). Generating the session ID on my server attaching the metadata works very fine. Stripe also POSTs everything correctly to my webhook server. The problems occurs while retrieving the metadata from the session object POSTed by Stripe.
Here is the error I get
TypeError: Cannot read property 'metadata' of undefined at /app/app.js:35:32
Here is the session obj posted by Stripe-
{
"id": "evt_1GRC7lAfcfWZXl7jQ3VzNo4y",
"object": "event",
"api_version": "2019-10-17",
"created": 1585292221,
"data": {
"object": {
"id": "cs_test_gLsHqtF8XhB3C3DlWKcLtNdTitp0St8ju5qgJgl6tHrMxxWvju9gb9Li",
"object": "checkout.session",
"billing_address_collection": null,
"cancel_url": "https://andropaym.firebaseapp.com/fail.html",
"client_reference_id": null,
"customer": "cus_GzASi1Klpydh8x",
"customer_email": null,
"display_items": [
{
"amount": 37500,
"currency": "inr",
"custom": {
"description": "Carefully modified Linux Distro Bundle for Android.",
"images": null,
"name": "Modded OS Bundle"
},
"quantity": 1,
"type": "custom"
}
],
"livemode": false,
"locale": null,
"metadata": {
"uid": "EB1m6nAOTVNcQhHO2O7COspap8y1",
"payID": "GPA.5620-9852-7063-44324"
},
"mode": "payment",
"payment_intent": "pi_1GRC7EAfcfWZXl7jhixrWHRS",
"payment_method_types": [
"card"
],
"setup_intent": null,
"shipping": null,
"shipping_address_collection": null,
"submit_type": null,
"subscription": null,
"success_url": "https://andropaym.firebaseapp.com/success.html"
}
},
"livemode": false,
"pending_webhooks": 4,
"request": {
"id": null,
"idempotency_key": null
},
"type": "checkout.session.completed"
}
Here is my webhook code -
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const stripe = require('stripe')('sk_test_xxxx');
const endpointSecret = 'whsec_xxxx';
// set the port of our application
// process.env.PORT lets the port be set by Heroku
var port = process.env.PORT || 8080;
app.post('/', bodyParser.raw({type: 'application/json'}), (request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
function handleCheckoutSession(uid) {
// Here we are getting the session obj and we can process it to check for the things we need
console.log("UID is " + uid);
}
// Handle the checkout.session.completed event
if (event.type === 'checkout.session.completed') {
const session = event.data.object;
let uid = request.data.metadata.uid;
// Fulfill the purchase...
handleCheckoutSession(uid);
}
// Return a response to acknowledge receipt of the event
response.json({received: true});
});
app.listen(port, function () {
console.log('Our app is running on http://localhost:' + port);
});
module.exports = app;
The code works fine without the metadata being parsed
More code links:
1. Highlighted error webhook code - https://gist.github.com/imprakharshukla/1e2315615983e0e9d492d2288e159832#file-webhook_backend-js-L40
You need to use the object returned by stripe.constructEvent, not the request body.
Change
let uid = request.data.metadata.uid;
to
let uid = session.metadata.uid
and it should work as expected.

Microsoft teams bot adaptive card carousel deleting a card

I am using Microsoft teams bot with nodejs. I am rendering a carousel of adaptive cards with action on each card. My requirement is to delete an individual card out on which the action was clicked. Is it possible?
Current code looks like below. i have given a try to deleteActive but that deletes entire carousel
const {
TurnContext,
TeamsActivityHandler,
CardFactory,
AttachmentLayoutTypes,
ActionTypes
} = require('botbuilder');
class TeamsConversationBot extends TeamsActivityHandler {
constructor() {
super();
this.onMessage(async (context:any, next:any) => {
TurnContext.removeRecipientMention(context.activity);
console.log("context activigty at the begin is:" + JSON.stringify(context.activity))
let msg = context.activity.text
let action = context.activity.value
if(msg.startsWith('lead')){
msg = 'lead'
}
if(action !== undefined){
console.log("user did some action on a card")
msg = action.action
}
switch (msg) {
case 'lead':
await this.lead(context)
break;
case 'qualify_lead':
await this.qualifyLead(context)
break;
}
await next();
});
}
/**
*
* #param context this method does a lead qualification
*/
async qualifyLead(context:any){
console.log("in qualifyLead:" + JSON.stringify(context.activity))
//await context.deleteActivity(context.activity.replyToId)
const leadId = context.activity.value.objectId
console.log("Lead to qualify is:" + leadId)
await context.sendActivity('Lead is qualified')
}
/**
* Search contact by name
* #param context
* #param keyword
*/
async lead(context:any){
console.log("Start of lead with context:" + JSON.stringify(context))
const cardArr = []
let items = [
{"Name": 'x', "LeadId": "1"},
{"Name": 'a', "LeadId": "2"},
{"Name": 'b', "LeadId": "3"},
{"Name": 'c', "LeadId": "4"},
{"Name": 'd', "LeadId": "5"}
]
for(const item of items){
const header = {
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": item.Name
}
const actions = [
{
"type": "Action.Submit",
"title": "Qualify",
"data": { "action" : "qualify_lead", "objectId" : item.LeadId }
}
]
const acard = CardFactory.adaptiveCard(
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
header,
''
],
"actions": actions
}
)
cardArr.push(acard)
console.log("payload is::::" + JSON.stringify(acard))
}
const reply = {
"attachments" : cardArr,
"attachmentLayout" : AttachmentLayoutTypes.Carousel
}
await context.sendActivity(reply);
}
}
module.exports.TeamsConversationBot = TeamsConversationBot;
As with this other answer, the answer will be similar to this one. I can see you're trying to use TypeScript but your code deviates very little from JavaScript so I'll just write my answer in JavaScript.
First, you'll need a way of saving state for your [carousel] so you can update the [carousel]'s activity.
this.carouselState = this.conversationState.createProperty('carouselState');
You'll want a consistent way to generate your [carousel] that you can use when you send the [carousel] initially and when you update the [carousel].
createCarousel(batchId, leads)
{
const cardArr = [];
let items = [
{ "Name": 'x', "LeadId": 1 },
{ "Name": 'a', "LeadId": 2 },
{ "Name": 'b', "LeadId": 3 },
{ "Name": 'c', "LeadId": 4 },
{ "Name": 'd', "LeadId": 5 }
];
items = items.filter(item => leads.includes(item.LeadId));
for (const item of items) {
const header = {
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": item.Name
};
const actions = [
{
"type": "Action.Submit",
"title": "Qualify",
"data": { [KEYACTION]: ACTIONQUALIFYLEAD, [KEYOBJECTID]: item.LeadId, [KEYBATCHID]: batchId }
}
];
const acard = CardFactory.adaptiveCard(
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.0",
"body": [
header
],
"actions": actions
}
);
cardArr.push(acard);
}
return {
"type": "message",
"attachments": cardArr,
"attachmentLayout": AttachmentLayoutTypes.Carousel
};
}
This is similar to your code but there are some important differences. First, I'm filtering the items array to allow for fewer items, which is how you'll end up deleting cards from your carousel. Second, I'm including a "batch ID" in each action's data, which is how your bot will know which activity to update when it receives the action's payload. Also, this isn't relevant to your question but I'm using string constants instead of string literals most everywhere I expect to use that string more than once, which is a practice I follow to avoid typo-related bugs etc.
Using this function, you can send the [carousel] initially like this
async testCarousel(turnContext) {
const batchId = Date.now();
const leads = [1, 2, 3, 4, 5];
const reply = this.createCarousel(batchId, leads);
const response = await turnContext.sendActivity(reply);
const dict = await this.carouselState.get(turnContext, {});
dict[batchId] = {
[KEYACTIVITYID]: response.id,
[KEYLEADS]: leads
};
}
And you can update the [carousel] in response to the card's [qualify] submit action like this
async handleSubmitAction(turnContext) {
const value = turnContext.activity.value;
switch (value[KEYACTION]) {
case ACTIONQUALIFYLEAD:
const dict = await this.carouselState.get(turnContext, {});
const batchId = value[KEYBATCHID];
const info = dict[batchId];
if (info) {
const leads = info[KEYLEADS];
const objectId = value[KEYOBJECTID];
var index = leads.indexOf(objectId);
if (index !== -1) leads.splice(index, 1);
const update = this.createCarousel(batchId, leads);
update.id = info[KEYACTIVITYID];
if (update.attachments.length) {
await turnContext.updateActivity(update);
} else {
await turnContext.deleteActivity(update.id);
}
}
break;
}
}

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