Terra Documentation
  • Docs
  • Whitepaper
  • Agora
  • GitHub

›MODULE SPECS

Overview

  • Introduction
  • Columbus-3 Mainnet

Terra Protocol

  • How Terra Works
  • Developer Guide
  • MODULE SPECS

    • Auth
    • Bank
    • Supply
    • Distribution
    • Staking
    • Slashing
    • Oracle
    • Market
    • Treasury
    • Governance

User Manual

    NODE SOFTWARE

    • Installation
    • User Guide
    • Light Client Daemon
    • terracli Reference
    • Ledger Nano Support
    • Production Settings
    • Join a Network
    • Deploy a Testnet

Validator Handbook

  • Introduction
  • Validator FAQ
  • GUIDES

    • Getting Started
    • Security Best Practices
    • Exchange Rate Oracle

Contribute

  • Contribution guidelines for Terra Core
  • Help to translate

Treasury

The Treasury module acts as the "central bank" of the Terra economy, measuring macroeconomic activity by observing indicators and adjusting monetary policy levers to modulate miner incentives toward stable, long-term growth.

While the Treasury stabilizes miner demand through adjusting rewards, the Market is responsible for Terra price-stability through arbitrage and market maker.

Observed Indicators

The Treasury observes three macroeconomic indicators for each epoch (set to 1 week) and keeps historical records of their values during previous epochs.

  • Tax Rewards: TTT, Income generated from transaction fees (stability fee) in a during the epoch.
  • Seigniorage Rewards: SSS, Amount of seignorage generated from Luna swaps to Terra during the epoch that is destined for ballot rewards inside the Oracle rewards.
  • Total Staked Luna: λ\lambdaλ, total Luna that has been staked by users and bonded by their delegated validators.

These indicators can be used to derive two other values, the Tax Reward per unit Luna represented by τ=T/λ\tau = T / \lambdaτ=T/λ, used in Updating Tax Rate, and total mining rewards R=T+SR = T + SR=T+S, simply the sum of the Tax Rewards and the Seigniorage Rewards, used in Updating Reward Weight.

The protocol can compute and compare the short-term (WindowShort) and long-term (WindowLong) rolling averages of the above indicators to determine the relative direction and velocity of the Terra economy.

Monetary Policy Levers

From Columbus-3, the Reward Weight lever replaces the previous lever for controlling the rate of Luna burn in seigniorage. Now, miners are compensated through burning from swap fees, and ballot rewards in the oracle.

  • Tax Rate rrr adjusts the amount of income coming from Terra transactions, limited by tax cap.

  • Reward Weight www which is the portion of seigniorage allocated for the reward pool for the ballot winners for correctly voting within the reward band of the weighted median of exchange rate in the Oracle module.

Updating Policies

Both Tax Rate and Reward Weight are stored as values in the KVStore, and can have their values updated through governance proposals once passed. The Treasury will also re-calibrate each lever once per epoch to stabilize unit returns for Luna, thereby ensuring predictable mining rewards from staking:

  • For Tax Rate, in order to make sure that unit mining rewards do not stay stagnant, the treasury adds a MiningIncrement so mining rewards increase steadily over time, described here.

  • For Reward Weight, The Treasury observes the portion of burden seigniorage needed to bear the overall reward profile, SeigniorageBurdenTarget, and hikes up rates accordingly, described here.

Policy Constraints

Policy updates from both governance proposals and automatic calibration are constrained by the TaxPolicy and RewardPolicy parameters, respectively. The type PolicyConstraints specifies the floor, ceiling, and the max periodic changes for each variable.

// PolicyConstraints defines constraints around updating a key Treasury variable
type PolicyConstraints struct {
    RateMin       sdk.Dec  `json:"rate_min"`
    RateMax       sdk.Dec  `json:"rate_max"`
    Cap           sdk.Coin `json:"cap"`
    ChangeRateMax sdk.Dec  `json:"change_max"`
}

The logic for constraining a policy lever update is performed by pc.Clamp(), shown below.

// Clamp constrains a policy variable update within the policy constraints
func (pc PolicyConstraints) Clamp(prevRate sdk.Dec, newRate sdk.Dec) (clampedRate sdk.Dec) {
    if newRate.LT(pc.RateMin) {
        newRate = pc.RateMin
    } else if newRate.GT(pc.RateMax) {
        newRate = pc.RateMax
    }

    delta := newRate.Sub(prevRate)
    if newRate.GT(prevRate) {
        if delta.GT(pc.ChangeRateMax) {
            newRate = prevRate.Add(pc.ChangeRateMax)
        }
    } else {
        if delta.Abs().GT(pc.ChangeRateMax) {
            newRate = prevRate.Sub(pc.ChangeRateMax)
        }
    }
    return newRate
}

