Last active
December 22, 2023 07:22
-
-
Save InvokerMaster/429eb2c317f678c78c1e9f5eef66a8f2 to your computer and use it in GitHub Desktop.
Buy ERC20 tokens privately on Uniswap V2. Don't forget to add a star here :)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const {ethers} = require("ethers"); | |
const { | |
FlashbotsBundleProvider, FlashbotsTransactionResolution, | |
} = require("@flashbots/ethers-provider-bundle"); | |
const utils = require('./utils').utils; | |
const flashbotRpc = 'https://relay.flashbots.net'; | |
/* --------------------- CHANGES ----------------- */ | |
const rpc = 'https://mainnet.infura.io/v3/'; | |
const relayPvtKey = '' // any private key (make sure it doesn't contain any asset. it's public. just choose random one) | |
const buyerPvtKey = ''; // buyer private key that contains some ETH to buy tokens | |
const buyers = [ | |
// '0x00001', | |
// '0x00002', | |
// '0x00003' | |
// Any number of wallets that you are going to buy. It doesn't need to contain ETH, it's just wallet to receive bought tokens. | |
]; | |
const tokenAddr = ''; // Token address to buy | |
const gasMultiplier = 1.1; // Gas price multiplier. If current gwei is 30, then it will send transactions with 33 | |
const amountToPayMiner = 0.001; // Tip to miner to make your transaction private. You can choose whatever amount 0.00001, 0.00000001. | |
const amountToBuy = 0.04; // Amount of ETH that you are going to buy for each account. total ETH u need in buyer wallet should be more than amountToBuy * buyers.length + gasPrice | |
const maxTx = 10; // if the token has max transaction limit or max wallet limit, set this. It's percentage value of TotalSupply | |
const gasLimitForBuy = 500000; // Gas limit, 500k is fine to buy. | |
const targetBlock = 2; // No need to change, you can just set what block you want your transaction to be confirmed. | |
const timeLimit = 120; // No need to chage, you can change this if 1 block generation time is more than 60 seconds. Generally 10 - 30 seconds | |
/* --------------------- CHANGES END ----------------- */ | |
const aggregatorAddr = '0x4c1d5C67936a2419ECFD337690eFAe0F25F93553'; // Dex Aggregator address. no need to change | |
const wethAddr = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; // WETH address on Ethereum | |
const factoryAddr = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f'; // Uniswap V2 Factory address | |
async function getGasPrice(provider) { | |
return utils.hex(BigInt(await provider.getGasPrice()) * BigInt(gasMultiplier * 1000) / BigInt(1000)); | |
} | |
const main = async function () { | |
const provider = new ethers.providers.JsonRpcProvider({ | |
url: rpc, | |
}); | |
const flashbotRelayowner = new ethers.Wallet(relayPvtKey); | |
const flashbotsProvider = await FlashbotsBundleProvider.create( | |
provider, | |
flashbotRelayowner, | |
flashbotRpc | |
); | |
const token = new ethers.Contract(tokenAddr, require('./abi/erc20.json'), provider); | |
const aggregator = new ethers.Contract(aggregatorAddr, require('./abi/aggregator.json'), provider); | |
const gasPrice = await getGasPrice(provider); | |
console.log('gasPrice', utils.formatBalance(gasPrice, 9)); | |
const signer = new ethers.Wallet(buyerPvtKey, provider); | |
const tSupply = await token.connect(signer).totalSupply(); | |
console.log('totalSupply', tSupply); | |
const buyTxs = []; | |
const recipients = []; | |
const amountIns = []; | |
const maxOuts = []; | |
for (let buyerAddr of buyers) { | |
recipients.push(buyerAddr); | |
amountIns.push(utils.ether(amountToBuy)); | |
maxOuts.push(utils.hex(BigInt(tSupply) * BigInt(maxTx * 10 ** 4) / BigInt(100 * 10 **4))); | |
} | |
const tx = await aggregator.populateTransaction.multicall( | |
wethAddr, | |
token.address, | |
recipients, | |
amountIns, | |
maxOuts, | |
factoryAddr, | |
true, | |
false, | |
utils.ether(amountToPayMiner), | |
{ | |
value: utils.ether(amountToBuy * buyers.length) | |
} | |
); | |
tx.gasPrice = gasPrice; | |
tx.gasLimit = gasLimitForBuy * buyers.length; | |
tx.chainId = provider._network.chainId; | |
buyTxs.push({ | |
signer: signer, | |
transaction: tx | |
}); | |
const blockNumber = await provider.getBlockNumber() | |
const minTimestamp = (await provider.getBlock(blockNumber)).timestamp | |
const maxTimestamp = minTimestamp + timeLimit | |
console.log('Target block number', blockNumber + targetBlock); | |
const bundledTransactions = [ | |
...buyTxs | |
]; | |
const signedBundle = await flashbotsProvider.signBundle(bundledTransactions); | |
const bundleReceipt = await flashbotsProvider.sendRawBundle( | |
signedBundle, | |
blockNumber + targetBlock, | |
{ | |
minTimestamp, | |
maxTimestamp | |
} | |
) | |
const result = await bundleReceipt.wait(); | |
const receipts = await bundleReceipt.receipts(); | |
console.log('Bundle Receipt', bundleReceipt); | |
console.log('simulation', await bundleReceipt.simulate()); | |
console.log('Result', result) | |
console.log('Receipts', receipts); | |
if (receipts && receipts.length > 0) { console.log('Private Buy succeeded') } else { console.log('Buy failed. See if the tx has reverts in console. Otherwise try running again'); } | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://twitter.com/onlyone_degen/status/1674968904148606976