Categories
Arbitrage Bot DeFi DEX javascript solidity

Trading and Arbitrage on Ethereum DEX: Get the rates (part 1)

In this series of tutorials, we explore how to build solutions around trading and making simple arbitrage bot using Ethereum decentralized exchanges (DEX). This series uses Javascript, Solidity, and the 1inch dex aggregator and flash loans.

As the subject is wide, we have split the series into several parts below:

The purpose of this series is to understand how to build decentralized applications that use DeFi protocols such as DEXes and ERC20 tokens. Following this tutorial might not make you rich but will give you the keys to build powerful applications leveraged by the Ethereum DeFi ecosystem. This guide uses JavaScript. If you’re into Python we recommend checking this one.

Introduction

What is a Decentralized exchange (DEX)?

It is an exchange run by code. It is a place where people can trade cryptocurrencies directly without a middleman. On a DEX every transaction is generally written to the blockchain.

Join the newsletter

Get a weekly summary of what is happening in the Ethereum developer space for free

What is a DEX aggregator?

A DEX aggregator is a platform that will search a set of DEX to find the best price to execute a trade at a given time and volume.

What is an ERC20 token?

An ERC20 is a token living on the Ethereum blockchain. Find how it works in our tutorial.

What is arbitrage?

Arbitrage is basically buying something in one market and simultaneously selling it in another market at a higher price, thereby profiting from the temporary difference in prices.

For this tutorial, we will refer to arbitrage as buying a token from one DEX and selling it at a higher price on another DEX. On the blockchain, the main and oldest arbitrage opportunities are made by trading across decentralized and centralized exchanges.

About 1inch DEX aggregator

1inch exchange is an on-chain decentralized exchange aggregator written by Anton Bukov and Sergej Kunz that gives users the best rates by splitting orders among multiple DEXes in one single transaction. The smart contract used by 1inch is open source and available on Github. We’ll see how to explore trading opportunities using the smart contract. You can access the user interface of 1inchexchange here.

To execute a swap on 1inch, the steps a simple:

  • Get the expected number of tokens you will get in return of your tokens or ETH.
  • Approve the use of your tokens by the exchange
  • make the trade with parameters gathered in the first step

We first need to take a close look at the 1inch exchange smart contract. The two functions that get our interest are:

  • getExpectedReturn()
  • swap()

Get the expected return of a swap

The getExpectedReturn function does not affect the state of the chain and can be called as much as you’d like without any fees as long as you have a node connected to the blockchain. Read this to learn how to call smart contracts functions with Javascript.

It accepts the trade parameters and will return the expected amount of tokens you’ll get in return and how the trade is distributed across DEXes.

function getExpectedReturn(
        IERC20 fromToken,
        IERC20 toToken,
        uint256 amount,
        uint256 parts,
        uint256 disableFlags
) public view
returns(
         uint256 returnAmount,
         uint256[] memory distribution
);

The function accepts 5 parameters:

  • fromToken: is the address of the token you currently own.
  • toToken: is the address of the token you want to swap your tokens for.
  • amount: the number of tokens you’d like to swap
  • parts: is used to describe along which platforms the place is distributed. Check the distribution return value for more details but we’ll use by default 100.
  • disableFlags: enables you to pass option to the function, for example, disabling a specific DEX

The function returns 2 results:

  • returnAmount: The amount of tokens will receive after executing the trade.
  • distribution: An array of uint256 representing how the trade is distributed across the different DEXes. So for example if you pass 100 as parts parameter and the trade is distributed 25% on Kyber and 75% on Uniswap the result will looks like this: [75, 25, 0, 0, …]. You can see the order of the DEXes in the array here.

At the time of writting the supported DEXes and format of the distribution is like this:

[
    "Uniswap",
    "Kyber",
    "Bancor",
    "Oasis",
    "CurveCompound",
    "CurveUsdt",
    "CurveY",
    "Binance",
    "Synthetix",
    "UniswapCompound",
    "UniswapChai",
    "UniswapAave"
]

Note that if you’d like to trade Eth instead od an ERC20 token the fromToken address you should use is: 0x0 or 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE

The return values of the getExpectedReturn function are important as they will be needed to initiate the swap on the blockchain.

Make the swap