Probation

A probationary period specified by the WindowProbation will prevent the network from performing updates for Tax Rate and Reward Weight during the first epochs after genesis to allow the blockchain to first obtain a critical mass of transactions and a mature and reliable history of indicators.

Governance Proposals

The Treasury module defines special proposals which allow the Tax Rate and Reward Weight values in the KVStore to be voted on and changed accordingly, subject to the policy constraints imposed by pc.Clamp().

TaxRateUpdateProposal

type TaxRateUpdateProposal struct {
    Title       string  `json:"title" yaml:"title"`             // Title of the Proposal
    Description string  `json:"description" yaml:"description"` // Description of the Proposal
    TaxRate     sdk.Dec `json:"tax_rate" yaml:"tax_rate"`       // target TaxRate
}

RewardWeightUpdateProposal

type RewardWeightUpdateProposal struct {
    Title        string  `json:"title" yaml:"title"`                 // Title of the Proposal
    Description  string  `json:"description" yaml:"description"`     // Description of the Proposal
    RewardWeight sdk.Dec `json:"reward_weight" yaml:"reward_weight"` // target RewardWeight
}

State

Tax Rate

  • k.GetTaxRate(ctx) sdk.Dec
  • k.SetTaxRate(ctx, taxRate sdk.Dec)

sdk.Dec representing the value of the Tax Rate policy lever for the current epoch.

  • default value: sdk.NewDecWithPrec(1, 3) (0.1%)

Reward Weight

  • k.GetRewardWeight(ctx) sdk.Dec
  • k.SetRewardWeight(ctx, rewardWeight sdk.Dec)

sdk.Dec representing the value of the Reward Weight policy lever for the current epoch.

  • default value: sdk.NewDecWithPrec(5, 2) (5%)

Tax Caps

  • k.GetTaxCap(ctx, denom string) sdk.Int
  • k.SetTaxCap(ctx, denom string, taxCap sdk.Int)

Treasury keeps a KVStore that maps a denomination denom to an sdk.Int that represents that maximum income that can be generated from taxes on a transaction in that denomination. This is updated every epoch with the equivalent value of TaxPolicy.Cap at the current exchange rate.

For instance, if a transaction's value were 100 SDT, and tax rate and tax cap 5% and 1 SDT respectively, the income generated from the transaction would be 1 SDT instead of 5 SDT, as it exceeds the tax cap.

Tax Proceeds

  • k.RecordEpochTaxProceeds(ctx, delta sdk.Coins)
  • k.PeekEpochTaxProceeds(ctx) sdk.Coins

The sdk.Coins that represents the Tax Rewards TTT for the current epoch.

Epoch Initial Issuance

  • k.RecordEpochInitialIssuance(ctx)
  • k.PeekEpochSeigniorage(ctx) sdk.Int

The sdk.Coins that represents the total supply of Luna at the beginning of the current epoch. This value is used in k.SettleSeigniorage() to calculate the seigniorage to distribute at the end of the epoch.

Recording the initial issuance will automatically use the Supply module to determine the total issuance of Luna. Peeking will return the epoch's initial issuance of µLuna as sdk.Int instead of sdk.Coins for convenience.

Indicators

The Treasury keeps track of following indicators for the present and previous epochs:

Tax Rewards

  • k.GetTR(ctx, epoch int64) sdk.Dec
  • k.SetTR(ctx, epoch int64, TR sdk.Dec)

An sdk.Dec representing the Tax Rewards TTT for the epoch.

Seigniorage Rewards

  • k.GetSR(ctx, epoch int64) sdk.Dec
  • k.SetSR(ctx, epoch int64, SR sdk.Dec)

An sdk.Dec representing the Seigniorage Rewards SSS for the epoch.

Total Staked Luna

  • k.GetTSL(ctx, epoch int64) sdk.Int
  • k.SetTSL(ctx, epoch int64, TSL sdk.Int)

An sdk.Int representing the Total Staked Luna λ\lambdaλ for the epoch.

Functions

k.UpdateIndicators()

func (k Keeper) UpdateIndicators(ctx sdk.Context)

