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.ERC4626Fees
: ERC4626 vault with fees on entry (deposit/mint) or exit (withdraw/redeem).
General
ERC20
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Allowlist.sol";
Extension of PaymasterERC20
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)
IERC20Errors
IERC20Metadata
IERC20
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/contracts/token/ERC20/extensions/ERC20Blocklist.sol";
Extension of PaymasterERC20
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)
IERC20Errors
IERC20Metadata
IERC20
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/contracts/token/ERC20/extensions/ERC20Collateral.sol";
Extension of PaymasterERC20
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
IERC6372
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)
IERC20Errors
IERC20Metadata
IERC20
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/contracts/token/ERC20/extensions/ERC20Custodian.sol";
Extension of PaymasterERC20
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)
IERC20Errors
IERC20Metadata
IERC20
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 ERC7786OpenBridge.UnsupportedNativeTransfer
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/contracts/token/ERC20/extensions/ERC20Freezable.sol";
Extension of PaymasterERC20
that allows to implement a freezing
mechanism that can be managed by an authorized account with the
_freezeTokens
and _unfreezeTokens
functions.
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 those
tokens are unfrozen using _unfreezeTokens
.
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)
IERC20Errors
IERC20Metadata
IERC20
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 user, uint256 amount)
internal
#Internal function to set the frozen token amount for a user.
_update(address from, address to, uint256 value)
internal
#See ERC20-_update
.
Requirements:
from
must have sufficient unfrozen balance.
ERC20InsufficientUnfrozenBalance(address user, uint256 needed, uint256 available)
error
#The operation failed because the user has insufficient unfrozen balance.
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Restricted.sol";
Extension of PaymasterERC20
that allows to implement user account transfer restrictions
through the IERC7943.isUserAllowed
function. Inspired by EIP-7943.
By default, each account has no explicit restriction. The IERC7943.isUserAllowed
function acts as
a blocklist. Developers can override IERC7943.isUserAllowed
to check that restriction == ALLOWED
to implement an allowlist.
Functions
- getRestriction(account)
- isUserAllowed(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)
IERC20Errors
IERC20Metadata
IERC20
Events
Errors
getRestriction(address account) → enum ERC20Restricted.Restriction
public
#Returns the restriction of a user account.
isUserAllowed(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 isUserAllowed(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:
from
must be allowed to transfer tokens (seeIERC7943.isUserAllowed
).to
must be allowed to receive tokens (seeIERC7943.isUserAllowed
).
_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/contracts/token/ERC20/extensions/ERC20uRWA.sol";
Extension of PaymasterERC20
according to EIP-7943.
Combines standard ERC-20 functionality with RWA-specific features like user restrictions, asset freezing, and forced asset transfers.
Functions
- isUserAllowed(user)
- supportsInterface(interfaceId)
- isTransferAllowed(from, to, , amount)
- getFrozen(user, )
- setFrozen(user, , amount)
- forceTransfer(from, to, , amount)
- _update(from, to, amount)
- _checkEnforcer(from, to, amount)
- _checkFreezer(user, amount)
IERC7943
ERC20Restricted
- getRestriction(account)
- _setRestriction(account, restriction)
- _blockUser(account)
- _allowUser(account)
- _resetUser(account)
- _checkRestriction(account)
ERC20Freezable
ERC165
IERC165
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)
IERC20Errors
IERC20Metadata
IERC20
Events
Errors
IERC7943
- ERC7943NotAllowedUser(account)
- ERC7943NotAllowedTransfer(from, to, tokenId, amount)
- ERC7943InsufficientUnfrozenBalance(user, tokenId, amount, unfrozen)
ERC20Restricted
ERC20Freezable
ERC165
IERC165
ERC20
IERC20Errors
- ERC20InsufficientBalance(sender, balance, needed)
- ERC20InvalidSender(sender)
- ERC20InvalidReceiver(receiver)
- ERC20InsufficientAllowance(spender, allowance, needed)
- ERC20InvalidApprover(approver)
- ERC20InvalidSpender(spender)
IERC20Metadata
IERC20
isUserAllowed(address user) → 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 isUserAllowed(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.
isTransferAllowed(address from, address to, uint256, uint256 amount) → bool
external
#See IERC7943.isTransferAllowed
.
CAUTION: 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.
getFrozen(address user, uint256) → uint256 amount
public
#setFrozen(address user, uint256, uint256 amount)
public
#See IERC7943.setFrozen
.
NOTE: The amount
is capped to the balance of the user
to ensure the IERC7943.Frozen
event
emits values that consistently reflect the actual amount of tokens that are frozen.
forceTransfer(address from, address to, uint256, uint256 amount)
public
#Bypasses the ERC20Restricted
restrictions for the from
address and adjusts the frozen balance
to the new balance after the transfer.
NOTE: 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 ERC7786OpenBridge.UnsupportedNativeTransfer
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 user, 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 user, uint256 amount) internal view override onlyRole(FREEZER_ROLE) {}
import "@openzeppelin/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)
- _decimalsOffset()
IERC4626
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)
IERC20Errors
IERC20Metadata
IERC20
Events
Errors
ERC4626
- ERC4626ExceededMaxDeposit(receiver, assets, max)
- ERC4626ExceededMaxMint(receiver, shares, max)
- ERC4626ExceededMaxWithdraw(owner, assets, max)
- ERC4626ExceededMaxRedeem(owner, shares, max)
IERC4626
ERC20
IERC20Errors
- ERC20InsufficientBalance(sender, balance, needed)
- ERC20InvalidSender(sender)
- ERC20InvalidReceiver(receiver)
- ERC20InsufficientAllowance(spender, allowance, needed)
- ERC20InvalidApprover(approver)
- ERC20InvalidSpender(spender)
IERC20Metadata
IERC20
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/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
#