Skip to content

catbagcrypto/YieldFu

Repository files navigation

YieldFu Project Documentation

Overview

YieldFu is an innovative decentralized finance (DeFi) protocol that builds upon the foundations of Olympus DAO while introducing several unique features. The protocol incorporates advanced yield generation strategies, risk management techniques, and various user-centric functionalities to create a sustainable and rewarding ecosystem.

YieldFu is structured as a modular system utilizing separate smart contracts (referred to as modules) for various functions such as token debasement, staking rewards, bonding, treasury management, and more. These modules interact through a central Kernel contract, which manages execution permissions and dependencies between the modules and policies.

Key Features

  • Modular Architecture: YieldFu separates key functionalities into distinct modules, providing flexibility and extensibility.
  • Debasing Mechanism: The protocol's native token, YieldFu, undergoes periodic debasement (devaluation) by 3% per day.
  • Staking Rewards: Users can stake YieldFu tokens to earn rewards with a base APY of 1333%, with occasional boosts to 8888%.
  • Bonding with Discounts: Users can purchase discounted bonds (ETH, USDT, or partner tokens) that mature in 3 days. The discount percentages are adjustable.
  • Treasury Management: The protocol manages its treasury through the TRSRY module, handling withdrawals, debt, and fund allocations.
  • Fully Permissioned: All module functionalities are gated by permissions managed via the Kernel contract, ensuring secure interaction between contracts.

Project Structure

Contracts and Modules

The YieldFu protocol is broken down into the following key components:

1. Kernel.sol

The Kernel is the core contract that manages all interactions between the modules and policies. It handles permissions, module installation, policy activation, and upgrading modules.

2. YieldFuToken.sol

The native ERC20 token, YieldFu, has minting, burning, and permissioned functionality. It is fully integrated with the modular system, allowing other contracts (like MINTR and DEBASE) to control token supply dynamics.

3. Modules

a) DEBASE.sol

This module handles the periodic debasement (devaluation) of the YieldFu token. It debases the token supply by 3% daily and is executed by the TokenPolicy contract.

b) MINTR.sol

This module manages minting and burning of YieldFu tokens. The module supports:

  • Minting tokens to specific addresses.
  • Burning tokens by transferring them to a black hole (dead address).
  • Emergency shutdown and reactivation of minting and burning functionality.

c) STAKE.sol

This module manages staking functionality. Users stake their YieldFu tokens to earn rewards at a base APY of 1333%, with the possibility of a temporary boost to 8888%. It keeps track of staking balances and calculates rewards based on the staking duration.

d) BOND.sol

This module allows users to bond ETH, USDT, or partner tokens to receive YieldFu tokens at a discount (e.g., 15% for ETH/USDT and 25% for partner tokens). The bond matures in 3 days, and the discount percentages are configurable by the governance.

e) TRSRY.sol

This module manages the protocol’s treasury. It allows for pre-approved withdrawals, manages debt, and keeps track of the protocol's reserves. This module is essential for handling the funds generated by bonding and other mechanisms.

4. Policies

Policies are external-facing contracts that interact with the modules to perform specific functions. The policies control user interactions such as staking, bonding, debasing, and treasury management. Each policy has specific permissions granted by the Kernel contract.

a) TokenPolicy.sol

Handles debasing and transferring of YieldFu tokens. Calls the debase function from the DEBASE module and manages token transfers.

b) StakingPolicy.sol

Allows users to stake and unstake YieldFu tokens through the STAKE module. Also supports claiming staking rewards and boosting APY temporarily.

c) BondingPolicy.sol

Manages bonding of ETH, USDT, and partner tokens through the BOND module. Users can bond these tokens and claim discounted YieldFu tokens after a 3-day maturity period.

d) TreasuryPolicy.sol

Allows for interaction with the TRSRY module. This policy handles treasury withdrawals, authorizes debt, and manages reserves.


Setup and Installation

Prerequisites

Ensure that you have the following installed:

  • Node.js (v16 or higher)
  • Hardhat
  • A Web3 wallet (e.g., MetaMask) for interaction

Installation Steps

  1. Clone the Repository:

    git clone https://github.com/your-repo/yieldfu.git
    cd yieldfu
  2. Install Dependencies:

    npm install
  3. Compile Contracts:

    npx hardhat compile
  4. Run Tests:

    npx hardhat test
  5. Deploy Contracts: Adjust deployment scripts under scripts/deploy.js and deploy to the network:

    npx hardhat run scripts/deploy.js --network <network-name>

YieldFu Kernel and Module Documentation

Kernel.sol

Overview

The Kernel contract is the central hub of the protocol, coordinating the interactions between various Modules and Policies. It manages permissions, ensures modularity, and enforces security across the protocol by restricting actions to approved entities.

