Join our community of builders on

Telegram!Telegram

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

OnTokenTransferAdapter

ERC20

ERC20Allowlist

ERC20Blocklist

ERC20Collateral

ERC20Custodian

ERC20Freezable

ERC20Restricted

ERC20uRWA

ERC4626Fees

ERC7540

ERC7540

ERC7540AdminDeposit

ERC7540AdminRedeem

ERC7540DelayDeposit

ERC7540DelayRedeem

ERC7540SyncDeposit

ERC7540SyncRedeem

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.

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.

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.

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

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.

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:

  • from must 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.

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:

_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.

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).

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:

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.

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

ERC20

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:

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 approved value.
  • MUST emit the IERC7540Operator.OperatorSet event 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

#

See IERC7540Redeem.pendingRedeemRequest.

Requirements:

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._isDepositAsync must return true.
  • owner must be msg.sender or msg.sender must be an approved operator of owner.
  • owner must have approved the vault for at least assets of 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

#

See IERC7540Deposit.deposit.

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:

  • assets must not exceed ERC7540.maxDeposit for the relevant account.
  • When async, msg.sender must be controller or an approved operator of controller.

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:

  • shares must not exceed ERC7540.maxMint for the relevant account.
  • When async, msg.sender must be controller or an approved operator of controller.

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:

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:

  • assets must not exceed ERC7540.maxWithdraw for ownerOrController.
  • When async, msg.sender must be ownerOrController or an approved operator.
  • When sync, msg.sender must be ownerOrController or 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:

  • shares must not exceed ERC7540.maxRedeem for ownerOrController.
  • When async, msg.sender must be ownerOrController or an approved operator.
  • When sync, msg.sender must be ownerOrController or 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:

_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:

  1. Synchronous (ERC7540._isDepositAsync returns false): transfers assets from callerOrController into the vault and mints new shares to receiver. Standard ERC-4626 behavior.
  2. Async, mint-on-claim (ERC7540._depositShareOrigin returns address(0)): decrements _totalPendingDepositAssets and mints new shares to receiver. No asset transfer occurs since assets were already transferred during ERC7540.requestDeposit.
  3. Async, pre-minted (ERC7540._depositShareOrigin returns non-zero): transfers pre-minted shares from the share origin address to receiver.

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 _totalPendingRedeemShares is incremented to keep ERC7540.totalSupply accurate.
  • 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:

_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:

  1. Synchronous (ERC7540._isRedeemAsync returns false): spends ERC-20 allowance if caller is not owner, burns shares from owner, and transfers underlying assets to receiver.
  2. Async: decrements _totalPendingRedeemShares (shares were already burned/escrowed during the Request or fulfillment phase) and transfers underlying assets to receiver.

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.

See ERC-4626 security considerations.

_depositShareOrigin() → address

internal

#

Returns the address from which shares are transferred to the receiver on deposit 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.

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

#

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

ERC7540
ERC20

_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:

  • assets must not exceed the pending deposit amount for the controller.

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

ERC7540
ERC20

_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:

  • shares must not exceed the pending redeem amount for the controller.

_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

ERC7540
ERC20

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.maxMint must not be 0 for controller. 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

ERC7540
ERC20

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

ERC7540
ERC20

_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

ERC7540
ERC20

_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.

onTokenTransfer(address from, uint256 amount, bytes data) → bool

public

#