Introduction To DEX Arbitrage
Intermediate Solidity Tutorial
Last updated
Intermediate Solidity Tutorial
Last updated
This Solidity tutorial will provide an introduction to DEX arbitrage with real, working, profitable open-source code. Solidity developers have the ability batch multiple swaps together and if they are not profitable revert the entire transaction only paying the transaction fee. The creation of EVM blockchains which have low transaction fees has created a playground for arbitrage traders.
There are many forms of arbitrage trading, in this tutorial we are going to concentrate on DEX arbitrage. This is buying a digital asset on one decentralized exchange and selling it on another. For this we will need a smart contract and a controller to execute the transactions.
The general idea is to take advantage of mispricing between exchanges. When someone executes a large trade in to one liquidity pool it can create an inbalance distorting the price and causing slippage for that trader. Arbitrage bots will then work to restore the balance by taking liquidity from other markets.
We will use a solidity smart contract as a relay between our controller and the exchanges. This is useful as it allows for fast execution of complex queries and batching multiple swaps into a single transaction. Critically we can revert the entire transaction and only lose the transaction fee if it is not profitable with one line of code.
This smart contract is using the Uniswap v2 router, which has been forked multiple times on almost every blockchain in existence.
The code is setup to query the router for a minimum quantity out given a specific input.
From there itβs possible to query multiple dexes in a single query with something like this:
The function above takes two router addresses for two different DEXβs and two tokens. It checks whether it would be profitable to swap token1 for token2 on router1 and then swap it back on router2.
This smart contract function doesnβt have any hard coded addresses which makes it quite flexible if a new DEX comes online or there is a new token which is being traded as it can be queried without deploying new contracts.
Note that this is very simplified and professional arbitrage bots would more likely use the getReserves to more accurately calculate optimal trade sizing.
Trades need to be batched together to make sure they are profitable before we let them go through. This means combining two swaps on two different exchanges into a single transaction.
This function is quite ugly from a gas optimisation perspective. Fortunately this wasnβt an issue because there are no transaction fees on Aurora.
The function checks balances then carries out two swaps before checking that the final balance is greater than what we started with. If no profit is made the whole transaction gets rolled back to the original state.
Finally we need a way to withdraw ERC20 funds from the contract.
I used DeFillama to get a list of exchanges on the Aurora chain. The four that I looked at were:-
Trisolaris 0x2CB45Edb4517d5947aFdE3BEAbF95A582506858B
WannaSwap 0xa3a1ef5ae6561572023363862e238afa84c72ef5
AuroraSwap 0xA1B1742e9c32C7cAa9726d8204bD5715e3419861
Rose 0xc90dB0d8713414d78523436dC347419164544A3f
You can find the router address by doing a swap manually and then using the block explorer to check the βInteracted Withβ contract address.
Next I needed some token addresses to trade. I found a list by looking at the managed token list section on the Trisolaris swap page.
Este link estΓ‘ aqui: https://raw.githubusercontent.com/aurora-is-near/bridge-assets/master/assets/aurora.tokenlist.json
A partir daΓ, foi o caso de testar cada token com os diferentes roteadores e descobrir quais tinham pools configurados e armazenΓ‘-los em um arquivo json. Isso criou uma lista de rotas ativas que pode ser percorrida, contendo roteadores e endereΓ§os de token que estavam ativos e disponΓveis para negociação.
O seguinte arquivo de configuração contém uma lista de [router1,router2,symbol,token1,token2]
So to get started letβs clone the github repo and deploy the smart contract.
Then put a private key in the .env file, donβt worry about the contract address yet as it hasnβt been deployed. There are no fees on Aurora so you donβt need a funded account. Next deploy the smart contract.
Add the deployed contract address to .env
Once the contract was deployed I purchased a number of different base assets to use as collateral. The more base assets the more opportunities there are to trade different markets. The assets I chose to deploy were wETH, wNEAR, USDT, Aurora, atUST, USDC simply because they had the most volume and opportunities.
There are 3 scripts in the directory for checking contract balances, sending funds and recovering funds easily so you donβt need to do it manually for each asset. If you are going to try setting this up it would pay to try it first with a tiny amount of assets and make sure you can recover the funds correctly from the contract when you are done.
The next step is to create a trading bot controller which fires routes and token addresses at the smart contract to find out if there are any opportunities for trades and then executes those trades.
Again I am only going to go through the important bits but the full code is here:
https://github.com/jamesbachini/DEX-Arbitrage/blob/main/scripts/trade.js
First step is to create an instance of our contract.
We then cycle through our routes to look for trades using the checkDualDexTrade function in our smart contract.
Once we have found a trade which is profitable we execute it using the dualDexTrade function.
Note that there is some logic to handle too many trades at any one time. I found that executing too quickly didnβt give the RPC nodes a chance to catch up which gave duplicate nonce errors. There is a hardhat module called NonceManager but I couldnβt get it to work consistently.
Another consistent headache is working with BigNumbers in Javascript. Token balances are huge because they have 18 decimals and 1 ETH is handled in wei as a BigNumber 1e18. There is a BigNumber library built in to Ethers utils but Iβd recommend thoroughly testing any kind of computation or comparisons using BigNumber values.
I allocated about $20 of capital to each base asset and after 12 hours of testing I ended up with the following results:
This is in basis points (percent of a percent) so wNear was the top performer which provided a 9.66% return in less than a day.
The issue is that it doesnβt scale well. The obvious next step was to start deploying more capital and the following evening I ran it with about $300 in each pool.
wNear again was the highest performer with just under a 2% return. Still very good but only $6 in the grand scheme of things. The more capital you allocate the less opportunities there are for trades because the slippage increases and makes them unprofitable.
For low volume there are some excellent returns to be had trading on small decentralised exchanges. Risk is limited to counterparty risk with the DEXβs and any bugs in our own smart contracts.
Once this is published the opportunity will likely be gone and the code is provided as an educational tool and starting point rather than something to git clone and profit with directly. Also note that the code is unaudited and not production ready to be used for financial transactions.
Here are some of the most profitable transactions that from that testing period:-
$2 USDT https://explorer.mainnet.aurora.dev/tx/0x06461ac7c56d1e973b2011640dfc9b9d4340a457d4419354eaead4eb4bdfabbd/token-transfers $4 NEAR https://explorer.mainnet.aurora.dev/tx/0x7e9aa5f29a89a5969c663825e4669bb24c0717835f6bb5f3dfc431d47c3e16cb/token-transfers $2 USDC https://explorer.mainnet.aurora.dev/tx/0xcc9857eda19701c7dff06e7bae50066b83953c961be1939b7b4f1981c339b318/token-transfers
Competing & Higher Stakes
The opportunities on Aurora are currently capped by the lack of trading volume which is very low. The reason this is profitable at all is because there is such a small opportunity that no one else is bothering to trade it. The competition is nearly non-existent.
In contrast to this if I wanted to arbitrage trade on Ethereum mainnet between Uniswap and Sushi the opportunities would be much greater because there is so much more trading volume. However this code wouldnβt be profitable and the trades wouldnβt even cover the transaction fees.
At higher stakes execution becomes critical and it becomes a search for MEV (Miner Extractable Value). There is a whole industry of developers known as searchers building MEV systems to profit from various on-chain opportunities.
Some common examples include:-
Sandwich trades front running big orders.
Liquidation bots collecting fees from DeFi borrowing/lending protocols.
Trading between multiple DEXβs, similar to what we are doing here but with 10+ hops between exchanges
Special situation opportunities such as NFT mints and other code to automate DeFi tasks
If you are interested in learning more about how the searcher community operates check crypto twitter and the flashbots discord server.
Searchers will usually use flashbots to bundle their transactions and bid for execution priority with a conglomerate of miners that operate on Ethereum L1.
Gas optimisation becomes a priority due to the high gas fees on Ethereum mainnet and itβs a good place to learn about how the cutting edge market participants are optimising their code.
There are plenty of easier opportunities in the far out regions of alternate layer 1 and layer 2 blockchains, low liquidity DEXβs and emerging DeFi ecosystems where anyone who can put together a smart contract can profit from DEX arbitrage strategies like the one show in this article.
If you wanted to take this a step further Iβd suggest looking into running a local or private geth RPC node for whatever chain you are trading and also expanding the complexity of the trades. I left in a function to show how this could be expanded to find triangular arbitrage opportunities.
Itβs basically the same function as our dualDexTrade but we now have 3 routers, 3 tokens and a lot more variations we can optimise for.
I hope that this has served as a good introduction to DEX arbitrage trading and a useful tutorial for solidity developers.