This function gets run at the end of an epoch ttt and records the current values of tax rewards TTT, seigniorage rewards SSS, and total staked Luna λ\lambdaλ as the historic indicators for epoch ttt before moving to the next epoch t+1t+1t+1.

  • TtT_tT​t​​ is the current value in TaxProceeds
  • St=Σ∗wS_t = \Sigma * wS​t​​=Σ∗w, with epoch seigniorage Σ\SigmaΣ and reward weight www.
  • λt\lambda_tλ​t​​ is simply the result of staking.TotalBondedTokens()

k.UpdateTaxPolicy()

func (k Keeper) UpdateTaxPolicy(ctx sdk.Context) (newTaxRate sdk.Dec)

This function gets called at the end of an epoch to calculate the next value of the Tax Rate monetary lever.

Consider rtr_tr​t​​ to be the current tax rate, and nnn to be the MiningIncrement parameter.

  1. Calculate the rolling average τy\tau_yτ​y​​ of Tax Rewards per unit Luna over the last year WindowLong.

  2. Calculate the rolling average τm\tau_mτ​m​​ of Tax Rewards per unit Luna over the last month WindowShort.

  3. If τm=0\tau_m = 0τ​m​​=0, there was no tax revenue in the last month. The tax rate should thus be set to the maximum permitted by the Tax Policy.

  4. Otherwise, the new tax rate is rt+1=(nrtτy)/τmr_{t+1} = (n r_t \tau_y)/\tau_mr​t+1​​=(nr​t​​τ​y​​)/τ​m​​, subject to the rules of pc.Clamp() (see constraints).

As such, the Treasury hikes up tax rates when tax revenues in a shorter time window is performing poorly in comparison to the longer term tax revenue average. It lowers tax rates when short term tax revenues are outperforming the longer term index.

k.UpdateRewardPolicy()

func (k Keeper) UpdateRewardPolicy(ctx sdk.Context) (newRewardWeight sdk.Dec)

This function gets called at the end of an epoch to calculate the next value of the Reward Weight monetary lever.

Consider wtw_tw​t​​ to be the current reward weight, and bbb to be the SeigniorageBurdenTarget parameter.

  1. Calculate the sum of SmS_mS​m​​ of seignorage rewards over the last month WindowShort.

  2. Calculate the sum of RmR_mR​m​​ of total mining rewards over the last month WindowLong.

  3. If either Rm=0R_m = 0R​m​​=0 or Sm=0S_m = 0S​m​​=0 there was no mining and seigniorage rewards in the last month. The Rewards Weight should thus be set to the maximum permitted by the Reward Policy.

  4. Otherwise, the new Reward Weight is wt+1=bwtSm/Rmw_{t+1} = b w_t S_m / R_mw​t+1​​=bw​t​​S​m​​/R​m​​, subject to the rules of pc.Clamp() (see constraints).

k.UpdateTaxCap()

func (k Keeper) UpdateTaxCap(ctx sdk.Context) sdk.Coins

This function is called at the end of an epoch to compute the Tax Caps for every denomination for the next epoch.

For each denomination in circulation, the new Tax Cap for that denomination is set to be the global Tax Cap defined in the TaxPolicy parameter, at current exchange rates.

k.SettleSeigniorage()

func (k Keeper) SettleSeigniorage(ctx sdk.Context)

This function is called at the end of an epoch to compute seigniorage and forwards the funds to the Oracle module for ballot rewards, and the Distribution for the community pool.

  1. The seigniorage Σ\SigmaΣ of the current epoch is calculated by taking the difference between the Luna supply at the start of the epoch (Epoch Initial Issuance) and the Luna supply at the time of calling.

    Note that Σ>0\Sigma > 0Σ>0 when the current Luna supply is lower than at the start of the epoch, because the Luna had been burned from Luna swaps into Terra. See here.

  2. The Reward Weight www is the percentage of the seigniorage designated for ballot rewards. Amount SSS of new Luna is minted, and the Oracle module receives S=Σ∗wS = \Sigma * wS=Σ∗w of the seigniorage.

  3. The remainder of the coins Σ−S\Sigma - SΣ−S is sent to the Distribution module, where it is allocated into the community pool.

Transitions

End-Block

If the blockchain is at the final block of the epoch, the following procedure is run:

  1. Update all the indicators with k.UpdateIndicators()

  2. If the this current block is under probation, skip to step 6.

  3. Settle seigniorage accrued during the epoch and make funds available to ballot rewards and the community pool during the next epoch.

  4. Calculate the Tax Rate, Reward Weight, and Tax Cap for the next epoch.

  5. Emit the policy_update event, recording the new policy lever values.

  6. Finally, record the Luna issuance with k.RecordEpochInitialIssuance(). This will be used in calculating the seigniorage for the next epoch.

