Decentralized exchanges are the foundation of any DeFi ecosystem. In collaboration with the WingRiders team, we talked about how such an exchange can be created on the Cardano platform. In this article, we look at how applications are built on Cardano in general and the challenges the WingRiders team faces. It is interesting to see which options the team has and which design decisions were made.
Centralized exchanges (CEXes) still dominate trading with digital assets. These hold control over private keys, exchange trading parameters, user information, fund security, fees, and exchange ratio. These exchanges do not offer a secure passive income for coin or token owners.
Traditional centralized exchanges use the so-called order book model. Traders can submit their buy or sell orders and a central market maker matches orders. During the matching of buy and sell orders, a specific matching strategy must be followed. Once a match is found, trade between the two participants can be executed. This is a very basic concept of exchanging two assets between users.
Exchanges using an order book can offer two types of orders. A market order allows a user to sell tokens A for the desired amount of tokens B. The user must take into account the current market values of the tokens. The user expects the market order to be executed almost immediately. The second type is a limit order where the user sells tokens A for the desired amount of tokens B but defines the exact price at which the trade is to be executed. The limit order can thus be pending for a longer period of time until market conditions approach the user’s expectations.
Decentralized Finance (DeFi) has given the world a different perspective on trading and brings demonstrably better conditions for users. Decentralized exchanges (DEXes) offer a non-custodial solution, fair treatment, lower fees, anonymity, global availability for everybody, decentralized and automated processing of orders, and opportunities to take advantage of passive income. A completely fundamental innovation is the Automated Market Maker (AMM) model.
Exchanges based on AMM model provide automated trading of assets through decentralized execution of smart contracts. User swap requests are executed completely automatically and there is no need to rely on a trusted third party. The AMM model employs so-called liquidity pools. You can think of a liquidity pool as a collection of two kinds of tokens. For example, a collection of tokens A and another collection of tokens B make one liquidity pool. AMM DEXes do not require the buyer and seller matching (no orders matching). Instead of that, users can simply exchange their tokens and assets using liquidity pools. Thus, swap requests can be fulfilled basically immediately.
Algorithms set the price of tokens based on a changing ratio in liquidity pools. A user swap request is always directed to a given pool containing identical tokens. A typical AMM exchange has multiple liquidity pools. For example, if it allows trading in tokens A, B, and C, it can have 3 pools. There will be 3 pools with the following pairs: A and B, A and C, B and C.
In order to be able to execute trades on the AMM exchange, it is necessary to have enough tokens in the pools. In other words, pools need sufficient liquidity. Token holders can provide this liquidity to the pools and thus become so-called liquidity providers (LPs). Liquidity providers typically deposit both types of tokens for a given pool. This means, for example, a liquidity provider needs to deposit ADA coins worth $100 and A tokens also worth $100 for a pool with the pair ADA and token A. Liquidity providers are rewarded for providing tokens. The reward is proportional to the liquidity provided and is taken from the fees for executed trades.
As a member of the WingRiders team explains, AMM is a modern approach to trading in a decentralized world and has become the new standard. It is the most convenient way to exchange crypto tokens. With the increased adoption of cryptocurrencies, we can expect users to routinely hold many types of coins and tokens in their wallets, including Bitcoin and Ethereum. These tokens will have some value, and users can profit if they provide the tokens to an AMM exchange. There is a wonderful symbiosis between traders and liquidity providers, with both groups benefiting from the existence of the service. That is why the WingRiders DEX will be based on the AMM model.
Before we look at some of the details of the WingRiders design, it’s important to understand the differences between the Cardano ecosystem and Ethereum. Many teams have tried to take a DEX that has been already running on Ethereum and copy the design in order to have quickly built DEX in the Cardano ecosystem. These teams soon realized that this was the wrong way to go and that they needed to think more about the design.
The VacuumLabs team powers the development of WingRiders DEX. The team has many years of experience with the ecosystem of Cardano and has collaborated with the IOG team. The team was able to avoid many mistakes from the very beginning and designed DEX correctly on the first try.
Let’s explain the differences between Cardano and Ethereum designs to better understand the challenges that teams face. This makes you understand why you can’t just copy a concept from the Ethereum ecosystem.
Each blockchain network is trying to reach a network consensus over changes in ownership of coins and tokens. In other words, each new block updates the state. In the blockchain industry, there are two widespread ways to work with coins. Each way has its benefits and costs. The first is Unspent Transaction Output (UTXO), which uses Bitcoin. The second is the Account-based model, which uses Ethereum, but also other projects like Solana, Polkadot, Algorand, etc. The IOG team has implemented an extended version of UTXO called Extended UTXO (EUTXO). EUTXO facilitates the use of smart contracts. In this article, we will continue to use the term UTXO, but in the context of Cardano, it is EUTXO.
Ethereum’s account-based model is similar to a bank account. Coins are stored on blockchain addresses and represent the current balances. Every transaction adjusts balances. When a user sends a given number of coins to another user, the account of the sender is decremented and the account of the recipient is incremented. It is an atomic operation.
Ethereum maintains a global shared state across all participants. Each new transaction changes the global state. During every single change, the global state is locked. It is important to understand that transactions depend on each other and their order in the block matters. The outcome of transactions has higher uncertainty and it is not deterministic. To validate a block, it is necessary to know the order of transactions. The validation of transactions must be done step by step. As a result, parallelization is very difficult since every transaction in a new block must be verified in the context of previous transactions and made changes.
Changing the global state is also possible by executing smart contracts, which can modify the balances. Ethereum’s design has the advantage for application developers of not having to worry about concurrency. Developers can freely work with user accounts and change the balance. Because it is ensured that balances are accessed individually, there is less risk of two agents accessing the same balance at the same time. Thus, it is relatively easy to design an application. The disadvantages are the aforementioned complexity of parallelization, frequently failed transactions, and having to pay a fee even for transactions that fail. Moreover, due to the ordering of transactions, there is an issue known as Miner Extractable Value (MEV).
Cardano’s EUTXO differs significantly from Ethereum’s account-based model. You can think of UTXOs as banknotes. If you imagine your physical wallet as a blockchain address, you could have more UTXOs in it (more banknotes). If a user wanted to know the total number of coins at an address, it would be needed to sum up the value of all UTXOs. Spending UTXO works the same way as with banknotes. If the user has a UTXO with a value of 100 ADA and she needs to pay 20 ADA, then 20 ADA will leave the address (and will be added to the recipient address) and 80 ADA will come back to the sender address. As a result, there will be two UTXOs (we neglected fees).
From the ledger perspective, a Cardano transaction has inputs and outputs, where the inputs are unspent outputs from previous transactions. Notice that we are still talking about UTXO. Assets are stored on the ledger in unspent outputs, rather than in accounts (balances). Remember that each UTXO can only be consumed once and as a whole.
Transaction validation is much simpler in the Cardano network. In the UTXO model, only the local state is important. It means that the outcome of the transaction depends only on the use of UTXOs which are immutable, single-use objects that act as the input and output of transactions. Thus, transactions are independent of each other and there is nothing like the global state.
The big advantage is that the validity of a transaction can be checked off-chain before the transaction is sent to the blockchain. If the transaction passes the local validation (e.g in the user’s wallet), there is a high probability that on-chain validation succeeds as well and the transaction makes it into the block. A node could, in principle, validate transactions in parallel, if those transactions do not try to consume the same input.
The high level of determinism and parallelization possibilities are advantages of the Cardano design. On the other hand, as the member of the WingRiders team explains, developers have to deal with concurrency themselves. Each UTXO can be consumed only once and it must be validated by the whole network. It technically means that UTXO can be consumed once per a given block. Transactions can run into contention if they depend on the same UTXO at the same time. If some funds were locked by a smart contract only a single actor can interact with it inside one block. As you can see, Cardano and Ethereum are two completely different worlds.
We asked the WingRiders team what options they have for building DEX and how they work with smart contracts on Cardano.
In some way, Cardano is somewhat analogous to bitcoins’ pay-to-script hash functionality, where the transaction is validated by a script. Unlike Bitcoin, in the case of Cardano, you can attach additional data (Datum) to EUTXO. Datum is arbitrary JSON-like data. Coins are represented by a triplet: address, value, and optionally a Datum. The address can be derived from a public key, or it can be the address of a script. In the case of the script address, the UTXO can only be spent if the validation defined in the associated script passes.
Cardano allows you to write program logic in two distinct parts. Firstly, there is an on-chain part that allows writing a validation script. Scripts are validated by Cardano nodes so this part ensures the guarantees offered by traditional smart contracts. Secondly, it is an off-chain part that allows creating a business logic for building transactions in accordance with the associated on-chain scripts. Both on-chain and off-chain logic can be written in Haskell. However, developers can use any language for the off-chain part.
On-chain and off-chain parts are not necessarily dependent on each other and do not form a single unit. Validator scripts can be created by the off-chain parts and define contract constraints. So, a UTXO can be spent by a transaction that is not necessarily created by the off-chain part. If the conditions in the validation script are satisfied then the UTXO is spent. As the member of the WingRiders team explains, the business logic could even be implemented as a part of software wallets or tightly coupled with wallets on the front-end.
During the execution, a UTXO validation script operates with Datum, Redeemer, and context that includes transaction data. As we said, the Datum is attached to UTXO. Redeemer is a user-specific argument that is provided in the transaction which was created to spend the UTXO. In general, the redeemer can serve different purposes. It can be viewed as a user intention (the transaction creator) on how to spend the UTXO.
The first DEXes appeared on Ethereum. In the beginning, liquidity was low on decentralized exchanges. It was not a good situation for classic order-book exchanges. Eventually, the idea of introducing liquidity pools became popular. The account-based model is well suited for this, as liquidity pools can essentially be just balances. Ethereum allows these balances to be changed arbitrarily within a block. Uniswap benefits from the success of the AMM concept.
We asked the WingRiders team how to design DEX on Cardano and which challenges they needed to overcome.
In Uniswap, actors interact with the liquidity pool atomically swapping tokens and updating the liquidity pool balances. This simple design cannot be copied and applied to Cardano. A liquidity pool is essentially a shared resource, as multiple traders try to use it at the same time. If the liquidity pool was represented as a single UTXO in Cardano (a naive way to create something like Ethereum balance), only a single swap could be executed per block. Any other attempts would fail. After every swap, a new liquidity pool UTXO would have to be created with the updated balance.
How to make a swap on the UTXO-based ledger? It is necessary to have a set of UTXOs available from which tokens can be taken and another set of UTXOs into which tokens can be inserted. The swap could be implemented in such a way that individual transactions would consume UTXOs from one set and insert UTXOs on the other side of the set. The swap is realized via the exchange of selected UTXOs. Users have to provide UTXOs with one type of token (that will stay in the liquidity pool) to receive UTXOs with the second type of token.
At this point, as we can see, we encounter a challenge for DEX designers on Cardano. They have to come up with some business logic to ensure that individual actors don’t try to use the same UTXO in the pool within a block. This is tricky because the transactions are independent of each other. Without some coordination or clever algorithm, users may try to use the same UTXO, but only one can succeed. The goal for designers is obvious. How to achieve many interactions with a single liquidity pool by different users in one block.
The member of the WingRiders team continues explaining. We basically see two strategies to tackle concurrency in Cardano: brute-force and bulk transactions. The brute-force strategy has limited scalability and usage scope. As transactions increased across the platform, the concurrency issue would become an extreme bottleneck. The team decided to use the second strategy: bulk transactions.
The bulk transactions strategy splits the interaction with smart contracts into two phases. In the first phase, the user needs to create a swap request. When the user signs the transaction, the UTXOs are locked at the script address, including the conditions under which the UTXOs can be spent. In the second phase, it is the agents’ turn (an agent is also called a batcher). Agents search for suitable request UTXOs that want to interact with a particular liquidity pool in order to fulfill the requested swap. It is necessary to satisfy the spending conditions. Agents do that with all other available swap requests and insert all the swaps into one big batch transaction. When creating a batch transaction, agents know what UTXOs have already been used. They can ensure that a particular UTXO is not used twice within a batch transaction. Such a transaction would fail, respectively it would be refused by the network as a single UTXO can be spent only once. Once the batch transactions are confirmed by the network, users have swapped their tokens. A big advantage is intuitive user experience as users are not competing with each other when creating swap requests, meaning no concurrency issue and swifter experience.
During the first phase, the conditions under which the swap can be canceled are also defined. The user can never lose the funds locked in the contract. The swap either takes place or it does not take place and the user gets his funds back.
Let’s explore the batching model in more detail. Firstly, users need to create swap requests (containing UTXOs) that must be submitted, settled, and confirmed. It can take approximately 5–30 seconds. Secondly, agents can take into consideration and collect new swap requests (transactions) as soon as they appear in the ledger. Agents create large batch transactions and effectively carry out many swaps at once using liquidity pools.
Notice that the off-chain part of the DEX functionality is any code creating DEX transactions. Users utilize off-chain code when they create swap requests. Agents utilize off-chain code when they create batch transactions.
These batch transactions are large because of the included scripts and a large number of inputs and outputs. The agent’s goal is to maximize efficiency and include as many swap requests as possible. The more requests the agent can fulfill in a single transaction, the less load on the blockchain, lower overall fees, and faster-perceived user experience.
Batch transactions must be also submitted and validated. The confirmation can take another 5–30 seconds. The total perceived time after creating a request to the point where swapped tokens arrive into your wallet could be 15–60 seconds, but in reality, most of it is waiting for confirmations. Anything over this could mean that the network might be congested or that agents are collecting fewer requests to reduce blockchain congestion.
It is still a challenge to design the batching model well. If a DEX has a high number of requests against a single liquidity pool, the batches need to be sequenced between blocks. This is likely to cause congestion when interacting with a single liquidity pool and swap wait times will be very long. This is what we could actually see in recent DEX-related issues that were discussed on Twitter.
Let’s see what can be changed and improved. The transaction size can be reduced. This doesn’t entirely solve the issue, but it allows inserting more transactions into a block. Similarly, smart-contract resource usage can be reduced as well. The more optimal implementation for the smart contract, the more smart-contract transactions can be inserted into the block.
Working on optimizations is a goal of every development team and a DEX is not an exception. As volume can go up fast, having well-optimized implementation is crucial. Luckily, the WingRiders team has very experienced Haskell developers. Focus on the optimizations is the priority for the team from the inception to main-net.
Having more agents and liquidity pools is usually a function of total users. A popular DEX attracts more users. It means that the DEX is more attractive for liquidity providers. Having a large number of users allows creating more liquidity pools with the same pair. Thus, swap requests can be better distributed to pools. Having more agents is difficult. WingRiders is built from the ground up to provide the opportunity for any user to become an agent. It’s a complicated engineering problem, but the team understands it pretty well, and we already run a proof of concept in a browser.
Swap speed is a very important factor for users. One of the advantages of WingRiders DEX will be the speed of swaps. Notice, that the perceived dApp speed is dependent on many factors, both internal and external. To get a handle on this, let’s break it down and put it back together. Every dApp runs in a distributed network setting and has its own properties and features. The dApp doesn’t exist in a vacuum and there could be many other dApps running in parallel. To be able to provide users with a smooth UX experience, dApps also use infrastructure that mirrors the blockchain state or even operate some business logic or processes. The infrastructure also introduces delays. These are all external factors.
A block time is a property of the blockchain and it is the external factor. Currently, Cardano has set the value at 20 seconds. Swap speed is partly impacted by the block time, but there are many parameters that may have an impact on the overall network throughput (the number of processed user requests). If transactions do not fit in the block, they must wait longer for inclusion. Some of the Cardano parameters are currently set conservatively. Other important parameters influencing the dApp speed are block size, the maximum transaction size limit, Plutus script memory units per transaction, etc.
The IOG team already showed a willingness to increase the parameters. Each parameter change is carefully considered and then observed by the team. Further updates are planned based on the results of the observations. The IOG team tries to meet the requirements of users and developers as far as possible.
At the time of writing the article, a block can contain around 4 max-size transactions or about 250 tiny transactions. In reality, there is a mix of both in blocks. It is roughly 12 — ~800 transactions per minute. If all transactions in a block would be script ones, that would narrow the range down to around 12–72 transactions per minute for generic dApps. Realistically, for transactions with larger scripts, it would be around 20 dApp transactions per minute.
In a typical block, there may be a few large transactions plus a larger number of smaller ones. With higher network utilization, the maximum space in the block is ideally occupied. If there were always a lot of large (script) transactions in a block and the small ones were not getting through, this could lead to more clogging.
Take into account that these numbers are slightly pessimistic approximations as smart contracts on Cardano are still in the early stage. Many improvements have been already proposed. Optimizations can be done on the side of dApps development and the WingRiders team constantly improves the design.
The number of processed swap requests depends on the transaction size, memory and cpu usage of the smart contracts. This is where the internal factors of the dApp design, concurrency solution, and tradeoffs become evident.
The WingRiders team made a test on the public Cardano test-net. In 10 blocks, agents fulfilled 175 swap requests. If the blocks were created every 20 seconds, the result would correspond to around 55 requests fulfilled per minute. All this with other dApps being present during testing. To put it into perspective, according to recent transaction numbers, Uniswap v3 is closer to 20–40 operations per minute.
Users submit their swap requests in a given order. Agents are able to find these requests in the ledger and fulfill them. WingRiders guarantees users that the order of submitted swaps will be respected by agents. The ordering is enforced to ensure fairness in the first-come-first-served principle.
Agents are expected to fairly find the oldest swap requests for a given liquidity pool. Once swap requests are selected, respecting the transaction size limit and script execution units, the batch transaction is created. Notice, that ordering even within these batch transactions is crucial. Imagine the following very simple batch transaction that contains only 2 swap orders:
Alice swaps 10,000,000 BTC to ADA which causes huge slippage
Brian swaps 10 ADA to BTC
When Brian swaps his 10 ADA after Alice’s swap was fulfilled, he will get much more BTC due to the slippage caused by Alice. However, when Brian’s swap is fulfilled before Alice’s swap, he would get much less, but it wouldn’t hugely impact Alice. Notice how the order is important in regard to users’ satisfaction and fairness.
Do you remember that there is nothing like a global state on the Cardano network, and the order of transactions does not matter? So, it is also true for the abilities of smart contracts. Developers have to take it into account during designing the logic of applications.
A transaction builder (off-chain part implemented in the agent’s code) knows the order in which the swaps need to be fulfilled. But even if the agent knows the correct order, the situation is not so simple. By design, similarly to transactions in a block, the order of inputs in a transaction has no particular meaning.
To follow WingRider’s design and ensure the promised fairness, there must be a mechanism in place to manage the correct order of inputs. To ensure that, indices inserted into Redeemers are employed. Putting a particular input first in a transaction is not enough and the inputs are sorted into a different ordering before the transaction can be read by the validator. That means that without the additional indices in Redeemers, the validator can’t know which input (which request) to carry out first and which to carry out second.
So, the transaction builder inserts Alice’s input first into the transaction and assigns the redeemer to index 0. It signals that it should be carried out first. Analogically, Brian’s Redeemer would be set to index 1. The indices help the validator to find out which transaction input (swap request) to carry out first and which to carry out second.
Without the described additional logic with indexes, the validators wouldn’t be able to ensure that the swap requests are really fulfilled in the correct order. To explain this in other words, it is necessary to create extra logic to maintain the desired order, as Cardano cannot provide this on the level of transactions and their inputs.
There is a reason why WingRiders wants to ensure that all swaps are fulfilled in the order they were created by the users. Besides fairness, it is also necessary to verify that all users have been compensated correctly. If not, the validation would fail. The team doesn’t want to compromise on the precision of the calculations inside the smart contracts just because the contracts cannot rely on the input order. We have designed a method that allows agents to ensure ordering even inside the transactions.
Every service needs users. This is especially important for DEX, because the higher the number of users and the higher the liquidity, the better the service becomes. WingRiders is an AMM exchange, so it needs two kinds of users. Users who want to exchange tokens. Next, liquidity providers who want to use their coins or tokens in order to earn rewards. Users have to pay for the swaps and part of the collected fees are used for rewarding the liquidity providers. The economic model is functional because the interests of two groups of users are met.
The user interface and accessibility of the service are crucial. This is where WingRiders has a big advantage thanks to its extensive experience with the Cardano project and its infrastructure. The team’s goal is to make WingRiders a core infrastructure element. This will be achieved through the possibility of very easy integration into any wallet or dApps. The team knows how to do this very well, as they have created the popular Adalite wallet in the past and are now working on a new wallet called NuFi (a staking platform with plans to support 10 blockchains). The team has also worked on integrating Cardano wallets with the Trezor and Ledger HW wallets. WingRiders will be a very modular and flexible piece of software suitable for integration. This is an effective strategy to get closer to users and expand the network effect.
Each team must carefully consider the design of the app to offer attractive features to users and guarantee specific functionality.
The security of the service is an absolutely key feature for gaining user trust. WingRiders will let external auditors (namely CertiK) examine the source code to ensure that coins and tokens sent to the exchange address are never lost. Users and liquidity providers can be sure that they will not lose their funds. The emphasis on high security is in line with the Cardano project philosophy and a security audit is almost a necessary requirement for success.
WingRiders promises users that swaps will be executed in canonical order. There’s no reason to give benefits to anyone. All users will be treated equally.
The team is working to make DEX as accessible as possible to all users. Fees are an important part of the decision-making. Through optimizations, the team is able to reduce the size of the fees for using the Cardano network. This will reduce the fees for the end-user of WingRiders.
Ease and clarity are other important requirements for success. The user interface is one of the priorities for the team. As already described, it will be very easy to integrate WingRiders with wallets and dApps. This can be already seen on their test net version where a deep integration into a wallet (NuFi) is shown.
In addition to the possibility of being rewarded for providing liquidity to the pools, the team seeks to retain the ability to participate in the staking of ADA coins and thus not miss out on staking rewards.
The Cardano ecosystem is evolving rapidly and the first version of the WingRiders exchange may not be completely optimal. The team will reflect the evolution of the Cardano infrastructure and will adjust the DEX accordingly.
The aim of the article was not to compare Cardano and Ethereum architecture. Both platforms are fundamentally different from each other and will find their own applications. With the WingRiders team, we wanted to show you how complex and challenging it is to build DEX on the Cardano network. Almost nothing but the basic concept can be taken from the Ethereum world and teams have to come up with completely new ideas.
WingRiders has a strong and experienced team that is well funded. DEX is already running on the test-net and will appear on the Cardano main-net in Q1 2022. Be sure to feel free to come and try out a swap or provide liquidity. You can check out the official project website or chat with the team on Discord.