CommitToken
Inherits: ERC4626, IERC7540Redeem, AccessManaged, ICommitToken, ERC20Pausable, ERC165
Title: CommitToken
ERC4626 vault with asynchronous redeem requests and cooldown periods
This contract is non-transferable as an implementation convenience for the current version. The non-transferability simplifies accounting and prevents transfer-related complexity in the async redeem request system. Future versions could support transferability if needed.
This contract implements a custom async redemption flow inspired by ERC-7540, but is NOT compliant with the ERC-7540 specification. It deviates from MUST requirements including: shares not removed from owner on request, preview functions not reverting, operator functionality not supported, and ERC-7575 share() method not implemented.
TODO: Add support for freezing
State Variables
supplyCap
Maximum total supply allowed
uint256 public supplyCap
unlockingDelay
Cooldown period for redeem requests (unlocking delay)
uint48 public unlockingDelay
redeemRequests
Mapping of user addresses to their redeem requests
mapping(address => Request) redeemRequests
denyList
Reference to the AddressList contract for deny list checking
IAddressList public denyList
Functions
constructor
Reference to the Silo contract for cooldown escrow
constructor(address authority_, address asset_, uint48 unlockingDelay_, address denyList_, uint256 supplyCap_)
AccessManaged(authority_)
ERC4626(IERC20(asset_))
ERC20(
string.concat(IERC20Metadata(asset_).name(), " Commit Token"),
string.concat("CT-", IERC20Metadata(asset_).symbol())
);
setUnlockingDelay
Sets the unlocking delay (redeem cooldown)
Only callable through AccessManager with ADMIN_ROLE
function setUnlockingDelay(uint48 newUnlockingDelay) external restricted;
Parameters
| Name | Type | Description |
|---|---|---|
newUnlockingDelay | uint48 | New unlocking delay in seconds |
setDenyList
Sets the deny list contract
Only callable through AccessManager with ADMIN_ROLE
function setDenyList(address newDenyList) external restricted;
Parameters
| Name | Type | Description |
|---|---|---|
newDenyList | address | Address of the new AddressList contract |
setSupplyCap
Sets the supply cap
Only callable through AccessManager with ADMIN_ROLE
function setSupplyCap(uint256 newSupplyCap) external restricted;
Parameters
| Name | Type | Description |
|---|---|---|
newSupplyCap | uint256 | New maximum total supply |
_revertIfDenied
function _revertIfDenied(address user) internal view;
supplyCapRemaining
Returns the remaining capacity before hitting the supply cap
function supplyCapRemaining() external view returns (uint256);
Returns
| Name | Type | Description |
|---|---|---|
<none> | uint256 | Amount of tokens that can still be minted |
pause
Pauses all token transfers
Only callable through AccessManager with ADMIN_ROLE
function pause() external restricted;
unpause
Unpauses all token transfers
Only callable through AccessManager with ADMIN_ROLE
function unpause() external restricted;
decimals
Returns the number of decimals used to get its user representation.
For example, if decimals equals 2, a balance of 505 tokens should
be displayed to a user as 5.05 (505 / 10 ** 2).
Tokens usually opt for a value of 18, imitating the relationship between
Ether and Wei. This is the default value returned by this function, unless
it's overridden.
NOTE: This information is only used for display purposes: it in
no way affects any of the arithmetic of the contract, including
{IERC20-balanceOf} and {IERC20-transfer}.
function decimals() public view override(ERC4626, ERC20) returns (uint8);
_update
Commit tokens are not transferable and only support minting and burning
Non-transferability is an implementation convenience for this version to simplify the async redeem request accounting. Future versions may support transferability.
function _update(address from, address to, uint256 value) internal override(ERC20, ERC20Pausable);
_convertToShares
Assets convert to shares at a 1:1 ratio
function _convertToShares(uint256 assets, Math.Rounding) internal pure override returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | The amount of assets to convert to shares |
<none> | Math.Rounding |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | The amount of shares |
_convertToAssets
Shares convert to assets at a 1:1 ratio
function _convertToAssets(uint256 shares, Math.Rounding) internal pure override returns (uint256 assets);
Parameters
| Name | Type | Description |
|---|---|---|
shares | uint256 | The amount of shares to convert to assets |
<none> | Math.Rounding |
Returns
| Name | Type | Description |
|---|---|---|
assets | uint256 | The amount of assets |
maxDeposit
Returns the maximum amount of assets that can be deposited for a receiver
Per ERC-4626, this must not revert and must return the max amount that would be accepted
function maxDeposit(address receiver) public view override returns (uint256 maxAssets);
Parameters
| Name | Type | Description |
|---|---|---|
receiver | address | The address that would receive the shares |
Returns
| Name | Type | Description |
|---|---|---|
maxAssets | uint256 | Maximum assets that can be deposited |
maxMint
Returns the maximum amount of shares that can be minted for a receiver
Per ERC-4626, this must not revert and must return the max amount that would be accepted
Since conversion is 1:1, this returns the same value as maxDeposit
function maxMint(address receiver) public view override returns (uint256 maxShares);
Parameters
| Name | Type | Description |
|---|---|---|
receiver | address | The address that would receive the shares |
Returns
| Name | Type | Description |
|---|---|---|
maxShares | uint256 | Maximum shares that can be minted |
_deposit
Deposit is only supported for the caller
function _deposit(address caller, address receiver, uint256 assets, uint256 shares)
internal
virtual
override
whenNotPaused;
Parameters
| Name | Type | Description |
|---|---|---|
caller | address | The address to deposit from |
receiver | address | The address to deposit to |
assets | uint256 | The amount of assets to deposit |
shares | uint256 | The amount of shares to deposit |
_requestRedeem
Shared functionality for requestRedeem and requestWithdraw
function _requestRedeem(Request storage request, address controller, address owner, uint256 assets, uint256 shares)
internal
virtual;
requestRedeem
Request an asynchronous redeem of shares
function requestRedeem(uint256 shares, address controller, address owner)
external
override
returns (uint256 requestId);
Parameters
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares to redeem |
controller | address | Address that will control the request (must be msg.sender) |
owner | address | Address that owns the shares (must be msg.sender) |
Returns
| Name | Type | Description |
|---|---|---|
requestId | uint256 | ID of the request (always 0 for this implementation) |
requestWithdraw
Request an asynchronous withdrawal of assets
function requestWithdraw(uint256 assets, address controller, address owner) external returns (uint256 requestId);
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets to withdraw |
controller | address | Address that will control the request (must be msg.sender) |
owner | address | Address that owns the shares (must be msg.sender) |
Returns
| Name | Type | Description |
|---|---|---|
requestId | uint256 | ID of the request (always 0) |
_cooldownRemaining
function _cooldownRemaining(Request storage request) internal view returns (uint48 cooldown);
cooldownRemaining
Returns the remaining cooldown time for a request
function cooldownRemaining(uint256, address owner) external view returns (uint48 cooldown);
Parameters
| Name | Type | Description |
|---|---|---|
<none> | uint256 | |
owner | address | Address that owns the shares (must be msg.sender) |
Returns
| Name | Type | Description |
|---|---|---|
cooldown | uint48 | Remaining cooldown time in seconds |
_isClaimable
function _isClaimable(Request storage request) internal view returns (bool);
isClaimable
Returns true if a request is claimable
function isClaimable(uint256, address owner) external view returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
<none> | uint256 | |
owner | address | Address that owns the shares (must be msg.sender) |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | true if the request is claimable, false otherwise |
pendingRedeemRequest
Returns pending redeem request amount that hasn't completed cooldown
Accepts a uint256 requestId as the first param to meet the 7540 spec
function pendingRedeemRequest(uint256, address owner) external view override returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
<none> | uint256 | |
owner | address | Address to query |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | Pending share amount |
claimableRedeemRequest
Returns claimable redeem request amount that has completed cooldown
Accepts a uint256 requestId as the first param to meet the 7540 spec
function claimableRedeemRequest(uint256, address owner) public view override returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
<none> | uint256 | |
owner | address | Address to query |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | Claimable share amount |
maxRedeem
Returns maximum redeem amount for an address
Returns claimable redeem request shares, or 0 if none
function maxRedeem(address owner) public view override returns (uint256);
_withdraw
function _withdraw(Request storage request, address caller, address receiver, address owner) internal;
redeem
Claims a pending redeem request and burns shares
Overrides ERC4626 to only work with pending requests (no instant redeems)
function redeem(uint256 shares, address receiver, address owner) public override returns (uint256 assets);
Parameters
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares to claim (must match request) |
receiver | address | Address to receive the assets |
owner | address | Address that owns the shares (must be msg.sender) |
Returns
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets received |
withdraw
Withdraws assets from the contract
function withdraw(uint256 assets, address receiver, address owner) public override returns (uint256 shares);
Parameters
| Name | Type | Description |
|---|---|---|
assets | uint256 | Amount of assets to withdraw |
receiver | address | Address to receive the assets |
owner | address | Address that owns the shares (must be msg.sender) |
Returns
| Name | Type | Description |
|---|---|---|
shares | uint256 | Amount of shares burned to receive assets |
setOperator
Not implemented in v0 - owner and controller must be msg.sender
Sets or removes an operator for the caller.
function setOperator(address, bool) external pure virtual returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
<none> | address | |
<none> | bool |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | Whether the call was executed successfully or not |
isOperator
Returns true if the operator is the controller
Returns true if the operator is approved as an operator for an controller.
function isOperator(address controller, address operator) public view virtual returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
controller | address | The address of the controller. |
operator | address | The address of the operator. |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | status The approval status |
supportsInterface
Returns true if the contract implements the interface
Does NOT claim ERC-7540 compliance via ERC-165. This contract implements a custom async redemption flow inspired by ERC-7540 but deviates from the specification.
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool);
Parameters
| Name | Type | Description |
|---|---|---|
interfaceId | bytes4 | The interface identifier to check |
Returns
| Name | Type | Description |
|---|---|---|
<none> | bool | true if the contract implements the interface |