The Kernel contract defines the execution flow of protocol-wide actions, including module installation, upgrades, policy activation, and permission handling. Its core functionality includes enabling Policies to execute protocol-specific logic within Modules, which store the protocol's state and operations.


Key Components and Types

Actions Enum

Defines a set of protocol-wide actions that can be executed by the Kernel or authorized entities:

  • InstallModule: Install a new module into the Kernel.
  • UpgradeModule: Upgrade an existing module.
  • ActivatePolicy: Activate a new policy, granting it permissions.
  • DeactivatePolicy: Deactivate a policy, removing its permissions.
  • ChangeExecutor: Change the address of the Kernel executor.
  • MigrateKernel: Migrate the protocol to a new Kernel.
  • ExecuteAction: Execute an arbitrary function in a module.

Instruction Struct

Encapsulates actions and their associated target addresses, which are executed by the Kernel.

struct Instruction {
    Actions action;
    address target;
}

Permissions Struct

Defines a permission request for a policy, consisting of a Keycode (identifying a module) and a function selector (which is the function signature).

struct Permissions {
    Keycode keycode;
    bytes4 funcSelector;
}

Keycode Type

A Keycode is a type-safe representation of a module identifier. It wraps around a bytes5 value, representing the key used to map a module to its specific functionality.


Utility Functions

toKeycode(bytes5 keycode_):

Converts a bytes5 value into a Keycode.

function toKeycode(bytes5 keycode_) pure returns (Keycode) {
    return Keycode.wrap(keycode_);
}

fromKeycode(Keycode keycode_):

Unwraps a Keycode to its underlying bytes5 value.

function fromKeycode(Keycode keycode_) pure returns (bytes5) {
    return Keycode.unwrap(keycode_);
}

ensureContract(address target_):

Ensures that the target address is a valid contract by checking if it has bytecode deployed.

function ensureContract(address target_) view {
    if (target_.code.length == 0) revert TargetNotAContract(target_);
}

ensureValidKeycode(Keycode keycode_):

Validates that a Keycode is a valid alphanumeric code, restricted to uppercase letters (A-Z).

function ensureValidKeycode(Keycode keycode_) pure {
    bytes5 unwrapped = Keycode.unwrap(keycode_);
    for (uint256 i = 0; i < 5; ) {
        bytes1 char = unwrapped[i];
        if (char < 0x41 || char > 0x5A) revert InvalidKeycode(keycode_); // A-Z only
        unchecked {
            i++;
        }
    }
}

Kernel Contract

Kernel Constructor

The Kernel constructor initializes the contract by granting the deployer the DEFAULT_ADMIN_ROLE and setting the deployer as the protocol’s initial executor.

constructor() {
    executor = msg.sender;
    _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); // Granting the deployer DEFAULT_ADMIN_ROLE
}

Executor Management

The executor is the address that can install modules, activate policies, and execute protocol-level actions. Only the executor has permission to perform sensitive operations such as upgrading modules, changing the executor, or migrating the Kernel.

Installation of Modules

Modules are installed into the Kernel through the executeAction() function. The InstallModule action installs a module by linking its Keycode with its contract address and initializing it.

function _installModule(Module newModule_) internal {
    Keycode keycode = newModule_.KEYCODE();
    require(Keycode.unwrap(keycode) != bytes5(0), "Kernel: invalid keycode");

    if (address(getModuleForKeycode[keycode]) != address(0))
        revert Kernel_ModuleAlreadyInstalled(keycode);

    getModuleForKeycode[keycode] = newModule_;
    getKeycodeForModule[newModule_] = keycode;
    newModule_.INIT();
}

Upgrade of Modules

The UpgradeModule action replaces an existing module with a new implementation while retaining the same Keycode. This ensures that dependent Policies will interact with the new module.

function _upgradeModule(Module newModule_) internal {
    Keycode keycode = newModule_.KEYCODE();
    Module oldModule = getModuleForKeycode[keycode];

    if (address(oldModule) == address(0) || oldModule == newModule_)
        revert Kernel_InvalidModuleUpgrade(keycode);

    getKeycodeForModule[oldModule] = Keycode.wrap(bytes5(0));
    getKeycodeForModule[newModule_] = keycode;
    getModuleForKeycode[keycode] = newModule_;

    newModule_.INIT();
    _reconfigurePolicies(keycode);
}

Policy Activation and Deactivation

Policies are activated and deactivated through the ActivatePolicy and DeactivatePolicy actions, respectively. Activation grants the policy permission to interact with specific modules, while deactivation revokes those permissions.

