# Distribution Module

## High-Level Overview

This contract manages a **Distribution** process of the Usual token. The contract should be called daily to calculate how many new tokens are created, distributed to on-chain vaults (UsualX and UsualS) and how many tokens are available to claim for the off-chain users with a valid merkle proof.

The core features of this contract include:

* **Daily Distribution**: The contract should be called daily to calculate how many new tokens are created, distributed to On Chain vaults (UsualX and UsualS) and how many tokens are available to claim for the off-chain users with a valid merkle proof. The amount distributed scales based on how much longer (after one day) it has been since the last distribution to account for calling the function at greater than 24 hour periods.
* **Merkle Proof for Off-Chain Users**: Ensures that only eligible users, validated through merkle proofs, can receive tokens from the distribution.
* **Challengeable Queue of Merkle Proofs**: The contract should have a queue of merkle proofs that can be challenged by a specific role (e.g. governance) to ensure that the off-chain distribution is fair and correct.
* **Access Control**: Each functionality should be controlled by a role with minimal required permissions.

### Contract Summary

#### Inherited Contracts

* **Initializable**: For upgradeable contract initialization.
* **PausableUpgradeable**: Allows contract execution to be paused or unpaused by an authorized role.
* **ReentrancyGuardUpgradeable**: Provides protection against reentrancy attacks.
* **IDistributionModule**: Interface that defines all functions that can be called by anyone.
* **IDistributionAllocator**: Interface that defines the functions that can be only called by the `DISTRIBUTION_ALLOCATOR_ROLE`
* **IDistributionOperator**: Interface that defines the functions that can be only called by the `DISTRIBUTION_OPERATOR_ROLE`. Those functions are used to daily distribute the Usual tokens to on-chain and off-chain buckets.
* **IOffChainDistributionChallenger**: Interface that defines the functions that can be only called by the `OFF_CHAIN_DISTRIBUTION_CHALLENGER_ROLE`. Those functions are used to challenge the off-chain distribution in the queue.

### Functionality Description

#### Public/External Functions

* **initialize**: Initializes the contract with the registry address and sets the initial `rate0` value
* **pause**: Admin function to pause the contract. Can be only called by `PAUSING_CONTRACTS_ROLE`
* **unpause**: Admin function to unpause the contract. Can be only called by `DEFAULT_ADMIN_ROLE`

#### &#x20;IDistributionModule

* **getBucketsDistribution**: Returns the current buckets distribution percentages in basis points. Off-chain buckets are LST, LYT, IYT, Bribe, Ecosystem, DAO, and Market Makers. On-Chain buckets are UsualX and UsualS.
* **calculateUsualDist**: Helper view function that calculates a simulated Usual Distribution for the provided `ratet` and `p90Rate` values. It returns `st`, `rt` and `kappa` values that were used in the calculation.
* **calculateSt**: Helper view function that returns a calculated `st` value based on provided `supplyPpt` and `pt` values.
* **calculateRt**: Helper view function that returns a calculated `rt` value based on provided `ratet` and `p90Rate` values.
* **calculateMt**: Helper view function that returns a calculated `mt` value based on provided `st`, `rt` and `kappa` values.
* **getOffChainDistributionData**: Returns the currently approved off-chain distribution `timestamp` and `merkleRoot` values. That can be used to pre-validate the merkle proof before calling the `claimOffChainDistribution` function.
* **getOffChainDistributionQueue**: Returns the current off-chain distribution queue. The queue is a list of `timestamp` and `merkleRoot` values that are used to claim the off-chain distribution.
* **getOffChainDistributionMintCap**: Returns the current off-chain distribution mint cap value. This value is the maximum amount of tokens that can be minted by through the off-chain distribution. It is reduced with every successful claim.
* **approveUnchallengedOffChainDistribution**: Approves the latest unchallenged off-chain distribution from the queue that was in the queue for more than `USUAL_DISTRIBUTION_CHALLENGE_PERIOD`. All distributions that were in the queue for more than `USUAL_DISTRIBUTION_CHALLENGE_PERIOD` are removed from the queue. Even if anyone can call this function, usually it will be called by the `DISTRIBUTION_OPERATOR_ROLE`.
* **getOffChainTokensClaimed**: Returns the amount of the tokens claimed by the given `account` address in the off-chain distribution until now.
* **claimOffChainDistribution**: Allows to claim the Usual tokens from the latest approved off-chain distribution. Caller should provide a valid merkle proof for the provided `account` and `amount`. The `amount` is the total amount of tokens that were assigned for the given `account` address since the beginning of the distribution. The Usual tokens are minted when the proof is valid and the given `account` has any tokens to claim in the approved off-chain distribution. The given `account` will only receive the tokens that were not claimed by them before based on the value returned by the `getOffChainTokensClaimed` function. The mint cap should be reduced by the claimed amount. The `getOffChainTokensClaimed` should return increased value.

