Understanding Terra’s market module and redemption capacity

11 min readApr 7, 2022

Normally, to swap between tokens a user has to do it on a centralized exchange (CEX)like Binance or a decentralized exchange (DEX) like Uniswap. However, because Terra has a special relationship between LUNA and Terra stablecoins, Terra has a special swapping capability built into its core. In Terra station, this capability appears as “Market” whenever you try to swap between the Terra native coins.

# Market Module

Sourced from official Terra docs on market module.

The Terra market module facilitates swaps between the Terra native coins. It operates similar to DEX pools like on Terraswap, Astroport, Uniswap etc. you might be familiar with but with the major difference that there are no actual liquidity pools. Instead, this mechanism employs virtual liquidity pools.

The major difference between the two kinds of swap mechanisms is that in a normal liquidity pool, you have a token that gets added to the pool and another token gets removed. In Terra’s Market Module, one of the token gets burnt while the other gets minted.

Differences between DEX liquidity pools and Terra market module

This swap mechanism is still based on the familiar Uniswap constant product X*Y=K formula, but differs in a few ways. Because there are no actual pools, market module’s variant of the X*Y=K formula uses the fiat value of LUNA instead of the size of the LUNA pool. This means that the swap rate is not directly influenced by LUNA price in the markets but rather the size of the Luna pool.

The market module starts with two virtual liquidity pools of equal sizes (just like a liquidity pool on a DEX) but as the swaps happen, the size of the pool changes. Terra uses a number 𝜹 which represents the deviation from the base size of the pools. The size of the Terra and Luna liquidity pools can be generated from 𝜹 using the following formulas:

At the end of each block, the market module tries to “replenish” the pools by decreasing the magnitude of 𝜹 between the pools.

Source: terra-money/core (Github)

The rate at which the pools are replenished toward equilibrium is set by the parameter PoolRecoveryPeriod. Lower periods mean lower sensitivity to trades i.e. previous trades are more quickly forgotten and offer swaps with lower spreads.

Source: terra-money/core (Github)

# LUNA<>UST Mint Mechanism

If you haven’t guessed by now, then the “Market module” is also known as seigniorage machine made famous by Danku_r.

Source: https://www.youtube.com/watch?v=Aym_gGA1TdM

This module enables the following arbitrage mechanics around LUNA<>UST swap mechanism which holds the peg around $1.

Source: The World of Terra

More importantly the adoption of UST is increasing beyond Terra. Therefore, everyday more and more LUNA is being burnt to create new UST. Market module is the only way to create new UST.

# But the market module has its limits

There are limits to how much new tokens (e.g. UST) the market module can mint everyday. This is complex dance of the following parameters:

  1. BasePool — The size of the Terra stablecoins and LUNA virtual liquidity pools
  2. PoolRecoveryPeriod — The number of blocks it takes for the Terra & Luna pools to naturally “reset” toward equilibrium (𝛿→0) through automated pool replenishing.
  3. Oracle VotePeriod — The number of blocks during which voting takes place (default is 5 blocks)
  4. MinSpread — The minimum spread charged on Terra<>Luna swaps to prevent leaking value from front-running attacks.

These parameters all work together to power the mint/burn mechanism but ensures Terra’s two important design goals:

  1. Deter front-running oracle attacks
  2. Ensure off-chain liquidity is larger than on-chain

Essentially, this means there’s a limit to how large this virtual liquidity pool can be. The redemption/minting capacity of $20 Million at 2% spreads in May 2021 was the reason we saw a violent draw down in LUNA price as Terra explained in their post-mortem.

The next question is if this is a virtual pool, then why isn’t the size infinite? Well, first we need to understand the redemption/minting capacity.

# Redemption/Minting Capacity

The BasePool parameter discussed previously represents how large the pool can before the spread makes swapping a large amount unprofitable. This is done to ensure on chain market is not more liquid than off chain markets (Kucoin, Curve, Binance etc.) so that the system can be prevented against oracle manipulation attacks. Let’s take an example where the on-chain liquidity > off-chain liquidity as explained by Terra team:

  1. A whale trying to manipulate the oracle price would — bid off-chain markets up without too much size, pushing up the LUNA/UST oracle price.
  2. Sell LUNA → UST on-chain at the increased Oracle price.
  3. Dump small amount of LUNA on the off-chain markets to bring the Oracle price back down
  4. Buy large amount of LUNA on-chain (UST → LUNA) at the lower Oracle price rate.
  5. End up with more LUNA, diluting other holders.

