Skip to content

Distribution Vault

Also called TTGVault or Vault.

Overview

The Distribution Vault is a core component of M0's economic infrastructure designed to collect, account for, and distribute various forms of protocol revenue and yield to Zero token holders. It serves as the primary mechanism for value capture within the protocol's Two Token Governance (TTG) system, turning protocol operations into direct economic benefits for governance participants.

Purpose and Role in the Ecosystem

The Distribution Vault functions as:

  1. Revenue Collector: Accumulates various forms of protocol revenue from multiple sources
  2. Yield Distributor: Enables proportional distribution of accumulated tokens to Zero token holders
  3. Value Capture Mechanism: Creates a direct incentive for governance participation
  4. Economic Bridge: Links protocol activity with governance token value

Architecture Overview

The Distribution Vault is referenced throughout the system as TTGVault or simply vault. Its address is established during deployment and made accessible to other core contracts via the Registrar or specific deployer contracts.

Adopted Guidance Change Process Flow

Revenue Sources

The Distribution Vault accumulates tokens from four primary sources:

1. Excess $M token Yield (from MinterGateway)

This represents a continuous source of $M token revenue coming from the interest rate spread between minters and earners.

Mechanism:
  • The MinterGateway.updateIndex() function calculates excessOwedM() - the difference between total owed M (based on minter interest rate) and actual total M supply (growing at the earner interest rate)
  • When positive, this excess amount is minted directly to the Distribution Vault
Origin of Excess:
  • Interest Rate Spread: The inherent difference between the higher interest rate charged to minters vs. the lower rate paid to earners (capped at 98% of the "safe rate")
  • Rounding Differences: Accumulated from principal-to-present value conversions within the continuous indexing math
  • Penalties: Charges imposed on minters for missed collateral updates or undercollateralization
Code Implementation:
// In MinterGateway.sol
function updateIndex() public override(IContinuousIndexing, ContinuousIndexing) returns (uint128 index_) {
    uint240 excessOwedM_ = excessOwedM();
    if (excessOwedM_ > 0) IMToken(mToken).mint(ttgVault, excessOwedM_); // Mint M to TTG Vault
    index_ = super.updateIndex(); // Update minter index and rate
    IMToken(mToken).updateIndex(); // Update earning index and rate
}

2. Power token Auction Proceeds (from Power token)

The Power token contract auctions off newly inflated tokens in non-voting epochs, with proceeds directed to the vault.

Mechanism:
  • Power token employs a Dutch auction mechanism where price decreases over time
  • When users purchase Power token via the buy() function, the calculated cost in cashToken is sent to the Distribution Vault
Code Implementation:
// In Power token.sol
function buy(
    uint256 minAmount_,
    uint256 maxAmount_,
    address destination_,
    uint16 expiryEpoch_
) external returns (uint240 amount_, uint256 cost_) {
    // ... calculations for amount_ and cost_ ...
    emit Buy(msg.sender, amount_, cost_ = getCost(amount_));
    _mint(destination_, amount_);
    if (!ERC20Helper.transferFrom(cashToken(), msg.sender, vault, cost_)) revert TransferFromFailed();
}

3. Forfeited Standard Governor Proposal Fees (from StandardGovernor)

Fees submitted with proposals in the StandardGovernor flow to the Distribution Vault if the proposal fails.

Mechanism:
  • When creating a proposal via ``StandardGovernor.propose(), proposers pay a fee in the system's cashToken
  • If the proposal succeeds, the fee is returned to the proposer
  • If the proposal is defeated or expires, the fee is sent to the Distribution Vault via sendProposalFeeToVault()
Code Implementation:
// In `StandardGovernor`.sol
function sendProposalFeeToVault(uint256 proposalId_) external {
    ProposalState state_ = state(proposalId_);
    if (state_ != ProposalState.Expired && state_ != ProposalState.Defeated) revert FeeNotDestinedForVault(state_);
    uint256 proposalFee_ = _proposalFees[proposalId_].fee;
    if (proposalFee_ == 0) revert NoFeeToSend();
    address cashToken_ = _proposalFees[proposalId_].cashToken;
    delete _proposalFees[proposalId_];
    emit ProposalFeeSentToVault(proposalId_, cashToken_, proposalFee_);
    _transfer(cashToken_, vault, proposalFee_);
}

4. Direct/Arbitrary Transfers

The Distribution Vault can receive tokens via direct transfers from any account or contract. These tokens need to be explicitly "registered" for distribution.

Mechanism:
  • Tokens can be transferred directly to the vault contract address at any time using a standard ERC20 transfer or transferFrom.
  • To make these externally transferred tokens available for claiming, the external distribute(token) function must be called for the specific token_ address.
  • The distribute(token) function calculates the increase in the vault's balance of token_ since the last time distribute was called for that token (using the internal _lastTokenBalances tracking). It then registers this newly available amount (amount_) within the currentEpoch_ (the epoch when distribute() is called).
  • Crucially, Zero token holders who held tokens during this currentEpoch_ become eligible to claim a share of this distributed amount_. The claimable share for each eligible holder is calculated pro-rata based on their fractional Zero token ownership as snapshotted at the end of that specific currentEpoch_.
