Introducing Laminar — An eUTxO scaling protocol for accounting-style smart contract
Minswap recognizes and embraces the many design challenges that come with working on new, cutting-edge blockchain solutions. Such a paradigm shift often requires innovative thinking and contending with difficult issues along the way. However, we believe that persistence in solving these challenges will be well worth the effort. The potential cost, security and scalability advantages of Cardano’s eUTXO model and Haskell-based smart contracts are exceptional.
In this article, we will outline the approaches we have explored, their respective trade-offs, why we launched our testnet without one, and the solutions we will implement in the short-term and long-term. We will also openly disclose additional challenges deploying on a real blockchain and how we intend to tackle them.
In DeFi engineering, it is often needed to have a global view on all liquidity or total token supply to determine actions. Some DeFi ideas like constant-product AMM are only possible in accounting-style smart contracts. That is why we came up with a novel way to execute accounting-style smart contracts multiple times in one block, without sacrificing decentralization.
Laminar, from Latin lamina or “slice, layer”, to signify the smooth and parallel flow of liquidity. Laminar is a batching protocol design that:
- Can scale indefinitely as transaction limit allows, without any parameter update.
- Is upgradable and composable without hard-fork or liquidity migration.
- Is functional on Layer-1 eUTxO chains.
- Designed to be resistant to MEV.
- Is decentralized and resistant to DoS.
- Provides side income to SPOs.
- Doesn’t require changes to the underlying accounting-style smart contract. (Which means that developers can iterate and launch their MVP contracts without worrying about making it concurrent out of the gate.)
SC[i] is smart contract at state i
U[i] is user i’s UTxOs
U[i]’ is user i’s UTxOs after interacting with smart contract
R[i] is user i’s batch request
Whenever a user needs to interact with an accounting-style smart contract (which is represented as a script UTxO), they need to lock that contract for one block, which means that if many users want to interact with a smart contract UTxO, only one can succeed. Batching means that we combine all user interactions with the smart contract UTxO in one transaction, so that all can succeed.
Laminar is based on a simple observation: An accounting-style UTxO being spent N times in N block will have the same result as being spent N times in 1 block. When a user wants to interact with the smart contract, instead of creating a transaction spending the smart contract UTxO directly with a redeemer, the user will encode the redeemer and lock their necessary UTxOs and a batcher fee to a batch request smart contract. In the next block, the batch requests will be picked up along with the smart contract UTxO to be executed, and the successful batcher will collect the fee as payment for their work. Therefore, one interaction with a smart contract will take around 2 blocks (average 30 seconds) to be finalized.
This solution is resistant to DoS by design. A DoS attack is carried out by an irrational attacker who would seek to lock the smart contract UTxO without fulfilling any batch request. Such an attacker will have to face the batch competition from all other batchers and thus has a very low chance of success. Even if an attack succeeds, it will only delay the request fulfillment by one block, before it is fulfilled by all other competing batchers.
Upgrade, fork and composability
Because the underlying accounting-style smart contract is independent of the batching protocol layer, users can voluntarily upgrade to a better batching system by switching to using a new batch request (BR) smart contract. An analogy is Uniswap upgrade, they just deploy a new version and users can choose to use the new version or keep using the old version. However, because Laminar is a batching layer, no liquidity migration is needed.
A fork happens when different parties want to use different BR smart contracts. However, it’s not a win-or-lose situation, because a batcher can batch multiple different BRs in one transaction. In the end, Laminar gives the power back to the users because users are the ones that choose the BR smart contract with the incentives that best benefit themselves, while also being attractive enough for batchers. For example, a user who puts out a market order will use a BR contract that awards the batcher based on how close the batcher execution price is compared to the user’s desired price.
The composability of batching naturally leads to an AMM feature that is expensive on accounting-model blockchains: limit orders. A limit order is just a BR contract that only awards batchers if the executed price is close to the expected limit order price, without any time constraint. A batcher will pick that order up when it’s profitable for them. Since the trader conditions the batcher fee on executing at a certain price, this will only be executable and rewarded if batchers can give the user the pre-specified price. Such a rule is coded in the BR UTxO validator script.
The composability model can be further extended to allow seamless DEX aggregation, by removing BR dependency on the base contract. For example, a market order contract only cares about whether the batchers give users the desired price, and doesn’t care which DEX the batchers use. Which means when a new AMM pool is launched in Minswap multi-pool ecosystem, all users will benefit from it immediately if their order can be executed with a better price in the new pool. And it doesn’t necessarily need to be one of Minswap multi-pool, batchers can match orders against other DEXes as well. We advocate for an open ecosystem and we don’t plan to have vendor lock-in. With batching protocols being composable, the possibilities are endless.
With Laminar’s approach, the transaction fee for a batch will be split among all users who participate in the batch. This presents a chicken and egg problem for the protocol’s initial stages, where there won’t be many requests per block, hence profitable batches result in high user fees and low user fees result in unprofitable batches. This makes bootstrapping decentralized batching difficult and will lead to the batching only being done by the project owners themselves.
We solve this the way we solve early liquidity providing, by additionally incentivizing early batchers with governance tokens. The governance tokens will keep the batch flow going on until batching is sustainable with BR fees alone. This is similar to how PoW/PoS chain validators are rewarded with the chain’s native tokens until it is sustainable with transaction fees alone. We can call this batch mining. A clever engineered batching tool can even earn multiple tokens of multiple protocols from winning one batch.
The requirement to become a batcher is simple, you just need to run a full node and the batching bot. Sybil attack can be prevented by requiring each batcher to put up a certain amount of ADA (e.g. 1000 ADA). The batching bot and documentation are open-sourced by Minswap. As a result, SPOs will probably become the first batchers as they are already running full nodes. Batch mining provides small SPOs with side income until they attract enough delegation. Making small SPOs sustainable will help prevent concentration of stake power into big businesses and exchanges. We believe that batch mining will not only help decentralize DEX batching, but also decentralize the whole Cardano network in general.
Does this introduce the problems of a bid-based ordering system and MEV?
No, the batcher fee doesn’t need to follow a bidding system. Laminar is flexible enough to have different incentivization schemes for different use cases that can align batchers’ interest with users’ interest. Let’s take swapping for example, the fee that each user puts out is a fixed amount of ADA, which decreases as time goes on and decreases if the execution price is far from the expected price given in the batch request. This way, the batchers will have an incentive to execute requests as soon as possible, and give users a price as close to their desired price as possible.
An effective way to prevent malicious ordering is to make each batch request hold a time token. A time token can only be created if its asset name is a timestamp that is after the validation range upper bound. This prevents anybody from creating a time token in the past. This BR will only be validated if all BRs in the same transaction are of the same type and are ordered by this time token. This enforces that there would only be one way to order BRs in one transaction.
The beautiful thing about Laminar is that the incentivization mechanism is not hardcoded into the protocol, which means that we can trivially upgrade and/or compose them. This is why it’s said that we can easily do an accounting model on top of the EUTxO model, but not the other way around.
On-chain datum discovery
When creating a BR, users need to store multiple pieces of data within it: redeemer, desired price, timestamp, etc. Unfortunately, an UTxO doesn’t store its datum on-chain. It only stores its datum hash and a transaction executor needs to provide a full datum value in the witness that hashes to the same hash. This means that we need to store the BR datum value somewhere that the batchers can have access to in order to spend the BR UTxO. There is a good place for it: transaction metadata. Whenever a BR is created, its datum value will be stored in the producing transaction metadata, and a batcher can read from it to spend the BR.
Limitations and trade-offs
No solution is fool-proof and like everything else in computer science, our solution has its trade-offs. We would like to honestly admit them and open up the community for ideas and feedback.
The first one is, in the case of high volume and volatile trading, a market order might be missed/delayed because of the batch limit or canceled because of high slippage. Though in this case, users only need to pay a small amount of ADA to update or withdraw BR.
The second one is the challenge of batching many state transitions of the base smart contract into one transaction. Since the base contract is independent of the batching protocol, its state transition can’t be affected by batch ordering. This means in our case, we will only make it possible to batch all swap actions in one transaction, or all add liquidity actions in one transaction, etc. This results in some batches taking longer than 2 blocks to be finalized.
The third one is since a batch transaction has no way to know the total number of BRs, a batcher can purposefully omit some orders. However, if a batcher wants to extract value from this, the cost will outweigh the profits as the batching network grows. Let’s hypothetically say that there are N batchers and the success rate of a batch is 1/N, a batcher who wants to extract value from omitting orders will need to create orders every block, a successful attempt will be accompanied with N-1 failed attempts which will profit liquidity providers and other batchers.
The last one is like all other on-chain solutions that require executing multiple script UTxOs in one transaction, it is limited by the transaction limits which we will go into more details down below.
Why didn’t you launch this with testnet?
Laminar is currently the solution we are most satisfied with and is what we will continue to develop and dive deeper into. However, like most other complex protocols, it takes a lot of time to build and audit. The point of Laminar is that it doesn’t require changes to the underlying accounting-style smart contract, therefore we can launch the base protocol first to get feedback before moving onto designing a well-rounded incentivized batching layer. In our testnet, we were able to gather important data for iterating on our scaling solution designs and get large-scale feedback on the UI/UX.
Off-chain sequencer — Short-term solution
With Laminar taking some time to build, test and audit, we decided that it is best to use an off-chain sequencing solution to restart our testnet soon and not letting our community wait any longer. Our incentivized testnet will restart in late September/early October. It is possible that Minswap will use the sequencer at first when releasing to mainnet, while building and rolling out Laminar.
A sequencer requires all transactions related to one liquidity pool to be submitted through one backend. Because they are all submitted through a centralized point, we can chain UTxOs in the same block, as we know what the UTxO in the current block will be before it is officially confirmed by the network in the next block. A sequencer can work on many pairs but any given sequencer must be dedicated to specific tokens pairs.
To reduce centralization and single point of failure, we will federate sequencer hosting to other projects having their tokens traded on Minswap. Minswap will provide the necessary backend and documentation so that other projects can host the sequencer and be responsible for their tokens’ trading activities. Federated sequencer host addresses will be updated in the frontend interface. For example, the Minswap sequencer will be used for the ADA/MIN pair, and Project X sequencer will be used for ADA/X pair. However, Minswap will provide backup sequencers for all pairs, reducing reliance on other projects hosting their own sequencers. The benefit of projects running their own sequencers is to reduce the traffic to Minswap’s backend server. Even in the absence of distributed federated sequencers, Minswap is incentivized to provide the best prices and user experience for its traders rather than partake in malicious ordering. While a form of MEV by Minswap is possible, our incentives to do so are not.
Splitting liquidity into multiple UTxOs: Why not?
One of the more obvious solutions to prevent UTxO contention that comes to mind is splitting the smart contract into multiple UTxOs, along with their liquidity. Two popular market-making models that work with this approach are order-book and concentrated liquidity AMM (Uniswap v3). However, this approach has two trade-offs:
- If all users interact with the chain independently, all users don’t know which UTxO is being held by another user in a particular block, hence, they will all create a transaction that spends the UTxO with the market price, and thus creating contention once again. In order to synchronize this, we either need a centralized backend that matches user(s) with order(s), or making each user creates take orders as an UTxO and build a batching layer like Laminar on top of that.
- Liquidity is fragmented, which means it is complicated for new projects to bootstrap liquidity with low capital. One could argue that with concentrated liquidity AMM, we can provide liquidity in full range (0 to ∞) to facilitate trading for low-cap tokens. However, it would be susceptible to UTxO contention again since all liquidity would be concentrated in a single UTxO now.
We think that splitting liquidity is a good and natural approach to solve the concurrency challenge on an eUTxO chain. However, current market-making models don’t meet our requirements for a solution as one of our primary goals is to make Minswap an attractive place to issue new tokens through constant-product AMM. This approach is also limited by the transaction limits.
Other technical challenges — Transaction limits
In order to keep a blockchain decentralized, it is crucial to impose some limits so that it is possible for broadly available consumer hardware to run a full node. These limits are often ignored if one develops smart contracts in a PAB or simulated environment, however, they become very noticeable when smart contracts are run on a real Cardano node. Currently, there are three important limits on an Alonzo transaction:
- Transaction size limit: 16kB.
- Memory limit: 10 million units.
- Computation limit: 10 billion units.
Out of those three, the memory limit is the easiest to bump into. The base memory cost of a validator script right now is around 2.5 million units. When we first tried to run our DEX on a real node, most of our transactions consumed 15–20 million memory units. We had to do some advanced Haskell optimization techniques. More specifically, we reduced thunk expansions and forced lazy evaluations. After optimizing, our DEX contracts now consume roughly 5–8 million memory units which fit the protocol limit. If you want to learn more about optimizing Plutus smart contracts, reading this is recommended: All About Strictness.
These limits are not critical to protocol design, they are just protocol parameters that can be voted to increase by governance. In the future, these limits will be alleviated by:
- Better consumer hardware allows for higher limits.
- Better node performance allows for higher limits.
- Upgraded Plutus version reduces execution costs.
- Upgraded Plutus compiler reduces execution costs.
For this reason, we believe that in the next few months we will see a lot of novel on-chain scaling solutions come to life. It’s definitely an exciting time with a promising road ahead.
Acknowledgment: Thanks Alessandro from Berry pool for the metadata-based datum discovery approach.