Explained: Best DEX Design & UDTswap for Nervos

Matt Quinn
6 min readMay 28, 2020

--

Some History

What would materialize into decentralized liquidity pools like Ethereum’s Uniswap, began with Vitalik taking some ideas from prediction market implementations (like Gnosis or Augur) and a previous on-chain DEX proposal from Nick Johnson. He presented them in this 2016 Reddit post and formalized them in this 2017 blog post (which included a small, very important simplification to the design from Martin Koppelmann).

This mechanism would be known as a “x*y=k” market maker: a fully decentralized on-chain exchange with no price feeds or order book necessary.

In 2017 Hayden Adams began implementing this proposal as Uniswap, initially to learn about Ethereum, Solidity and Web3. Uniswap is an inspiring story about the impact one person can have when supported by a community, highly suggested reading.

In 2017, Bancor raised a $150m ICO to build a “universal decentralized exchange” protocol. Bancor’s flaws were described in this extensive blog entry by Emin Gün Sirer and Phil Daian (IC3), however the permissionless-ness of the “x*y=k” is really what has driven its humble success amidst well-funded competition.

Want to read this story later? Save it in Journal.

As summed up in the story of Uniswap:

It was censorship resistant. No one could stop it.

It was decentralized. No one controlled it.

It was permissionless. Anyone could use it.

It was secure. Anyone could verify execution

The model was formally specified by Yi Zhang, Xiaohong Chen and Daejun Park of Runtime Verification in 2018.

How does it work?

Most people know how a conventional exchange works- a seller puts a certain number of units up for sale at a certain price, and a buyer comes along to purchase them at that price.

“x*y=k” market makers are a very different way of creating an exchange.

“Liquidity providers” deposit their assets into a smart contract, and buyers will then purchase these assets at approximately market price. A small fee is taken on each trade to compensate the liquidity providers.

It’s all accomplished using some simple, but very clever math:

x*y = k

x is number of units of asset x in the pool

y is number of units of asset y in the pool

The product k is called an “invariant” (doesn’t change) and you’ll see that the relationship between x and y (as they are multiplied to reach k) is how pricing of these assets is maintained.

A smart contract enforces that the value of k will not change- the value of x multiplied by y will always have to equal the constant k. This creates a ratio between x and y, which anyone can deposit their own assets into the pool (and withdraw the other asset) to change.

The ratio of x to y is held roughly at the relative market values of the two assets because of rational economic interests of the users interacting with the pool. To demonstrate how it works let’s look at an example:

Depositing assets on-chain

The liquidity pool begins with someone depositing an equivalent value of two different assets.

In our example let’s use assets X and Y. When they are deposited, we’ll say asset X is valued at $10 and asset Y is valued at $5.

The liquidity provider creates a pool with $1000 worth of each asset, depositing 100 units of asset X and 200 units of asset Y. No prices are provided for these assets on-chain, this ratio of number of units of asset X to number of units to asset Y serves as the initial ratio in the pool.

If you asked “why do they have to be equivalent?”, it should be clear soon.

Exchange

After the initial deposit into the pool, the values of x, y and k are 100 * 200 = 20000

In order to trade into the pool, a user deposits a certain amount of asset x or y, and then gets the amount of the other asset that keeps k constant. For example, if a user deposited 50 of X into the pool, some algebra can find us the amount of Y they would receive:

(100+50) * (200–66.667) = 20000

The user depositing 50 of X would receive 66.667 of Y. After this trade, the x, y, and k values of the pool would be 150 * 133.333 = 20000

Pricing

The permissionless nature of the liquidity pool allows any user to take advantage of any imbalances in the ratios of assets in the pool.

Let’s re-initialize that pool from the previous example with 100 of asset X valued at $10 and 200 of asset Y valued at $5. The x,y and k values will be 100 * 200 = 20000

What would happen if asset X depreciated to $7.50 and asset Y appreciated to $7.50? Until a user interacted with the pool, there would be a profit opportunity, the only way the pool discovers price is through user interactions.

If a user deposits 5 of asset X, they’d receive 9.5 of Y, a profit of (4.5*7.50) = $33.75

(100+5) * (200–9.5) = 20000

Naturally the user would want to maximize their profit. There are no fancy strategies here, the rational economic thing to do is re-balance the pool to reflect the current market value of the assets. In this example the asset values are equal, so the square root of 20000 (~141.42) will give optimum values.

If a user deposits 41.42 of asset X, they’d receive 58.58 of Y, a profit of (17.16*7.50) = $128.70

(100+41.42) * (200–58.58) = 20000

What if the user deposits a little more, 42 of asset X? They’d receive 59.15 of Y, a profit of (17.15*7.50) = $128.62

(100+42) * (200–59.15) = 20000

It’s in user’s economic interest to only interact with the pool in ways that are profitable for them. Combining this with the permissionless-ness of the liquidity pool constantly drives the pool toward reflecting the current exchange rate between the two assets. Formal specification of the model can be found here.

UDTswap on Nervos

UDTswap implements the “x*y=k” market maker in a sUDT compatible set of smart contracts on Nervos. It allows users to create new liquidity pools, deposit into liquidity pools and withdraw from liquidity pools. It will pay liquidity providers a 0.3% fee on each transaction for their service.

Nervos uses the cell model instead of the common account model, and this means the implementation must start from the ground-up. You can check out the GrowFi team’s pioneering work here.

There is an interesting advantage to using the cell model: in an account model paradigm, a miner can front-run a user’s trade and the user’s original transaction will still be valid, but this is not the case in the cell model.

This frontrunning can create slippage for the user and must be handled in the x*y=k implementation. However, in the cell model, all transactions are deterministic. If anyone accesses the pool before the user’s transaction is accepted, the user’s transaction will no longer be valid. When a user signs a transaction, they will receive the assets on those terms, or no transaction will be completed.

In addition to this key difference, UDTswap will also support multiple liquidity pools for an asset pair and swaps from multiple pools in a single transaction!

UDTswap is being built by the GrowFi team and is funded by a grant from the Nervos Foundation. To find more information about the implementation and follow the progress of the project, check out the UDTswap grant RFC on the Nervos Talk forum or join the Nervos Discord server today!

📝 Save this story in Journal.

👩‍💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. Read the Noteworthy in Tech newsletter.

--

--