From 85d854aa12f700a1d0ac8c23266ca44f584806c4 Mon Sep 17 00:00:00 2001 From: Alexander Date: Fri, 19 Jul 2024 17:09:01 -0400 Subject: [PATCH] init --- data/abi/ERC20.json | 1 + data/abi/Uniswapv2_Router.json | 1 + data/rpc_servers.json | 4 + generate-wallets.py | 110 +++++++++++++++++++ main.py | 194 +++++++++++++++++++++++++++++++++ requirements.txt | 3 + sample.env | 3 + 7 files changed, 316 insertions(+) create mode 100644 data/abi/ERC20.json create mode 100644 data/abi/Uniswapv2_Router.json create mode 100644 data/rpc_servers.json create mode 100644 generate-wallets.py create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 sample.env diff --git a/data/abi/ERC20.json b/data/abi/ERC20.json new file mode 100644 index 0000000..5b86559 --- /dev/null +++ b/data/abi/ERC20.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}] \ No newline at end of file diff --git a/data/abi/Uniswapv2_Router.json b/data/abi/Uniswapv2_Router.json new file mode 100644 index 0000000..5d94908 --- /dev/null +++ b/data/abi/Uniswapv2_Router.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WPLS","type":"address"}],"type":"constructor"},{"inputs":[],"name":"WPLS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"amountADesired","type":"uint256"},{"internalType":"uint256","name":"amountBDesired","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountTokenDesired","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"addLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"},{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountIn","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"reserveIn","type":"uint256"},{"internalType":"uint256","name":"reserveOut","type":"uint256"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsIn","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"}],"name":"getAmountsOut","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"reserveA","type":"uint256"},{"internalType":"uint256","name":"reserveB","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETH","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"removeLiquidityETHSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermit","outputs":[{"internalType":"uint256","name":"amountToken","type":"uint256"},{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountTokenMin","type":"uint256"},{"internalType":"uint256","name":"amountETHMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityETHWithPermitSupportingFeeOnTransferTokens","outputs":[{"internalType":"uint256","name":"amountETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenA","type":"address"},{"internalType":"address","name":"tokenB","type":"address"},{"internalType":"uint256","name":"liquidity","type":"uint256"},{"internalType":"uint256","name":"amountAMin","type":"uint256"},{"internalType":"uint256","name":"amountBMin","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bool","name":"approveMax","type":"bool"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"removeLiquidityWithPermit","outputs":[{"internalType":"uint256","name":"amountA","type":"uint256"},{"internalType":"uint256","name":"amountB","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapETHForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactETHForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForETHSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapExactTokensForTokensSupportingFeeOnTransferTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactETH","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"amountInMax","type":"uint256"},{"internalType":"address[]","name":"path","type":"address[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"swapTokensForExactTokens","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"type":"receive"}] \ No newline at end of file diff --git a/data/rpc_servers.json b/data/rpc_servers.json new file mode 100644 index 0000000..2727d92 --- /dev/null +++ b/data/rpc_servers.json @@ -0,0 +1,4 @@ +[ + "https://rpc.pulsechain.com", + "https://rpc-pulsechain.g4mm4.io" +] \ No newline at end of file diff --git a/generate-wallets.py b/generate-wallets.py new file mode 100644 index 0000000..71a1ca7 --- /dev/null +++ b/generate-wallets.py @@ -0,0 +1,110 @@ +import json +import os +import sys + +from dotenv import load_dotenv +from web3 import Web3 +from web3_multi_provider import FallbackProvider + +load_dotenv() +web3 = Web3(FallbackProvider(json.load(open('./data/rpc_servers.json')))) + + +def generate_wallet(amount): + addresses = [] + for i in list(range(0, amount)): + account = Web3().eth.account.create() + keystore = Web3().eth.account.encrypt(account.key.hex(), os.getenv('SECRET')) + folder = "./data/wallets/{}".format(account.address) + os.makedirs("data/wallets", exist_ok=True) + os.makedirs(folder, exist_ok=True) + open("{}/keystore".format(folder), 'w').write(json.dumps(keystore, indent=4)) + addresses.append(account) + return addresses + + +# show help +if 'help' == sys.argv[1].lower(): + print("Generates 1 or more number of wallet keystores and optionally displays their private keys.\n") + print("Example Usage:") + command = "python {}".format(sys.argv[0]) + examples = ['--create', '--create 1', '--create 1 --show-private-keys', '--show-private-keys 0x1234567891234567891234567891234567891234'] + for e in examples: + print("{} {}".format(command, e)) + sys.exit() + +# make sure you have a unique secrets +secret = os.getenv('SECRET') +if secret == 'changeme' or not secret: + print('Change your secret in .env') + sys.exit() + +# display private key arg to add to metamask/rabby +if "--show-private-keys" in sys.argv: + public_key_index = sys.argv.index("--show-private-keys") + show_private_key = True +elif "-s" in sys.argv: + public_key_index = sys.argv.index("-s") + show_private_key = True +else: + public_key_index = None + show_private_key = False + +# create specified number of wallets arg +if "--create" in sys.argv or "-c" in sys.argv: + if "--create" in sys.argv: + amount_index = sys.argv.index("--create") + else: + amount_index = sys.argv.index("-c") + + try: + amount = sys.argv[amount_index + 1] + except IndexError: + arg = sys.argv[amount_index].split('=') + if len(arg) == 1 or not arg[1].isnumeric(): + amount = 1 + else: + amount = arg[1] + if not str(amount).isnumeric(): + print("Invalid amount") + sys.exit() + else: + amount = int(amount) +else: + amount = 0 + +# create new wallets +if amount: + # generate X wallets based on sys.argv + wallets = generate_wallet(int(amount)) + print("\nGenerated {} wallets\n".format(amount)) + # display the generated wallet's keys + for wallet in wallets: + print("Public Key: {}".format(wallet.address)) + if show_private_key: + print("Private Key: {}\n".format(wallet.key.hex())) + +# show private keys only +elif show_private_key: + wallet_address = None + try: + wallet_address = sys.argv[public_key_index + 1] + except IndexError: + arg = sys.argv[public_key_index].split('=') + if len(arg) > 1: + wallet_address = arg[1] + else: + print("Not enough args") + sys.exit() + + try: + wallet_address = web3.to_checksum_address(wallet_address) + except ValueError: + print("Invalid wallet address {}".format(wallet_address)) + else: + print("\nPublic Key: {}".format(wallet_address)) + secret = os.getenv('SECRET') + private_key = Web3().eth.account.decrypt("\n".join([line.strip() for line in open("./data/wallets/{}/keystore".format(wallet_address), 'r+')]), secret) + wallet = Web3().eth.account.from_key(private_key) + print("Private Key: {}".format(wallet.key.hex())) + print() diff --git a/main.py b/main.py new file mode 100644 index 0000000..9f540dc --- /dev/null +++ b/main.py @@ -0,0 +1,194 @@ +import json +import os +import sys +import time +import logging +from logging.handlers import TimedRotatingFileHandler + +from dotenv import load_dotenv +from web3 import Web3 +from web3_multi_provider import FallbackProvider + +load_dotenv() +secret = os.getenv('SECRET') +wallet_address = os.getenv('WALLET_ADDRESS') +burn_address = os.getenv('BURN_ADDRESS') +if not wallet_address or not burn_address or not secret or secret == 'changeme': + print("Setup your .env file") + sys.exit() +os.makedirs(log_dir := "./data/logs/", exist_ok=True) +logging.basicConfig( + format='%(asctime)s %(name)s %(levelname)s %(message)s', + datefmt='%H:%M:%S', + level='INFO', + handlers=[ + TimedRotatingFileHandler( + "{}/{}.log".format(log_dir, wallet_address), + when="midnight", + interval=1, + backupCount=69 + ), + logging.StreamHandler(sys.stdout) + ] +) +os.makedirs("data/wallets", exist_ok=True) +wallet_keystore = "./data/wallets/{}/keystore".format(wallet_address) +if not os.path.isfile(wallet_keystore): + logging.error("Wallet keystore not found") + sys.exit() +private_key = Web3().eth.account.decrypt("\n".join([line.strip() for line in open(wallet_keystore, 'r+')]), secret) +account = Web3().eth.account.from_key(private_key) +web3 = Web3(FallbackProvider(json.load(open('./data/rpc_servers.json')))) + +# token contract addresses +reward_tokens = [ + "0xCc78A0acDF847A2C1714D2A925bB4477df5d48a6", # atropa + "0x0b1307dc5D90a0B60Be18D2634843343eBc098AF", # legal + "0x0EB4EE7d5Ff28cbF68565A174f7E5e186c36B4b3", # mantissa + "0xd6c31bA0754C4383A41c0e9DF042C62b5e918f6d", # teddy bear + "0x463413c579D29c26D59a65312657DFCe30D545A1", # treasury bill + "0x4243568Fa2bbad327ee36e06c16824cAd8B37819" # tsfi +] +wpls_address = "0xA1077a294dDE1B09bB078844df40758a5D0f9a27" + +# frens +frens_address = '0x67e3fec6F92e1bCD82E1CD96835220FF9121595E' +frens_abi = json.load(open('./data/abi/ERC20.json')) +frens_contract = web3.eth.contract(address=frens_address, abi=frens_abi) + +# router +router_address = '0x165C3410fC91EF562C50559f7d2289fEbed552d9' +router_abi = json.load(open('./data/abi/Uniswapv2_Router.json')) +router_contract = web3.eth.contract(address=router_address, abi=router_abi) + + +def main(): + # begin work + for token_address in reward_tokens: + # load target token + token_contract = web3.eth.contract(address=token_address, abi=json.load(open('./data/abi/ERC20.json'))) + token_symbol = token_contract.functions.symbol().call() + + # check token is approved + token_balance = token_contract.functions.balanceOf(wallet_address).call() + if not token_balance: + logging.info("No balance found for {}. Skipping...".format(token_symbol)) + continue + else: + logging.info("Adding LP for {}...".format(token_symbol)) + + # approve target token with pulsex + allowed_balance = token_contract.functions.allowance(wallet_address, router_address).call() + if token_balance > allowed_balance: + try: + total_supply = token_contract.functions.totalSupply().call() + tx = token_contract.functions.approve(router_address, total_supply).build_transaction({ + "nonce": web3.eth.get_transaction_count(web3.to_checksum_address(wallet_address)), + "from": wallet_address, + "chainId": 369 + }) + web3.eth.estimate_gas(tx) + tx_signed = web3.eth.account.sign_transaction(tx, private_key=account.key) + tx_hash = web3.eth.send_raw_transaction(tx_signed.rawTransaction) + web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) + except Exception as e: + logging.error("Could not approve token spender: {}".format(e)) + continue + else: + logging.info("Approved PulseX v2 as spender for {} ({})".format(token_symbol, tx_hash.hex())) + + # approve frens with pulsex + frens_balance = frens_contract.functions.balanceOf(wallet_address).call() + allowed_balance = frens_contract.functions.allowance(wallet_address, router_address).call() + if frens_balance > allowed_balance: + try: + total_supply = frens_contract.functions.totalSupply().call() + tx = frens_contract.functions.approve(router_address, total_supply).build_transaction({ + "nonce": web3.eth.get_transaction_count(web3.to_checksum_address(wallet_address)), + "from": wallet_address, + "chainId": 369 + }) + web3.eth.estimate_gas(tx) + tx_signed = web3.eth.account.sign_transaction(tx, private_key=account.key) + tx_hash = web3.eth.send_raw_transaction(tx_signed.rawTransaction) + web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) + except Exception as e: + logging.error("Could not approve token spender: {}".format(e)) + continue + else: + logging.info("Approved PulseX v2 as spender for FRENS ({})".format(tx_hash.hex())) + + try: + expected_output_amounts = router_contract.functions.getAmountsOut( + token_balance, + [token_address, wpls_address, frens_address] + ).call() + except Exception as e: + logging.error("Could not estimate output amounts: {}".format(e)) + continue + + # add liquidity to frens + try: + tx = router_contract.functions.addLiquidity( + token_address, + frens_address, + expected_output_amounts[0], + expected_output_amounts[-1], + 0, + 0, + account.address, + int(time.time()) + (60 * 5) + ).build_transaction({ + "nonce": web3.eth.get_transaction_count(web3.to_checksum_address(wallet_address)), + "from": wallet_address, + "chainId": 369 + }) + web3.eth.estimate_gas(tx) + tx_signed = web3.eth.account.sign_transaction(tx, private_key=account.key) + tx_hash = web3.eth.send_raw_transaction(tx_signed.rawTransaction) + tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) + except Exception as e: + logging.error("Could not add to LP: {}".format(e)) + continue + else: + logging.info("Added LP for {} and FRENS ({})".format(token_symbol, tx_hash.hex())) + + # transfer lp tokens to burn address + for log in tx_receipt['logs']: + if log['topics'][0].hex() != '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef': + continue + if web3.to_checksum_address('0x' + log['topics'][2].hex()[-40:]) != wallet_address: + continue + # extract lp tokens just created + lp_balance = int(log['data'].hex(), 16) + # load the lp token contract + lp_contract = web3.eth.contract(address=log['address'], abi=json.load(open('./data/abi/ERC20.json'))) + # check if wallet has enough lp tokens to send + _lp_balance = lp_contract.functions.balanceOf(wallet_address).call() + if _lp_balance < lp_balance: + break + # send only the amount of lp tokens created earlier + try: + tx = lp_contract.functions.transfer(burn_address, lp_balance).build_transaction({ + "nonce": web3.eth.get_transaction_count(web3.to_checksum_address(wallet_address)), + "from": wallet_address, + "chainId": 369 + }) + web3.eth.estimate_gas(tx) + tx_signed = web3.eth.account.sign_transaction(tx, private_key=account.key) + tx_hash = web3.eth.send_raw_transaction(tx_signed.rawTransaction) + web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) + except Exception as e: + logging.error("Could not transfer LP tokens: {}".format(e)) + else: + logging.info("Transfered LP tokens to {} ({})".format(burn_address, tx_hash.hex())) + finally: + break + + +if __name__ == '__main__': + print("-" * 50) + print("Frens LP and Burn") + print("-" * 50) + main() + print("-" * 50) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..54608a3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +web3 +web3-multi-provider +python-dotenv \ No newline at end of file diff --git a/sample.env b/sample.env new file mode 100644 index 0000000..d2e9cf6 --- /dev/null +++ b/sample.env @@ -0,0 +1,3 @@ +SECRET=changeme +WALLET_ADDRESS= +BURN_ADDRESS= \ No newline at end of file