Liqwid Batching System: Supply and Demand Action Queues
The Liqwid Batching System collects the result of Mint (supply) & Redeem (demand) transactions to update the MarketState & the qToken exchange rate at fixed time intervals.
This allows a Market to scale by processing multiple supply and demand-action transactions in batches. In this technical demonstration of the batching system we explore this process in detail and analyze all of the Market contracts involved in a batching cycle.
The first part of the process is Market bootstrapping, which sets up the initial Plutarch scripts & datums for the test Market.
This will later be integrated with Agora governance, but for testnet we will follow a more manual process.
To compile the InitMarket contract we must first include the required state-thread tokens:
To mint the state-thread tokens, we select a TxOutRef. A UTXO from a previous transaction, owned by a testnet wallet is used.
Next we select parameters to bootstrap the Market with. For this demo we define a single ADA Market, with some default parameters suitable for testing.
With the parameters, we can compile the Plutarch scripts. This is provided by a cli tool in our Haskell codebase liqwid-onchain.
This gives us some output in liqwid-markets.json which we can provide to the off-chain dApp components.
The liqwid-offchain codebase uses Cardano Transaction Library (CTL) to build off-chain contracts that run in-browser, integrating with the React front-end UI.
The data from liqwid-markets.json is used by the off-chain components to construct transactions using the compiled Plutarch scripts.
The InitMarket contract initialises the ADA Market we defined, by minting the state-thread tokens & initiasing the protocol UTXOs with initial Datum values.
The contract creates a transaction, which is sent to a testnet Nami wallet for signing:
After confirming & signing the transaction with Nami, the transaction is submitted to the testnet. We can view this on cardanoscan.
Note: the script Memory & Steps displayed by cardanoscan, as well as the transaction fee are overestimates as proper transaction fee calculation for scripts in cardano-transaction-lib is currently a Work-In-Progress.
We also mint some extra state-thread tokens incase we need them for testing later.
The Batching process collects the result of Mint & Redeem actions to update the MarketState & the qToken Exchange Rate.
The first part of the process, collecting the actions involves 5 scripts:
The transactions are, again, triggered in-browser by the testing dashboard.
In our testnet Market, this executes in 4 transactions.
The first 2 transactions each merge 4 Supply Actions UTXOs:
The next 2 transactions continue, each merging 4 Demand Actions:
The last transaction in the batching cycle ‘finalizes’ the Batching process and allows the Market to begin accepting supply and demand-action transactions again.
The MarketState is updated & the qToken exchange rate is recalculated based on interest owed to suppliers.
In this case, no ADA has been borrowed from the Market, so no interest is accrued and the qToken exchange rate (ADA to qADA) remains the same.
This transaction spends from the following two scripts:
- MarketInbox: The current state and parameters of the Market
- BatchFinal: Validator responsible for batch finalization logic
And outputs UTXOs at the following scripts:
- MarketInbox: The updated state of the Market
- SupplyBatch: Waiting for the next batching cycle
- DemandAction: Demand Action UTXOs, with Market supply collected by the batching process, ready to accept demand-action transactions again.
- SupplyAction: Supply Action UTXOs, ready to accept supply-action transactions again.
The finalization transaction can be viewed here.
Submitting Actions to the Market
Before or after the batching process is executed, we can submit ‘Actions’ to the Market to be batched later.
The design of the Action Queue system allows us to receive the result of those actions immediately.
These actions can be also be submitted by users via the Liqwid frontend app, but for this demonstration we will use our testing dashboard again.
Let’s first demonstrate minting qTokens.
qTokens can be minted by depositing some amount of ‘underlying’ assets to a Market. We have an ADA (or rather, testnet ADA) Market, so we can mint qADA.
When we deposit ADA to the Market, we receive qADA based on the current qToken exchange rate given by the Market. For example, our Market currently has an exchange rate of 1 ADA to 50 qADA (.02), so we can mint 50 qADA for every ADA we deposit.
Note: As borrow interest accrues in a Market the exchange rate increases. This allows suppliers (qToken holders) to redeem for an increased amount of underlying assets representing their earned yield.
To deposit to the Market, we submit a transaction which spends from a ‘Supply Action’ UTXO, allowing us to Mint qTokens at the same time.
Minting transactions spend from the Supply Action validator and Mint with the qToken (qADA) minting-policy.
For example, we can submit a transaction which deposits 100 ADA in order to mint 5,000 qADA.
We spend 100 ADA, plus some ADA for fees.
Note: The amount of fees shown here are an overestimate, due to current limitations of CTL.
We can view this transaction on cardanoscan.
Note: The amount of qADA displayed on cardanoscan are denominated in qLovelace, which is equivalent to 1/1,000,000 qADA.
To exchange qADA back to ADA again, we can submit a Redeem action to the Market.
Redeem actions, like with Minting, can be executed by submitting a single transaction to the Market.
Inversely, we burn an amount of qADA, which allows us to redeem an amount of ADA based on the Market’s current ADA-qADA exchange rate.
Again, with our Market’s current exchange rate of 1 ADA to 50 qADA, we can receive 1 ADA for every 50 qADA we burn.
We can submit a Redeem transaction which spends from the DemandAction validator and uses the qToken policy (this time to burn qADA).
We burn 5,000 qADA, allowing us to receive 100 ADA:
This transaction can be viewed on cardanoscan.