Crypto Arbitrage Bot with Sandwich Trading functionality, powered by Python, intended for Uniswap & another decentralized exchange (DEX)
Everybody on the internet is probably searching how to create a Crypto Trading Bot. Well, I can help you get started here with not only a Crypto Trading Bot, but some bult-in trading strategy functions to elevate the performance of your trading by leveraging some important logic.
Yes, we’re going to leverage Python to create your ideal Crypto Trading Bot. However, this code is introductory and needs to be modified, checked, tested, validated and so forth…check the addresses, please run this in a test environment first! Do not be discouraged, I make it clear where you have to replace certain key values. I intentionally placed non-working links in the code, because it is your duty to independently confirm everything is working properly first…and I don’t want you to suffer any losses as a result when you get your bot running live. So be patient and make sure you understand all aspects of a trading bot. If you would like to get more content on this topic, I have some additional articles posted here: https://medium.com/@rjaloudi Moving on….
The Python script below outlines an advanced strategy known as “sandwich trading,” a specific form of arbitrage that capitalizes on unconfirmed transactions seen in the pool of a blockchain network. This method is often associated with the term “front-running” in the context of decentralized finance (DeFi). The script is designed to interact with Ethereum blockchain and DeFi protocols, leveraging smart contracts to execute trades. Below is a detailed explanation tailored to understanding the intricacies of this strategy and the script’s operational framework.
Before we get into the script itself, let’s assume you’re doing everything for the first time, including the installation of Python and all applicable libraries. This section covers setting up your environment:
Installation of Python Environment
To get your Python environment set up on a Windows machine and ensure all the imports in your script work correctly, follow these steps:
1. Install Python
- Download Python:
- Go to the official Python website at python.org.
- Download the latest version of Python for Windows. Ensure you select the installer that matches your system architecture (32-bit or 64-bit).
- Install Python:
- Run the downloaded installer.
- Select “Add Python X.X to PATH” at the bottom of the installer window. This step is crucial as it makes Python accessible from the Command Prompt.
- Click on “Install Now”.
- Verify Installation:
- Open Command Prompt and type
python --version
and press Enter. If Python is installed correctly, you should see the Python version number.
- Open Command Prompt and type
2. Set Up a Virtual Environment (Optional but Recommended)
A virtual environment is a self-contained directory that contains a Python installation for a particular version of Python, plus a number of additional packages.
- Open Command Prompt and navigate to your project directory.
- Create a Virtual Environment by typing:
python -m venv myenv
- Replace
myenv
with the name of your virtual environment. - Activate the Virtual Environment:
- For Command Prompt, use:
myenv\Scripts\activate
- You should now see
(myenv)
in your Command Prompt, indicating the virtual environment is active.
3. Install Required Packages
With Python installed and your virtual environment ready, you can now install the packages required by your script using pip
, Python’s package installer.
- Ensure pip is up-to-date by running:
python -m pip install --upgrade pip
Install the Required Packages:
- You need to install
requests
,web3
, andpyotp
. You can do this by running the following commands in your Command Prompt (ensure your virtual environment is activated if you are using one):
pip install requests
pip install web3
pip install pyotp
- These commands download and install the packages from the Python Package Index (PyPI).
4. Verify the Installation
To ensure that the packages were installed correctly and can be imported without issues, you can do a quick check:
- Start Python in the Command Prompt by typing
python
and pressing Enter. - Try Importing the Packages by entering the following commands one at a time:
import json
import getpass
import time
import requests
from web3 import Web3
from web3.middleware import geth_poa_middleware
import pyotp
If none of these commands result in an error, the packages have been installed correctly and are ready to be used in your script.
Now, proceed with your Project. You’re now set up with Python and all the necessary packages to run your script on a Windows machine. Remember to activate your virtual environment whenever you’re working on your project to keep your dependencies properly managed.
How to Activate a Virtual Environment on Windows
Assuming you’ve already created a virtual environment for your project, here’s how to activate it:
- Open Command Prompt: Start by opening the Command Prompt (cmd) on your Windows machine.
- Navigate to Your Project: Use the
cd
command to navigate to your project directory where the virtual environment is located. For example:
cd path\to\your\project
Activate the Virtual Environment: Once in the project directory, activate the virtual environment by running the activation script located in the Scripts
subdirectory of your virtual environment folder. The command looks like this:
.\venv\Scripts\activate
Replace venv
with the name of your virtual environment folder if you’ve named it differently.
- Confirmation: After running the activation command, you should see the name of your virtual environment in parentheses at the beginning of the command prompt line. This indicates that the virtual environment is currently active.
- Example:
(venv) C:\path\to\your\project>
- Example:
- Work on Your Project: With the virtual environment activated, any Python or pip commands you run will now be restricted to this environment. You can safely install, update, or remove packages without affecting the global Python installation.
- Deactivate: When you’re done working in the virtual environment, you can deactivate it by running:
deactivate
- This will return you to the global Python environment.
Remember to activate your virtual environment each time you resume work on your project to ensure that you’re using the correct set of dependencies. This practice helps maintain a consistent development environment and avoids potential conflicts between project requirements.
Now, let’s proceed with the Project.
Sandwich Trading: An Overview
Sandwich trading is a strategy used in the cryptocurrency market where a trader, often referred to as a “bot,” places orders on both sides of an unconfirmed transaction to profit from the price movement caused by the initial transaction. This is considered a form of front-running because the bot anticipates and exploits the price impact of pending transactions seen in the pool, the collection of all transactions waiting to be confirmed and added to a block.
Below is the actual code and you will need to update certain values, especially the wallet addresses:
import json
import getpass
import time
import requests
from web3 import Web3
from web3.middleware import geth_poa_middleware
import pyotp
# Constants
UNISWAP_API_URL = 'https://api.1inch.exchange/v3.0/1/quote'
OTHER_EXCHANGE_API_URL = 'https://api.otherexchange.com/quote'
INFURA_PROJECT_ID = 'your_infura_project_id'
PRIVATE_KEY_PROMPT = "Enter your private key or seed phrase: "
CONTRACT_ADDRESS_PROMPT = "Enter the contract address: "
# Initialize Web3 provider
w3 = Web3(Web3.HTTPProvider(f'https://mainnet.infura.io/v3/{INFURA_PROJECT_ID}'))
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
# Load contract ABI
with open('contract_abi.json', 'r') as f:
contract_abi = json.load(f)
# Helper functions
def get_token_balance(contract_address, wallet_address):
contract = w3.eth.contract(contract_address, abi=contract_abi)
balance = contract.functions.balanceOf(wallet_address).call()
return balance
def get_eth_balance(wallet_address):
balance = w3.eth.get_balance(wallet_address)
return w3.fromWei(balance, 'ether')
def get_token_price(token_address, exchange_url):
url = f'{exchange_url}?fromTokenAddress={token_address}&toTokenAddress=0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2&amount=1'
response = requests.get(url)
data = response.json()
price = data['toTokenAmount'] / 10 ** 18 # Convert to ETH price
return price
# The address 0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 in the URL is the contract address for WETH (Wrapped Ether) on the Ethereum blockchain.
# WETH is a wrapped version of Ether (ETH), which allows ETH to be traded as an ERC-20 token on Ethereum-based decentralized exchanges (DEXs) like Uniswap.
# This wrapping is necessary because ETH itself does not conform to the ERC-20 standard, which is required for compatibility with most DEXs and other smart contracts that handle tokens.
def find_arbitrage_opportunity(token_address):
uniswap_price = get_token_price(token_address, UNISWAP_API_URL)
other_exchange_price = get_token_price(token_address, OTHER_EXCHANGE_API_URL)
if other_exchange_price > uniswap_price:
return "Uniswap", "Other Exchange"
elif uniswap_price > other_exchange_price:
return "Other Exchange", "Uniswap"
else:
return None, None
def execute_arbitrage(trade_data, contract_address, wallet_address):
# Find the profitable exchange
profitable_exchange, losing_exchange = find_arbitrage_opportunity(token_address)
if profitable_exchange is None:
print("No arbitrage opportunity found.")
return
print(f"Arbitrage opportunity found: {profitable_exchange} -> {losing_exchange}")
# Connect to the contract
contract = w3.eth.contract(contract_address, abi=contract_abi)
# Prepare transaction
txn = contract.functions.executeArbitrage(trade_data).buildTransaction({
'from': wallet_address,
'nonce': w3.eth.get_transaction_count(wallet_address),
'gas': 2000000,
'gasPrice': w3.eth.gas_price
})
# Sign and send the transaction
signed_txn = w3.eth.account.sign_transaction(txn, private_key=private_key)
w3.eth.send_raw_transaction(signed_txn.rawTransaction)
print("Arbitrage transaction sent. Waiting for confirmation...")
# Wait for transaction confirmation
while True:
txn_receipt = w3.eth.get_transaction_receipt(signed_txn.hash)
if txn_receipt is not None:
break
time.sleep(1)
print("Arbitrage transaction confirmed!")
# Main program
def main():
# Get inputs from the user
private_key = getpass.getpass(PRIVATE_KEY_PROMPT)
contract_address = input(CONTRACT_ADDRESS_PROMPT)
# Get wallet address from the private key or seed phrase
wallet_address = w3.eth.account.from_key(private_key).address
# Get token balances
token_address = '0x123abc...' # Replace with the token address you want to trade
token_balance = get_token_balance(token_address, wallet_address)
eth_balance = get_eth_balance(wallet_address)
# Prompt for confirmation
print(f"Wallet address: {wallet_address}")
print(f"Token balance: {token_balance}")
print(f"ETH balance: {eth_balance}")
confirm = input("Confirm the balances. Proceed with the arbitrage? (y/n): ")
if confirm.lower() != 'y':
print("Aborted.")
return
# Perform arbitrage
trade_data = b'\x01\x02\x03...' # Replace with your arbitrage trade data
execute_arbitrage(trade_data, contract_address, wallet_address)
if __name__ == '__main__':
main()
Before we delve into the Script Breakdown and Operational Logic, we have to discuss changing values in the script. There are several values and aspects that you need to update or consider changing to ensure the script runs correctly and securely:
INFURA_PROJECT_ID
: Replace'your_infura_project_id'
with your actual Infura project ID. This is necessary to connect to the Ethereum network.contract_abi.json
: Make sure thecontract_abi.json
file exists in the same directory as your script and contains the correct ABI for the contract you are interacting with.token_address
: Thetoken_address
is hardcoded as'0x123abc...'
. You need to replace this with the actual contract address of the token you intend to trade.trade_data
: Thetrade_data
variable is set tob'\x01\x02\x03...'
. This is placeholder data and should be replaced with the actual data required for executing your arbitrage trade. This data typically includes encoded function calls to the smart contract that will execute the trade.- Private Key Handling: The script uses
getpass.getpass()
to securely input the private key. Ensure that this private key is handled securely and never hard-coded or exposed. - Gas and Transaction Parameters: The transaction parameters such as
'gas'
and'gasPrice'
in theexecute_arbitrage
function are set to fixed values. These may need to be adjusted based on the current network conditions. You may also want to implement a strategy to estimate gas prices dynamically. - Error Handling: Consider adding error handling throughout the script, especially around network requests (e.g., checking the response status of
requests.get(url)
), interactions with the blockchain (e.g., transaction submission and confirmation), and user inputs. - Security Considerations: Ensure that any sensitive information, especially the private key, is handled securely. Avoid logging sensitive information or exposing it in any way.
- API URLs: The
UNISWAP_API_URL
andOTHER_EXCHANGE_API_URL
are predefined. Ensure these URLs are correct and point to the API endpoints you intend to use. The API structure or base URL may change over time, so it’s good to verify these. - Dynamic Configuration: Consider making parts of your script configurable through external configuration files or environment variables, such as API URLs, Infura project ID, and gas settings. This can make your script more flexible and secure.
Before running the script, double-check all external dependencies, such as the Infura endpoint, the availability and correctness of the ABI file, and the validity of API URLs. Additionally, thorough testing in a safe environment (e.g., testnet) is recommended to ensure the script behaves as expected without risking real assets.
Now, we can move onto the logic built into the script.
Script Breakdown and Operational Logic
1. Blockchain and Smart Contract Initialization
- The script initializes a connection to the Ethereum network using Web3.py and sets up middleware for PoA compatibility. This is crucial for interacting with certain Ethereum-based networks that use a different consensus mechanism than Proof of Work (PoW).
- It loads the ABI for a smart contract, which is essential for the script to interact with predefined functions within that contract on the blockchain.
2. Utility Functions
- Functions like
get_token_balance
andget_eth_balance
are used to fetch the current balance of a specified ERC-20 token and Ether, respectively, for a given wallet address. get_token_price
queries specified exchange APIs to fetch the current price of a given token in Ether, facilitating the assessment of potential arbitrage opportunities.
3. Identifying Arbitrage Opportunities
- The
find_arbitrage_opportunity
function compares token prices between Uniswap and another exchange, identifying potential price discrepancies that could be exploited for profit.
4. Executing the Trade
- Upon identifying a profitable opportunity, the
execute_arbitrage
function builds and sends a transaction to the blockchain, calling a smart contract function designed to execute the sandwich trade. This involves placing buy and sell orders around a detected unconfirmed transaction to profit from the price difference.
5. Main Execution Flow
- The script’s main flow involves prompting the user for sensitive inputs like the private key and contract address, retrieving the wallet address from the private key, and confirming token and ETH balances before proceeding with the trade execution.
Ethical and Regulatory Considerations
Sandwich trading and front-running in DeFi raise significant ethical and regulatory considerations. These practices can lead to market manipulation, where individuals with advanced tools and access to blockchain data can unfairly profit at the expense of regular users. Regulatory bodies are increasingly scrutinizing such activities to ensure fair trading practices.
The script provides a sophisticated means to engage in sandwich trading, leveraging the transparency and immutability of the Ethereum blockchain. While it showcases the potential for profit in the DeFi space, it also highlights the complexities and ethical considerations inherent in using advanced trading strategies in an unregulated market. Users must navigate these waters with caution, understanding the risks and the potential impact of their trading activities on the broader cryptocurrency ecosystem.
Disclaimer: Please test this on your own risk before running on a live exchange. Furthermore, you must change several values in this script including the addresses. By all means, check the laws in your jurisdiction, do your due diligence, consult the experts (accountants, attorneys, etc.)…this is for educational purposes only.