What is the 0x project?

As 0x project’s whitepaper described “0x is an open protocol for decentralized exchange on the Ethereum blockchain.” In simple terms, 0x defines all the rules which an exchange need’s to perform trade functionality. Anyone can build a decentralized exchange using 0x protocol. Linda Xie wrote an awesome blog about 0x protocol which you can read here.

What is decentralize exchange (DeX)?

The main property of a decentralize exchange is giving custodianship to user/traders of their own asset. Unlike a centralized crypto exchange (ex — coinbase, binance) where the exchange stores a user’s private key, in decentralized exchange, private keys always remain with the the users, hence the user is always in control of his own asset. Isn’t it amazing?

What Is A Relayer?

A Relayer hosts an off-chain order book. Using relayers, users can find, create, fill or cancel orders. Relayers helps traders discover counter-parties and move cryptographically orders between them. A relayer can talk to other relayers and create a pool of orders to increase liquidity.

How To Build A Relayer?

I’ve been following the 0x protocol since last year and they have one of the best developer wikis out there!

0x makes it so just about anyone can build a decentralized exchange and earn money. If an entire exchange is too much, you can start with just building a relayer. Today decentralized exchanges are in their infancy stage, but improved tech and UX will attract more people towards DeX in future. Now let’s start building our relayer.

Prerequisite-

Basic understanding of smart contracts.

Basic Understanding of node js

Basic Understanding of ganache

Basic understanding of exchange terminology

Installation

Clone the 0x starter project from GitHub.

git clone https://github.com/0xProject/0x-starter-project.git

Install all the dependencies (we use Yarn: brew install yarn --without-node ):

yarn

Pull the latest Ganache 0x snapshot with all the 0x contracts pre-deployed:

yarn download_snapshot

In a separate terminal, navigate to the project directory and start Ganache:

yarn ganache-cli

Create, validate and fill an order

In this tutorial, we will perform three operations.

Create an order

Validate it

Fill it using ganache

Now we will understand how to create an order, validate it and fill it using 0x-starter-project.

Imports

We will import different utilities which will help us throughout this tutorial. We will also import configuration files. For now, we are using ganache with already 0x smart contracts are deployed privately.



assetDataUtils,

BigNumber,

ContractWrappers,

generatePseudoRandomSalt,

Order,

orderHashUtils,

signatureUtils,

} from '0x.js';

import { Web3Wrapper } from ' import {assetDataUtils,BigNumber,ContractWrappers,generatePseudoRandomSalt,Order,orderHashUtils,signatureUtils,} from '0x.js';import { Web3Wrapper } from ' @0x/web3-wrapper '; import { NETWORK_CONFIGS, TX_DEFAULTS } from '../configs';

import { DECIMALS, NULL_ADDRESS, ZERO } from '../constants';

import { getContractAddressesForNetwork, getContractWrappersConfig } from '../contracts';

import { PrintUtils } from '../print_utils';

import { providerEngine } from '../provider_engine';

import { getRandomFutureDateInSeconds } from '../utils';

You can find all these files the 0x-starter-project. Go check out them, look at configurations and settings.

Provider and constructor

We need to initialize contractWrappers. This provides helper functions around 0x contracts as well as ERC20/ERC721 token contracts on the blockchain. It takes provideEngine and network configuration of ganache, which will be running by default on http://localhost:8545.

const contractWrappers = new ContractWrappers(providerEngine, getContractWrappersConfig(NETWORK_CONFIGS.networkId));

Web3Provider - Web3 provider allows your application to communicate with an Ethereum Node. A provider can be any module or class instance that implements the sendAsync method (simply send in web3 V1.0 Beta). That's it. What this sendAsync method does is take JSON RPC payload requests and handles them. Web3.js is an example of a javascript module that contains a Web3 HTTP Provider. Using a configured Web3 Provider, the application can request signatures, estimate gas and gas price, and submit transactions to an Ethereum node. The simplest example of a provider is:

const provider = new web3.providers.HttpProvider('http://localhost:8545');

This provider simply takes each JSON RPC payload it receives and sends it on to the Ethereum node running on port 8545.¹

Initialize web3wrapper and get address information

Initialize the Web3Wrapper using provideEngine , this provides helper functions around fetching account information.

const web3Wrapper = new Web3Wrapper(providerEngine);

const [maker, taker] = await web3Wrapper.getAvailableAddressesAsync();

Initializing address

For this tutorial, we are trading ZRX token with WETH (wrapped eth, erc20 compatible version of eth, eth itself is not ERC20 compatible token, Eth and weth has 1:1 exchange ratio). For that, we need to have the contract address of our 0x protocol to execute orders and ZRX token and WETH token’s ERC20 contract.