Code Implementation:
// In DistributionVault.sol
function distribute(address token_) external returns (uint256 amount_) {
    uint256 currentEpoch_ = clock();
    amount_ = getDistributable(token_);
    emit Distribution(token_, currentEpoch_, amount_);
    unchecked {
        distributionOfAt[token_][currentEpoch_] += amount_; // Add to the distribution for the current epoch
        _lastTokenBalances[token_] += amount_; // Track this contract's latest balance
    }
}
 
function getDistributable(address token_) public view returns (uint256) {
    return IERC20(token_).balanceOf(address(this)) - _lastTokenBalances[token_];
}

Distribution Mechanism

The Distribution Vault implements a sophisticated epoch-based, pro-rata distribution system that ensures fair allocation of accumulated tokens to Zero token holders.

Core Mechanics

  1. Epoch-Based Accounting: Each token distribution is recorded in the current epoch (a time period, typically 15 days)
  2. Historical Balances: Claims are calculated based on Zero token holders' balances during past epochs
  3. Pro-Rata Distribution: Tokens are distributed proportionally to Zero token holdings
  4. Claimable Design: Zero token holders must claim their share of distributed tokens

Distribution Process

  1. Accumulation: Tokens flow into the vault through various mechanisms
  2. Registration: The distribute(token) function is called, recording the new token amount for the current epoch
  3. Claiming: Zero token holders call claim() or claimBySig() to receive their share for specific epochs
  4. Calculation: The claimable amount is computed based on the holder's proportional ownership during each claimed epoch

Key Functions

// Allows claiming tokens for a specific address
function claim(
    address token_,
    uint256 startEpoch_,
    uint256 endEpoch_,
    address destination_
) external returns (uint256 claimed_)
 
// Allows claiming with a signature (helpful for gas-less transactions)
function claimBySig(
    address account_,
    address token_,
    uint256 startEpoch_,
    uint256 endEpoch_,
    address destination_,
    uint256 deadline_,
    bytes memory signature_
) external returns (uint256 claimed_)
 
// Calculates the claimable amount for a given address, token, and epoch range
function getClaimable(
    address token_,
    address account_,
    uint256 startEpoch_,
    uint256 endEpoch_
) public view returns (uint256 claimable_)

Technical Implementation Details

Contract Structure

The Distribution Vault inherits from StatefulERC712, which provides EIP-712 signature verification functionality:

contract DistributionVault is IDistributionVault, StatefulERC712 {
    // ... implementation ...
}

Key State Variables

// The scale to apply when accumulating an account's claimable token to minimize precision loss
uint256 internal constant _GRANULARITY = 1e9;
 
// EIP-712 typehash for claim function
bytes32 public constant CLAIM_TYPEHASH = 0x4b4633c3c305de33d5d9cf70f2712f26961648cd68d020c2556a9e43be58051d;
 
// Address of the Zero Token contract
address public immutable zeroToken;
 
// Mapping of last recorded balance per token
mapping(address token => uint256 balance) internal _lastTokenBalances;
 
// Mapping of distributions per token, epoch, and amount
mapping(address token => mapping(uint256 epoch => uint256 amount)) public distributionOfAt;
 
// Mapping of claimed status per token, epoch, and account
mapping(address token => mapping(uint256 epoch => mapping(address account => bool claimed))) public hasClaimed;

Signature-Based Claims

The Distribution Vault supports signature-based claims, allowing users to authorize others to claim on their behalf:

function claimBySig(
    address account_,
    address token_,
    uint256 startEpoch_,
    uint256 endEpoch_,
    address destination_,
    uint256 deadline_,
    uint8 v_,
    bytes32 r_,
    bytes32 s_
) external returns (uint256) {
    // ... signature verification logic ...
    // ... deadline verification ...
    return _claim(account_, token_, startEpoch_, endEpoch_, destination_);
}

Economic Implications

The Distribution Vault creates several important economic effects:

  1. Zero token Value Accrual: By directing protocol revenue to Zero token holders, it increases the token's economic value
  2. Governance Incentive Alignment: Creates a direct financial incentive for governance participation
  3. Sustainable Protocol Economics: Ensures that excess yield and protocol fees flow back to governance participants
  4. Diversified Revenue Streams: Collects different token types from multiple sources, creating a robust revenue model

Integration with the Wrapped $M token (Extended)

The Distribution Vault also interacts with the Wrapped $M (wM) system:

  1. Excess Yield Destination: The wM contract can send excess yield to the Distribution Vault via its claimExcess() function
  2. Economic Loop Completion: This creates a complete economic loop where even wrapped token operations can generate value for governance participants

Conclusion

The Distribution Vault stands as a critical infrastructure component in the M0 protocol, serving as the central hub for value capture and redistribution. Through its sophisticated pro-rata distribution mechanism, it ensures that protocol operations consistently generate value for governance participants, creating strong economic incentives for system participation and alignment.

By collecting revenue from multiple sources and making it claimable by Zero token holders, the vault creates a direct financial link between protocol usage and governance value, strengthening the economic sustainability of the entire ecosystem.

One can check straight on the dashboard the number of M accumulated by the vault and not claimed so far.