Overview

Decentralized finance (DeFi) is a type of finance created on the blockchain. It does not rely on financial institutions such as brokerages, exchanges or banks to provide financial instruments, but uses smart contracts on the blockchain to conduct financial activities. There are a large number of arbitrage opportunities in DeFi, including but not limited to liquidation and spread arbitrage. This article will analyze the arbitrage opportunities that may exist in the contract code of some decentralized exchanges (DEX) and aggregators.

analyze

Uniswap

Uniswap is a decentralized cryptocurrency trading platform that uses the automated market maker (AMM) model. There are currently two popular versions, Uniswap V2 and Uniswap V3. We will analyze the possible arbitrage opportunities in each of them.

Uniswap V2 Router

In Uniswap V2, users generally interact with the Pair contract and the Factory contract through the Router contract. Generally speaking, the Router only transfers tokens in transactions and does not store them. However, due to various reasons, such as airdrops and transfer errors, some tokens are stored in the Router contract. So how do you extract these tokens?

By analyzing the code of Uniswap V2 Router 02 contract, we found the removeLiquidityETHSupportingFeeOnTransferTokens function:

This function is used to remove liquidity where one of the tokens is WETH. The address passed to the removeLiquidity function is address(this), which means that the two tokens will be transferred to the Router contract first, and then the Router contract will transfer the two tokens to the specified address. Although the amount of WETH transferred here is returned by removeLiquidity and cannot be modified, the amount of the other token transferred is balanceOf(address(this)), which is the balance of the token in the Router contract.

Therefore, based on the above analysis, we can get an arbitrage process:

The Router 02 contract was monitored to have ERC 20 tokens;

  • The Router 02 contract was monitored to have ERC 20 tokens;

  • Call addLiquidityETH to add the liquidity of the ERC 20 token and WETH;

  • Call removeLiquidityETHSupportingFeeOnTransferTokens to remove liquidity.

limitation:

  • If the token has no liquidity with WETH before, a small amount of liquidity (MINIMUM_LIQUIDITY) will be lost when liquidity is added for the first time;

  • No method has been found to extract WETH and ETH from the Router 02 contract.

Uniswap V2 Pair

The Uniswap V2 Pair contract, the so-called liquidity pool, stores two tokens that provide liquidity. Because the Pair contract uses reserve to record the balance instead of balanceOf(address(this)), when someone mistakenly transfers the liquidity token directly to the contract, there will be a difference between balance and reserve. There is a balance function skim in the Pair contract, which we can call to extract the difference in tokens:

It can be seen that this function will transfer the balance and reserve difference of the two liquidity tokens in the liquidity pool to the to address.

In addition to these two tokens, there are other ERC 20 tokens in the liquidity pool due to mistransfers, airdrops, etc. How to withdraw these tokens?

After analyzing the code of the Pair contract, it was found that this part of the tokens cannot be withdrawn, with only one exception: when the LP token of the pool exists in the liquidity pool.

In this case, we can call the burn function of the Pair contract to remove liquidity and take out the corresponding two liquidity tokens:

Uniswap V3 SwapRouter

The SwapRouter contract of Uniswap V3 will also have the same situation as Uniswap V2 Router, with ERC 20 tokens and ETH, but fortunately the SwapRouter contract provides several functions to easily extract the tokens.

To extract ERC 20 tokens we can use the sweepToken function:

To withdraw ETH we can use the refundETH function:

You can also directly call the unwrapWETH 9 function to convert WETH to ETH and extract it:

The above is an arbitrage analysis of the Uniswap V3 SwapRouter contract.

After analyzing the code of the Uniswap V3 Pool contract, it was found that there was no way to extract other tokens in its contract, and there was no difference between balance and reserve like in the Uniswap V2 Pair contract.

SushiSwap

SushiSwap started out as a fork of Uniswap and has since grown into an independent ecosystem that offers many different financial services and products.

Because SushiSwap is the same as Uniswap V2, the above arbitrage methods for Uniswap V2 also apply to SushiSwap.

SushiXSwap

SushiXSwap is a full-chain trading protocol based on LayerZero launched by SushiSwap. Supported networks include Optimism, Arbitrum, Fantom, BNB Chain, Polygon and Avalanche. Users can conduct cross-chain transactions between supported networks and assets.

How to withdraw tokens from the SushiXSwap contract?

The main functions of SushiXSwap are implemented through the cook function, which provides a series of operations. The supported operations are listed as follows:

There is an operation ACTION_DST_WITHDRAW_TOKEN, and its code implementation is as follows:

First, decode the data passed to the cook function, and then determine whether amount is equal to 0. If it is equal to 0, set the value of amount to the balance of the ERC 20 token or the balance of ETH of the contract. Finally, call _transferTokens to transfer the tokens to the specified address:

Therefore, we only need to construct the actions and datas passed into the cook function, that is, set the actions to ACTION_DST_WITHDRAW_TOKEN, construct the tokens to be transferred, the receiving address, and the amount in the data, and the tokens in the SushiXSwap contract can be transferred.