const contractAddresses = getContractAddressesForNetwork(NETWORK_CONFIGS.networkId); const zrxTokenAddress = contractAddresses.zrxToken; const etherTokenAddress = contractAddresses.etherToken;

Encoding Assets

For this tutorial, maker (seller/order creator) is creating an order of 5 ZRX tokens in exchange of .1 weth. We also need to take care of decimal points as different tokens can use different decimal points. In our case both weth/eth and zrx has 18 decimal points. 0x v2 uses Ethereum ABI encoding scheme to encode asset data into hex strings to encode all the information needed to identify an asset. That’s what we are doing here.

const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(5), DECIMALS); const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(0.1), DECIMALS); const makerAssetData = assetDataUtils.encodeERC20AssetData(zrxTokenAddress); const takerAssetData = assetDataUtils.encodeERC20AssetData(etherTokenAddress);

Allowance for transacting assets

We need to allow 0x contract to move assets on the behalf or maker and taker. As we are transacting ETH so first we need to convert ETH into WETH by depositing ETH on WETH contract.

const makerZRXApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(

zrxTokenAddress,

maker,

); const takerWETHApprovalTxHash = await contractWrappers.erc20Token.setUnlimitedProxyAllowanceAsync(

etherTokenAddress,

taker,

); const takerWETHDepositTxHash = await contractWrappers.etherToken.depositAsync(

etherTokenAddress,

takerAssetAmount,

taker,

);

Creating an Order

0x protocol has below specific parameters which are needed to create an order. We will set these parameters and will create our order. We will use NULL_ADDRESS allowing anyone to fill our order.

exchangeAddress : The Exchange address.

: The Exchange address. makerAddress : Ethereum address of our Maker .

: Ethereum address of our . takerAddress : Ethereum address of our Taker .

: Ethereum address of our . senderAddress : Ethereum address of a required Sender (none for now).

: Ethereum address of a required (none for now). feeRecipientAddress : Ethereum address of our Relayer (none for now).

: Ethereum address of our (none for now). expirationTimeSeconds : When will the order expire (in unix time).

: When will the order expire (in unix time). salt : Random number to make the order (and therefore its hash) unique.

: Random number to make the order (and therefore its hash) unique. makerAssetAmount : The amount of token the Maker is offering.

: The amount of token the is offering. takerAssetAmount : The amount of token the Maker is requesting from the Taker .

: The amount of token the is requesting from the . makerAssetData : The token address the Maker is offering.

: The token address the is offering. takerAssetData : The token address the Maker is requesting from the Taker .

: The token address the is requesting from the . makerFee : How many ZRX the Maker will pay as a fee to the Relayer .

: How many ZRX the will pay as a fee to the . takerFee : How many ZRX the Taker will pay as a fee to the Relayer.

const randomExpiration = getRandomFutureDateInSeconds();const exchangeAddress = contractAddresses.exchange; const order: Order = {

exchangeAddress,

makerAddress: maker,

takerAddress: NULL_ADDRESS,

senderAddress: NULL_ADDRESS,

feeRecipientAddress: NULL_ADDRESS,

expirationTimeSeconds: randomExpiration,

salt: generatePseudoRandomSalt(),

makerAssetAmount,

takerAssetAmount,

makerAssetData,

takerAssetData,

makerFee: ZERO,

takerFee: ZERO,

};

Signing Order

Now we need to sign the order with maker’s private key so we can prove that specificed maker address is owned by maker. We will append this signature to our order. To do that we will perform these steps:

Generate a hash from our order.

Sign generated the hash using maker’s private key

append signature with the order

const orderHashHex = orderHashUtils.getOrderHashHex(order); const signature = await signatureUtils.ecSignHashAsync(providerEngine, orderHashHex, maker); const signedOrder = { ...order, signature };

Validating Order

We need to validate the order is Fillable before calling fillOrder This checks both the maker and taker balances and allowances to ensure it is fillable up to takerAssetAmount. Also if there is any order manipulation by anyone, we will also get to know by validating our order.

await contractWrappers.exchange.validateFillOrderThrowIfInvalidAsync

(signedOrder, takerAssetAmount, taker);

Filling Order

Now if everything worked fine then we can validate our order now using 0x protocol. We will also set the gas limit for executing our order.

txHash = await contractWrappers.exchange.fillOrderAsync(signedOrder, takerAssetAmount, taker, {

gasLimit: TX_DEFAULTS.gas,

});

That’s it. We have filled our order.

yarn scenario:fill_order_erc20

You will see output something like below. If you see the fill_order_erc20.ts . The above code is available in the same file and it is using printUtils to print the output below.

yarn run v1.10.1

$ cross-env yarn build && node ./lib/scenarios/fill_order_erc20.js