For executing the swap of your tokens on-chain, we’ll use the swap function. This function requires you to pass the return value of the getExpectedReturn function and will cost you gas to execute. If you’re swapping ERC20 tokens you also need to approve beforehand the 1plsit contract to be able to manipulate the token you want to swap.

 function swap(
        IERC20 fromToken,
        IERC20 toToken,
        uint256 amount,
        uint256 minReturn,
        uint256[] memory distribution,
        uint256 disableFlags
 ) public payable;

The swap function accepts 6 parameters:

  • fromToken: is the address of the token you currently own.
  • toToken: is the address of the token you want to swap your tokens for.
  • amount: the number of tokens you’d like to swap
  • minReturn: the minimum number of tokens you want in return.
  • distribution: the array of numbers representing which dex should be used in what proportions.
  • parts: is used to describe along which platforms the place is distributed. Check the distribution return value for more details but we’ll use by default 100.
  • disableFlags: enables you to pass option to the function, for example, disabling a specific DEX

Discovering our fist trade

We’ll now try to use what we saw to get the rate for our first automated trade using JavaScript and a smart contract. If you did not read our guide about setting up web3js you should read it.

To make it easy for you, we published the source code on Github so you can easily get the code on your side:

git clone git@github.com:jdourlens/ethereumdevio-dex-tutorial.git && cd ethereumdevio-dex-tutorial/part1

And install the required dependencies web3.js and bignumber.js:

npm install

To execute the code:

node index.js

Note that you might need to change the Ethereum provider address line 15 of index.js according to the one you want to use.

Here is the content of the script explained:

var Web3 = require('web3');
const BigNumber = require('bignumber.js');

const oneSplitABI = require('./abis/onesplit.json');
const onesplitAddress = "0xC586BeF4a0992C495Cf22e1aeEE4E446CECDee0E"; // 1plit contract address on Main net

const fromToken = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // ETHEREUM
const fromTokenDecimals = 18;

const toToken = '0x6b175474e89094c44da98b954eedeac495271d0f'; // DAI Token
const toTokenDecimals = 18;

const amountToExchange = 1

const web3 = new Web3('http://127.0.0.1:8545');

const onesplitContract = new web3.eth.Contract(oneSplitABI, onesplitAddress);

const oneSplitDexes = [
    "Uniswap",
    "Kyber",
    "Bancor",
    "Oasis",
    "CurveCompound",
    "CurveUsdt",
    "CurveY",
    "Binance",
    "Synthetix",
    "UniswapCompound",
    "UniswapChai",
    "UniswapAave"
]


onesplitContract.methods.getExpectedReturn(fromToken, toToken, new BigNumber(amountToExchange).shiftedBy(fromTokenDecimals).toString(), 100, 0).call({ from: '0x9759A6Ac90977b93B58547b4A71c78317f391A28' }, function (error, result) {
    if (error) {
        console.log(error)
        return;
    }
    console.log("Trade From: " + fromToken)
    console.log("Trade To: " + toToken);
    console.log("Trade Amount: " + amountToExchange);
    console.log(new BigNumber(result.returnAmount).shiftedBy(-fromTokenDecimals).toString());
    console.log("Using Dexes:");
    for (let index = 0; index < result.distribution.length; index++) {
        console.log(oneSplitDexes[index] + ": " + result.distribution[index] + "%");
    }
});

Line 4: We load the ABI so we can instanciate a web3.js instance of the 1split contract line 17.

Line 19: This array of DEXes will enable us to describe which DEXes will be used according to the returned distribution array from line 45 to 47.

Line 35: Here we call the getExpectedReturn function to get the result of our trade.

If you’re not familiar with the BigNumber.js library, you should read our article explaining how to deal with uint256 in JavaScript.

The result of executing the script should give you something similar to ours:

At the time of writing, the DEX aggregator offers to buy 148.47 DAI for 1 Ether (While Coinbase rate is at 148,12). The trade is distributed across 2 exchanges: Uniswap 96% and Bancor 4%. 1inch dex aggregator works really well to find the best rates across decentralized exchanges.

Note that this price feed should not be used as an Oracle for your smart contracts as it could be heavily manipulated by having a DEX offering really low price due to a bug or a user error.

Being able to use a DEX aggregator that easily is an awesome feature of DeFi. As most protocols are open, you just have to understand them to use the power of decentralized finance.


Now the we explored how to get exchanges rates betweend two tokens and have all the informations required to make the swap, check the next part where we execute a swap using 1inch DEX aggregator in JavaScript.

15 replies on “Trading and Arbitrage on Ethereum DEX: Get the rates (part 1)”

