Skip to content

Instantly share code, notes, and snippets.

@InvokerMaster
Last active December 22, 2023 07:22
Show Gist options
  • Save InvokerMaster/429eb2c317f678c78c1e9f5eef66a8f2 to your computer and use it in GitHub Desktop.
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 :)
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();
@InvokerMaster
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment