The Oneswap project introduces an on-chain order book based on the Constant Function Market Maker (CFMM) model to improve the Automated Market Maker (AMM) trading experience. Users can place and withdraw orders just like a centralized exchange, and the order matching logic on the chain will match the counterorders in the trading pool and order book alternately according to the user’s placing needs, so as to provide users with the optimal average transaction price.

UniswapV2 comes with the FlashSwap feature, which allows anyone calling any swap on a pair contract to borrow a certain amount of tokens from Uniswap’s trading pool, arbitrage from other contracts, and then return enough tokens to pair. The final realization of nearly zero cost arbitrage behavior.

To help users take advantage of Uniswap’s flash loan feature to arbitrage from OneSwap’s order book and bridge the price gap between Uniswap and OneSwap projects, we provided this arbitrage guide and deployed the required arbitrage contracts. The user only needs to perform a Uniswap swap call whenever he finds an arbitrage space to profit from it.

1. Arbitrage discovery

Assume that the ABC/USDT pair of UniswapV2 is currently in the pool state (100ABC, 40000USDT), and the ABC/USDT pair of Oneswap is in the pool state (100ABC, 43000USDT), and there are other buy and sell orders that cannot be traded with the pool. That is, buy orders not higher than 430=43000/100, and sell orders not lower than 430. At this point, there is a large price difference between Uniswap’s ABC/USDT pair and Oneswap’s ABC/USDT pair, and there is a corresponding arbitrage space.

2. swap

After discovering the existence of arbitrage space, users need to calculate the type and quantity of tokens they need to borrow in the chain.

In this case, the user borrows ABC from UniswapV2’s ABC/USDT trading pool, takes Oneswap’s ABC/USDT trading market and sells part of the USDT back to Uniswap’s ABC/USDT trading pool. The remaining USDT is the arbitrage profit of the transaction. Suppose the user calculates and decides to borrow 10 ABCs from Uniswap’s ABC/USDT trading pool and then calls the swap method of the UniswapV2Pair contract instance with the following parameters:

// In a uniswap ABC/USDT transaction pair, ABC corresponds to token0 and USDT corresponds to token1.Copy the code

Here flashSwapAddr is the address of the deployed arbitrage contract, and data is the parameter passed to the arbitrage contract. See the next section for an introduction to the arbitrage contract.

3. Arbitrage contracts

The arbitrage contract is a pre-deployed implementation of the contract to assist the user in lightning loan arbitrage. It implements the following interface defined by UniswapV2 for UniswapV2Pair to call the external contract during swap:

interface IUniswapV2Callee {
    function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
}
Copy the code

For the average user, all you need to know are the last two parameters passed in when you call the swap method in step 2: the address of the contract, and the data field. Two Boolean values are encoded in the data byte array (token0IsStock, isOnlySwap). Since the oneswap factory contract creates trading pair instances, four trading pair markets can be created for (ABC, USDT) token pairs: (stock=ABC, money=USDT, order book is not supported), (stock=ABC, money=USDT, order book is supported), (stock=USDT, money=ABC, order book is supported), So users need to specify their oneswap target arbitrage market by setting these two Boolean variables.

Once these two inputs are in hand, the arbitrage contract attempts to trade at market value in the target market and calculates whether the resulting token is sufficient to return to UniswapV2’s trading pair. If not, the entire transaction fails, and if not, the number of tokens that need to be returned is returned to UniswapV2’s pair and the remaining tokens are transferred to the user.

In this case, assume that the input given by the user is (true,false) and that the token0 of the UNISWAp ABC/USDT transaction pair is ABC. Arbitrage contracts will attempt to add market orders selling 10 ABCs from the Oneswap trading pair market (Stock =ABC, Money =USDT, support order book). Due to the order book market, it is impossible to estimate the number of USDTs that 10 ABCs can be exchanged for, but as long as the number exceeds the number of USDTs that need to be returned to UniswapV2Pair ABC/USDT contract instances, the user will benefit.

Finally, the source code of arbitrage contract and the sample script of sending swap transaction are attached. The relevant source code is stored in the directory for users’ reference.

Contract source

The arbitrage contract source code is as follows, and the deployment address on the ETH main network is (TODO) :