function _activatePolicy(Policy policy_) internal {
    if (isPolicyActive(policy_)) revert Kernel_PolicyAlreadyActivated(address(policy_));
    activePolicies.push(policy_);
    getPolicyIndex[policy_] = activePolicies.length - 1;

    Keycode[] memory dependencies = policy_.configureDependencies();
    for (uint256 i = 0; i < dependencies.length; ++i) {
        moduleDependents[dependencies[i]].push(policy_);
    }

    Permissions[] memory requests = policy_.requestPermissions();
    _setPolicyPermissions(policy_, requests, true);
}

Permissions System

The Kernel uses a permission system to restrict the actions of Policies on Modules. Permissions are granted or revoked on a per-function basis, with function selectors specifying which functions the policy is allowed to execute.

function _setPolicyPermissions(
    Policy policy_,
    Permissions[] memory requests_,
    bool grant_
) internal {
    for (uint256 i = 0; i < requests_.length; ) {
        Permissions memory request = requests_[i];
        modulePermissions[request.keycode][policy_][request.funcSelector] = grant_;
        emit PermissionsUpdated(request.keycode, policy_, request.funcSelector, grant_);
        unchecked {
            ++i;
        }
    }
}

Module.sol

Module Contract

Modules store the protocol’s core logic and state. Modules do not execute business logic directly but instead respond to external calls from the Kernel, allowing Policies to interact with them based on granted permissions.

Permission Enforcement

The permissioned modifier ensures that only authorized Policies or the Kernel can execute certain functions.

modifier permissioned() {
    if (msg.sender == address(kernel)) {
        _;
        return;
    }
    bool hasPermission = kernel.modulePermissions(KEYCODE(), Policy(msg.sender), msg.sig);
    if (!hasPermission) {
        revert Module_PolicyNotPermitted(msg.sender);
    }
    _;
}

Keycode and Versioning

Each Module has a unique Keycode and supports versioning. The KEYCODE() function is used to identify the module, while the VERSION() function allows for tracking updates.


Policy.sol

Policy Contract

Policies contain the business logic of the protocol and interact with Modules via the Kernel. Policies must request permission from the Kernel to interact with specific functions within Modules.

Requesting Permissions

Policies declare their dependencies on Modules and the permissions they require to interact with those modules using the requestPermissions() and configureDependencies() functions.

function requestPermissions() external view virtual returns (Permissions[] memory requests) {}
function configureDependencies() external virtual returns (Keycode[] memory dependencies) {}

BONDS Module Documentation

Overview

The BONDS module is a key component of the YieldFu protocol. It allows users to bond ETH or partner tokens in exchange for discounted YieldFu tokens, promoting liquidity for the treasury while offering discounted tokens to users. This module handles bonding, calculating payouts, and managing bond claims. The contract is tightly integrated with the Kernel, ensuring that permissions are checked and enforced through the protocol's governance.

Features

  • ETH and Partner Token Bonding: Users can bond ETH or partner tokens to receive discounted payouts in YieldFu tokens.
  • Payout Calculations: Discount rates are configurable for both ETH and partner tokens.
  • Bond Maturity: Bonds mature after a fixed period, and users can claim their payouts after the bond matures.
  • Treasury Interaction: The contract withdraws payouts from the protocol's treasury to fulfill bond claims.
  • Bond Management: Allows for pausing/unpausing bonding, adjusting discount rates, and setting maximum bond sizes and cooldown periods.

Key Components

State Variables

  • YieldFuToken public token: The YieldFu token associated with the bond. Users receive this token as a payout for bonding.
  • address public treasury: The address of the treasury where ETH or partner tokens are sent upon bonding.
  • uint256 public ethDiscount: The discount applied to ETH bonding, represented in basis points (e.g., 150 represents a 15% discount).
  • uint256 public partnerDiscount: The discount applied to partner token bonding.
  • uint256 public constant BOND_MATURITY: The duration after which a bond matures (3 days).
  • uint256 public maxBondSize: The maximum size of any single bond.
  • uint256 public bondCooldown: The cooldown period between consecutive bond creations by the same user.

Bond Information Struct

The BondInfo struct stores details about each bond:

  • payout: The amount of YieldFu tokens the user is entitled to receive.
  • maturity: The timestamp when the bond matures.
  • claimed: Whether the bond has been claimed.
struct BondInfo {
    uint256 payout;
    uint256 maturity;
    bool claimed;
}

Events

The contract emits several events to notify external observers about key actions within the contract:

  1. BondCreated: Emitted when a bond is successfully created.

    • Parameters: user, amount, payout
  2. BondClaimed: Emitted when a bond is claimed.

    • Parameters: user, payout
  3. DiscountChanged: Emitted when the discount rates are changed.

    • Parameters: isEth, newDiscount
  4. BondingPaused: Emitted when bonding is paused.

    • Parameters: by
  5. BondingUnpaused: Emitted when bonding is unpaused.

    • Parameters: by
  6. MaxBondSizeChanged: Emitted when the maximum bond size is changed.

    • Parameters: newSize
  7. BondCooldownChanged: Emitted when the cooldown period is changed.

    • Parameters: newCooldown
  8. TreasuryChanged: Emitted when the treasury address is changed.

    • Parameters: newTreasury