#### IDistributionAllocator

* **setBucketsDistribution**: Allows to set the new buckets distribution percentages in basis points. The sum of all values should be equal to `BASIS_POINT_BASE`. The function can be only called by the `DISTRIBUTION_ALLOCATOR_ROLE`. Off-chain buckets are LST, LYT, IYT, Bribe, Ecosystem, DAO, and Market Makers. On-Chain buckets are Usual+ and Usual\*.
* **setFeeRates**: Allows to set the Treasury, UsualX and Burn fee rates.
* **getFeeRates**: Allows to get the Treasury, UsualX and Burn fee rates.
* **setD**: Set `D` parameter used it the emissions calculation formula. The function can be only called by the `DISTRIBUTION_ALLOCATOR_ROLE`.
* **getD**: Returns the current `D` parameter value.
* **setM0**: Set `m0` parameter used it the emissions calculation formula. The function can be only called by the `DISTRIBUTION_ALLOCATOR_ROLE`.
* **getM0**: Returns the current `m0` parameter value.
* **setRateMin**: Set `rateMin` parameter used it the emissions calculation formula. The function can be only called by the `DISTRIBUTION_ALLOCATOR_ROLE`.
* **getRateMin**: Returns the current `rateMin` parameter value.
* **setBaseGamma**: Set `baseGamma` parameter used it the emissions calculation formula. The function can be only called by the `DISTRIBUTION_ALLOCATOR_ROLE`.
* **getBaseGamma**: Returns the current `baseGamma` parameter value.

#### IDistributionOperator

* **distributeUsualToBuckets**: Calculates the Usual emissions based on the current state of parameters and provided `ratet` and `p90Rate` values. For on-chain buckets (Usual+ and Usual\*) the tokens are minted directly to the bucket and a vault specific distribution is started. For off-chain buckets (LST, LYT, IYT, Bribe, Ecosystem, DAO, and Market Makers) mint is delayed until the off-chain distribution is approved and tokens are claimed. The function can be only called by the `DISTRIBUTION_OPERATOR_ROLE`.
* **queueOffChainDistribution**: Queues the off-chain distribution for the given `merkleRoot`. The function can be only called by the `DISTRIBUTION_OPERATOR_ROLE`. This function cannot be called more than once per 24 hours.
* **resetOffChainDistributionQueue**: Removes all off-chain distributions from the queue. This is an emergency functionality is queue ever gets to big to be pruned during a `approveUnchallengedOffChainDistribution` call. The function can be only called by the `DISTRIBUTION_OPERATOR_ROLE`.

#### IOffChainDistributionChallenger

* **challengeOffChainDistribution**: Challenges all off-chain distributions in the queue that are older than specified timestamp. They are marked as challenged and cannot be approved. The function can be only called by the `DISTRIBUTION_CHALLENGER_ROLE`.
* **challengeAndProposeOffChainDistribution**: Challenges all off-chain distributions in the queue that are older than specified timestamp. They are marked as challenged and cannot be approved. The new off-chain distribution is proposed with the given `merkleRoot` and it still has to wait in the queue for `USUAL_DISTRIBUTION_CHALLENGE_PERIOD` before it can be approved. The function can be only called by the `DISTRIBUTION_CHALLENGER_ROLE`.