Essentially, the whale is executing the following steps:

Raise LUNA price off-chain -> Sell large amount of LUNA on-chain at high price -> Lower LUNA price off-chain -> Buyback LUNA on-chain at the reset lower price.

If on-chain liquidity < off-chain liquidity, then to execute the above attack would cost more than the profitability of the attack.

At the time of this writing, the current virtual pool has been set at 50M SDR (~37M UST) which can be queried from — https://lcd.terra.dev/market/parameters.

Market module parameters as of April 6, 2022

Notice, that this is smaller than the LUNA-UST pool on Astroport but larger than Terraswap.

Source: Astroport
Source: Terraswap

Therefore, if you were to swap a large amount of LUNA to UST, swapping through Astroport would offer the best rate with the lowest slippage.

# Market Module Parameter changes

As the off-chain liquidity grows, the on-chain parameters need to be scaled up as well. Without sufficient on-chain liquidity conservatively matching the expanding off-chain liquidity, LUNA cannot sufficiently absorb extreme volatility, dislocating the UST peg.

Therefore, in the past the market module parameters have been subject to change as Terra grows. Here’s a brief history of the various parameter change proposals passed so far. Some of these were initiated by Jump Crypto who are one of the top market-makers who know what they are talking about 😁

  1. Prop 10 (6/7/2020) — Increase Tobin tax from 0.25% to 0.35% to prevent arbitrage attacks.
  2. Prop 11 (9/2/2020)— Decrease minspread of Terra to Luna swaps from 2% to 0.5%.
  3. Prop 12 (9/2/2020) — Increase basepool value from 250,000 SDR to 625,000 SDR.
  4. Prop 18 (11/20/2020) — Decrease PoolRecoveryPeriod from 24-hour to 8-hour (4800 blocks).
  5. Prop 27 (1/25/2021) — Increase basepool to 7M SDR and decrease PoolRecoveryPeriod to 200 blocks. Estimated minting capacity of $10M.
  6. Prop 36 (2/10/2021) — Increase basepool to 13M SDR and decrease PoolRecoveryPeriod to 130 blocks. Estimated minting capacity of $20M.
  7. Prop 90 (5/23/2021) — Increase basepool to 32.5M SDR and decrease PoolRecoveryPeriod to 49 blocks. Estimated minting capacity of $135M.
  8. Prop 185 (2/1/2022) — Increase basepool to 50M SDR and decrease PoolRecoveryPeriod to 36 blocks. Estimated minting capacity of $293M.

For e.g. if the Prop 90 above had been live during during the May draw-down, the volatility from LUNA liquidations on Anchor would’ve been sufficiently absorbed on-chain maintaining the UST peg. In the future, Terra could even make these parameters adjust algorithmically!

Source: https://twitter.com/terra_money/status/1397397503746654212

## A practical example of the need for parameter changes

Furthermore, a practical example of viewing how these parameter updates help is to take a look at swapping a large amount of LUNA in Terra Station. Here’s an example comparing the “Market Module” vs “Astroport”. In this example, I’m using the same amount of LUNA that was used in the Columbus-5 Community Pool burn transactions i.e. 520,000 LUNA per transaction.

Here’s an example transaction — https://terrasco.pe/mainnet/tx/50912BF794754E036CA9F5DC46B60A1C3900AFE2054F46C6AEAD9676D354CF00

If you notice, 520,000 LUNA was swapped for ~16M UST at nearly ~32 UST per LUNA. However, the actual price of LUNA at the time was ~50UST. That’s an almost 56% delta between the market price and the swap price! This is why we need to always keep adjusting these parameters.

# Data Analysis

## Redemption Capacity

Let’s start with the redemption capacity but before that we need to understand a key thing about redemption capacity. This capacity doesn’t mean you can’t swap anymore. It’s a theoretical limit based on a set of parameters that is set to not exceed a specific spread/slippage target (I believe this is 2%. If anyone knows otherwise, let me know). This means that users can swap more than redemption capacity but they would be doing so with a high amount of slippage like we saw with the TFL community pool burn transactions above.