Functions

Bonding ETH

Users can bond ETH through the bondEth function, which checks the bond size, calculates the payout based on the ETH discount, and transfers the ETH to the treasury.

function bondEth(address sender, uint256 value) external payable nonReentrant permissioned whenNotPaused {
    value.validateBond(maxBondSize, lastBondTime[sender], bondCooldown);
    uint256 payout = value.calculatePayout(ethDiscount);
    _createBond(sender, payout);
    (bool success, ) = payable(treasury).call{value: value}("");
    require(success, "ETH transfer to treasury failed");
    lastBondTime[sender] = block.timestamp;
}

Bonding Partner Tokens

Users can bond partner tokens using the bondPartnerToken function. It validates the bond size, calculates the payout using the partner token discount, and transfers the partner tokens to the treasury.

function bondPartnerToken(address sender, address tokenAddress, uint256 amount) external nonReentrant permissioned whenNotPaused {
    amount.validateBond(maxBondSize, lastBondTime[sender], bondCooldown);
    uint256 payout = amount.calculatePayout(partnerDiscount);
    _createBond(sender, payout);
    IERC20 partnerToken = IERC20(tokenAddress);
    bool success = partnerToken.transferFrom(sender, treasury, amount);
    require(success, "Token transfer to treasury failed");
    lastBondTime[sender] = block.timestamp;
}

Claiming a Bond

Once a bond has matured, the user can call claimBond to receive their YieldFu token payout from the treasury.

function claimBond() external permissioned whenNotPaused {
    BondInfo storage bond = bonds[msg.sender];
    require(block.timestamp >= bond.maturity, "Bond not matured");
    resetBond(msg.sender);
    kernel.executeAction(
        Actions.ExecuteAction,
        address(treasury),
        abi.encodeWithSelector(
            bytes4(keccak256("withdrawReserves(address,address,uint256)")),
            msg.sender,
            address(token),
            bond.payout
        )
    );
}

Managing Bond Settings

  1. changeDiscount(bool isEth, uint256 newDiscount): Allows adjusting the discount rates for ETH and partner token bonding.
  2. pauseBonding(): Pauses all bonding operations.
  3. unpauseBonding(): Unpauses bonding operations.
  4. setMaxBondSize(uint256 newSize): Sets the maximum bond size allowed.
  5. setBondCooldown(uint256 newCooldown): Sets the cooldown period between bond creations for a user.
  6. setTreasury(address newTreasury): Changes the treasury address to a new one.

Permission Management

The BONDS module interacts with the Kernel through permissioned functions. Only authorized Policies can call key functions, ensuring that only approved actions are taken on the bond contracts.

The permissioned modifier checks that the caller is either the Kernel or a Policy with the necessary permissions to call the function.


Treasury Interaction

The BONDS module is tightly integrated with the treasury, where all bonded ETH and partner tokens are stored. When a user claims their bond, the treasury releases the YieldFu token payout using the withdrawReserves function.


DEBASE Module Documentation

Overview

The DEBASE module implements a periodic debasement mechanism for the YieldFu token, enabling supply reduction by adjusting a debase index. This is done using a rebasing mechanism that decreases the effective supply of tokens over time based on a configurable debasement rate and interval. The debasement is only executed if the total supply meets a specified threshold, ensuring the debasement process doesn't go below a critical minimum supply.

Features

  • Adjustable Debasement: The debasement rate, interval, and minimum debasement threshold can be configured by authorized policies.
  • Rebasing Mechanism: The YieldFu token is debased by updating its internal index, which affects the effective balance of all token holders.
  • Pausable Operations: Debasement can be paused and unpaused by authorized policies to safeguard the protocol during emergency situations.
  • Time-based Execution: Debasement can only be triggered after a configurable interval has passed since the last debase event.

Key Components

State Variables

  • YieldFuToken public yieldFu: Reference to the YieldFu token, which is debased by this module.
  • uint256 public debaseRate: The debasement rate in basis points (bps), where 1 bp = 0.01%. For example, 100 would represent a 1% debasement.
  • uint256 public debaseInterval: The time interval between each debase event, specified in seconds.
  • uint256 public lastDebaseTime: The timestamp of the last debase event.
  • uint256 public minDebaseThreshold: Minimum token supply required to perform a debase event.
  • uint256 public debaseIndex: A scaling factor representing the debase effect, initialized at 1e18 (1:1 ratio).

Events