## Functionality Breakdown

### Setting up calculations parameters

* `DISTRIBUTION_ALLOCATOR_ROLE` can change all the values that are used in the emissions calculations formula. There is no timelock for those changes.
* `DISTRIBUTION_ALLOCATOR_ROLE` can change the distribution buckets shares percentages. The sum of all values should be equal to 100% (in basis points). There is no timelock for those changes.

### Daily Emissions

* `DISTRIBUTION_OPERATOR_ROLE` is required to call the `distributeUsualToBuckets` every 24 hours to:
  1. Fetch the current blended weekly interest rate and P90 rate from the [Yield Module](https://app.gitbook.com/o/qhhUYNkfevGh3Y1MDQaS/s/pUhQzPJGdJzuLTQ5sCym/~/changes/119/smart-contracts/protocol-contracts/usual-distribution/yield-module)
  2. Calculate the new emissions based on these rates
  3. Distribute tokens to the on-chain buckets
  4. Increase the off-chain buckets mint cap that can be claimed after successful approval of the off-chain distribution (must be unchallenged and in queue for more than `USUAL_DISTRIBUTION_CHALLENGE_PERIOD`)
* By calling `distributeUsualToBuckets`, this contract will sweep fees accumulated on `Usd0PP` and `UsualX` contracts and, if any, will redistribute those fees depending of fee rates. Those fees will be distributed to UsualX holders, Yield Treasury contract and will be burned. Fee rates can only be changed by the `FEE_RATE_SETTER_ROLE` and the sum of them should be equal to `BASIS_POINT_BASE` (10\_000).
* `DISTRIBUTION_OPERATOR_ROLE` is required to call the `queueOffChainDistribution` with a valid `merkleRoot` as soon as the off-chain distribution mint cap is increased.

### Off-Chain Distribution Queue, Approval and Challenges

* Once `DISTRIBUTOR_OPERATOR_ROLE` puts a new off-chain distribution in the queue, it has to wait for `USUAL_DISTRIBUTION_CHALLENGE_PERIOD` before it can be approved.
* Anyone can call the `approveUnchallengedOffChainDistribution` function to approve the latest unchallenged off-chain distribution from the queue that was in the queue for more than `USUAL_DISTRIBUTION_CHALLENGE_PERIOD`. This function will remove all distributions that were in the queue for more than `USUAL_DISTRIBUTION_CHALLENGE_PERIOD` which impacts the gas cost of the function. In normal circumstances, there should be only one distribution in the queue that is older than `USUAL_DISTRIBUTION_CHALLENGE_PERIOD` and has to be removed.
* `DISTRIBUTION_CHALLENGER_ROLE` can call the `challengeOffChainDistribution` function to challenge all off-chain distributions in the queue that are older than specified timestamp. They are marked as challenged and cannot be approved. In rare cases, a new off-chain distribution can be proposed and it is put in the queue.
* The queue should be treated as unordered.

### Merkle Tree Root

* The merkle tree root that is queued should be calculated off-chain and should include `account` and `amount` values for each user that is eligible for claiming the off-chain distribution. The `amount` value should be the total amount of tokens that were assigned for the given `account` address since the beginning of the distribution. The `amount` value for the given `account` should never decrease. The `account` address should be in the format `0x123...` and the `amount` should be in wei.

### Merkle Proof Validation

All claims are validated against a Merkle tree using the `MerkleProof` library, ensuring that only eligible users can receive tokens. Separate Merkle roots are set for the initial and vesting periods.

### Admin Control

Administrators have control over setting key contract parameters, such as the Merkle roots and penalty percentages, as well as pausing the contract in case of emergencies.

## Constants

* **DEFAULT\_ADMIN\_ROLE**: Role required to unpause the contract
* **PAUSING\_CONTRACTS\_ROLE**: Role required to pause the contract
* **DISTRIBUTION\_ALLOCATOR\_ROLE**: Role required to set the distribution parameters and buckets distribution percentages
* **DISTRIBUTION\_OPERATOR\_ROLE**: Role required to distribute the Usual tokens to buckets and queue the off-chain distribution
* **DISTRIBUTION\_CHALLENGER\_ROLE**: Role required to challenge the off-chain distribution in the queue
* **FEE\_RATE\_SETTER\_ROLE**: Role required to set the fee rates.
* **USUAL\_DISTRIBUTION\_CHALLENGE\_PERIOD**: The period in seconds that the off-chain distribution has to wait in the queue before it can be approved. It is 7 days by default.
* **CONTRACT\_REGISTRY\_ACCESS**: This constant is used to define the address of the registry access contract.
* **CONTRACT\_ORACLE**: This constant is used to define the address of the oracle contract.
* **CONTRACT\_USD0**: This constant is used to define the address of the USD0 contract.
* **CONTRACT\_USD0PP**: This constant is used to define the address of the USD0PP contract.
* **CONTRACT\_USUAL**: This constant is used to define the address of the Usual contract.
* **CONTRACT\_USUALSP**: This constant is used to define the address of the UsualSP contract.
* **CONTRACT\_USUALX**: This constant is used to define the address of the UsualX contract.
* **CONTRACT\_DAO\_COLLATERAL**: This constant is used to define the address of the DAO collateral contract.
* **CONTRACT\_YIELD\_TREASURY**: This constant is used to define the address of the Yield Treasury contract.
* **BASIS\_POINT\_BASE**: The base value for calculating percentages in basis points.
* **BPS\_SCALAR**: The scalar value for calculating percentages in basis points.
* **SCALAR\_ONE**: The scalar value for calculating percentages with 10^18 precision.
* **REWARD\_PERIOD\_SCALAR**: The scalar value for calculating time since last distribution above 1 day in seconds.

## Yield Module Integration

The Distribution Module relies on the Yield Module for two critical values used in emissions calculations:

1. Blended Weekly Interest Rate: A weighted average of all RWA interest rates based on their USD value in the treasury
2. P90 Interest Rate: The 90th percentile interest rate maintained by the Yield Module

These values are fetched during the daily distribution process and used to calculate the emission rate for new USUAL tokens. This integration ensures that token emissions are directly tied to the actual yield being generated by the protocol's RWA holdings. More details on the Yield Module [here](https://app.gitbook.com/o/qhhUYNkfevGh3Y1MDQaS/s/pUhQzPJGdJzuLTQ5sCym/~/changes/119/smart-contracts/protocol-contracts/usual-distribution/yield-module).

## Safeguard Implementation

### Reentrancy Guard

All external function handling token mints and transfer are protected with `ReentrancyGuard` to prevent reentrancy attacks.

### Access Control

The contract uses `CheckAccessControl` to enforce role-based access control, ensuring only authorized addresses can perform actions that require specific permissions. Each role has minimal required permissions.

### Pausability

The contract is pausable by `PAUSING_CONTRACTS_ROLE`, enabling the ability to pause all operations in case of an emergency. The contract can be resumed only by `DEFAULT_ADMIN_ROLE`.

### Off-chain distribution queue and challenge mechanism

The off-chain distribution can be validated for `USUAL_DISTRIBUTION_CHALLENGE_PERIOD` before it can be approved. If an invalid distribution is proposed, it can be challenged by the `DISTRIBUTION_CHALLENGER_ROLE` making it impossible to be approved.

If the queue ever gets too big, the `resetOffChainDistributionQueue` function can be called by the `DISTRIBUTION_OPERATOR_ROLE` to remove all off-chain distributions from the queue.

### Restrictions how often certain functions can be called

There is a 24 hours limit for the `calculateUsualDist` function to be called. To prevent unwanted emissions and distribution.
