Table of content:
Problems and Solutions
IOG invited us to participate in Cardano smart contract testing in 2021. We agreed without hesitation, and today we are very proud to be one of the world's first five companies to develop Plutus use cases.
We needed to learn the Plutus Platform framework to take part in the Cardano collaboration. Plutus has a high entry barrier because it requires developers to have knowledge of the Haskell functional language (Plutus Core, Cardano's smart contract language, uses the Haskell codebase) as well as blockchain experience. Because our engineers have been programming in a functional style since 2018 and have already mastered Haskell, we chose Cardano and the integration went smoothly. Since then, we've been working on blockchain projects for over a year.
While testing how Plutus smart contracts behave in real applications, we worked closely with the IOG internal team. Our developers tested the IPFS protocol, helped to find bugs and participated in brainstorming on how to improve it.
The main task was to design and develop the idea of an application on a new platform. We could also choose an idea from the list of organizers, which we did. Our five-person team worked on its implementation from August to December 2021, resulting in the development of the first NFT marketplace with an auction on the Cardano blockchain.
We developed a smart contract application, which is the application's back-end that interacts with the blockchain. This means we've written code to construct transactions as well as code to validate transactions. We created a front-end for manual testing and a stunning user interface for the app's users. We also designed a specific architecture and implementation specification for the application before starting to develop it.
To create an NFT on our platform, you need to follow these steps:
- upload the associated metadata (can be any file, such as an image) to the repository. This step requires content addressability, which the IPFS protocol provides by assigning a unique identifier to each file (a cryptographic hash);
- directly mint the token, which will go to the wallet of the creator on the blockchain.
The primary function of our NFT trading platform's smart contract is to provide a direct link between the token in the user's wallet and the content identifier. Users of our marketplace have the following options:
- Creating their own non-fungible tokens;
- Combining NFTs into collections;
- Launching an auction;
- Determining the auction's price and duration.
Commissions from the sale of NFTs and auctions are used to finance the platform.
Problems and Solutions
IOG did not support smart contracts, as well as documentation to help us understand some things, at the time of our participation. All developments were tested in simulation. Following the release of the Alonzo hard fork, an update that added the ability to create smart contracts and dApps, it was discovered that it does not support many features. We detail the issues we encountered and how they impacted the project's launch:
1. Metadata storage (images, video or audio files associated with the NFT).
It is well known that storing a file on the network significantly raises the cost of a transaction. Furthermore, a maximum of 16 KB of storage is allocated per unit (due to the transaction size limit). This means that to store large files, you must first dice them like a classic salsa recipe, and then devise a method for combining the pieces into the original veggie file.
The solution was adapted from existing Ethereum blockchain practice. We've decided to use IPFS, a distributed file storage system. The file is uploaded to IPFS and assigned a content ID that is a hash of its content. The system works because the hash continues to uniquely identify the files (in other words, they assign addressability to the content).
This solution kills two birds with one stone:
- allows you to uniquely identify NFT with something (thus rendering the token non-fungible);
- allows you to store only a short hash in the chain rather than a large file.
2. Storage of the content ID, which is generated after the file is uploaded to IPFS.
In Ethereum, each token is a smart contract that allows you to use storage. In Cardano, all tokens are native, backed by the ledger, and can behave similarly to the main currency, ADA. In Cardano, the token is only represented by a PolicyID and an immutable asset name, allowing you to uniquely identify, transfer, and track it.
We had two options for solving the problem:
- Use the asset's name for storage — the only place in the token where you can store something unchanged;
- Keep all hashes in the data of some scripts (for example, a marketplace script), with the option to change them. A lot of other related information can also be stored there.
To make the token as lightweight as possible, we decided to store the content identifier in the token name and other information — such as a description of the token — inside the script data.
3. Plutus framework general readiness.
The library was brand new and experimental at the time, and the Cardano network did not support smart contacts. Plutus, the custom smart contract development language and execution platform, was developed concurrently with our marketplace application, so something was constantly not working.
We were prepared, though, and tried to stay up to date on Plutus updates and provide feedback to IOG engineers. We were constantly discussing ways to fix the errors that occurred in a dedicated Discord community with the Plutus developers and other members of the test program. At IOG, we were also supported by the core in-house team.
4. The existing transaction size limitation.
Cardano's maximum transaction size is 16 KB. The number of calculation steps (CPU) and random access memory (RAM) consumed by the script are also limited.
Our scripts proved to be far too large to run online. It was later discovered that this was due to the use of a Plutus framework state machine abstraction. Abstraction is very expensive because it adds a lot of code and makes the script larger.
To fix the size limit issue, we had to rewrite the entire implementation by removing the use of the state machine.
5. PAB does not support remote (browser) wallets.
To buy NFT, the user must navigate to his browser wallet through the browser interface, enter a password, and submit transactions (those who are familiar with MetaMask know this scheme).
However, it was discovered that the Plutus App Framework does not support PAB integration with a remote (browser) wallet. This was the most significant problem, which was discovered only during the course of work. It was then impossible to find a genuine solution.
True, we did not give up right away and planned an alternate scenario for launching the application via the Cardano Wallet Backend. We decided to deploy a PAB locally along with a Cardano wallet, chain-index, and Cardano-node applications. As a result, the dApp user runs all applications on his computer and employs a local wallet with a full node. As a provider, we have undertaken to store all of his data and perform actions on his behalf.
This script violated privacy policies and appeared unappealing to the user, who would have to give us access to his wallet. As a result, we were unable to implement the script and launch the project on the testnet.
PAB is still not working in browser wallet mode as of this writing (September 2022). Any DeFi application must be able to work with browser wallets. More work must be done to explore alternative solutions that will allow the application to run for a wide range of users.
It took 5 months to develop the application. This required the efforts of a five-person team:
- one Frontend developer;
- three Plutus & frontend developers;
- one project manager.
We used the following technology stack to create the application: Haskell, PureScript, NixOS, Docker, and TypeScript.