USUALx
High-Level Overview
The UsualX contract is an upgradeable ERC4626-compliant yield-bearing vault. It extends the YieldBearingVaultUpgradeable contract, incorporating features such as whitelisting, blacklisting, withdrawal fees, and yield distribution linearly over a predefined yield period. The contract leverages OpenZeppelin's upgradeable contracts for enhanced security and flexibility, including pausability and reentrancy protection. It also implements EIP712 for secure off-chain signing capabilities.
The primary objective of Usualx is to provide a secure, controllable environment for yield generation and distribution, while maintaining strict control over who can interact with the contract. This design allows for potential regulatory compliance and risk management in decentralized finance applications.
Contract Summary
The contract provides the following main functions:
initialize
: Sets up the contract with customizable parameters.initializeV1
: Sets up the contract with new customizable parameters.pause
/unpause
: Controls the operational state of the contract.blacklist
/unBlacklist
: Manages addresses prohibited from interacting with the contract.whitelist
/unWhitelist
: Controls addresses permitted to transfer tokens.transfer
/transferFrom
: Overridden to enforce whitelist restrictions.startYieldDistribution
: Initiates a new yield accrual period with specified parameters.withdraw
/redeem
: Handles asset withdrawals and share redemptions, incorporating withdrawal fees.previewWithdraw
/previewRedeem
: Simulates withdrawal and redemption operations for users. The contract uses a separate storage structure (UsualXStorageV0) to store state variables for UsualX implementation.sweepFees
: Sweeps accumulated fees to a collector address.setBurnRatio
: Updates the burn ratio basis points (bps) in the contract.
Inherited Contracts
YieldBearingVaultUpgradeable: Provides core yield accrual and distribution mechanisms.
PausableUpgradeable: Enables emergency halt of contract operations.
ReentrancyGuardUpgradeable: Prevents reentrancy attacks in critical functions.
EIP712Upgradeable: Implements EIP712 for secure off-chain message signing.
Functionality Breakdown
Access Control and Security:
Utilizes a registry contract for role-based access control.
Implements blacklist to prevent specific addresses from interacting with the contract.
Enforces whitelist for token transfers, allowing only approved addresses to transfer tokens at launch but anyone not blacklisted to mint or interact with the vault.
Yield Management:
Allows admin-controlled yield distribution periods.
Accrues yield over time based on configurable parameters.
Integrates yield accrual with deposit and withdrawal operations.
Asset Management:
Implements ERC4626 standard for standardized vault interactions.
Handles deposits, withdrawals, and redemptions with consideration for accrued yield.
Applies withdrawal fees, potentially for protocol revenue or discouraging rapid withdrawals.
Upgradability and Pause Mechanism:
Utilizes OpenZeppelin's upgradeable contract pattern for future improvements.
Includes pause functionality for emergency situations.
Security Analysis
Method: initialize
Initializes the vault, token, yield module, EIP712 domain, registry contract and access control, setting up the vault's initial state.
1-8. Set the registry contract, withdrawal fee in BPS, token name and symbol for the vault, underlying asset, and the max yield period length.
9-14. Initializes inherited contracts, with initializer parameters.
16-18. Validates withdrawal fee is below 25% preventing excessive fees that could harm users.
20-22. Ensures a valid registry contract, reverts if zero address.
24-26. Sets up contract storage with validated parameter.
Points at the access control registry in the registry contract.
Method: initializeV1
Initializes the burn ration in bips and a variable linked to the USUAL token.
1. Uses the reintializer
modifier to set the second version of the the initialize function.
2. Load the contract storage.
3-5. Validates INITIAL_BURN_RATIO_BPS
constant isns't higher 100% preventing excessive burning fees.
6-7. Sets up contract storage with validated parameter.
8. Emits an event with the new burn ration.
Method: blacklist
Adds an address to the blacklist, preventing it from interacting with the contract.
Mark function as external to save gas.
2-4. Prevents blacklisting of zero address, and reverts if trying to pass zero address.
5-6. Utilizes the registry for role-based access control, restricting to admin.
7-9. Reverts if the account is already blacklisted.
Adds the account to the blacklist in UsualXStorageV0.
Emits an event to log the blacklisting action.
Method: _update
Internal hook ensuring that both sender and receiver are not blacklisted before updating the token balances.
1-4. Internal function overriding the base ERC20Upgradeable implementation.
Retrieves storage pointer for UsualXStorageV0. 6-8. Checks both sender and receiver against blacklist, reverting if either is blacklisted.
Passes through to parent implementation if checks pass.
Method: transfer
Overrides the standard ERC20 transfer function to enforce whitelist restrictions on token transfers when the contract is deployed. This can later be removed via smart contract upgrade.
1-5. Public function overriding ERC20 transfer base implementation.
6-7. Uses _msgSender()
for potential meta-transaction support, and retrieve storage pointer.
8-11. Allows whitelisted senders to transfer tokens, otherwise reverts.
Reverts if sender is not whitelisted.
Security considerations:
Correctly enforces whitelist for senders, but doesn't check recipient's whitelist status.
Consider adding a check for the contract's paused state.
The function doesn't emit a custom event for whitelisted transfers, which could aid in monitoring.
Method: startYieldDistribution
Initiates a new yield distribution period with specified parameters wrapping the internal call to add proper access control.
1-3. External function for starting a new yield period.
Ensures only admin set on registry access can call this function.
Delegates to internal function for yield distribution logic.
Method: withdraw
Overrides the ERC4626 withdraw function to include withdrawal fees and enforce withdrawal limits, calculates shares internally to avoid another storage fetch from calling previewWithdraw.
1-5. Public function overriding the ERC4626 withdraw function.
6-7. Retrieves storage pointers for UsualXStorageV0 and YieldDataStorage.
9-12. Checks if the withdrawal amount exceeds the maximum allowed, and reverts if so.
14-15. Calculates the withdrawal fee based on the number of assets user wants to withdraw taking the precision into account.
Converts assets to shares, considering the fee.
Deducts the fee from the total deposits in the yield storage.
Calls parent withdrawal function with calculated values.
Method: sweepFees
Sweeps accumulated fees to the specified collector address, optionally burning a portion of the fees based on the burn ratio. Enforces non-reentrancy, role-based access control, and validates input.
1. A public external function marked as nonReentrant
to prevent reentrancy attacks.
2-4. Checks if the collector
address is non-zero. If not, reverts with NullAddress()
.
5-6. Retrieves storage pointer for UsualXStorageV0
and validates that the caller has the FEE_SWEEPER_ROLE
permission.
7-9. Reads the accumulated fees from storage and reverts with AmountIsZero()
if no fees are available to sweep.
11. Sets the accumulatedFees
storage variable to 0
.
12-14. Calculates the portion of fees to burn (burnAmount
) based on the burnRatioBps
, and computes the remaining amount (transferAmount
) to be transferred to the collector.
Retrieves the reference to the
usualToken
used for burning and transferring.
16-18. If burnAmount
is greater than 0
, burns the calculated amount of fees.
19-21. If transferAmount
is greater than 0
, safely transfers the fees to the collector address.
Emits a
FeeSwept
event with the collector address, total fees swept, and amount burned.
Method: setBurnRatio
Updates the burn ratio basis points (bps) in the contract. Enforces role-based access control and ensures the new ratio does not exceed the basis point maximum.
1. A public external function to update the burn ratio.
2-3. Retrieves the storage pointer for UsualXStorageV0
and validates that the caller has the BURN_RATIO_UPDATER_ROLE
permission.
4-6. Checks if the provided burnRatioBps
exceeds the maximum allowable value (BASIS_POINT_BASE
, typically 10,000 for 100%). If it does, the function reverts with AmountTooBig()
.
7. Updates the burnRatioBps
storage variable with the new value.
8. Emits a BurnRatioUpdated
event with the updated burn ratio value.
Last updated