Parameters

The subspace for the Treasury module is treasury.

type Params struct {
    TaxPolicy               PolicyConstraints `json:"tax_policy" yaml:"tax_policy"`
    RewardPolicy            PolicyConstraints `json:"reward_policy" yaml:"reward_policy"`
    SeigniorageBurdenTarget sdk.Dec           `json:"seigniorage_burden_target" yaml:"seigniorage_burden_target"`
    MiningIncrement         sdk.Dec           `json:"mining_increment" yaml:"mining_increment"`
    WindowShort             int64             `json:"window_short" yaml:"window_short"`
    WindowLong              int64             `json:"window_long" yaml:"window_long"`
    WindowProbation         int64             `json:"window_probation" yaml:"window_probation"`
}

TaxPolicy

  • type: PolicyConstraints
  • default value:
DefaultTaxPolicy = PolicyConstraints{
    RateMin:       sdk.NewDecWithPrec(5, 4), // 0.05%
    RateMax:       sdk.NewDecWithPrec(1, 2), // 1%
    Cap:           sdk.NewCoin(core.MicroSDRDenom, sdk.OneInt().MulRaw(core.MicroUnit)), // 1 SDR Tax cap
    ChangeRateMax: sdk.NewDecWithPrec(25, 5), // 0.025%
}

Constraints / rules for updating the Tax Rate monetary policy lever.

RewardPolicy

  • type: PolicyConstraints
  • default value:
DefaultRewardPolicy = PolicyConstraints{
    RateMin:       sdk.NewDecWithPrec(5, 2), // 5%
    RateMax:       sdk.NewDecWithPrec(90, 2), // 90%
    ChangeRateMax: sdk.NewDecWithPrec(25, 3), // 2.5%
    Cap:           sdk.NewCoin("unused", sdk.ZeroInt()), // UNUSED
}

Constraints / rules for updating the Reward Weight monetary policy lever.

SeigniorageBurdenTarget

  • type: sdk.Dec
  • default value: sdk.NewDecWithPrec(67, 2) (67%)

Multiplier specifying portion of burden seigniorage needed to bear the overall reward profile for Reward Weight updates during epoch transition.

MiningIncrement

  • type: sdk.Dec
  • default value: sdk.NewDecWithPrec(107, 2) (107%; exponential growth)

Multiplier determining an annual growth rate for Tax Rate policy updates during epoch transition.

WindowShort

  • type: int64
  • default value: 4 (month = 4 weeks)

A number of epochs that specifuies a time interval for calculating short-term moving average.

WindowLong

  • type: int64
  • default value: 52 (year = 52 weeks)

A number of epochs that specifies a time interval for calculating long-term moving average.

WindowProbation

  • type: int64
  • default value: 12 (3 months = 12 weeks)

A number of epochs that specifies a time interval for the probationary period.

Events

The Treasury module emits the following events:

policy_update

KeyValue
"tax_rate"tax rate
"reward_weight"reward weight
"tax_cap"tax cap

Errors

ErrInvalidEpoch

Called when the epoch queried exceeds the current epoch.

Last updated on 11/30/2019
← MarketGovernance →
  • Observed Indicators
  • Monetary Policy Levers
    • Updating Policies
    • Policy Constraints
    • Probation
  • Governance Proposals
    • TaxRateUpdateProposal
    • RewardWeightUpdateProposal
  • State
    • Tax Rate
    • Reward Weight
    • Tax Caps
    • Tax Proceeds
    • Epoch Initial Issuance
    • Indicators
  • Functions
    • k.UpdateIndicators()
    • k.UpdateTaxPolicy()
    • k.UpdateRewardPolicy()
    • k.UpdateTaxCap()
    • k.SettleSeigniorage()
  • Transitions
    • End-Block
  • Parameters
    • TaxPolicy
    • RewardPolicy
    • SeigniorageBurdenTarget
    • MiningIncrement
    • WindowShort
    • WindowLong
    • WindowProbation
  • Events
    • policy_update
  • Errors
    • ErrInvalidEpoch
Terra Documentation
Docs
Getting Started (or other categories)Guides (or other categories)API Reference (or other categories)
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHubStar
Follow @terra_money
© 2019 Terra