Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CommitToken

Git Source

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

NameTypeDescription
newUnlockingDelayuint48New unlocking delay in seconds

setDenyList

Sets the deny list contract

Only callable through AccessManager with ADMIN_ROLE

function setDenyList(address newDenyList) external restricted;

Parameters

NameTypeDescription
newDenyListaddressAddress of the new AddressList contract

setSupplyCap

Sets the supply cap

Only callable through AccessManager with ADMIN_ROLE

function setSupplyCap(uint256 newSupplyCap) external restricted;

Parameters

NameTypeDescription
newSupplyCapuint256New 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

NameTypeDescription
<none>uint256Amount 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

NameTypeDescription
assetsuint256The amount of assets to convert to shares
<none>Math.Rounding

Returns

NameTypeDescription
sharesuint256The 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

NameTypeDescription
sharesuint256The amount of shares to convert to assets
<none>Math.Rounding

Returns

NameTypeDescription
assetsuint256The 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

NameTypeDescription
receiveraddressThe address that would receive the shares

Returns

NameTypeDescription
maxAssetsuint256Maximum 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

NameTypeDescription
receiveraddressThe address that would receive the shares

Returns

NameTypeDescription
maxSharesuint256Maximum 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

NameTypeDescription
calleraddressThe address to deposit from
receiveraddressThe address to deposit to
assetsuint256The amount of assets to deposit
sharesuint256The 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

NameTypeDescription
sharesuint256Amount of shares to redeem
controlleraddressAddress that will control the request (must be msg.sender)
owneraddressAddress that owns the shares (must be msg.sender)

Returns

NameTypeDescription
requestIduint256ID 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

NameTypeDescription
assetsuint256Amount of assets to withdraw
controlleraddressAddress that will control the request (must be msg.sender)
owneraddressAddress that owns the shares (must be msg.sender)

Returns

NameTypeDescription
requestIduint256ID 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

NameTypeDescription
<none>uint256
owneraddressAddress that owns the shares (must be msg.sender)

Returns

NameTypeDescription
cooldownuint48Remaining 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

NameTypeDescription
<none>uint256
owneraddressAddress that owns the shares (must be msg.sender)

Returns

NameTypeDescription
<none>booltrue 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

NameTypeDescription
<none>uint256
owneraddressAddress to query

Returns

NameTypeDescription
sharesuint256Pending 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

NameTypeDescription
<none>uint256
owneraddressAddress to query

Returns

NameTypeDescription
sharesuint256Claimable 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

NameTypeDescription
sharesuint256Amount of shares to claim (must match request)
receiveraddressAddress to receive the assets
owneraddressAddress that owns the shares (must be msg.sender)

Returns

NameTypeDescription
assetsuint256Amount of assets received

withdraw

Withdraws assets from the contract

function withdraw(uint256 assets, address receiver, address owner) public override returns (uint256 shares);

Parameters

NameTypeDescription
assetsuint256Amount of assets to withdraw
receiveraddressAddress to receive the assets
owneraddressAddress that owns the shares (must be msg.sender)

Returns

NameTypeDescription
sharesuint256Amount 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

NameTypeDescription
<none>address
<none>bool

Returns

NameTypeDescription
<none>boolWhether 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

NameTypeDescription
controlleraddressThe address of the controller.
operatoraddressThe address of the operator.

Returns

NameTypeDescription
<none>boolstatus 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

NameTypeDescription
interfaceIdbytes4The interface identifier to check

Returns

NameTypeDescription
<none>booltrue if the contract implements the interface