The module emits several events during key actions:

  1. Debase(uint256 newIndex): Emitted when a debasement occurs.

    • Parameters: newIndex (the updated debase index)
  2. DebaseRateChanged(uint256 newRate): Emitted when the debase rate is changed.

    • Parameters: newRate (the new debase rate)
  3. DebaseIntervalChanged(uint256 newInterval): Emitted when the debase interval is changed.

    • Parameters: newInterval (the new interval between debase events)
  4. MinDebaseThresholdChanged(uint256 newThreshold): Emitted when the minimum debase threshold is changed.

    • Parameters: newThreshold (the new minimum token supply threshold for debasing)
  5. DebasePaused(address indexed by): Emitted when debasing is paused.

    • Parameters: by (the address that paused debasing)
  6. DebaseUnpaused(address indexed by): Emitted when debasing is unpaused.

    • Parameters: by (the address that unpaused debasing)

Errors

The module defines several custom errors that are reverted upon failure:

  1. DEBASE_TooSoon(): Reverted if an attempt is made to debase before the debase interval has passed.
  2. DEBASE_InvalidRate(): Reverted if an invalid debase rate is provided.
  3. DEBASE_InvalidInterval(): Reverted if the debase interval is set outside the allowable range (1 hour to 30 days).
  4. DEBASE_InvalidThreshold(): Reverted if the minimum debase threshold is set to 0.
  5. DEBASE_BelowMinThreshold(): Reverted if the total supply of YieldFu is below the minimum threshold during a debase attempt.

Functions

Debase

The core function of the module is the debase() function, which reduces the effective supply of YieldFu by adjusting the debase index. This function is permissioned, meaning only authorized policies can call it, and it checks that the debase interval has passed since the last debase event.

function debase() external permissioned onlyAfterInterval whenNotPaused {
    uint256 totalSupply = yieldFu.totalSupply();
    if (totalSupply < minDebaseThreshold) {
        revert DEBASE_BelowMinThreshold();
    }

    uint256 newDebaseIndex = (yieldFu.debaseIndex() * (10000 - debaseRate)) / 10000;
    yieldFu.updateDebaseIndex(newDebaseIndex);

    lastDebaseTime = block.timestamp;
    emit Debase(newDebaseIndex);
}

Changing Debase Parameters

Authorized policies can modify the parameters that control the debasement process using the following functions:

  1. changeDebaseRate(uint256 newRate): Changes the debasement rate, with a maximum allowed value of 10% (1000 basis points).
function changeDebaseRate(uint256 newRate) external permissioned {
    if (newRate > 1000) revert DEBASE_InvalidRate(); // Max 10% debasement
    debaseRate = newRate;
    emit DebaseRateChanged(newRate);
}
  1. changeDebaseInterval(uint256 newInterval): Adjusts the time interval between debasements. The interval must be between 1 hour and 30 days.
function changeDebaseInterval(uint256 newInterval) external permissioned {
    if (newInterval < 1 hours || newInterval > 30 days) revert DEBASE_InvalidInterval();
    debaseInterval = newInterval;
    emit DebaseIntervalChanged(newInterval);
}
  1. changeMinDebaseThreshold(uint256 newThreshold): Sets the minimum token supply required for debasement to occur.
function changeMinDebaseThreshold(uint256 newThreshold) external permissioned {
    if (newThreshold == 0) revert DEBASE_InvalidThreshold();
    minDebaseThreshold = newThreshold;
    emit MinDebaseThresholdChanged(newThreshold);
}

Pause/Unpause Debasement

Debasement can be paused or unpaused by authorized policies using the pauseDebase and unpauseDebase functions, respectively. This provides the protocol with a safeguard to pause debasement during emergencies or when necessary for protocol management.

function pauseDebase() external permissioned {
    _pause();
    emit DebasePaused(msg.sender);
}

function unpauseDebase() external permissioned {
    _unpause();
    emit DebaseUnpaused(msg.sender);
}

View Functions

  1. getDebaseInfo(): Returns current debase settings and the status of the debase process, including the current rate, interval, and whether debasement is paused.
function getDebaseInfo() external view returns (
    uint256 currentRate,
    uint256 currentInterval,
    uint256 lastDebase,
    uint256 nextDebase,
    bool isPaused,
    uint256 currentIndex
) {
    return (
        debaseRate,
        debaseInterval,
        lastDebaseTime,
        lastDebaseTime + debaseInterval,
        paused(),
        debaseIndex
    );
}
  1. getEffectiveBalance(address account): Calculates the effective token balance for an account based on the debase index.
function getEffectiveBalance(address account) public view returns (uint256) {
    return (yieldFu.balanceOf(account) * debaseIndex) / 1e18;
}

Permission Management

