环境准备 :
安装 nodejs,vscode
使用方法 :
- 安装npm 依赖
- 重命名
.env.example
文件为.env
文件,并按照要求添加相应参数(具体要求下面会写) - 执行 node sniper-bot.js
.env配置文件
# 节点 默认BSC主网 测试网:https://data-seed-prebsc-1-s1.binance.org:8545
node=https://bsc-dataseed.binance.org/
# 预售地址(复制PinkSale或DxSale的预售地址,注意不是Token合约地址)
presaleContractAddress=0xA926e7C0F6afA20569f7425bB3E93017C813****
# 购买bnb数量
buyingBnbAmount=2# 助记词和私钥二选一,助记词优先级高于私钥,若助记词为空,则读取私钥。私钥支持多个钱包,使用英文逗号隔开,助记词暂时不支持多个钱包
# 助记词
mnemonic="civil planet ......"
# 私钥(支持多个私钥,使用英文逗号隔开)
senderPrivateKey=# 热度高的项目建议使用200-2000
gasPrice=25
# 机器人延时启动
hours=0
mins=0
secs=0
代码:
sniper-bot.js
// const fs = require('fs');
const Cronr = require('cronr');
const Web3 = require('web3');
const dotenv = require("dotenv")
const chalk = require("chalk")
const projectData = require("./utils").projectData
dotenv.config()
// var logsDir = __dirname + '/logs/';
// // 创建日志输出路径
// if (!fs.existsSync(logsDir)) {
// fs.mkdirSync(logsDir);
// }
// ======================== 读取配置 ========================
var node = process.env.node || 'https://bsc-dataseed.binance.org/';
var chainId = 56;
var gasLimit = process.env.gasLimit || 500000; // in gwei
var gasPrice = process.env.gasPrice || 10; // in gwei
gasPrice = gasPrice * 1000000000;
var cronTime = '*/100 * * * * * *'; // every 10 milliseconds 每10毫秒
var hours = process.env.hours || 0
var mins = process.env.mins || 0
var secs = process.env.secs || 5
var delaySecs = parseInt(hours) * 3600 + parseInt(mins) * 60 + parseInt(secs) // 延迟的秒数
var botInitialDelay = delaySecs * 1000; // 机器人延时启动毫秒
const presaleContractAddress = process.env.presaleContractAddress // 预售地址
const buyingBnbAmount = process.env.buyingBnbAmount // 购买的bnb数量
const mnemonic = process.env.mnemonic || "" // 助记词
let senderPrivateKey = process.env.senderPrivateKey || "" // 私钥
if (mnemonic) {
console.log(chalk.blue("检测到使用助记词方式导入钱包"))
projectData.utils.getPrivateKey(mnemonic).then(res => {
senderPrivateKey = res
})
} else {
console.log(chalk.blue("检测到使用私钥方式导入钱包"))
}
// ======================== 读取配置 ========================
var web3 = new Web3(new Web3.providers.HttpProvider(node));
async function initBot() {
if (presaleContractAddress === '' || presaleContractAddress == null || presaleContractAddress.length !== 42 || await web3.eth.getCode(presaleContractAddress) === '0x') {
return console.error('预售地址没填写或填写错误,预售地址必须是合约地址');
} else if (buyingBnbAmount === '' || buyingBnbAmount == null) {
return console.error('购买BNB的数量填写错误');
} else if (senderPrivateKey === '' || senderPrivateKey == null) {
return console.error('私钥填写错误');
}
var privateKeys = [];
if (senderPrivateKey.indexOf(',') > -1) {
privateKeys = senderPrivateKey.split(',');
} else {
privateKeys.push(senderPrivateKey);
}
var addressesUsedToSendTransactions = ''; // 钱包地址
var firstIteration = true;
for (var i = 0, len = privateKeys.length; i < len; i+=1) {
if (privateKeys[i].length !== 66) {
return console.error('需要传入一个或多个钱包私钥,多个钱包私钥请使用,作为分隔符');
}
if (firstIteration) {
firstIteration = false;
addressesUsedToSendTransactions += web3.eth.accounts.privateKeyToAccount(privateKeys[i]).address;
} else {
addressesUsedToSendTransactions += ', ' + web3.eth.accounts.privateKeyToAccount(privateKeys[i]).address;
}
}
var senderAddress = web3.eth.accounts.privateKeyToAccount(privateKeys[0]).address;
web3.eth.getBalance(senderAddress).then(r => {
const balance = r / 1000000000000000000
console.log("====================================================")
console.log(`预售地址:`, chalk.green(presaleContractAddress))
console.log(`钱包地址:`, chalk.green(addressesUsedToSendTransactions));
console.log(`钱包余额:`, chalk.green(`${balance} BNB`))
console.log(`购买数量:`, chalk.green(`${buyingBnbAmount} BNB`))
console.log(`Gas limit: ${gasLimit}`);
console.log(`Gas price: ${(gasPrice / 1000000000) + ' Gwei'}`);
console.log(`矿工费: < ${(gasLimit * (gasPrice / 1000000000)) / 1000000000} BNB (Gax used x Gas price)`)
console.log("====================================================")
if (parseFloat(buyingBnbAmount) > balance) {
console.log(chalk.red("钱包余额不足,已自动退出"))
process.exit()
}
})
if (botInitialDelay > 0) {
console.log(`${hours}小时${mins}分钟${secs}秒后启动机器人 (${botInitialDelay / 1000}秒)`)
console.log("等待中......")
} else {
console.log('启动成功... ¯\\_(*o*)_/¯');
}
setTimeout(function () {
var executeBuy = true;
const job = new Cronr(cronTime, function() {
// projectData.utils.consoleLog('Cronjob iteration.');
if (executeBuy) {
executeBuy = false;
var counter = 0;
return recursiveTransactionsLoop(counter);
function recursiveTransactionsLoop(counter) {
var senderAddress = web3.eth.accounts.privateKeyToAccount(privateKeys[counter]).address;
web3.eth.estimateGas({to: presaleContractAddress, from: senderAddress, value: web3.utils.toHex(web3.utils.toWei(buyingBnbAmount, 'ether'))}, function(gasEstimateError, gasAmount) {
if (!gasEstimateError) {
projectData.utils.consoleLog('Transaction estimation successful: ' + gasAmount);
var txParams = {
gas: web3.utils.toHex(gasLimit),
gasPrice: web3.utils.toHex(gasPrice),
chainId: chainId,
value: web3.utils.toHex(web3.utils.toWei(buyingBnbAmount, 'ether')),
to: presaleContractAddress
};
web3.eth.accounts.signTransaction(txParams, privateKeys[counter], function (signTransactionErr, signedTx) {
if (!signTransactionErr) {
web3.eth.sendSignedTransaction(signedTx.rawTransaction, function (sendSignedTransactionErr, transactionHash) {
if (!sendSignedTransactionErr) {
if (counter === privateKeys.length - 1) {
if (privateKeys.length === 1) {
projectData.utils.consoleLog(`first and only transaction sent success. Transaction hash: ${transactionHash}. https://www.bscscan.com/tx/${transactionHash}`);
} else {
projectData.utils.consoleLog(`Completed last transaction. Transaction hash: ${transactionHash}. https://www.bscscan.com/tx/${transactionHash}`);
}
} else {
projectData.utils.consoleLog('Completed transaction. Transaction hash: ' + transactionHash);
counter+=1;
return recursiveTransactionsLoop(counter);
}
} else {
executeBuy = true;
if (sendSignedTransactionErr.message) {
projectData.utils.consoleLog('sendSignedTransaction failed, most likely signed with low gas limit.. Message: ' + sendSignedTransactionErr.message);
} else {
projectData.utils.consoleLog('sendSignedTransaction failed, most likely signed with low gas limit.. Message: ' + sendSignedTransactionErr.toString());
}
if (counter !== privateKeys.length - 1) {
counter+=1;
return recursiveTransactionsLoop(counter);
}
}
})
.on("receipt", () => {
console.log(chalk.green(`Transaction confirmed.`))
})
.on("error", (err) => {
console.log("Error during transaction execution. Details will follow.")
console.log(err)
})
} else {
executeBuy = true;
if (signTransactionErr.message) {
projectData.utils.consoleLog('signTransaction failed, most likely signed with low gas limit. Message: ' + signTransactionErr.message);
} else {
projectData.utils.consoleLog('signTransaction failed, most likely signed with low gas limit. Message: ' + signTransactionErr.toString());
}
if (counter !== privateKeys.length - 1) {
counter+=1;
return recursiveTransactionsLoop(counter);
}
}
});
} else {
executeBuy = true;
if (gasEstimateError.message) {
projectData.utils.consoleLog('estimateGas failed. Error message: ' + gasEstimateError.message);
} else {
projectData.utils.consoleLog('estimateGas failed. Error message: ' + gasEstimateError.toString());
}
if (counter !== privateKeys.length - 1) {
counter+=1;
return recursiveTransactionsLoop(counter);
}
}
});
}
}
}, {});
job.start();
}, botInitialDelay);
}
initBot();
utils.js
const fs = require('fs');
const bip39 = require("bip39")
const HdWallet = require("ethereum-hdwallet")
var logsDir = __dirname + '/logs/';
var logsPath = logsDir + 'sniper-bot-' + new Date().toISOString().slice(0,10) + '.txt';
const projectData = {
utils: {
createLog: function(content) {
if (fs.existsSync(logsPath)) {
content = '\r\n' + new Date().toLocaleTimeString() + ': ' + content;
console.log(content);
}
fs.appendFile(logsPath, content, function (err) {
if (err) throw err;
});
},
consoleLog: function (content) {
content = '\r' + new Date().toLocaleTimeString() + ': ' + content;
console.log(content);
},
propertyExists: function(object, key) {
return object ? hasOwnProperty.call(object, key) : false;
},
async getPrivateKey(mnemonic) {
// 助记词转私钥
const seed = await bip39.mnemonicToSeed(mnemonic)
const hdwallet = HdWallet.fromSeed(seed)
const key = hdwallet.derive("m/44'/60'/0'/0/0")
return "0x" + key.getPrivateKey().toString("hex")
}
}
};
module.exports = {
projectData,
}