Alright, now on to the data. Here are the events in order:

  1. We can see that beginning in March 2021, we were already exceeding the expected redemption capacity of $20M per day. This was around the time of Anchor Protocol launch so exceeding this capacity by almost 2–3x was most likely TFL burning LUNA to fund Anchor yield reserve.
  2. After the May crypto market crash, we see the effect of Prop 90 raising the redemption capacity to 135M which was not exceeded until TFL started burning ~89M LUNA from the community burn wallet. As we saw from the example transaction above, burning that much LUNA, TFL paid spread fees of almost 56% on every transactions or 9M UST!
  3. As UST has continued its expansion cross-chain, we can see the effect of Prop 185 which raised the estimated redemption capacity to $293M. With this new capacity, we’re nowhere near to the cap even though almost $150M UST is being minted everyday.
Interactive version

If we look at the daily max mints, we can see that the redemption capacity has always far exceeded any one transaction. The peak that we see in this chart is the TFL Community pool burns.

Interactive version

Expressing the redemption capacity consumed as a %age of max, we can see the redemption capacity exceed 100% usage. Like I said previously, this is possible but it results in high slippage.

Interactive version

## Market module

Now let’s look at the market module data which makes this mint/burn behavior possible. I’m going to track the market module prices against the oracle/market prices. The goal of the market module is to allow minting $1 worth of UST into $1 worth of LUNA or vice-versa. If there is a large spread/slippage, then the market module is not achieving its goal.

We can see that over the last year, the average swap price on the market module is pretty close to the average oracle price and it’s hard to find a difference in both the lines.

Interactive version

If we look at the average swap spread, we can see that after a turbulent period, the spread has largely been hovering close to equilibrium i.e. 0. Back in March, average daily spread in excess of 2% was a common occurence.

NOTE: The formula I use for calculating spread % uses the swap_luna_price as a denominator. Therefore, if the LUNA price is greater than the Oracle price which is the case when swapping a large amount of UST → LUNA, then the spread % will appear negative in the charts.

Now if we look at the both directions of the swaps we can see some interesting behavior.

### LUNA → UST

On the LUNA → UST path, the swap price tends to depeg downwards from oracle price. Min price here represents the absolute lowest LUNA price paid on that day for a swap. Compared the the average oracle and swap prices we saw above, here we see definite daylight between the min prices.

Interactive version

But if we look at the overall spreads between the Oracle and Swap prices, we can see that the spreads have gotten tighter as a result of the parameter changes in Prop 185. We have not see na 10–20% spike in the max spread since February.

Interactive version

### UST → LUNA

Now looking at the reverse path, the swap price tends to depeg upwards from oracle price which is why we look at the “Max Price”. Compared to the “Min price (LUNA→UST)” above, there is less of a delta between the max prices.

Interactive version

> For this next chart, the y-axis values are different compared to the above chart because of the skew due to community pool burns in November 2021.

If we look at the spreads between the prices, we see less volatility here compared to the LUNA → UST chart normalized to the same max y-axis value.

Interactive version
LUNA → UST chart from above normalized to same y-axis max as UST → LUNA chart

# Conclusion

The market module is a fascinating piece of engineering and the promise of $UST hinges on this algorithm. Therefore, it’s very important to every Terra investor that this mechanism be as robust as possible. Overall, the market module seems to be doing its job pretty well of providing a swap mechanism with competitive spreads. And in the situations where its capabilities are exceeded, it’s clear that the Terra team (with the help of Jump Crypto who made a bunch of parameter proposals) are clearly watching this closely and are ready to respond.

# Appendix: A possible on-chain oracle attack?

As part of investigating the market module, I discovered some interesting behavior where people were paying 2x the price of LUNA by swapping UST → LUNA in small amounts. In these scenarios, here’s what the Max Oracle vs Swap price chart looked like.

Here is an example transactions where someone paid $234 for LUNA compared to the $118 market price at the time.


Since this kind of behavior started in October 2021, I counted 38 transactions where someone paid a 30% premium over the market price of LUNA. In each of these cases the UST amount swapped for LUNA was always less than $1.

I believe this is someone trying to execute an Oracle attack similar to the sequence described by Jump Crypto and Terra.




Product strategy by day. On-chain crypto analyst by night.