Sushi BentoBox

Sushi BentoBox is a component in the SushiSwap ecosystem. BentoBox is a highly flexible decentralized finance (DeFi) interest rate optimization product. In simple terms, it is a smart contract platform that allows users to store, borrow, and earn interest. The main purpose of BentoBox is to optimize users' returns in the DeFi field.

The BentoBox contract on Ethereum stores a large number of tokens, so is there room for arbitrage in this contract?

In the BentoBox contract, users can make deposits through the deposit function. The implementation of the function is as follows:

It can be seen that the user passes in the specified token address, deduction address, receiving address, amount, and number of shares. The function first performs a series of checks and then converts amount or share. The key point is in lines 195-198, where a check is performed: amount <= _tokenBalanceOf(token).sub(total.elastic).

The balance of a token in the BentoBox contract is recorded using total.elastic, which is similar to the reserve in the Uniswap Pair contract. In some cases, there will be a difference with _tokenBalanceOf(token). We can use the characteristics of the deposit function here to convert the difference into the balance in the BentoBox contract.

Therefore, when we pass in parameters, we set token to the token address with the difference, set the value of amount to the difference, then set from to the address of the BentoBox contract, and set to to our own address. In line 207, since the address is the BentoBox contract address, no transfer will be made. It just balances the values ​​of total.elastic and _tokenBalanceOf(token) and converts it to the balance of the to address in the contract.

DODO

DODO is a decentralized trading platform that uses an original proactive market maker (PMM) algorithm to provide efficient on-chain liquidity for Web3 assets. DODO not only provides its own liquidity, but also aggregates liquidity from other exchanges.

DODO has a series of contracts, in which users will exchange tokens through the DODO V2 Proxy 02 contract. Similar to the Uniswap Router contract, this contract will also have some tokens for various reasons. How should we extract these tokens?

DODO V2 Proxy 02

In the DODO V2 Proxy 02 contract, there is an externalSwap function, which is used to call external platforms aggregated by DODO for exchange, such as 0x and 1inch. The code implementation is as follows:

Lines 1719-1721 verify the parameters passed in, and line 1724 verifies whether fromToken is ETH. If not, the caller's token will be transferred to the contract, and then authorized. After analyzing the code of the DODOAPPROVE contract, it is found that only fromTokenAmount needs to be set to 0 to bypass it:

Then the called external contract will be verified, and only those in the whitelist can be called. The swapTarget and calldataConcat here are controllable by the user, so you can set swapTarget to the contract address of 0x or 1inch, and then set calldataConcat to the code of the view function of its contract, so that the returned value is true and can pass the subsequent require check:

Next, all toToken in the contract will be transferred to the caller. Here, toToken can be ERC 20 tokens or ETH. After sending, the minimum expected amount will be checked. We set the value of minReturnAmount to a very small value to pass. The last two function calls are irrelevant.

Through the above steps, we can extract the ERC 20 tokens and ETH from the DODO V2 Proxy 02 contract.

1inch

1inch is a decentralized exchange (DEX) aggregator that pools liquidity from multiple DEXs in order to provide users with the best token exchange prices. By integrating liquidity from different sources, 1inch helps users optimize transactions and find the best prices across platforms. 1inch's smart contracts automatically trade across decentralized exchanges, allowing users to easily get the best prices and lowest slippage across different exchanges. In addition, 1inch also provides other features such as liquidity mining and governance tokens.

The main contract of 1inch is AggregationRouter. The most commonly used versions are V 4 and V 5. These two contracts will also have some tokens for various reasons. We can extract the tokens in the contract through the parameters in the constructed incoming function.

AggregationRouterV 5

The AggregationRouterV 5 contract has a swap function, which is implemented as follows:

After verifying the minReturnAmount in desc, srcToken and dstToken are obtained from desc. The following lines 986-997 can be bypassed by constructing flags and srcToken in the desc structure:

Then execute the function _execute, where a call is made and the execution status is checked. Since the executor is passed in by the user, we can use the 0 address to bypass it:

Then get the balance of dstToken in the contract. In lines 1007-1018, we can construct flags and minReturnAmount in desc to bypass:

Finally, the dstToken balance in the contract will be transferred to the dstReceiver address, which is also controlled by the user:

Through the above steps, we can construct the parameters passed into the swap function to withdraw the tokens in the AggregationRouterV 5 contract.

AggregationRouterV 4

AggregationRouterV 4 is not much different from AggregationRouterV 5. AggregationRouterV 4 also has a swap function, which is implemented as follows:

It can be found that the implementation of the swap function is the same as that of AggregationRouterV 5, except that AggregationRouterV 5 optimizes the call, so the tokens in the AggregationRouterV 4 contract can be extracted using the same method as AggregationRouterV 5.

Summarize

This article briefly introduces some decentralized exchanges and aggregators, and discusses the possible arbitrage among them. It analyzes the principle of arbitrage from the contract code level. However, whether it can be successful in practice is also related to many factors, such as GAS, node speed, etc.