The DEBASE module interacts with the Kernel to ensure that only authorized policies can call key functions. The permissioned modifier is used to restrict access to sensitive functions like debasement, changing rates, intervals, and thresholds, as well as pausing or unpausing the debase process.


MINTR Module Documentation

Overview

The MINTR module is responsible for managing the minting and burning of YieldFu tokens. It includes mechanisms for enforcing minting limits on a daily basis as well as per-policy minting caps. Authorized policies can request token minting and burning actions, and the module ensures that these operations adhere to the defined minting limits.

Key Features

  • Daily Minting Cap: A maximum number of tokens that can be minted daily, enforced across all policies.
  • Policy-Specific Mint Limits: Each policy has its own minting limit, restricting how much it can mint in a given day.
  • Token Burning: Authorized policies can burn YieldFu tokens.
  • Logging and Safety: Includes event logging for mint and burn operations and a pausable function to halt operations when necessary.

Key Components

State Variables

  • YieldFuToken public immutable yieldFu: Reference to the YieldFu token that will be minted or burned.
  • uint256 public dailyMintCap: The total number of tokens that can be minted across all policies in a single day.
  • uint256 public mintedToday: Tracks how many tokens have been minted so far today.
  • uint256 public lastMintDay: The timestamp of the last day when minting occurred.
  • mapping(address => uint256) public policyLimits: Specifies the maximum number of tokens that each policy is allowed to mint.
  • mapping(address => uint256) public policyMinted: Tracks how many tokens each policy has minted so far today.

Events

The module emits several events during key actions:

  1. Minted(address indexed to, uint256 amount): Emitted when tokens are successfully minted.
    • Parameters: to (the recipient address), amount (number of tokens minted)
  2. Burned(address indexed from, uint256 amount): Emitted when tokens are burned.
    • Parameters: from (the address whose tokens were burned), amount (number of tokens burned)
  3. MintCapChanged(uint256 newDailyMintCap): Emitted when the daily minting cap is changed.
    • Parameters: newDailyMintCap (the updated minting cap)
  4. MintLimitChanged(address indexed policy, uint256 newLimit): Emitted when a policy's minting limit is changed.
    • Parameters: policy (the policy whose limit is changed), newLimit (the updated mint limit)

Errors

The module defines custom errors for various failure scenarios:

  1. MINTR_Unauthorized(): Reverted if an unauthorized entity attempts to mint or burn tokens.
  2. MINTR_DailyCapExceeded(): Reverted if a mint request would exceed the daily minting cap.
  3. MINTR_PolicyLimitExceeded(): Reverted if a mint request would exceed the policy's specific minting limit.
  4. MINTR_InvalidMintCap(): Reverted if an attempt is made to set the daily mint cap to zero.
  5. MINTR_InvalidMintLimit(): Reverted if an invalid mint limit (e.g., zero) is provided for a policy.

Functions

Minting

The core function of this module is mint(), which allows authorized policies to mint new YieldFu tokens. This function checks that the requested mint amount is within the daily mint cap and the policy's specific limit before proceeding with the mint.

function mint(address policy_, address to_, uint256 amount_) external permissioned {
    _updateDailyMint(); // Reset daily limits if it's a new day
    _checkMintLimits(policy_, amount_); // Ensure minting stays within limits

    yieldFu.mint(to_, amount_); // Proceed with minting tokens
    policyMinted[policy_] += amount_; // Update policy minted amount
    mintedToday += amount_; // Update total minted today

    emit Minted(to_, amount_);
}

Burning

The burn() function allows authorized policies to burn tokens from a specified address. This is useful for reducing the total token supply or correcting mistakes.

function burn(address from, uint256 amount) external permissioned whenNotPaused {
    yieldFu.burnFrom(from, amount);
    emit Burned(from, amount);
}

Policy Limit Management

Each policy has a predefined limit that restricts how many tokens it can mint. These limits can be set or updated by authorized policies via the setPolicyLimit() function.

function setPolicyLimit(address policy, uint256 limit) external permissioned {
    if (limit == 0) revert MINTR_InvalidMintLimit();
    policyLimits[policy] = limit;
    emit MintLimitChanged(policy, limit);
}

Changing the Daily Mint Cap

The changeDailyMintCap() function allows the daily minting cap to be changed. This cap defines the maximum number of tokens that can be minted by all policies combined in a single day.

function changeDailyMintCap(uint256 newCap) external permissioned {
    if (newCap == 0) revert MINTR_InvalidMintCap();
    dailyMintCap = newCap;
    emit MintCapChanged(newCap);
}

Internal Utility Functions

  1. _updateDailyMint(): Resets the daily minting amount if the current day is different from the last recorded mint day.