Hi. Are you going to make an arbitraje bot ? I haven’t programming skills, but quite interest about arbitraging dexs. My tg @OjosdePez

Arbitrage bots are really hard to run without any programming/technical skills. You should hire someone to make something for you but keep in mind that profits are not garanteed.

We add some articles every week. If you like my work and want to support it consider donating: 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE
Thanks

Hi! I get this error. Could you help me please.?

Error: Invalid JSON RPC response: “”
at Object.InvalidResponse (/root/ethereumdevio-dex-tutorial-master/part1/node_modules/web3-core-helpers/src/errors.js:45:16)
at XMLHttpRequest.request.onreadystatechange (/root/ethereumdevio-dex-tutorial-master/part1/node_modules/web3-providers-http/src/index.js:107:32)
at XMLHttpRequestEventTarget.dispatchEvent (/root/ethereumdevio-dex-tutorial-master/part1/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
at XMLHttpRequest._setReadyState (/root/ethereumdevio-dex-tutorial-master/part1/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
at XMLHttpRequest._onHttpRequestError (/root/ethereumdevio-dex-tutorial-master/part1/node_modules/xhr2-cookies/dist/xml-http-request.js:349:14)
at ClientRequest. (/root/ethereumdevio-dex-tutorial-master/part1/node_modules/xhr2-cookies/dist/xml-http-request.js:252:61)
at emitOne (events.js:116:13)
at ClientRequest.emit (events.js:211:7)
at Socket.socketErrorListener (_http_client.js:387:9)
at emitOne (events.js:116:13)
at Socket.emit (events.js:211:7)
at emitErrorNT (internal/streams/destroy.js:64:8)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)

same error, web3 1.2.11, windows 10 OS.
error appears after about 2 mins. Have tried older version of web3 – issue remains.
console.log works fine by providing the json data.

Hello I was able to run getExpectedReturn on Mainnet and was wondering, what is the purpose of this address 0x9759A6Ac90977b93B58547b4A71c78317f391A28

This addressed was used to have an address that already owns a lot of DAI for testing purpose but it looks like it doesn’t anymore.. I need to update the address

Hi Peter, this is awesome work, thank you!

Could you let me know how I would go about adding the new listings from 1inch? Balance, PMM, Pathfinder, etc.
Also, I don’t seem to be getting any feedback from Mooniswap or Kyber – which seem to give best results when I check on the 1inch interface?

Otherwise, works great!

Thanks for this helpful tutorial Peter.

I’ve had the same “Invalid JSON Response” error, and this maybe due to incompatible Web3 and ganach-cli version. A solution for this to remove dependecies from package.json and do fresh npm install for project depedencies and ganache-cli.

Run ganache-cli from project node modules

./node_modules/.bin/ganache-cli -f https://mainnet.infura.io/v3/%5B%5D -d -i 66 –unlock 0x6dcb8492b5de636fd9e0a32413514647d00ef8d0 -l 8000000

Hey can you point me in the right direction to get this working ?

(node:27815) Warning: N-API is an experimental feature and could change at any time.
Error: Invalid JSON RPC response: “”
at Object.InvalidResponse (/home/dextut/ethereumdevio-dex-tutorial/part1/node_modules/web3-core-helpers/src/errors.js:45:16)
at XMLHttpRequest.request.onreadystatechange (/home/dextut/ethereumdevio-dex-tutorial/part1/node_modules/web3-providers-http/src/index.js:107:32)
at XMLHttpRequestEventTarget.dispatchEvent (/home/dextut/ethereumdevio-dex-tutorial/part1/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
at XMLHttpRequest._setReadyState (/home/dextut/ethereumdevio-dex-tutorial/part1/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
at XMLHttpRequest._onHttpRequestError (/home/dextut/ethereumdevio-dex-tutorial/part1/node_modules/xhr2-cookies/dist/xml-http-request.js:349:14)
at ClientRequest. (/home/dextut/ethereumdevio-dex-tutorial/part1/node_modules/xhr2-cookies/dist/xml-http-request.js:252:61)
at emitOne (events.js:116:13)
at ClientRequest.emit (events.js:211:7)
at Socket.socketErrorListener (_http_client.js:387:9)
at emitOne (events.js:116:13)
at Socket.emit (events.js:211:7)
at emitErrorNT (internal/streams/destroy.js:64:8)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)

Leave a Reply