$ cross-env yarn pre_build && tsc

$ run-s copy_artifacts

$ copyfiles -u 1 './src/artifacts/**/*' ./lib

┌────────────┐

│ Fill Order │

└────────────┘

Accounts

Maker 0x5409ed021d9299bf6814279a6a1411a7e866a631

Taker 0x6ecbe1db9ef729cbe972c83fb886247691fb6beb

Setup

Maker ZRX Approval 0xffa485e507252a439be2677a977e791d9b5ba829251481a3b43479326f55da45

Taker WETH Approval 0x45cdef330d7d0a97cb058a8ba7b25735ee548aabcf00787d178e1fe3fd41200b

Taker WETH Deposit 0xd57ac57521b8040f2a8cf8ee6d1109bd55c283ca4b818d0ca58599ba58625e7d

Order

exchangeAddress 0x48bacb9266a570d521063ef5dd96e61686dbe788

makerAddress 0x5409ed021d9299bf6814279a6a1411a7e866a631

takerAddress 0x0000000000000000000000000000000000000000

senderAddress 0x0000000000000000000000000000000000000000

feeRecipientAddress 0x0000000000000000000000000000000000000000

expirationTimeSeconds 1540250008

salt 1030945504406868252934488830320408688272183043132497204228070027505852506…

makerAssetAmount 5000000000000000000

takerAssetAmount 100000000000000000

makerAssetData 0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c

takerAssetData 0xf47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082

makerFee 0

takerFee 0 Allowances

Token Maker Taker

WETH 0 MAX_UINT

ZRX MAX_UINT 0 Balances

Token Maker Taker

WETH 0.5 0.1

ZRX 999999980 20 Transaction

┌───────────┬────────────────────────────────────────────────────────────────────┐

│ fillOrder │ 0x3dbe6243a61507aff8f60b88585baa2781bfc959c19334dd953c45a90cbadb28 │

├───────────┼────────────────────────────────────────────────────────────────────┤

│ orderHash │ 0x4fce1fd1a979119fdc4dc09a199a3bc3284bfabde3c455b24d222110dad2425c │

├───────────┼────────────────────────────────────────────────────────────────────┤

│ gasUsed │ 111748 │

├───────────┼────────────────────────────────────────────────────────────────────┤

│ status │ Success │

└───────────┴────────────────────────────────────────────────────────────────────┘ Logs

Fill

contract 0x48bacb9266a570d521063ef5dd96e61686dbe788

makerAddress 0x5409ed021d9299bf6814279a6a1411a7e866a631

feeRecipientAddress 0x0000000000000000000000000000000000000000

takerAddress 0x6ecbe1db9ef729cbe972c83fb886247691fb6beb

senderAddress 0x6ecbe1db9ef729cbe972c83fb886247691fb6beb

makerAssetFilledAmount 5000000000000000000

takerAssetFilledAmount 100000000000000000

makerFeePaid 0

takerFeePaid 0

orderHash 0x4fce1fd1a979119fdc4dc09a199a3bc3284bfabde3c455b24d222110dad2425c

makerAssetData 0xf47261b0000000000000000000000000871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c

takerAssetData 0xf47261b00000000000000000000000000b1ba0af832d7c05fd64161e0db78e85978e8082

Transfer

contract 0x871dd7c2b4b25e1aa18728e9d5f2af4c4e431f5c

_from 0x5409ed021d9299bf6814279a6a1411a7e866a631

_to 0x6ecbe1db9ef729cbe972c83fb886247691fb6beb

_value 5000000000000000000

Transfer

contract 0x0b1ba0af832d7c05fd64161e0db78e85978e8082

_from 0x6ecbe1db9ef729cbe972c83fb886247691fb6beb

_to 0x5409ed021d9299bf6814279a6a1411a7e866a631

_value 100000000000000000 Balances

Token Maker Taker

WETH 0.6 0

ZRX 999999975 25

Done in 8.25s.

Conclusion

So, we have learned how we can create and fill orders using 0x protocol.

We are using ganache but we can submit and fill the order to a relayer using 0x/connect (which is a wrapper of 0x protocol’s Standard Relayer API) We will check this in future tutorials. Check out the Crowdbotics blog for more on this topic.

Next, we’ll discuss different order book strategies. If you have any doubt you can refer to 0x project docs. They have awesome documentation.

Please comment below, if you have more questions about building your own decentralize exchange or relayer.

Thanks!

Starting a new blockchain project, or looking for a Solidity developer?

Crowdbotics helps business build cool things with Solidity (among other things). If you have a blockchain project where you need additional developer resources, drop us a line. Crowbotics can help you estimate build time for given product and feature specs, and provide specialized Solidity developers as you need them. If you’re building with Solidity, check out Crowdbotics.