Token
Smart contract token utilities and implementations
Set of extensions and utilities for tokens (e.g ERC-20, ERC-721, ERC-1155) and derivated ERCs (e.g. ERC-4626, ERC-1363).
OnTokenTransferAdapter: Adapter of the ERC-1363 receiver interface to comply with Chainlink’s 667 interface.ERC20Allowlist: Extension of ERC20 with transfers and approvals that require users to be registered into an allowlist.ERC20Blocklist: Extension of ERC20 with transfers and approvals that can be disabled by adding users into a blocklist.ERC20Collateral: Oracle-agnostic extension of ERC20 that limits the total supply based on a collateral amount.ERC20Custodian: Extension of ERC20 that implements an access-control agnostic approach to define a custodian that can freeze user’s transfers and approvals.ERC20Freezable: Extension of ERC20 that allows an authorized account to freeze a portion of an account’s balance, preventing it from being transferred until unfrozen.ERC20Restricted: Extension of ERC20 that allows implementing user account transfer restrictions (blocklist or allowlist) inspired by EIP-7943.ERC20uRWA: Extension of ERC20 according to EIP-7943, combining standard ERC-20 with RWA-specific features like account restrictions, asset freezing, and forced transfers.ERC4626Fees: ERC4626 vault with fees on entry (deposit/mint) or exit (withdraw/redeem).ERC7540: Implementation of ERC-7540 "Asynchronous ERC-4626 Tokenized Vaults", providing a base for vaults with asynchronous deposit and/or redemption flows.ERC7540AdminDeposit: Admin-controlled (operator-triggered) fulfillment strategy for asynchronous deposits in ERC-7540 vaults.ERC7540AdminRedeem: Admin-controlled (operator-triggered) fulfillment strategy for asynchronous redemptions in ERC-7540 vaults.ERC7540DelayDeposit: Time-delay fulfillment strategy for asynchronous deposits, becoming permissionlessly claimable after a configurable waiting period.ERC7540DelayRedeem: Time-delay fulfillment strategy for asynchronous redemptions, becoming permissionlessly claimable after a configurable waiting period.ERC7540SyncDeposit: Module for enabling synchronous (ERC-4626) deposit flow in an ERC-7540 vault.ERC7540SyncRedeem: Module for enabling synchronous (ERC-4626) redeem flow in an ERC-7540 vault.
General
ERC20
ERC7540
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Allowlist.sol";Extension of ERC20 that allows to implement an allowlist
mechanism that can be managed by an authorized account with the
ERC20Allowlist._disallowUser and ERC20Allowlist._allowUser functions.
The allowlist provides the guarantee to the contract owner
(e.g. a DAO or a well-configured multisig) that any account won't be
able to execute transfers or approvals to other entities to operate
on its behalf if ERC20Allowlist._allowUser was not called with such account as an
argument. Similarly, the account will be disallowed again if
ERC20Allowlist._disallowUser is called.
Deprecated. Use ERC20Restricted instead.
Functions
- allowed(account)
- _allowUser(user)
- _disallowUser(user)
- _update(from, to, value)
- _approve(owner, spender, value, emitEvent)
ERC20
- name()
- symbol()
- decimals()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _spendAllowance(owner, spender, value)
Events
Errors
allowed(address account) → bool
public
#Returns the allowed status of an account.
_allowUser(address user) → bool
internal
#Allows a user to receive and transfer tokens, including minting and burning.
_disallowUser(address user) → bool
internal
#Disallows a user from receiving and transferring tokens, including minting and burning.
_update(address from, address to, uint256 value)
internal
#See ERC20-_update.
_approve(address owner, address spender, uint256 value, bool emitEvent)
internal
#See ERC20-_approve.
UserAllowed(address indexed user)
event
#Emitted when a user is allowed to transfer and approve.
UserDisallowed(address indexed user)
event
#Emitted when a user is disallowed.
ERC20Disallowed(address user)
error
#The operation failed because the user is not allowed.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Blocklist.sol";Extension of ERC20 that allows to implement a blocklist
mechanism that can be managed by an authorized account with the
ERC20Blocklist._blockUser and ERC20Blocklist._unblockUser functions.
The blocklist provides the guarantee to the contract owner
(e.g. a DAO or a well-configured multisig) that any account won't be
able to execute transfers or approvals to other entities to operate
on its behalf if ERC20Blocklist._blockUser was not called with such account as an
argument. Similarly, the account will be unblocked again if
ERC20Blocklist._unblockUser is called.
Deprecated. Use ERC20Restricted instead.
Functions
- blocked(account)
- _blockUser(user)
- _unblockUser(user)
- _update(from, to, value)
- _approve(owner, spender, value, emitEvent)
ERC20
- name()
- symbol()
- decimals()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _spendAllowance(owner, spender, value)
Events
Errors
blocked(address account) → bool
public
#Returns the blocked status of an account.
_blockUser(address user) → bool
internal
#Blocks a user from receiving and transferring tokens, including minting and burning.
_unblockUser(address user) → bool
internal
#Unblocks a user from receiving and transferring tokens, including minting and burning.
_update(address from, address to, uint256 value)
internal
#See ERC20-_update.
_approve(address owner, address spender, uint256 value, bool emitEvent)
internal
#See ERC20-_approve.
UserBlocked(address indexed user)
event
#Emitted when a user is blocked.
UserUnblocked(address indexed user)
event
#Emitted when a user is unblocked.
ERC20Blocked(address user)
error
#The operation failed because the user is blocked.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Collateral.sol";Extension of ERC20 that limits the supply of tokens based
on a collateral amount and time-based expiration.
The ERC20Collateral.collateral function must be implemented to return the collateral
data. This function can call external oracles or use any local storage.
Functions
ERC20
- name()
- symbol()
- decimals()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
Errors
constructor(uint48 liveness_)
internal
#Sets the value of the _liveness. This value is immutable, it can only be
set once during construction.
liveness() → uint48
public
#Returns the minimum liveness duration of collateral.
clock() → uint48
public
#Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
CLOCK_MODE() → string
public
#Description of the clock
collateral() → uint256 amount, uint48 timestamp
public
#Returns the collateral data of the token.
_update(address from, address to, uint256 value)
internal
#See ERC20-_update.
ERC20ExceededSupply(uint256 increasedSupply, uint256 cap)
error
#Total supply cap has been exceeded.
ERC20ExpiredCollateral(uint48 timestamp, uint48 expiration)
error
#Collateral amount has expired.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Custodian.sol";Extension of ERC20 that allows to implement a custodian
mechanism that can be managed by an authorized account with the
ERC20Custodian.freeze function.
This mechanism allows a custodian (e.g. a DAO or a well-configured multisig) to freeze and unfreeze the balance of a user.
The frozen balance is not available for transfers or approvals
to other entities to operate on its behalf if. The frozen balance
can be reduced by calling ERC20Custodian.freeze again with a lower amount.
Deprecated. Use ERC20Freezable instead.
Modifiers
Functions
- frozen(user)
- freeze(user, amount)
- availableBalance(account)
- _isCustodian(user)
- _update(from, to, value)
ERC20
- name()
- symbol()
- decimals()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
Errors
onlyCustodian()
internal
#Modifier to restrict access to custodian accounts only.
frozen(address user) → uint256
public
#Returns the amount of tokens frozen for a user.
freeze(address user, uint256 amount)
external
#Adjusts the amount of tokens frozen for a user.
availableBalance(address account) → uint256 available
public
#Returns the available (unfrozen) balance of an account.
_isCustodian(address user) → bool
internal
#Checks if the user is a custodian.
_update(address from, address to, uint256 value)
internal
#Transfers a value amount of tokens from from to to, or alternatively mints (or burns) if from
(or to) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
this function.
Emits a Transfer event.
TokensFrozen(address indexed user, uint256 amount)
event
#Emitted when tokens are frozen for a user.
TokensUnfrozen(address indexed user, uint256 amount)
event
#Emitted when tokens are unfrozen for a user.
ERC20InsufficientUnfrozenBalance(address user)
error
#The operation failed because the user has insufficient unfrozen balance.
ERC20InsufficientFrozenBalance(address user)
error
#The operation failed because the user has insufficient frozen balance.
ERC20NotCustodian()
error
#Error thrown when a non-custodian account attempts to perform a custodian-only operation.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Freezable.sol";Extension of ERC20 that allows to implement a freezing
mechanism that can be managed by an authorized account with the
ERC20Freezable._setFrozen function.
The freezing mechanism provides the guarantee to the contract owner
(e.g. a DAO or a well-configured multisig) that a specific amount
of tokens held by an account won't be transferable until the frozen amount
is reduced using ERC20Freezable._setFrozen.
Functions
ERC20
- name()
- symbol()
- decimals()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
Errors
frozen(address account) → uint256
public
#Returns the frozen balance of an account.
available(address account) → uint256
public
#Returns the available (unfrozen) balance of an account. Up to balanceOf.
_setFrozen(address account, uint256 amount)
internal
#Internal function to set the frozen token amount for a account.
_update(address from, address to, uint256 value)
internal
#See ERC20-_update.
Requirements:
frommust have sufficient unfrozen balance.
ERC20InsufficientUnfrozenBalance(address account, uint256 needed, uint256 available)
error
#The operation failed because the account has insufficient unfrozen balance.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC20Restricted.sol";Extension of ERC20 that allows to implement user account transfer restrictions
through the ERC20Restricted.canTransact function. Inspired by EIP-7943.
By default, each account has no explicit restriction. The ERC20Restricted.canTransact function acts as
a blocklist. Developers can override ERC20Restricted.canTransact to check that restriction == ALLOWED
to implement an allowlist.
Functions
- getRestriction(account)
- canTransact(account)
- _update(from, to, value)
- _setRestriction(account, restriction)
- _blockUser(account)
- _allowUser(account)
- _resetUser(account)
- _checkRestriction(account)
ERC20
- name()
- symbol()
- decimals()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
Errors
getRestriction(address account) → enum ERC20Restricted.Restriction
public
#Returns the restriction of a user account.
canTransact(address account) → bool
public
#Returns whether a user account is allowed to interact with the token.
Default implementation only disallows explicitly BLOCKED accounts (i.e. a blocklist).
To convert into an allowlist, override as:
function canTransact(address account) public view virtual override returns (bool) {
return getRestriction(account) == Restriction.ALLOWED;
}_update(address from, address to, uint256 value)
internal
#See ERC20-_update. Enforces restriction transfers (excluding minting and burning).
Requirements:
frommust be allowed to transfer tokens (seeERC20Restricted.canTransact).tomust be allowed to receive tokens (seeERC20Restricted.canTransact).
_setRestriction(address account, enum ERC20Restricted.Restriction restriction)
internal
#Updates the restriction of a user account.
_blockUser(address account)
internal
#Convenience function to block a user account (set to BLOCKED).
_allowUser(address account)
internal
#Convenience function to allow a user account (set to ALLOWED).
_resetUser(address account)
internal
#Convenience function to reset a user account to default restriction.
_checkRestriction(address account)
internal
#Checks if a user account is restricted. Reverts with ERC20Restricted if so.
UserRestrictionsUpdated(address indexed account, enum ERC20Restricted.Restriction restriction)
event
#Emitted when a user account's restriction is updated.
ERC20UserRestricted(address account)
error
#The operation failed because the user account is restricted.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC20uRWA.sol";Extension of ERC20 according to EIP-7943.
Combines standard ERC-20 functionality with RWA-specific features like account restrictions, asset freezing, and forced asset transfers. This contract doesn't expose minting or burning capabilities; if implemented in derived contracts as needed, they must include 7943-specific logic.
Functions
- canTransact(account)
- supportsInterface(interfaceId)
- canTransfer(from, to, amount)
- getFrozenTokens(account)
- setFrozenTokens(account, amount)
- forcedTransfer(from, to, amount)
- _update(from, to, amount)
- _checkEnforcer(from, to, amount)
- _checkFreezer(account, amount)
ERC20Restricted
ERC20Freezable
ERC20
- name()
- symbol()
- decimals()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7943Fungible
ERC20Restricted
Errors
IERC7943Fungible
ERC20Restricted
ERC20Freezable
canTransact(address account) → bool
public
#Returns whether a user account is allowed to interact with the token.
Default implementation only disallows explicitly BLOCKED accounts (i.e. a blocklist).
To convert into an allowlist, override as:
function canTransact(address account) public view virtual override returns (bool) {
return getRestriction(account) == Restriction.ALLOWED;
}supportsInterface(bytes4 interfaceId) → bool
public
#Returns true if this contract implements the interface defined by
interfaceId. See the corresponding
ERC section
to learn more about how these ids are created.
This function call must use less than 30 000 gas.
canTransfer(address from, address to, uint256 amount) → bool
external
#See IERC7943Fungible.canTransfer.
This function is only meant for external use. Overriding it will not apply the new checks to
the internal ERC20Allowlist._update function. Consider overriding ERC20Allowlist._update accordingly to keep both functions in sync.
getFrozenTokens(address account) → uint256 amount
public
#It could return an amount higher than the account's balance.
setFrozenTokens(address account, uint256 amount) → bool result
public
#See IERC7943Fungible.setFrozenTokens. Always returns true if successful. Reverts otherwise.
The amount is capped to the balance of the account to ensure the IERC7943Fungible.Frozen event
emits values that consistently reflect the actual amount of tokens that are frozen.
forcedTransfer(address from, address to, uint256 amount) → bool result
public
#See IERC7943Fungible.forcedTransfer. Always returns true if successful. Reverts otherwise.
Bypasses the ERC20Restricted restrictions for the from address and adjusts the frozen balance
to the new balance after the transfer.
This function uses ERC20Allowlist._update to perform the transfer, ensuring all standard ERC20
side effects (such as balance updates and events) are preserved. If you override ERC20Allowlist._update
to add additional restrictions or logic, those changes will also apply here.
Consider overriding this function to bypass newer restrictions if needed.
_update(address from, address to, uint256 amount)
internal
#Transfers a value amount of tokens from from to to, or alternatively mints (or burns) if from
(or to) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
this function.
Emits a Transfer event.
_checkEnforcer(address from, address to, uint256 amount)
internal
#Internal function to check if the enforcer is allowed to forcibly transfer the amount of tokens.
Example usage with AccessControl-onlyRole:
function _checkEnforcer(address from, address to, uint256 amount) internal view override onlyRole(ENFORCER_ROLE) {}_checkFreezer(address account, uint256 amount)
internal
#Internal function to check if the freezer is allowed to freeze the amount of tokens.
Example usage with AccessControl-onlyRole:
function _checkFreezer(address account, uint256 amount) internal view override onlyRole(FREEZER_ROLE) {}import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC4626Fees.sol";ERC-4626 vault with entry/exit fees expressed in basis point (bp).
Functions
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- _deposit(caller, receiver, assets, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _entryFeeBasisPoints()
- _exitFeeBasisPoints()
- _entryFeeRecipient()
- _exitFeeRecipient()
ERC4626
- decimals()
- asset()
- totalAssets()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit()
- maxMint()
- maxWithdraw(owner)
- maxRedeem(owner)
- deposit(assets, receiver)
- mint(shares, receiver)
- withdraw(assets, receiver, owner)
- redeem(shares, receiver, owner)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
ERC20
- name()
- symbol()
- totalSupply()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
Errors
ERC4626
previewDeposit(uint256 assets) → uint256
public
#Preview taking an entry fee on deposit. See IERC4626-previewDeposit.
previewMint(uint256 shares) → uint256
public
#Preview adding an entry fee on mint. See IERC4626-previewMint.
previewWithdraw(uint256 assets) → uint256
public
#Preview adding an exit fee on withdraw. See IERC4626-previewWithdraw.
previewRedeem(uint256 shares) → uint256
public
#Preview taking an exit fee on redeem. See IERC4626-previewRedeem.
_deposit(address caller, address receiver, uint256 assets, uint256 shares)
internal
#Send entry fee to ERC4626Fees._entryFeeRecipient. See IERC4626-_deposit.
_withdraw(address caller, address receiver, address owner, uint256 assets, uint256 shares)
internal
#Send exit fee to ERC4626Fees._exitFeeRecipient. See IERC4626-_deposit.
_entryFeeBasisPoints() → uint256
internal
#_exitFeeBasisPoints() → uint256
internal
#_entryFeeRecipient() → address
internal
#_exitFeeRecipient() → address
internal
#import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC7540.sol";Implementation of the ERC-7540 "Asynchronous ERC-4626 Tokenized Vaults" as defined in ERC-7540.
This abstract contract provides a single base for building vaults with asynchronous deposit and/or redemption
flows on top of ERC-4626. It integrates operator management (IERC7540Operator), the full ERC-4626 vault
interface, and routing logic that delegates to either synchronous (standard ERC-4626) or asynchronous paths
depending on the return value of ERC7540._isDepositAsync and ERC7540._isRedeemAsync.
Subcontracts choose their async behavior by overriding two internal pure boolean selectors:
ERC7540._isDepositAsync: whentrue, the deposit side uses the Request lifecycle (Pending -> Claimable -> Claimed).ERC7540._isRedeemAsync: whentrue, the redeem side uses the Request lifecycle.
Each async path requires a fulfillment strategy that implements the virtual hooks declared in this contract
(e.g. ERC7540._consumeClaimableDeposit, ERC7540._pendingDepositRequest, ERC7540._asyncMaxDeposit, etc.).
Deposit and redeem strategies are independent: a vault can combine any deposit strategy with any redeem strategy (e.g. delay-based deposits with admin-based redeems).
Share custody during the async lifecycle is configurable via ERC7540._depositShareOrigin and
ERC7540._redeemShareDestination. When these return address(0) (the default), shares are minted/burned
at claim time. When they return a non-zero address, shares are pre-minted to (or transferred to)
that address at fulfillment time and then transferred to the receiver on claim.
When implementing a custom fulfillment strategy, the following virtual hooks MUST be overridden for each async side enabled:
-
Async deposits:
ERC7540._pendingDepositRequest,ERC7540._claimableDepositRequest,ERC7540._consumeClaimableDeposit,ERC7540._consumeClaimableMint,ERC7540._asyncMaxDeposit,ERC7540._asyncMaxMint. -
Async redeems:
ERC7540._pendingRedeemRequest,ERC7540._claimableRedeemRequest,ERC7540._consumeClaimableWithdraw,ERC7540._consumeClaimableRedeem,ERC7540._asyncMaxWithdraw,ERC7540._asyncMaxRedeem.
ERC-7540 introduces operator permissions that allow operators to manage requests on behalf of controllers. An operator approved by a controller can request deposits using the controller's assets, request redemptions using the controller's shares, and claim assets or shares on behalf of the controller. Users should only approve operators they fully trust with both their assets and shares.
This contract assumes the underlying asset is a well-behaved ERC-20: transfers move exactly the requested
amount, balances do not change without explicit transfers, and balanceOf reports faithfully. Fee-on-transfer,
rebasing, and similar non-standard asset behaviors are out of scope. When the asset misbehaves, internal
accounting (notably ERC7540.totalAssets) can revert and freeze claim paths that depend on live conversions.
Functions
- constructor(asset_)
- supportsInterface(interfaceId)
- isOperator(controller, operator)
- setOperator(operator, approved)
- _setOperator(controller, operator, approved)
- _checkOperatorOrController(async, controller, operator)
- decimals()
- asset()
- share()
- vault(asset_)
- totalAssets()
- totalSupply()
- pendingDepositRequest(requestId, controller)
- claimableDepositRequest(requestId, controller)
- pendingRedeemRequest(requestId, controller)
- claimableRedeemRequest(requestId, controller)
- totalPendingDepositAssets()
- totalPendingRedeemShares()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit(owner)
- maxMint(owner)
- maxWithdraw(owner)
- maxRedeem(owner)
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- requestDeposit(assets, controller, owner)
- deposit(assets, receiver)
- deposit(assets, receiver, controller)
- mint(shares, receiver)
- mint(shares, receiver, controller)
- requestRedeem(shares, controller, owner)
- withdraw(assets, receiver, ownerOrController)
- redeem(shares, receiver, ownerOrController)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _requestDeposit(assets, controller, owner, requestId)
- _mintSharesOnDepositFulfill(assets, shares)
- _deposit(callerOrController, receiver, assets, shares)
- _requestRedeem(shares, controller, owner, requestId)
- _burnSharesOnRedeemFulfill(, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
- _depositShareOrigin()
- _redeemShareDestination()
- _isDepositAsync()
- _isRedeemAsync()
- _pendingDepositRequest(, )
- _claimableDepositRequest(, )
- _pendingRedeemRequest(, )
- _claimableRedeemRequest(, )
- _consumeClaimableDeposit(, )
- _consumeClaimableMint(, )
- _consumeClaimableWithdraw(, )
- _consumeClaimableRedeem(, )
- _asyncMaxDeposit()
- _asyncMaxMint()
- _asyncMaxWithdraw()
- _asyncMaxRedeem()
ERC20
- name()
- symbol()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7540Redeem
IERC7540Deposit
IERC7540Operator
Errors
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
- ERC7540InvalidOperator(controller, operator)
- ERC7540SyncDeposit()
- ERC7540AsyncDeposit()
- ERC7540SyncRedeem()
- ERC7540AsyncRedeem()
- ERC7540MissingAsync()
- ERC7540UnauthorizedMintSharesOnDepositFulfill()
- ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
onlyOperatorOrController(bool async, address controller, address operator)
internal
#Modifier that enforces operator-or-controller authorization.
When async is false the check is skipped, which allows the standard ERC-4626 flow where
msg.sender is the caller and no controller/operator distinction exists.
constructor(contract IERC20 asset_)
internal
#Sets the underlying asset contract. This must be an ERC-20-compatible contract.
Caches the asset's decimals() value at construction time. If the call fails (e.g. the asset has not
been created yet), a default of 18 is used.
Requirements:
- At least one of
ERC7540._isDepositAsyncorERC7540._isRedeemAsyncmust returntrue.
Either ERC7540._isDepositAsync or ERC7540._isRedeemAsync must return true. Use ERC4626 otherwise.
supportsInterface(bytes4 interfaceId) → bool
public
#See IERC165-supportsInterface.
Reports support for IERC7540Operator unconditionally. Support for IERC7540Deposit and
IERC7540Redeem is conditional on the corresponding async selector returning true.
isOperator(address controller, address operator) → bool status
public
#Returns true if the operator is approved as an operator for a controller.
setOperator(address operator, bool approved) → bool
public
#Grants or revokes permissions for operator to manage requests on behalf of the caller.
- MUST set the operator status to the
approvedvalue. - MUST emit the
IERC7540Operator.OperatorSetevent when the operator status is set. - MUST return true.
_setOperator(address controller, address operator, bool approved)
internal
#Sets the operator approval status for controller to approved.
Emits an IERC7540Operator.OperatorSet event.
_checkOperatorOrController(bool async, address controller, address operator)
internal
#Reverts with ERC7540.ERC7540InvalidOperator if operator is not the controller and is not
approved as an operator for controller. When async is false the check is a no-op,
preserving standard ERC-4626 authorization semantics.
decimals() → uint8
public
#Decimals are computed by adding the decimal offset on top of the underlying asset's decimals. This "original" value is cached during construction of the vault contract. If this read operation fails (e.g., the asset has not been created yet), a default of 18 is used to represent the underlying asset's decimals.
See IERC20Metadata-decimals.
asset() → address
public
#Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
- MUST be an ERC-20 token contract.
- MUST NOT revert.
share() → address
public
#The address of the underlying share received on deposit into the vault.
vault(address asset_) → address
public
#The address of the vault for a specific asset.
totalAssets() → uint256
public
#See IERC4626-totalAssets.
Pending deposit assets are subtracted from the vault's token balance, since they have not yet been converted into shares and must not be treated as yield for outstanding shareholders.
Internal flows preserve the invariant balanceOf(asset, vault) >= totalPendingDepositAssets() for
any well-behaved ERC-20. Assets with transfer fees, negative rebases, or externally-mutable balances can
violate it and cause this function to revert with an underflow. Strategies that read ERC7540.totalAssets on the
claim path become uncallable in that state. Strategies that lock the rate at fulfillment time
are unaffected.
totalSupply() → uint256
public
#See IERC20-totalSupply.
Adds ERC7540.totalPendingRedeemShares to the ERC-20 supply. When shares are burned at request time
(i.e. ERC7540._redeemShareDestination returns address(0)), pending redeem shares are removed from
the on-chain supply but still logically outstanding until claimed; this override compensates.
As a consequence, two standard ERC-20 assumptions do not hold: (a) totalSupply() may
exceed the sum of all balanceOf() (pending shares are virtual and unowned); (b) totalSupply()
can change without a matching Transfer event when ERC7540.totalPendingRedeemShares changes. Integrators
that snapshot supply for governance or reward weighting, or reconstruct supply from event logs
(indexers, bridges), must account for this.
pendingDepositRequest(uint256 requestId, address controller) → uint256
public
#claimableDepositRequest(uint256 requestId, address controller) → uint256
public
#pendingRedeemRequest(uint256 requestId, address controller) → uint256
public
#claimableRedeemRequest(uint256 requestId, address controller) → uint256
public
#totalPendingDepositAssets() → uint256
public
#Returns the total amount of underlying assets currently pending in deposit Requests.
totalPendingRedeemShares() → uint256
public
#Returns the total amount of vault shares currently pending in redeem Requests.
convertToShares(uint256 assets) → uint256
public
#Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal scenario where all the conditions are met.
- MUST NOT be inclusive of any fees that are charged against assets in the Vault.
- MUST NOT show any variations depending on the caller.
- MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
- MUST NOT revert.
This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and from.
convertToAssets(uint256 shares) → uint256
public
#Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal scenario where all the conditions are met.
- MUST NOT be inclusive of any fees that are charged against assets in the Vault.
- MUST NOT show any variations depending on the caller.
- MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
- MUST NOT revert.
This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and from.
maxDeposit(address owner) → uint256
public
#See IERC4626-maxDeposit.
When the deposit flow is synchronous, returns type(uint256).max (no vault-imposed limit).
When async, delegates to ERC7540._asyncMaxDeposit which must be provided by the fulfillment strategy.
maxMint(address owner) → uint256
public
#See IERC4626-maxMint.
When the deposit flow is synchronous, returns type(uint256).max.
When async, delegates to ERC7540._asyncMaxMint.
maxWithdraw(address owner) → uint256
public
#See IERC4626-maxWithdraw.
When the redeem flow is synchronous, returns the asset-equivalent of the owner's share balance.
When async, delegates to ERC7540._asyncMaxWithdraw.
maxRedeem(address owner) → uint256
public
#See IERC4626-maxRedeem.
When the redeem flow is synchronous, returns the owner's share balance.
When async, delegates to ERC7540._asyncMaxRedeem.
previewDeposit(uint256 assets) → uint256
public
#See IERC4626-previewDeposit.
MUST revert when ERC7540._isDepositAsync returns true, per the ERC-7540 specification which
mandates that preview functions revert for async flows.
previewMint(uint256 shares) → uint256
public
#See IERC4626-previewMint.
MUST revert when ERC7540._isDepositAsync returns true.
previewWithdraw(uint256 assets) → uint256
public
#See IERC4626-previewWithdraw.
MUST revert when ERC7540._isRedeemAsync returns true.
previewRedeem(uint256 shares) → uint256
public
#See IERC4626-previewRedeem.
MUST revert when ERC7540._isRedeemAsync returns true.
requestDeposit(uint256 assets, address controller, address owner) → uint256
public
#See IERC7540Deposit.requestDeposit.
Transfers assets from owner into the vault and submits a deposit Request for controller.
The Request enters Pending state. Uses requestId = 0 by default; override ERC7540._requestDeposit
to use non-zero request IDs.
Requirements:
ERC7540._isDepositAsyncmust returntrue.ownermust bemsg.senderormsg.sendermust be an approved operator ofowner.ownermust have approved the vault for at leastassetsof the underlying token.
The controller is the only address authorized to claim the resulting Request. Passing an address
with no claim authority (e.g. address(0), 0x...dead) or any contract that cannot itself call
ERC7540.deposit/ERC7540.mint or designate an operator via ERC7540.setOperator will permanently lock the committed
assets, since claims are gated by ERC7540.onlyOperatorOrController on controller and there is no
cancellation path. Callers are responsible for supplying a controller capable of authorizing claims.
deposit(uint256 assets, address receiver) → uint256
public
#See IERC4626-deposit. Calls the three-argument overload with msg.sender as controller.
deposit(uint256 assets, address receiver, address controller) → uint256
public
#When async, claims assets worth of shares from a Claimable deposit Request controlled by controller,
and transfers the resulting shares to receiver. When sync, behaves as standard ERC-4626 deposit.
When ERC7540._isDepositAsync is false, the controller parameter is ignored and receiver is used
for limit checks, matching standard ERC-4626 semantics.
Requirements:
assetsmust not exceedERC7540.maxDepositfor the relevant account.- When async,
msg.sendermust becontrolleror an approved operator ofcontroller.
mint(uint256 shares, address receiver) → uint256 assets
public
#See IERC4626-mint. Calls the three-argument overload with msg.sender as controller.
mint(uint256 shares, address receiver, address controller) → uint256
public
#See IERC7540Deposit.mint.
When async, claims exactly shares from a Claimable deposit Request controlled by controller,
and transfers them to receiver. When sync, behaves as standard ERC-4626 mint.
When ERC7540._isDepositAsync is false, the controller parameter is ignored.
Requirements:
sharesmust not exceedERC7540.maxMintfor the relevant account.- When async,
msg.sendermust becontrolleror an approved operator ofcontroller.
requestRedeem(uint256 shares, address controller, address owner) → uint256
public
#See IERC7540Redeem.requestRedeem.
Assumes control of shares from owner and submits a redeem Request for controller.
The Request enters Pending state. Uses requestId = 0 by default.
Authorization for a msg.sender not equal to owner may come from either ERC-20 approval
over the shares of owner or from operator approval (see IERC7540Operator). This is
consistent with the approach described in ERC-6909.
Requirements:
ERC7540._isRedeemAsyncmust returntrue.
The controller is the only address authorized to claim the resulting Request. Passing an address
with no claim authority (e.g. address(0), 0x...dead) or any contract that cannot itself call
ERC7540.withdraw/ERC7540.redeem or designate an operator via ERC7540.setOperator will permanently lock the committed
shares, since claims are gated by ERC7540.onlyOperatorOrController on controller and there is no
cancellation path. Callers are responsible for supplying a controller capable of authorizing claims.
withdraw(uint256 assets, address receiver, address ownerOrController) → uint256
public
#See IERC4626-withdraw.
When async, claims assets from a Claimable redeem Request controlled by ownerOrController,
and transfers the underlying assets to receiver. When sync, behaves as standard ERC-4626 withdraw.
Per ERC-7540, when async the ownerOrController parameter acts as the controller
(replacing the traditional ERC-4626 owner role).
Requirements:
assetsmust not exceedERC7540.maxWithdrawforownerOrController.- When async,
msg.sendermust beownerOrControlleror an approved operator. - When sync,
msg.sendermust beownerOrControlleror have sufficient ERC-20 allowance.
redeem(uint256 shares, address receiver, address ownerOrController) → uint256
public
#See IERC4626-redeem.
When async, claims shares from a Claimable redeem Request controlled by ownerOrController,
and transfers the corresponding underlying assets to receiver. When sync, behaves as standard
ERC-4626 redeem.
Per ERC-7540, when async the ownerOrController parameter acts as the controller.
Requirements:
sharesmust not exceedERC7540.maxRedeemforownerOrController.- When async,
msg.sendermust beownerOrControlleror an approved operator. - When sync,
msg.sendermust beownerOrControlleror have sufficient ERC-20 allowance.
_convertToShares(uint256 assets, enum Math.Rounding rounding) → uint256
internal
#Internal conversion function (from assets to shares) with support for rounding direction.
Uses virtual shares and virtual assets to mitigate the inflation attack vector described in
ERC-4626 security considerations.
The offset is configurable via ERC7540._decimalsOffset.
_convertToAssets(uint256 shares, enum Math.Rounding rounding) → uint256
internal
#Internal conversion function (from shares to assets) with support for rounding direction.
_requestDeposit(uint256 assets, address controller, address owner, uint256 requestId) → uint256
internal
#Internal handler for deposit Requests. Increments ERC7540.totalPendingDepositAssets, transfers
assets from owner into the vault, and emits IERC7540Deposit.DepositRequest.
Strategy extensions (e.g. ERC7540AdminDeposit) should override this to record per-controller
pending state before calling super._requestDeposit(...).
Pending accounting is updated before ERC7540._transferIn to follow Checks-Effects-Interactions.
Assets with transfer hooks (e.g. ERC-777) may observe ERC7540.totalAssets temporarily understated
during the transfer, since _totalPendingDepositAssets is already incremented while the
token balance has not yet increased.
Requirements:
ERC7540._isDepositAsyncmust returntrue, otherwise reverts withERC7540SyncDeposit.
_mintSharesOnDepositFulfill(uint256 assets, uint256 shares)
internal
#Mints shares to ERC7540._depositShareOrigin as part of deposit fulfillment when using the
pre-mint share custody model. Decrements ERC7540.totalPendingDepositAssets by assets.
This function requires ERC7540._depositShareOrigin to return a non-zero address.
When ERC7540._depositShareOrigin returns address(0), shares are minted directly at claim time
inside ERC4626Fees._deposit and this function must not be called.
_deposit(address callerOrController, address receiver, uint256 assets, uint256 shares)
internal
#Common workflow for deposit and mint claim operations.
Handles three cases depending on the vault configuration:
- Synchronous (
ERC7540._isDepositAsyncreturnsfalse): transfers assets fromcallerOrControllerinto the vault and mints new shares toreceiver. Standard ERC-4626 behavior. - Async, mint-on-claim (
ERC7540._depositShareOriginreturnsaddress(0)): decrements_totalPendingDepositAssetsand mints new shares toreceiver. No asset transfer occurs since assets were already transferred duringERC7540.requestDeposit. - Async, pre-minted (
ERC7540._depositShareOriginreturns non-zero): transfers pre-minted shares from the share origin address toreceiver.
Emits IERC4626-Deposit. Per ERC-7540, the first event parameter is the controller in async
mode and msg.sender in sync mode.
_requestRedeem(uint256 shares, address controller, address owner, uint256 requestId) → uint256
internal
#Internal handler for redeem Requests. Assumes control of shares from owner and
emits IERC7540Redeem.RedeemRequest.
Share custody depends on ERC7540._redeemShareDestination:
address(0)(default): shares are burned immediately and_totalPendingRedeemSharesis incremented to keepERC7540.totalSupplyaccurate.- Non-zero address: shares are transferred to that address and held until fulfillment.
Authorization for a msg.sender not equal to owner is checked via operator status first;
if that fails, ERC-20 allowance is spent via ERC20-_spendAllowance. This dual-authorization
approach is consistent with ERC-6909 semantics.
Requirements:
ERC7540._isRedeemAsyncmust returntrue, otherwise reverts withERC7540SyncRedeem.
_burnSharesOnRedeemFulfill(uint256, uint256 shares)
internal
#Burns shares from ERC7540._redeemShareDestination as part of redeem fulfillment when using
the escrow share custody model. Increments ERC7540.totalPendingRedeemShares by shares.
This function requires ERC7540._redeemShareDestination to return a non-zero address.
When ERC7540._redeemShareDestination returns address(0), shares were already burned at request
time inside ERC7540._requestRedeem and this function must not be called.
_withdraw(address caller, address receiver, address owner, uint256 assets, uint256 shares)
internal
#Common workflow for withdraw and redeem claim operations.
Handles two cases depending on the vault configuration:
- Synchronous (
ERC7540._isRedeemAsyncreturnsfalse): spends ERC-20 allowance ifcalleris notowner, burns shares fromowner, and transfers underlying assets toreceiver. - Async: decrements
_totalPendingRedeemShares(shares were already burned/escrowed during the Request or fulfillment phase) and transfers underlying assets toreceiver.
Emits IERC4626-Withdraw.
_transferIn(address from, uint256 assets)
internal
#Performs a transfer-in of underlying assets from from to the vault.
The default implementation uses SafeERC20-safeTransferFrom. Used by ERC4626Fees._deposit (sync) and
ERC7540._requestDeposit (async).
_transferOut(address to, uint256 assets)
internal
#Performs a transfer-out of underlying assets from the vault to to.
The default implementation uses SafeERC20-safeTransfer. Used by ERC4626Fees._withdraw.
_decimalsOffset() → uint8
internal
#Returns the decimal offset used for virtual shares/assets in ERC7540._convertToShares and
ERC7540._convertToAssets. Defaults to 0. Increase to strengthen inflation-attack protection
at the cost of share-price granularity.
_depositShareOrigin() → address
internal
#Returns the address from which shares are transferred to the receiver on deposit claim.
address(0)(default): shares are minted directly to the receiver at claim time. Pending deposit assets are tracked viaERC7540.totalPendingDepositAssetsand decremented inERC4626Fees._deposit.- Non-zero address: shares are pre-minted to this address during fulfillment (via
ERC7540._mintSharesOnDepositFulfill) and transferred to the receiver on claim.
If overridden to return a non-zero address, that address must not be able to transfer
shares (otherwise pre-minted shares could be moved before they are claimed). Use an unowned
address such as address(0xdead). Avoid addresses in the precompile reserved range
(address(1) through address(0x1ff), see EIP-7587).
_redeemShareDestination() → address
internal
#Returns the address to which shares are transferred (escrowed) on redeem request.
address(0)(default): shares are burned immediately at request time. Pending redeem shares are tracked viaERC7540.totalPendingRedeemSharesso thatERC7540.totalSupplyremains accurate.- Non-zero address: shares are transferred to this address on request and burned during
fulfillment (via
ERC7540._burnSharesOnRedeemFulfill).
If overridden to return a non-zero address, that address must not be able to transfer
shares (otherwise escrowed shares could be moved before they are burned). Use an unowned
address such as address(0xdead). Avoid addresses in the precompile reserved range
(address(1) through address(0x1ff), see EIP-7587).
_isDepositAsync() → bool
internal
#Returns true if the deposit flow is asynchronous (Request-based). When false, ERC7540.deposit and
ERC7540.mint behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async deposit fulfillment strategy.
_isRedeemAsync() → bool
internal
#Returns true if the redeem flow is asynchronous (Request-based). When false, ERC7540.withdraw and
ERC7540.redeem behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async redeem fulfillment strategy.
_pendingDepositRequest(uint256, address) → uint256
internal
#Returns the amount of assets in Pending state for controller with the given requestId.
_claimableDepositRequest(uint256, address) → uint256
internal
#Returns the amount of assets in Claimable state for controller with the given requestId.
_pendingRedeemRequest(uint256, address) → uint256
internal
#Returns the amount of shares in Pending state for controller with the given requestId.
_claimableRedeemRequest(uint256, address) → uint256
internal
#Returns the amount of shares in Claimable state for controller with the given requestId.
_consumeClaimableDeposit(uint256, address) → uint256
internal
#Consumes assets worth of a Claimable deposit for controller and returns the corresponding
number of shares. Called by ERC7540.deposit (three-argument overload) in async mode.
In async mode, this function may be susceptible to the inflation attack vector described in
ERC-4626 security considerations
if the shares are freed automatically (e.g. after a certain time period). Consider using ERC7540._decimalsOffset
to mitigate this risk.
_consumeClaimableMint(uint256, address) → uint256
internal
#Consumes shares worth of a Claimable deposit for controller and returns the corresponding
number of assets. Called by ERC7540.mint (three-argument overload) in async mode.
_consumeClaimableWithdraw(uint256, address) → uint256
internal
#Consumes assets worth of a Claimable redeem for controller and returns the corresponding
number of shares. Called by ERC7540.withdraw in async mode.
_consumeClaimableRedeem(uint256, address) → uint256
internal
#Consumes shares worth of a Claimable redeem for controller and returns the corresponding
number of assets. Called by ERC7540.redeem in async mode.
_asyncMaxDeposit(address) → uint256
internal
#Returns the maximum assets that can be claimed via ERC7540.deposit for an async owner.
_asyncMaxMint(address) → uint256
internal
#Returns the maximum shares that can be claimed via ERC7540.mint for an async owner.
_asyncMaxWithdraw(address) → uint256
internal
#Returns the maximum assets that can be claimed via ERC7540.withdraw for an async owner.
_asyncMaxRedeem(address) → uint256
internal
#Returns the maximum shares that can be claimed via ERC7540.redeem for an async owner.
ERC4626ExceededMaxDeposit(address receiver, uint256 assets, uint256 max)
error
#Attempted to deposit more assets than the max amount for receiver.
ERC4626ExceededMaxMint(address receiver, uint256 shares, uint256 max)
error
#Attempted to mint more shares than the max amount for receiver.
ERC4626ExceededMaxWithdraw(address owner, uint256 assets, uint256 max)
error
#Attempted to withdraw more assets than the max amount for owner.
ERC4626ExceededMaxRedeem(address owner, uint256 shares, uint256 max)
error
#Attempted to redeem more shares than the max amount for owner.
ERC7540InvalidOperator(address controller, address operator)
error
#The operator is not the caller or an approved operator of the controller.
ERC7540SyncDeposit()
error
#A deposit Request was attempted but ERC7540._isDepositAsync returns false.
ERC7540AsyncDeposit()
error
#A synchronous deposit preview was attempted but ERC7540._isDepositAsync returns true.
ERC7540SyncRedeem()
error
#A redeem Request was attempted but ERC7540._isRedeemAsync returns false.
ERC7540AsyncRedeem()
error
#A synchronous redeem preview was attempted but ERC7540._isRedeemAsync returns true.
ERC7540MissingAsync()
error
#Neither ERC7540._isDepositAsync nor ERC7540._isRedeemAsync returns true.
ERC7540UnauthorizedMintSharesOnDepositFulfill()
error
#Invalid attempt at minting shares on a deposit fulfill when configuration mints them during claim.
ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
error
#Invalid attempt at burning shares on a redeem fulfill when configuration burns them during request.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC7540AdminDeposit.sol";Admin-controlled (operator-triggered) fulfillment strategy for asynchronous deposits.
Extends ERC7540 with a deposit flow where a privileged caller explicitly transitions requests
from Pending to Claimable by calling ERC7540AdminDeposit._fulfillDeposit. The caller provides both the assets
amount and the corresponding shares, giving the fulfiller explicit control over the exchange rate.
This is the most flexible fulfillment model. Epoch-based batch settlement, FIFO queues, and cross-chain oracle-gated settlement can all be composed on top.
Production equivalents include USDai, Nest (Plume), MetaVault, and Centrifuge.
All requests share requestId = 0 (per-controller accounting only).
Functions
- _isDepositAsync()
- _requestDeposit(assets, controller, owner, requestId)
- _fulfillDeposit(assets, shares, controller)
- _consumeClaimableDeposit(assets, controller)
- _consumeClaimableMint(shares, controller)
- _pendingDepositRequest(, controller)
- _claimableDepositRequest(, controller)
- _asyncMaxDeposit(owner)
- _asyncMaxMint(owner)
ERC7540
- supportsInterface(interfaceId)
- isOperator(controller, operator)
- setOperator(operator, approved)
- _setOperator(controller, operator, approved)
- _checkOperatorOrController(async, controller, operator)
- decimals()
- asset()
- share()
- vault(asset_)
- totalAssets()
- totalSupply()
- pendingDepositRequest(requestId, controller)
- claimableDepositRequest(requestId, controller)
- pendingRedeemRequest(requestId, controller)
- claimableRedeemRequest(requestId, controller)
- totalPendingDepositAssets()
- totalPendingRedeemShares()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit(owner)
- maxMint(owner)
- maxWithdraw(owner)
- maxRedeem(owner)
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- requestDeposit(assets, controller, owner)
- deposit(assets, receiver)
- deposit(assets, receiver, controller)
- mint(shares, receiver)
- mint(shares, receiver, controller)
- requestRedeem(shares, controller, owner)
- withdraw(assets, receiver, ownerOrController)
- redeem(shares, receiver, ownerOrController)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _mintSharesOnDepositFulfill(assets, shares)
- _deposit(callerOrController, receiver, assets, shares)
- _requestRedeem(shares, controller, owner, requestId)
- _burnSharesOnRedeemFulfill(, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
- _depositShareOrigin()
- _redeemShareDestination()
- _isRedeemAsync()
- _pendingRedeemRequest(, )
- _claimableRedeemRequest(, )
- _consumeClaimableWithdraw(, )
- _consumeClaimableRedeem(, )
- _asyncMaxWithdraw()
- _asyncMaxRedeem()
ERC20
- name()
- symbol()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7540Redeem
IERC7540Deposit
IERC7540Operator
Errors
ERC7540
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
- ERC7540InvalidOperator(controller, operator)
- ERC7540SyncDeposit()
- ERC7540AsyncDeposit()
- ERC7540SyncRedeem()
- ERC7540AsyncRedeem()
- ERC7540MissingAsync()
- ERC7540UnauthorizedMintSharesOnDepositFulfill()
- ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
_isDepositAsync() → bool
internal
#Returns true if the deposit flow is asynchronous (Request-based). When false, ERC7540.deposit and
ERC7540.mint behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async deposit fulfillment strategy.
_requestDeposit(uint256 assets, address controller, address owner, uint256 requestId) → uint256
internal
#Records per-controller pending state before delegating to ERC7540._requestDeposit.
_fulfillDeposit(uint256 assets, uint256 shares, address controller)
internal
#Fulfills a pending deposit request by transitioning it from Pending to Claimable state.
The caller provides both assets and shares, locking the exchange rate at fulfillment time.
The fulfiller should ensure the vault's totalAssets() already reflects the deposited assets
(e.g. after deploying them to a yield source) to avoid diluting existing holders.
Emits a ERC7540AdminDeposit.DepositClaimable event.
Requirements:
assetsmust not exceed the pending deposit amount for thecontroller.
Multiple fulfillments with different exchange rates will blend into a weighted average.
For example, fulfilling 50 assets → 100 shares (2:1) then 50 assets → 25 shares (0.5:1) produces
claimableAssets=100 and claimableShares=125. A partial claim of 50 assets yields
mulDiv(50, 125, 100) = 62 shares: the weighted average rate, not either original rate.
Integrators expecting per-fulfillment rate isolation should use a different strategy (e.g. epochs).
_consumeClaimableDeposit(uint256 assets, address controller) → uint256
internal
#Consumes assets from the claimable deposit and returns the proportional shares (rounded down).
_consumeClaimableMint(uint256 shares, address controller) → uint256
internal
#Consumes shares from the claimable deposit and returns the proportional assets (rounded up).
_pendingDepositRequest(uint256, address controller) → uint256
internal
#Returns the amount of assets in Pending state for controller with the given requestId.
_claimableDepositRequest(uint256, address controller) → uint256
internal
#Returns the amount of assets in Claimable state for controller with the given requestId.
_asyncMaxDeposit(address owner) → uint256
internal
#Returns the maximum assets that can be claimed via ERC7540.deposit for an async owner.
_asyncMaxMint(address owner) → uint256
internal
#Returns the maximum shares that can be claimed via ERC7540.mint for an async owner.
DepositClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares)
event
#Emitted when a deposit request transitions from Pending to Claimable.
ERC7540DepositInsufficientPendingAssets(uint256 assets, uint256 pendingAssets)
error
#The assets to fulfill exceeds the pendingAssets for the controller.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC7540AdminRedeem.sol";Admin-controlled (operator-triggered) fulfillment strategy for asynchronous redemptions.
Extends ERC7540 with a redeem flow where a privileged caller explicitly transitions requests
from Pending to Claimable by calling ERC7540AdminRedeem._fulfillRedeem. The caller provides both the shares
amount and the corresponding assets, giving the fulfiller explicit control over the exchange rate.
The fulfiller must ensure the vault holds enough underlying assets before calling ERC7540AdminRedeem._fulfillRedeem.
Asset sourcing (unwinding positions, bridging cross-chain, etc.) is application-specific and is
not part of this contract.
Production equivalents include USDai, Nest (Plume), MetaVault, and Centrifuge.
All requests share requestId = 0 (per-controller accounting only).
Functions
- _isRedeemAsync()
- _requestRedeem(shares, controller, owner, requestId)
- _fulfillRedeem(shares, assets, controller)
- _consumeClaimableWithdraw(assets, controller)
- _consumeClaimableRedeem(shares, controller)
- _pendingRedeemRequest(, controller)
- _claimableRedeemRequest(, controller)
- _asyncMaxWithdraw(owner)
- _asyncMaxRedeem(owner)
ERC7540
- supportsInterface(interfaceId)
- isOperator(controller, operator)
- setOperator(operator, approved)
- _setOperator(controller, operator, approved)
- _checkOperatorOrController(async, controller, operator)
- decimals()
- asset()
- share()
- vault(asset_)
- totalAssets()
- totalSupply()
- pendingDepositRequest(requestId, controller)
- claimableDepositRequest(requestId, controller)
- pendingRedeemRequest(requestId, controller)
- claimableRedeemRequest(requestId, controller)
- totalPendingDepositAssets()
- totalPendingRedeemShares()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit(owner)
- maxMint(owner)
- maxWithdraw(owner)
- maxRedeem(owner)
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- requestDeposit(assets, controller, owner)
- deposit(assets, receiver)
- deposit(assets, receiver, controller)
- mint(shares, receiver)
- mint(shares, receiver, controller)
- requestRedeem(shares, controller, owner)
- withdraw(assets, receiver, ownerOrController)
- redeem(shares, receiver, ownerOrController)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _requestDeposit(assets, controller, owner, requestId)
- _mintSharesOnDepositFulfill(assets, shares)
- _deposit(callerOrController, receiver, assets, shares)
- _burnSharesOnRedeemFulfill(, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
- _depositShareOrigin()
- _redeemShareDestination()
- _isDepositAsync()
- _pendingDepositRequest(, )
- _claimableDepositRequest(, )
- _consumeClaimableDeposit(, )
- _consumeClaimableMint(, )
- _asyncMaxDeposit()
- _asyncMaxMint()
ERC20
- name()
- symbol()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7540Redeem
IERC7540Deposit
IERC7540Operator
Errors
ERC7540
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
- ERC7540InvalidOperator(controller, operator)
- ERC7540SyncDeposit()
- ERC7540AsyncDeposit()
- ERC7540SyncRedeem()
- ERC7540AsyncRedeem()
- ERC7540MissingAsync()
- ERC7540UnauthorizedMintSharesOnDepositFulfill()
- ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
_isRedeemAsync() → bool
internal
#Returns true if the redeem flow is asynchronous (Request-based). When false, ERC7540.withdraw and
ERC7540.redeem behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async redeem fulfillment strategy.
_requestRedeem(uint256 shares, address controller, address owner, uint256 requestId) → uint256
internal
#Records per-controller pending state before delegating to ERC7540._requestRedeem.
_fulfillRedeem(uint256 shares, uint256 assets, address controller)
internal
#Fulfills a pending redeem request by transitioning it from Pending to Claimable state.
The caller provides both shares and assets, locking the exchange rate at fulfillment time.
The fulfiller must ensure the vault holds enough underlying assets to cover the assets amount
before calling this function.
Emits a ERC7540AdminRedeem.RedeemClaimable event.
Requirements:
sharesmust not exceed the pending redeem amount for thecontroller.
_consumeClaimableWithdraw(uint256 assets, address controller) → uint256
internal
#Consumes assets from the claimable redeem and returns the proportional shares (rounded up).
_consumeClaimableRedeem(uint256 shares, address controller) → uint256
internal
#Consumes shares from the claimable redeem and returns the proportional assets (rounded down).
_pendingRedeemRequest(uint256, address controller) → uint256
internal
#Returns the amount of shares in Pending state for controller with the given requestId.
_claimableRedeemRequest(uint256, address controller) → uint256
internal
#Returns the amount of shares in Claimable state for controller with the given requestId.
_asyncMaxWithdraw(address owner) → uint256
internal
#Returns the maximum assets that can be claimed via ERC7540.withdraw for an async owner.
_asyncMaxRedeem(address owner) → uint256
internal
#Returns the maximum shares that can be claimed via ERC7540.redeem for an async owner.
RedeemClaimable(address indexed controller, uint256 indexed requestId, uint256 assets, uint256 shares)
event
#Emitted when a redeem request transitions from Pending to Claimable.
ERC7540RedeemInsufficientPendingShares(uint256 shares, uint256 pendingShares)
error
#The shares to fulfill exceeds the pendingShares for the controller.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC7540DelayDeposit.sol";Time-delay fulfillment strategy for asynchronous deposits.
Extends ERC7540 with a deposit flow where requests become permissionlessly claimable after a
configurable waiting period. No privileged fulfiller is needed — once the delay elapses, the
controller (or any keeper) can claim. The exchange rate is computed at claim time using the vault's
live ERC7540.convertToShares.
Production equivalents (redeem side): BeefySonic, MagmaV2, Tangle.
Requests are tracked using Checkpoints-Trace208, storing cumulative deposit amounts keyed by
their maturity timepoint. The requestId returned by ERC7540.requestDeposit equals the absolute
timestamp at which the request becomes claimable (clock() + depositDelay(controller)).
Override ERC7540DelayDeposit.depositDelay to customize the waiting period (default: 1 hour) and ERC20Collateral.clock to
change the time source (default: block.timestamp).
This module does not support temporary share custody through ERC7540._depositShareOrigin. The constructor
tries to enforce that property, but the check may be insufficient if ERC7540._depositShareOrigin reads from
storage that is not yet initialized when the parent's constructor runs
Functions
- constructor()
- clock()
- CLOCK_MODE()
- depositDelay()
- _isDepositAsync()
- _requestDeposit(assets, controller, owner, )
- _consumeClaimableDeposit(assets, controller)
- _consumeClaimableMint(shares, controller)
- _pendingDepositRequest(requestId, controller)
- _claimableDepositRequest(requestId, controller)
- _asyncMaxDeposit(owner)
- _asyncMaxMint(owner)
- _readyDepositAt(owner, timepoint)
ERC7540
- supportsInterface(interfaceId)
- isOperator(controller, operator)
- setOperator(operator, approved)
- _setOperator(controller, operator, approved)
- _checkOperatorOrController(async, controller, operator)
- decimals()
- asset()
- share()
- vault(asset_)
- totalAssets()
- totalSupply()
- pendingDepositRequest(requestId, controller)
- claimableDepositRequest(requestId, controller)
- pendingRedeemRequest(requestId, controller)
- claimableRedeemRequest(requestId, controller)
- totalPendingDepositAssets()
- totalPendingRedeemShares()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit(owner)
- maxMint(owner)
- maxWithdraw(owner)
- maxRedeem(owner)
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- requestDeposit(assets, controller, owner)
- deposit(assets, receiver)
- deposit(assets, receiver, controller)
- mint(shares, receiver)
- mint(shares, receiver, controller)
- requestRedeem(shares, controller, owner)
- withdraw(assets, receiver, ownerOrController)
- redeem(shares, receiver, ownerOrController)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _mintSharesOnDepositFulfill(assets, shares)
- _deposit(callerOrController, receiver, assets, shares)
- _requestRedeem(shares, controller, owner, requestId)
- _burnSharesOnRedeemFulfill(, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
- _depositShareOrigin()
- _redeemShareDestination()
- _isRedeemAsync()
- _pendingRedeemRequest(, )
- _claimableRedeemRequest(, )
- _consumeClaimableWithdraw(, )
- _consumeClaimableRedeem(, )
- _asyncMaxWithdraw()
- _asyncMaxRedeem()
ERC20
- name()
- symbol()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7540Redeem
IERC7540Deposit
IERC7540Operator
Errors
ERC7540
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
- ERC7540InvalidOperator(controller, operator)
- ERC7540SyncDeposit()
- ERC7540AsyncDeposit()
- ERC7540SyncRedeem()
- ERC7540AsyncRedeem()
- ERC7540MissingAsync()
- ERC7540UnauthorizedMintSharesOnDepositFulfill()
- ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
constructor()
internal
#clock() → uint48
public
#Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
CLOCK_MODE() → string
public
#Description of the clock
depositDelay(address) → uint48
public
#Returns the delay duration before a deposit request becomes claimable. Defaults to 1 hour.
For any given controller, the maturity timepoint clock() + depositDelay(controller) MUST
be non-decreasing across successive ERC7540.requestDeposit calls. Overrides that shrink the delay faster
than clock() advances will cause new requests to revert until the previous maturity is reached.
_isDepositAsync() → bool
internal
#Returns true if the deposit flow is asynchronous (Request-based). When false, ERC7540.deposit and
ERC7540.mint behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async deposit fulfillment strategy.
_requestDeposit(uint256 assets, address controller, address owner, uint256) → uint256
internal
#Pushes a new cumulative checkpoint at the maturity timepoint and delegates to
ERC7540._requestDeposit with the timepoint as requestId.
_consumeClaimableDeposit(uint256 assets, address controller) → uint256
internal
#Consumes assets from claimable deposits, returns proportional shares (rounded down).
Requirements:
ERC7540.maxMintmust not be 0 forcontroller. Panics with division by zero otherwise.
_consumeClaimableMint(uint256 shares, address controller) → uint256
internal
#Consumes shares from claimable deposits, returns proportional assets (rounded up).
_pendingDepositRequest(uint256 requestId, address controller) → uint256
internal
#Returns the assets in Pending state for a specific requestId (timepoint).
A request is pending only if its timepoint is strictly in the future.
_claimableDepositRequest(uint256 requestId, address controller) → uint256
internal
#Returns the assets in Claimable state for a specific requestId (timepoint).
A request is claimable once its timepoint has elapsed and the assets haven't been claimed yet.
_asyncMaxDeposit(address owner) → uint256
internal
#Returns the total claimable assets across all matured timepoints for owner.
_asyncMaxMint(address owner) → uint256
internal
#Returns the share-equivalent of ERC7540._asyncMaxDeposit (rounded down).
_readyDepositAt(address owner, uint48 timepoint) → uint256
internal
#Internal helper: fetch the amount that is expected to be claimable at a given timepoint, if any. Any amount that has already been claimed is taken into consideration.
ERC7540DelayInvalidDepositShareOrigin()
error
#Triggered if ERC7540._depositShareOrigin is not address(0), as this is not supported by this module.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC7540DelayRedeem.sol";Time-delay fulfillment strategy for asynchronous redemptions.
Extends ERC7540 with a redeem flow where requests become permissionlessly claimable after a
configurable waiting period. No privileged fulfiller is needed — once the delay elapses, the
controller (or any keeper) can claim. The exchange rate is computed at claim time using the vault's
live ERC7540.convertToAssets.
Production equivalents: BeefySonic (protocol-dictated SFC unbonding), MagmaV2 (admin-configurable delay), Tangle (protocol-dictated).
Requests are tracked using Checkpoints-Trace208, storing cumulative redeem amounts keyed by
their maturity timepoint. The requestId returned by ERC7540.requestRedeem equals the absolute
timestamp at which the request becomes claimable (clock() + redeemDelay(controller)).
Override ERC7540DelayRedeem.redeemDelay to customize the waiting period (default: 1 hour) and ERC20Collateral.clock to
change the time source (default: block.timestamp).
This module does not support temporary share custody through ERC7540._redeemShareDestination. The constructor
tries to enforce that property, but the check may be insufficient if ERC7540._redeemShareDestination reads from
storage that is not yet initialized when the parent's constructor runs.
Functions
- constructor()
- clock()
- CLOCK_MODE()
- redeemDelay()
- _isRedeemAsync()
- _requestRedeem(shares, controller, owner, )
- _consumeClaimableWithdraw(assets, controller)
- _consumeClaimableRedeem(shares, controller)
- _pendingRedeemRequest(requestId, controller)
- _claimableRedeemRequest(requestId, controller)
- _asyncMaxWithdraw(owner)
- _asyncMaxRedeem(owner)
- _readyRedeemAt(owner, timepoint)
ERC7540
- supportsInterface(interfaceId)
- isOperator(controller, operator)
- setOperator(operator, approved)
- _setOperator(controller, operator, approved)
- _checkOperatorOrController(async, controller, operator)
- decimals()
- asset()
- share()
- vault(asset_)
- totalAssets()
- totalSupply()
- pendingDepositRequest(requestId, controller)
- claimableDepositRequest(requestId, controller)
- pendingRedeemRequest(requestId, controller)
- claimableRedeemRequest(requestId, controller)
- totalPendingDepositAssets()
- totalPendingRedeemShares()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit(owner)
- maxMint(owner)
- maxWithdraw(owner)
- maxRedeem(owner)
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- requestDeposit(assets, controller, owner)
- deposit(assets, receiver)
- deposit(assets, receiver, controller)
- mint(shares, receiver)
- mint(shares, receiver, controller)
- requestRedeem(shares, controller, owner)
- withdraw(assets, receiver, ownerOrController)
- redeem(shares, receiver, ownerOrController)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _requestDeposit(assets, controller, owner, requestId)
- _mintSharesOnDepositFulfill(assets, shares)
- _deposit(callerOrController, receiver, assets, shares)
- _burnSharesOnRedeemFulfill(, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
- _depositShareOrigin()
- _redeemShareDestination()
- _isDepositAsync()
- _pendingDepositRequest(, )
- _claimableDepositRequest(, )
- _consumeClaimableDeposit(, )
- _consumeClaimableMint(, )
- _asyncMaxDeposit()
- _asyncMaxMint()
ERC20
- name()
- symbol()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7540Redeem
IERC7540Deposit
IERC7540Operator
Errors
ERC7540
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
- ERC7540InvalidOperator(controller, operator)
- ERC7540SyncDeposit()
- ERC7540AsyncDeposit()
- ERC7540SyncRedeem()
- ERC7540AsyncRedeem()
- ERC7540MissingAsync()
- ERC7540UnauthorizedMintSharesOnDepositFulfill()
- ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
constructor()
internal
#clock() → uint48
public
#Clock used for flagging checkpoints. Can be overridden to implement timestamp based checkpoints (and voting).
CLOCK_MODE() → string
public
#Description of the clock
redeemDelay(address) → uint48
public
#Returns the delay duration before a redeem request becomes claimable. Defaults to 1 hour.
For any given controller, the maturity timepoint clock() + redeemDelay(controller) MUST
be non-decreasing across successive ERC7540.requestRedeem calls. Overrides that shrink the delay faster
than clock() advances will cause new requests to revert until the previous maturity is reached.
_isRedeemAsync() → bool
internal
#Returns true if the redeem flow is asynchronous (Request-based). When false, ERC7540.withdraw and
ERC7540.redeem behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async redeem fulfillment strategy.
_requestRedeem(uint256 shares, address controller, address owner, uint256) → uint256
internal
#Pushes a new cumulative checkpoint at the maturity timepoint and delegates to
ERC7540._requestRedeem with the timepoint as requestId.
_consumeClaimableWithdraw(uint256 assets, address controller) → uint256
internal
#Consumes assets from claimable redeems, returns proportional shares (rounded up).
_consumeClaimableRedeem(uint256 shares, address controller) → uint256
internal
#Consumes shares from claimable redeems, returns proportional assets (rounded down).
_pendingRedeemRequest(uint256 requestId, address controller) → uint256
internal
#Returns the shares in Pending state for a specific requestId (timepoint).
A request is pending only if its timepoint is strictly in the future.
_claimableRedeemRequest(uint256 requestId, address controller) → uint256
internal
#Returns the shares in Claimable state for a specific requestId (timepoint).
A request is claimable once its timepoint has elapsed and the shares haven't been claimed yet.
_asyncMaxWithdraw(address owner) → uint256
internal
#Returns the asset-equivalent of ERC7540._asyncMaxRedeem (rounded down).
_asyncMaxRedeem(address owner) → uint256
internal
#Returns the total claimable shares across all matured timepoints for owner.
_readyRedeemAt(address owner, uint48 timepoint) → uint256
internal
#Internal helper: fetch the amount that is expected to be claimable at a given timepoint, if any. Any amount that has already been claimed is taken into consideration.
ERC7540DelayInvalidRedeemShareDestination()
error
#Triggered if ERC7540._redeemShareDestination is not address(0), as this is not supported by this module.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC7540SyncDeposit.sol";Module for enabling synchronous behavior (ERC-4626) for the deposit flow of an ERC-7540 vault.
Note that an ERC-7540 vault is required to have at least one flow operating in asynchronous mode, so this module
cannot be combined with ERC7540SyncRedeem.
Functions
- _isDepositAsync()
- _consumeClaimableDeposit(, )
- _consumeClaimableMint(, )
- _pendingDepositRequest(, )
- _claimableDepositRequest(, )
- _asyncMaxDeposit()
- _asyncMaxMint()
ERC7540
- supportsInterface(interfaceId)
- isOperator(controller, operator)
- setOperator(operator, approved)
- _setOperator(controller, operator, approved)
- _checkOperatorOrController(async, controller, operator)
- decimals()
- asset()
- share()
- vault(asset_)
- totalAssets()
- totalSupply()
- pendingDepositRequest(requestId, controller)
- claimableDepositRequest(requestId, controller)
- pendingRedeemRequest(requestId, controller)
- claimableRedeemRequest(requestId, controller)
- totalPendingDepositAssets()
- totalPendingRedeemShares()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit(owner)
- maxMint(owner)
- maxWithdraw(owner)
- maxRedeem(owner)
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- requestDeposit(assets, controller, owner)
- deposit(assets, receiver)
- deposit(assets, receiver, controller)
- mint(shares, receiver)
- mint(shares, receiver, controller)
- requestRedeem(shares, controller, owner)
- withdraw(assets, receiver, ownerOrController)
- redeem(shares, receiver, ownerOrController)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _requestDeposit(assets, controller, owner, requestId)
- _mintSharesOnDepositFulfill(assets, shares)
- _deposit(callerOrController, receiver, assets, shares)
- _requestRedeem(shares, controller, owner, requestId)
- _burnSharesOnRedeemFulfill(, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
- _depositShareOrigin()
- _redeemShareDestination()
- _isRedeemAsync()
- _pendingRedeemRequest(, )
- _claimableRedeemRequest(, )
- _consumeClaimableWithdraw(, )
- _consumeClaimableRedeem(, )
- _asyncMaxWithdraw()
- _asyncMaxRedeem()
ERC20
- name()
- symbol()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7540Redeem
IERC7540Deposit
IERC7540Operator
Errors
ERC7540
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
- ERC7540InvalidOperator(controller, operator)
- ERC7540SyncDeposit()
- ERC7540AsyncDeposit()
- ERC7540SyncRedeem()
- ERC7540AsyncRedeem()
- ERC7540MissingAsync()
- ERC7540UnauthorizedMintSharesOnDepositFulfill()
- ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
_isDepositAsync() → bool
internal
#Returns true if the deposit flow is asynchronous (Request-based). When false, ERC7540.deposit and
ERC7540.mint behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async deposit fulfillment strategy.
_consumeClaimableDeposit(uint256, address) → uint256
internal
#Consumes assets from the claimable deposit and returns the proportional shares (rounded down).
_consumeClaimableMint(uint256, address) → uint256
internal
#Consumes shares from the claimable deposit and returns the proportional assets (rounded up).
_pendingDepositRequest(uint256, address) → uint256
internal
#Returns the amount of assets in Pending state for controller with the given requestId.
_claimableDepositRequest(uint256, address) → uint256
internal
#Returns the amount of assets in Claimable state for controller with the given requestId.
_asyncMaxDeposit(address) → uint256
internal
#Returns the maximum assets that can be claimed via ERC7540.deposit for an async owner.
_asyncMaxMint(address) → uint256
internal
#Returns the maximum shares that can be claimed via ERC7540.mint for an async owner.
import "@openzeppelin/community-contracts/token/ERC20/extensions/ERC7540SyncRedeem.sol";Module for enabling synchronous behavior (ERC-4626) for the redeem flow of an ERC-7540 vault.
Note that an ERC-7540 vault is required to have at least one flow operating in asynchronous mode, so this module
cannot be combined with ERC7540SyncDeposit.
Functions
- _isRedeemAsync()
- _consumeClaimableWithdraw(, )
- _consumeClaimableRedeem(, )
- _pendingRedeemRequest(, )
- _claimableRedeemRequest(, )
- _asyncMaxWithdraw()
- _asyncMaxRedeem()
ERC7540
- supportsInterface(interfaceId)
- isOperator(controller, operator)
- setOperator(operator, approved)
- _setOperator(controller, operator, approved)
- _checkOperatorOrController(async, controller, operator)
- decimals()
- asset()
- share()
- vault(asset_)
- totalAssets()
- totalSupply()
- pendingDepositRequest(requestId, controller)
- claimableDepositRequest(requestId, controller)
- pendingRedeemRequest(requestId, controller)
- claimableRedeemRequest(requestId, controller)
- totalPendingDepositAssets()
- totalPendingRedeemShares()
- convertToShares(assets)
- convertToAssets(shares)
- maxDeposit(owner)
- maxMint(owner)
- maxWithdraw(owner)
- maxRedeem(owner)
- previewDeposit(assets)
- previewMint(shares)
- previewWithdraw(assets)
- previewRedeem(shares)
- requestDeposit(assets, controller, owner)
- deposit(assets, receiver)
- deposit(assets, receiver, controller)
- mint(shares, receiver)
- mint(shares, receiver, controller)
- requestRedeem(shares, controller, owner)
- withdraw(assets, receiver, ownerOrController)
- redeem(shares, receiver, ownerOrController)
- _convertToShares(assets, rounding)
- _convertToAssets(shares, rounding)
- _requestDeposit(assets, controller, owner, requestId)
- _mintSharesOnDepositFulfill(assets, shares)
- _deposit(callerOrController, receiver, assets, shares)
- _requestRedeem(shares, controller, owner, requestId)
- _burnSharesOnRedeemFulfill(, shares)
- _withdraw(caller, receiver, owner, assets, shares)
- _transferIn(from, assets)
- _transferOut(to, assets)
- _decimalsOffset()
- _depositShareOrigin()
- _redeemShareDestination()
- _isDepositAsync()
- _pendingDepositRequest(, )
- _claimableDepositRequest(, )
- _consumeClaimableDeposit(, )
- _consumeClaimableMint(, )
- _asyncMaxDeposit()
- _asyncMaxMint()
ERC20
- name()
- symbol()
- balanceOf(account)
- transfer(to, value)
- allowance(owner, spender)
- approve(spender, value)
- transferFrom(from, to, value)
- _transfer(from, to, value)
- _update(from, to, value)
- _mint(account, value)
- _burn(account, value)
- _approve(owner, spender, value)
- _approve(owner, spender, value, emitEvent)
- _spendAllowance(owner, spender, value)
Events
IERC7540Redeem
IERC7540Deposit
IERC7540Operator
Errors
ERC7540
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
- ERC7540InvalidOperator(controller, operator)
- ERC7540SyncDeposit()
- ERC7540AsyncDeposit()
- ERC7540SyncRedeem()
- ERC7540AsyncRedeem()
- ERC7540MissingAsync()
- ERC7540UnauthorizedMintSharesOnDepositFulfill()
- ERC7540UnauthorizedBurnSharesOnRedeemFulfill()
_isRedeemAsync() → bool
internal
#Returns true if the redeem flow is asynchronous (Request-based). When false, ERC7540.withdraw and
ERC7540.redeem behave as standard synchronous ERC-4626 operations.
Override to return true in extensions that provide an async redeem fulfillment strategy.
_consumeClaimableWithdraw(uint256, address) → uint256
internal
#Consumes assets from the claimable redeem and returns the proportional shares (rounded up).
_consumeClaimableRedeem(uint256, address) → uint256
internal
#Consumes shares from the claimable redeem and returns the proportional assets (rounded down).
_pendingRedeemRequest(uint256, address) → uint256
internal
#Returns the amount of shares in Pending state for controller with the given requestId.
_claimableRedeemRequest(uint256, address) → uint256
internal
#Returns the amount of shares in Claimable state for controller with the given requestId.
_asyncMaxWithdraw(address) → uint256
internal
#Returns the maximum assets that can be claimed via ERC7540.withdraw for an async owner.
_asyncMaxRedeem(address) → uint256
internal
#Returns the maximum shares that can be claimed via ERC7540.redeem for an async owner.
import "@openzeppelin/community-contracts/token/OnTokenTransferAdapter.sol";This contract exposes the 667 onTokenTransfer hook on top of IERC1363Receiver-onTransferReceived.
Inheriting from this adapter makes your ERC1363Receiver contract automatically compatible with tokens, such as
Chainlink's Link, that implement the 667 interface for transferAndCall.
Functions
onTokenTransfer(address from, uint256 amount, bytes data) → bool
public
#