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:
- Get the exchange rates to make an on chain token swap (this one)
- Make the swap using JavaScript and 1inch dex aggregator.
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.
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 [email protected]: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.