function _updateDailyMint() internal {
    uint256 currentDay = block.timestamp / 1 days;
    if (currentDay > lastMintDay) {
        mintedToday = 0; // Reset daily mint
        lastMintDay = currentDay;
    }
}
  1. _checkMintLimits(): Ensures that the requested minting amount is within the policy's limit and the daily mint cap.
function _checkMintLimits(address policy, uint256 amount) internal view {
    if (mintedToday + amount > dailyMintCap) {
        revert MINTR_DailyCapExceeded();
    }
    if (policyMinted[policy] + amount > policyLimits[policy]) {
        revert MINTR_PolicyLimitExceeded();
    }
}

View Functions

  1. getPolicyInfo(): Returns the minting limit and the amount minted for a given policy.
function getPolicyInfo(address policy) external view returns (uint256 mintLimit, uint256 mintedAmount) {
    return (policyLimits[policy], policyMinted[policy]);
}
  1. getDailyMintInfo(): Returns the daily mint cap, the number of tokens minted today, and the remaining amount that can still be minted.
function getDailyMintInfo() external view returns (uint256 cap, uint256 minted, uint256 remaining) {
    return (dailyMintCap, mintedToday, dailyMintCap - mintedToday);
}

Permission Management

The MINTR module ensures that only authorized policies can call minting or burning functions using the permissioned modifier. The Kernel enforces permissions, ensuring that only policies with the necessary rights can request new tokens or burn existing ones.


Here is the detailed documentation for the STAKE contract:


STAKE Module Documentation

Overview

The STAKE module is responsible for handling the staking of YieldFu tokens. Users can stake tokens to earn rewards at a base Annual Percentage Yield (APY), with the possibility of temporary APY boosts. The contract also includes mechanisms for penalizing early unstaking, as well as imposing maximum staking caps and cooldown periods to control the flow of tokens.

Key Features

  • Staking and Unstaking: Users can stake YieldFu tokens and earn rewards over time, based on a configurable APY.
  • Reward Calculation: Rewards are calculated per token staked and are distributed periodically. A user’s rewards are updated each time they interact with the staking module.
  • Cooldowns: A cooldown period is enforced to prevent immediate withdrawal after staking, with penalties for early unstaking.
  • APY Management: The base and boosted APYs can be adjusted, allowing for reward increases for specific durations.
  • Max Stake Cap: Limits the total amount of tokens that can be staked in the system.
  • Early Unstaking Penalty: If users attempt to unstake before the cooldown period, a penalty (slash rate) is applied to the withdrawn amount.
  • Pausing: The contract can be paused in case of emergency, suspending staking and unstaking activities.

Key Components

State Variables

  • YieldFuToken public token: The YieldFu token that users will stake to earn rewards.
  • uint256 public baseAPY: The base Annual Percentage Yield (APY) that users earn when they stake tokens.
  • uint256 public boostedAPY: The boosted APY, applied for a limited time to increase rewards.
  • uint256 public boostEndTime: The time when the boosted APY will expire and revert to the base APY.
  • uint256 public cooldownPeriod: The cooldown period (in seconds) after staking, during which users cannot unstake without penalty.
  • uint256 public earlyUnstakeSlashRate: The percentage of tokens slashed when a user unstakes before the cooldown period ends.
  • uint256 public maxStakeCap: The maximum amount of tokens that can be staked in total.
  • uint256 public totalStaked: The total number of tokens currently staked in the system.
  • uint256 public rewardRate: The reward rate, which determines how rewards accumulate over time.
  • uint256 public lastUpdateTime: The last time the reward calculation was updated.
  • uint256 public rewardPerTokenStored: Tracks the cumulative rewards per staked token.

StakeInfo Struct

  • uint256 amount: The number of tokens the user has staked.
  • uint256 lastStakeTime: The last time the user staked tokens, used to calculate cooldowns.
  • uint256 userRewardPerTokenPaid: Tracks the reward per token that the user has already been paid.
  • uint256 rewards: The total rewards the user has earned but not yet claimed.

Mappings

  • mapping(address => StakeInfo) public stakes: Stores the staking information for each user.

Events

  1. Stake(address indexed user, uint256 amount): Emitted when a user stakes tokens.
    • Parameters: user (the address staking tokens), amount (the number of tokens staked)
  2. Unstake(address indexed user, uint256 amount): Emitted when a user unstakes tokens.
    • Parameters: user (the address unstaking tokens), amount (the number of tokens unstaked)
  3. APYChanged(uint256 newBaseAPY, uint256 newBoostedAPY): Emitted when the APY is updated.
    • Parameters: newBaseAPY (the updated base APY), newBoostedAPY (the updated boosted APY)
  4. APYBoosted(uint256 boostEndTime): Emitted when the APY is boosted.
    • Parameters: boostEndTime (the timestamp when the boost will expire)
  5. CooldownSet(uint256 newCooldown): Emitted when the cooldown period is updated.
    • Parameters: newCooldown (the updated cooldown period)
  6. EarlyUnstakeSlashSet(uint256 newSlashRate): Emitted when the early unstake penalty (slash rate) is updated.
    • Parameters: newSlashRate (the updated slash rate)
  7. MaxStakeCapSet(uint256 newMaxStakeCap): Emitted when the maximum staking cap is updated.
    • Parameters: newMaxStakeCap (the updated staking cap)

