The following sections will describe our experience and issues we faced during almost one year of developing on Cardano. We will try to get an answer as to whether Cardano is ready for production usage.
Last year (2021) Cardano had two major hard forks and moved to the Mary era, introducing Native Tokens. Later, the Alonzo era introduced Smart Contracts written in Haskell and compiled to Plutus Core for executing at Cardano nodes.
Coin Selection and Wallets – Show me the UTxOs
Cardano wallet can have (and usually has) multiple addresses and on every address, it can contain UTxOs. If this is your first time hearing about UTxO, or you’re not familiar with it, here’s a good explainer video.
The balance of the wallet is calculated as a sum of the balance of ADA and Native Tokens on all of the wallet addresses. During the process of building the transaction, there is a challenge to choose the optimal selection of UTxOs from all of the addresses.
We need to take care to choose only the minimal set of UTxOs required to be spent for the transaction (we want to leave the others so it can be spent in parallel) and also to make sure that for change, we are not receiving too small amounts which are creating a “dust”.
Dust can happen if the amount of ADA in UTxOs is too small, and is not enough to be spent alone. If you add all of them (the small amounts of UTxOs that are creating “dust”) in a single transaction, they will not fit due to the size limitations, thus making the transaction invalid.
Regarding the coin selection, the wallet will handle everything that is needed. There are two popular algorithms for coin selection: Larger-First and Random-Improve.
One more complication that can happen is connection to dApps. The officially supported Cardano wallets Daedalus and Yoroi don’t have the dApp connector yet. This means that they can’t interact with dApps over a web browser which might come as a surprise if you’re coming from Etheruem ecosystem for example. It’s expected that this will be solved in the next releases.
On the other hand, third-party wallets like Nami wallet and CCVualt have a dApp connector which makes them more interesting for general usage, especially for DeFi projects.
The tricky thing here is that not all wallets are compatible with each other. Although all wallets are HD, not all of them use the same set of addresses. Wallets like Nami, for example, are using just one address (the first one generated), while Daedalus is always creating new ones. At the first run, Deadlus generates 20 addresses, and when any of them are used it creates an additional one, so in the end, it always has 20 unused addresses.
These multiple addresses have the main purpose of making transactions much more anonymous and more secure. Recreating addresses on another wallet type can produce the undesired effect – that not all of our UTxOs are visible, so our total balance can be displayed incorrectly. This can cause the wallet to show less amount on the balance than it really is.
Not all is dark. Smart Contract model developed in Cardano has a cool feature – dApp can create an unbalanced transaction and doesn’t need to know anything about user wallet and funds. So dApp will create transactions just with the desired output of smart contract action.
At the same time, the wallet at the user side is responsible for providing inputs to the transaction, aka balancing transaction, signing it and submitting.
Unfortunately, this is still not possible with web browser wallets like Nami, CCVault, Yoroi, etc. Now, these wallets can sign transactions for transferring funds and not for executing Plutus scripts.
Concurrency issue – Fight for the UTxO
The most common pattern to save the state is using NFT in one UTxO on the Plutus script address. Everyone can send arbitrary tokens and data to the script, which cannot be prevented, so the main state must be maintained separately.
The state is usually saved together with NFT, generated to UTxO by the script owner or authorized user. At the UTxO with NFT, we can put various important data for our dApp. For example, some value provided by Oracle or calculated ratio for tokens swap.
A transaction can consume this UTxO to produce the new one with the same NFT and updated data. The issue occurs when two or more transactions require the same UTxO because UTxO can be consumed only once in the same block.
In this case, only one transaction will be successfully executed, while the rest will fail (without a spending transaction fee). This issue should be solved by using UTxO with a single point of truth that can be just read without the need to be consumed, as is announced in one of the Cardano updates in 2022.
In the meantime, developers are solving this issue by creating and managing more UTxOs with different NFTs containing the same data or collecting all transactions that need to consume UTxO in a batch and executing them all together.
Haskell – Not the most popular kid in the block
Functional programming language is very suitable for developing systems based on UTxO. While Haskell is a very good, stable and mature programming language, it is not too much used in the industry.
It is mainly used for university purposes, and there are not too many Haskell developers. It is hard to learn for most developers who work with imperative programming languages, but if you manage to make a switch, there is a big chance that you just start to love it.
Blockchain limits – Don’t push the boundaries!
Development on Cardano requires us to observe current limits and make sure that we write the most optimized Plutus scripts and generate transactions that do not exceed limits. Most of the following limits are set as protocol parameters and can be changed without forking blockchain.
Currently, this is handled by IOHK, but in the future,this decision will be made by the decentralized governance that will decide regarding those kinds of questions.
There are two types of resources used by Plutus transactions:
1. runtime cost – the amount of CPU and RAM used to run the script.
2. network cost – the size of the transaction, which determines network load and storage needs when the transaction is added to the blockchain.
Scripts need to be sent together with transactions and cannot be referenced like in Ethereum, where you can provide the address of the script. In Cardano, instead, we always need to send a compiled script within a transaction.
Also, just data hash is stored, representing the state on the blockchain, so we always need to send raw data together with a transaction. This causes a big transaction size.
Currently, the size of a transaction is limited to 16kB. It is announced that in the next fork expected in 2022, both script and data will be sent as a reference in the transaction.
Currently, the block size is 80kB. If transactions are full (16kB each) maximum five transactions can be placed in the block. Block is mining for around 20 seconds. Propagation of a new block across the network is done in 5 seconds.
Plutus Script CPU and memory utilization
Running Plutus scripts on a validating node uses CPU time and RAM space, paid for by transaction fees.
So when we’re developing on Cardano, we need to make sure that we don’t exceed memory and CPU limits set for running the script (or multiple scripts in the batch).
Currently, that limit is not that great, and if you were to develop some really complex logic (ex. DAO), it’s all the likelihood that those scripts would fail CPU and RAM limitations. But those limits are raised gradually and the possibility of larger and more complex scripts will get better over time.
TPS and network congestion
Therefore in this regard, IOHK, the company backing Cardano, stressed looking at throughput, finality, and concurrency as crucial metrics. While throughput is the volume of data processed within a specified period, finality is the time taken by the system to finalize a transaction.
Additionally, IOHK defined concurrency as the amount of work different actors can do without blocking each other.
We can look at SundaeSwap (largest and busiest dApp on Cardano) to see their experiences regarding the speed of transactions (which utilize Plutus scripts) on the mainnet. And their finding is that they can run 3 scoops a minute (with 3 user actions inside it).
PAB not yet ready – Waiting for PAB
PAB – Plutus Application Backend is a collection of tools connecting front-end applications to the wallet and Cardano node.
Currently, there is a working PAB, but usage is limited. It provides a REST API that dApps can access. PAB requires a Cardano wallet API to connect to it, and for starting Cardano wallet API, it is required to have a full Cardano node.
To use it, every user needs to have a locally started Cardano node, wallet API, and PAB, which is far from convenient for average users. In the future, the plan is for PAB to be located on the dApp provider’s node and light wallets like Nami, CCVault, or Yoroi can receive an unbalanced transaction from PAB to balance it (add input UTxOs and output addresses) and signed from the wallet and submitted. This is currently in active development.
Currently, most developers use cardano-cli to generate and submit transactions cause this approach is easier than using hosted PAB with all required components.
Besides the above limitation, Cardano is pretty much ready for development and it is actively improving. In 2022 it is announced a lot of scaling improvements, and more info can be found here.
These are some of the resources that we can recommend for every new developer:
- IOG’s Technical Community Discord
- Github Plutus – on-chain part
- Github Pluts Apps – off-chain part
- Plutus Use Cases – use cases from IOHK partners companies, there are different git branches per company.
- Cardano Stack Exchange
- IOHK blog
- IOGAcademy on Youtube – Plutus Pioneer Program Lectures
- Cardano 360 monthly updates on Youtube
- Cardano Forum