YB Token
The source code of the YB.vy
contract can be found on GitHub. The contract is written with Vyper version 0.4.3.
Overview
The YB contract is the governance token for the Yield Basis protocol. It extends standard ERC20 functionality with inflationary emissions that follow an exponential decay curve. The token implements a minting mechanism where authorized minters (GaugeController
) can emit tokens based on time elapsed and a configurable rate factor.
- Exponential Decay Emissions: Token emissions follow an exponential decay curve
- Reserve-Based Minting: Total supply is limited by a reserve amount
- Rate Factor Control: Minters can control emission rate via rate_factor parameter
- Ownable + Minter System: Combines ownership control with minter permissions
- Controlled Emissions Start: Emissions clock starts when ownership is renounced or
start_emissions()
is called - Minter-Based Minting: Actual tokens are minted only when authorized minters call
emit()
Related: See GaugeController for emission distribution and VotingEscrow for governance voting.
Public Variables
Emission State
reserve
(uint256
) - Remaining tokens available to be mintedlast_minted
(uint256
) - Timestamp of the last minting eventmax_mint_rate
(uint256
) - Maximum emission rate (immutable)
Function Documentation
_emissions()
Description:
Internal function that calculates the amount of tokens to emit based on time elapsed and rate factor. Uses exponential decay formula.
Returns:
uint256
- Amount of tokens to emit.
Input | Type | Description |
---|---|---|
t | uint256 | Target timestamp for emission calculation |
rate_factor | uint256 | Rate factor (100% = 10^18) |
📄 View Source Code
@internal
@view
def _emissions(t: uint256, rate_factor: uint256) -> uint256:
assert rate_factor <= 10**18
last_minted: uint256 = self.last_minted
if last_minted == 0 or t <= last_minted:
return 0
else:
dt: int256 = convert(t - last_minted, int256)
rate_36: int256 = convert(max_mint_rate * rate_factor, int256)
reserve: int256 = convert(self.reserve, int256)
return convert(
reserve * (10**18 - math._wad_exp(-dt * rate_36 // 10**18)) // 10**18,
uint256)
Formula:
amount = reserve * (1 - exp(-dt * max_mint_rate * rate_factor / 10^18))
Where rate_factor ∈ [0, 10^18]
and dt
is the time elapsed since last mint.
</div>
</details>
</div>
---
<div class="doc-box">
### `preview_emissions()`
<div class="divider"></div>
**Description:**
View function to preview the amount of tokens that would be emitted for a given timestamp and rate factor.
> **Note:** This is the token-level `preview_emissions()`. The `GaugeController` also has a `preview_emissions(gauge, at_time)` function for per-gauge emission previews.
**Returns:**
`uint256` - Preview of tokens to be emitted.
| Input | Type | Description |
|-------|------|-------------|
| `t` | `uint256` | Target timestamp for emission calculation |
| `rate_factor` | `uint256` | Rate factor (100% = 10^18) |
<details>
<summary>📄 View Source Code</summary>
<div>
```python
@external
@view
def preview_emissions(t: uint256, rate_factor: uint256) -> uint256:
return self._emissions(t, rate_factor)
start_emissions()
Description:
Starts the emission process by setting the initial timestamp. Only callable by the owner.
Emits:
None.
📄 View Source Code
@external
def start_emissions():
ownable._check_owner()
if self.last_minted == 0:
self.last_minted = block.timestamp
renounce_ownership()
Description:
Renounces ownership and forces emissions to start if not already started. Removes the caller as a minter.
Emits:
RoleMinterChanged
- When the caller is removed as a minter.
📄 View Source Code
@external
def renounce_ownership():
ownable._check_owner()
# Force-start emissions when renouncing ownership
if self.last_minted == 0:
self.last_minted = block.timestamp
erc20.is_minter[msg.sender] = False
log erc20.RoleMinterChanged(minter=msg.sender, status=False)
ownable._transfer_ownership(empty(address))
emit()
Description:
Mints tokens to the specified owner based on time elapsed and rate factor. Only callable by authorized minters.
Returns:
uint256
- Amount of tokens minted.
Emits:
Transfer
- When tokens are minted from address(0)
to the owner.
Input | Type | Description |
---|---|---|
owner | address | Address to receive the minted tokens |
rate_factor | uint256 | Rate factor (100% = 10^18) |
📄 View Source Code
@external
def emit(owner: address, rate_factor: uint256) -> uint256:
assert erc20.is_minter[msg.sender], "erc20: access is denied"
amount: uint256 = 0
if self.last_minted > 0:
amount = self._emissions(block.timestamp, rate_factor)
self.reserve -= amount
self.last_minted = block.timestamp
erc20._mint(owner, amount)
return amount
Emission Mechanics
The YB token implements a sophisticated emission system with exponential decay and rate factor control:
Exponential Decay Formula
The emission calculation uses an exponential decay formula:
emission = reserve * (1 - e^(-dt * rate * rate_factor))
Where:
dt
= time elapsed since last mintrate
= max_mint_rate (immutable)rate_factor
= percentage of max rate (0-100%)reserve
= remaining tokens available
Rate Factor Control
- 100% rate =
rate_factor = 10^18
- 50% rate =
rate_factor = 5 * 10^17
- 0% rate =
rate_factor = 0
Integration with GaugeController
The YB token is designed to work with the GaugeController that:
- Controls emission rates via
rate_factor
parameter - Distributes rewards to different gauges/pools
- Manages governance through token voting power
In Yield Basis deployments, GaugeController
is granted the minter role and mints to itself, then distributes to gauges. The token contract is generic and simply enforces erc20.is_minter[address]
permissions.
See also: VotingEscrow for how locked YB tokens provide voting power in the governance system.
Emission Timeline
- Deployment:
reserve
andmax_mint_rate
are set (max_mint_rate is immutable) - Emissions Start: Clock starts when ownership is renounced or
start_emissions()
is called - Minting: Authorized minters call
emit()
to mint tokens - Decay: Emission rate decreases exponentially over time
- Completion: When reserve reaches 0, no more tokens can be minted