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

OnTokenTransferAdapter

ERC20

ERC20Allowlist

ERC20Blocklist

ERC20Collateral

ERC20Custodian

ERC4626Fees

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.

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.

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.

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

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.

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.

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:

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

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

#

See IERC7943.forceTransfer.

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

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.

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

public

#