Errors

The module defines several custom errors to handle failure scenarios:

  1. STAKE_ZeroAmount(): Reverted if the stake or unstake amount is zero.
  2. STAKE_InsufficientBalance(): Reverted if a user tries to unstake more tokens than they have staked.
  3. STAKE_CooldownNotMet(): Reverted if a user tries to unstake before the cooldown period has passed.
  4. STAKE_MaxStakeCapExceeded(): Reverted if the total staked amount exceeds the maximum staking cap.
  5. STAKE_InvalidAPY(): Reverted if an invalid APY (above 100%) is set.
  6. STAKE_InvalidCooldown(): Reverted if an invalid cooldown period (above 30 days) is set.
  7. STAKE_InvalidSlashRate(): Reverted if an invalid slash rate (above 50%) is set.

Functions

Staking

Users can stake tokens using the stake() function. This function transfers tokens from the user to the contract and updates their staking balance. It also checks that the stake amount does not exceed the maximum stake cap.

function stake(address from, uint256 amount) external whenNotPaused updateReward(from) {
    if (amount == 0) revert STAKE_ZeroAmount();
    if (totalStaked + amount > maxStakeCap) revert STAKE_MaxStakeCapExceeded();
    // Transfer and update balances
    ...
}

Unstaking

The unstake() function allows users to withdraw staked tokens, with or without an early unstake penalty depending on whether the cooldown period has passed.

function unstake(address from, uint256 amount) external nonReentrant whenNotPaused permissioned updateReward(from) {
    StakeInfo storage userStake = stakes[from];
    if (block.timestamp < userStake.lastStakeTime + cooldownPeriod) {
        slashAmount = (amount * earlyUnstakeSlashRate) / 10000;
    }
    // Transfer and burn slashed tokens
    ...
}

Reward Calculation

Rewards are calculated based on the APY and the amount of time that has passed since the user staked their tokens.

  • rewardPerToken(): Calculates the reward per token since the last update.
  • earned(): Calculates the total rewards earned by a user based on the staked amount and the reward per token.
function rewardPerToken() public view returns (uint256) {
    if (totalStaked == 0) {
        return rewardPerTokenStored;
    }
    uint256 timeElapsed = block.timestamp - lastUpdateTime;
    return rewardPerTokenStored + (timeElapsed * rewardRate * 1e18 / totalStaked);
}

APY Management

The base and boosted APYs can be adjusted via the setAPY() function. Additionally, the APY can be boosted for a specific duration using boostAPY().

function setAPY(uint256 newBaseAPY, uint256 newBoostedAPY) external permissioned updateReward(address(0)) {
    if (newBaseAPY > 10000 || newBoostedAPY > 10000) revert STAKE_InvalidAPY();
    baseAPY = newBaseAPY;
    boostedAPY = newBoostedAPY;
    rewardRate = getCurrentAPY() * 1e18 / (365 days * 1e4);
    ...
}

Cooldown and Early Unstake Penalty

Users must wait until the cooldown period has passed before unstaking without penalty. If they unstake early, a portion of their tokens is slashed (burned).

function setCooldownPeriod(uint256 newCooldown) external permissioned {
    if (newCooldown > 30 days) revert STAKE_InvalidCooldown();
    cooldownPeriod = newCooldown;
}
function setEarlyUnstakeSlashRate(uint256 newSlashRate) external permissioned {
    if (newSlashRate > 5000) revert STAKE_InvalidSlashRate();
    earlyUnstakeSlashRate = newSlashRate;
}

View Functions

  1. getStakeInfo(): Returns the staking information for a user, including the staked amount, pending rewards, and whether they are in the cooldown period.
function getStakeInfo(address user) external view returns (uint256 stakedAmount, uint256 pendingRewards, uint256 lastStakeTime, bool inCooldown) {
    ...
}
  1. **`getPending

Reward()`**: Returns the pending rewards for a user based on their staked tokens and the reward rate.

function getPendingReward(address user) external view returns (uint256) {
    uint256 reward = earned(user);
    return reward;
}

Pausing and Emergency Controls

The contract includes functions to pause and unpause the staking module. When paused, no staking or unstaking actions can be performed.

function pause() external permissioned {
    _pause();
}

function unpause() external permissioned {
    _unpause();
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published