Pragma solidity 0.6.12; import './libraries/UniswapV2Library.sol'; import './interfaces/IUniswapV2Pair.sol'; import './interfaces/IOneswapPair.sol'; import './interfaces/IERC20.sol'; import './interfaces/IUniswapV2Factory.sol'; import './interfaces/IOneSwapFactory.sol'; import './interfaces/IWETH.sol'; struct PairInfo{ address token0; address token1; address token0ForOneswap; address token1ForOneswap; bool token0IsStock; bool isOnlySwap; } contract FlashSwap {// uniswap factory address public immutable uniswapFactory; // address public immutable oneswapFactory; // address address public immutable weth; event AmountReturn(address,address,uint256); constructor(address _factory, address _oneswapFactory, address _weth) public { uniswapFactory = _factory; oneswapFactory = _oneswapFactory; weth = _weth; } receive() external payable { } function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external{ PairInfo memory pairInfo; pairInfo.token0 = IUniswapV2Pair(msg.sender).token0(); pairInfo.token1 = IUniswapV2Pair(msg.sender).token1(); pairInfo.token0ForOneswap = (pairInfo.token0 == weth) ? address(0) : pairInfo.token0; pairInfo.token1ForOneswap = (pairInfo.token1 == weth) ? address(0) : pairInfo.token1; address uniswapPair = IUniswapV2Factory(uniswapFactory).getPair(pairInfo.token0,pairInfo.token1); {// This call must be initiated by uniswap pair assert(msg.sender == uniswapPair); require(amount0 == 0 || amount1 == 0,'Either amount0 or amount1 should be zero'); // this strategy is unidirectional } address pairAddress; {// The data parameter is used to help the contract find the oneswap pair target for user arbitrage (pairinfo.token0isstock, pairinfo.isonLyswap) = abi.decode(data,(bool,bool)); address stock = pairInfo.token0IsStock ? pairInfo.token0ForOneswap: pairInfo.token1ForOneswap; address money = pairInfo.token0IsStock ? pairInfo.token1ForOneswap: pairInfo.token0ForOneswap; pairAddress = IOneSwapFactory(oneswapFactory).tokensToPair(stock,money,pairInfo.isOnlySwap); require(pairAddress ! = address(0), 'OneSwap Pair does not exist! '); } IOneSwapPair pair = IOneSwapPair(pairAddress); (uint reserve0,uint reserve1,) = IUniswapV2Pair(uniswapPair).getReserves(); If (amount0 > 0) {// if (amount0 > 0) {// If (amount0 > 0) {// If (amount0 > 0) {// If (amount0 > 0) { // If the number of Token1s obtained by selling Token0 in oneswap pair is greater than the minimum number calculated above, // The minimum number of token1s will be returned to uniswap pair and the remaining number of token1s will be transferred to user _safeTransferWETHToETH(pairInfo.token0, pairAddress, amount0); uint amountReceived = pair.addMarketOrder(pairInfo.token0ForOneswap,address(this),uint112(amount0)); uint amountRequired = UniswapV2Library.getAmountIn(amount0, reserve1, reserve0); require(amountReceived > amountRequired,'No profit to earn'); _safeTransferETHToWETH(pairInfo.token1,msg.sender,amountRequired); _safeTransfer(pairInfo.token1, sender,amountReceived - amountRequired); emit AmountReturn(pairInfo.token1, sender, amountReceived - amountRequired); } else {// Add market order to oneswap pair, sell amount1 quantity token1, // If the number of token0s obtained by selling Token1 in oneswap pair is greater than the minimum number calculated above, // The minimum number of token0 will be returned to uniswap pair and the remaining number of token0 will be transferred to user _safeTransferWETHToETH(pairInfo.token1, pairAddress, amount1); uint amountReceived = pair.addMarketOrder(pairInfo.token1ForOneswap,address(this),uint112(amount1)); uint amountRequired = UniswapV2Library.getAmountIn(amount1, reserve0, reserve1); require(amountReceived > amountRequired,'No profit to earn'); _safeTransferETHToWETH(pairInfo.token0,msg.sender,amountRequired); _safeTransfer(pairInfo.token0, sender, amountReceived - amountRequired); emit AmountReturn(pairInfo.token0, sender, amountReceived - amountRequired); } } function _safeTransferWETHToETH(address token, address to, uint amount) internal { if (token == weth){ IWETH(weth).withdraw(amount); _safeTransferETH(to,amount); }else{ require(IERC20(token).transfer(to,amount),'ERC20 transfer failed'); } } function _safeTransferETHToWETH(address token, address to, uint amount) internal { if (token == weth){ IWETH(weth).deposit{value:amount}(); require(IWETH(weth).transfer(to, amount),'WETH transfer failed'); }else{ require(IERC20(token).transfer(to,amount),'ERC20 transfer failed'); } } function _safeTransfer(address token, address to, uint amount) internal { if (token == weth){ _safeTransferETH(to, amount); }else{ require(IERC20(token).transfer(to,amount),'ERC20 transfer failed'); } } function _safeTransferETH(address to, uint value) internal { (bool success,) = to.call{value:value}(new bytes(0)); require(success, 'ETH transfer failed'); }}Copy the code

The Web3 script

The following is an example script for sending a swap transaction:

const uniswapPair = artifacts.require('UniSwapV2Pair');

// To use the IP address, replace it with the corresponding contract address on the eth main network
const flashSwapAddr = '0x82c102D3f260216F8E57C161A8f6B9996Ca5B6d2';
const uniswapPairAddr = '0x5F6E9Ce32ab4821cB62B315361E7307Fb80EDc2a';

async function flashSwap(){
    pair = await uniswapPair.at(uniswapPairAddr);
    bytes = web3.eth.abi.encodeParameters(['bool'.'bool'], [true.false]);
    result = await pair.swap(100.0,flashSwapAddr,bytes);
    console.log(result.logs);
}

async function exit(){
    process.exit(1);
}

module.exports = async function(){
    await flashSwap();
    await exit();
}

Copy the code

OneSwap Series 2 – How to Configure Smart Contracts

OneSwap Chinese community