ERC20
This module provides interfaces, presets, and utilities related to ERC20 contracts.
For an overview of ERC20, read our ERC20 guide.
Interfaces
Starting from version 3.x.x, the interfaces are no longer part of the openzeppelin_token package. The references documented here are contained in the openzeppelin_interfaces package version v2.1.0.
IERC20
use openzeppelin_interfaces::erc20::IERC20;Interface of the IERC20 standard as defined in EIP-20.
Functions
total_supply()balance_of(account)allowance(owner, spender)transfer(recipient, amount)transfer_from(sender, recipient, amount)approve(spender, amount)
Events
Functions
total_supply() → u256 external
Returns the amount of tokens in existence.
balance_of(account: ContractAddress) → u256 external
Returns the amount of tokens owned by account.
allowance(owner: ContractAddress, spender: ContractAddress) → u256 external
Returns the remaining number of tokens that spender is allowed to spend on behalf of owner through transfer_from. This is zero by default.
This value changes when approve or transfer_from are called.
transfer(recipient: ContractAddress, amount: u256) → bool external
Moves amount tokens from the caller’s token balance to to. Returns true on success, reverts otherwise.
Emits a Transfer event.
transfer_from(sender: ContractAddress, recipient: ContractAddress, amount: u256) → bool external
Moves amount tokens from sender to recipient using the allowance mechanism. amount is then deducted from the caller’s allowance. Returns true on success, reverts otherwise.
Emits a Transfer event.
approve(spender: ContractAddress, amount: u256) → bool external
Sets amount as the allowance of spender over the caller’s tokens. Returns true on success, reverts otherwise.
Emits an Approval event.
Events
Transfer(from: ContractAddress, to: ContractAddress, value: u256) event
Emitted when value tokens are moved from one address (from) to another (to).
Note that value may be zero.
Approval(owner: ContractAddress, spender: ContractAddress, value: u256) event
Emitted when the allowance of a spender for an owner is set. value is the new allowance.
IERC20Metadata
use openzeppelin_interfaces::erc20::IERC20Metadata;Interface for the optional metadata functions in EIP-20.
Functions
Functions
name() → ByteArray external
Returns the name of the token.
symbol() → ByteArray external
Returns the ticker symbol of the token.
decimals() → u8 external
Returns the number of decimals the token uses - e.g. 8 means to divide the token amount by 100000000 to get its user-readable representation.
For example, if decimals equals 2, a balance of 505 tokens should be displayed to a user as 5.05 (505 / 10 ** 2).
Tokens usually opt for a value of 18, imitating the relationship between Ether and Wei. This is the default value returned by this function. To create a custom decimals implementation, see Customizing decimals.
This information is only used for display purposes: it in no way affects any of the arithmetic of the contract.
IERC20Permit
use openzeppelin_interfaces::erc20::IERC20Permit;Interface of the ERC20Permit standard to support gasless token approvals as defined in EIP-2612.
Functions
Functions
permit(owner: ContractAddress, spender: ContractAddress, amount: u256, deadline: u64, signature: Span<felt252>) external
Sets amount as the allowance of spender over owner's tokens after validating the signature.
nonces(owner: ContractAddress) → felt252 external
Returns the current nonce of owner. A nonce value must be included whenever a signature for permit call is generated.
DOMAIN_SEPARATOR() → felt252 external
Returns the domain separator used in generating a message hash for permit signature. The domain hashing logic follows the SNIP12 standard.
IERC4626
use openzeppelin_interfaces::erc4626::IERC4626;Interface of the IERC4626 standard as defined in EIP-4626.
Functions
asset()total_assets()convert_to_shares(assets)convert_to_assets(shares)max_deposit(receiver)preview_deposit(assets)deposit(assets, receiver)max_mint(receiver)preview_mint(shares)mint(shares, receiver)max_withdraw(owner)preview_withdraw(assets)withdraw(assets, receiver, owner)max_redeem(owner)preview_redeem(shares)redeem(shares, receiver, owner)
Events
Functions
asset() → ContractAddress external
Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
Requirements:
- MUST be an ERC20 token contract.
- MUST NOT panic.
total_assets() → u256 external
Returns the total amount of the underlying asset that is “managed” by Vault.
Requirements:
- SHOULD include any compounding that occurs from yield.
- MUST be inclusive of any fees that are charged against assets in the Vault.
- MUST NOT panic.
convert_to_shares(assets: u256) → u256 external
Returns the amount of shares that the Vault would exchange for the amount of assets provided irrespective of slippage or fees.
Requirements:
- 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 panic unless due to integer overflow caused by an unreasonably large input.
- MUST round down towards 0.
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.
convert_to_assets(shares: u256) → u256 external
Returns the amount of assets that the Vault would exchange for the amount of shares provided irrespective of slippage or fees.
Requirements:
- 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 panic unless due to integer overflow caused by an unreasonably large input.
- MUST round down towards 0.
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.
max_deposit(receiver: ContractAddress) → u256 external
Returns the maximum amount of the underlying asset that can be deposited into the Vault for receiver, through a deposit call.
Requirements:
- MUST return a limited value if receiver is subject to some deposit limit.
- MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
- MUST NOT panic.
preview_deposit(assets: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
Requirements:
- MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit call in the same transaction i.e. IERC4626::deposit should return the same or more shares as
preview_depositif called in the same transaction. - MUST NOT account for deposit limits like those returned from IERC4626::max_deposit and should always act as though the deposit would be accepted, regardless if the user has enough tokens approved, etc.
- MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
- MUST NOT panic.
Any unfavorable discrepancy between IERC4626::convert_to_shares and preview_deposit SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing.
deposit(assets: u256, receiver: ContractAddress) → u256 external
Mints Vault shares to receiver by depositing exactly amount of assets.
Requirements:
- MUST emit the IERC4626::Deposit event.
- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the deposit execution, and are accounted for during deposit.
- MUST panic if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc).
Most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
max_mint(receiver: ContractAddress) → u256 external
Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
Requirements:
- MUST return a limited value if receiver is subject to some mint limit.
- MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
- MUST NOT panic.
preview_mint(shares: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
Requirements:
- MUST return as close to and no fewer than the exact amount of assets that would be deposited in a
mintcall in the same transaction. I.e. IERC4626::mint should return the same or fewer assets aspreview_mintif called in the same transaction. - MUST NOT account for mint limits like those returned from IERC4626::max_mint and should always act as though the mint would be accepted, regardless if the user has enough tokens approved, etc.
- MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
- MUST NOT panic.
Any unfavorable discrepancy between IERC4626::convert_to_assets and preview_mint SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by minting.
mint(shares: u256, receiver: ContractAddress) → u256 external
Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
Requirements:
- MUST emit the IERC4626::Deposit event.
- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint execution, and are accounted for during mint.
- MUST panic if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not approving enough underlying tokens to the Vault contract, etc).
Most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
max_withdraw(owner: ContractAddress) → u256 external
Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call.
Requirements:
- MUST return a limited value if owner is subject to some withdrawal limit or timelock.
- MUST NOT panic.
preview_withdraw(assets: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
Requirements:
- MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw call in the same transaction i.e. IERC4626::withdraw should return the same or fewer shares as
preview_withdrawif called in the same transaction. - MUST NOT account for withdrawal limits like those returned from IERC4626::max_withdraw and should always act as though the withdrawal would be accepted, regardless if the user has enough shares, etc.
- MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
- MUST NOT panic.
Any unfavorable discrepancy between IERC4626::convert_to_shares and preview_withdraw SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by depositing.
withdraw(assets: u256, receiver: ContractAddress, owner: ContractAddress) → u256 external
Burns shares from owner and sends exactly assets of underlying tokens to receiver.
Requirements:
- MUST emit the IERC4626::Withdraw event.
- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the withdraw execution, and are accounted for during withdraw.
- MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc).
Some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately.
max_redeem(owner: ContractAddress) → u256 external
Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a redeem call.
Requirements:
- MUST return a limited value if owner is subject to some withdrawal limit or timelock.
- MUST return
ERC20::balance_of(owner)ifowneris not subject to any withdrawal limit or timelock. - MUST NOT panic.
preview_redeem(shares: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
Requirements:
- MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call in the same transaction i.e. IERC4626::redeem should return the same or more assets as preview_redeem if called in the same transaction.
- MUST NOT account for redemption limits like those returned from IERC4626::max_redeem and should always act as though the redemption would be accepted, regardless if the user has enough shares, etc.
- MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
- MUST NOT panic.
Any unfavorable discrepancy between IERC4626::convert_to_assets and preview_redeem SHOULD be considered slippage in share price or some other type of condition, meaning the depositor will lose assets by redeeming.
redeem(shares: u256, receiver: ContractAddress, owner: ContractAddress) → u256 external
Burns exactly shares from owner and sends assets of underlying tokens to receiver.
Requirements:
- MUST emit the IERC4626::Withdraw event.
- MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the redeem execution, and are accounted for during redeem.
- MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner not having enough shares, etc).
Some implementations will require pre-requesting to the Vault before a withdrawal may be performed. Those methods should be performed separately.
Events
Deposit(sender: ContractAddress, owner: ContractAddress, assets: u256, shares: u256) event
Emitted when sender exchanges assets for shares and transfers those shares to owner.
Withdraw(sender: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256) event
Emitted when sender exchanges shares, owned by owner, for assets and transfers those assets to receiver.
Core
ERC20Component
use openzeppelin_token::erc20::ERC20Component;ERC20 component extending IERC20 and IERC20Metadata.
See Hooks to understand how are hooks used.
Hooks
ERC20HooksTrait
Embeddable Mixin Implementations
ERC20MixinImpl
Embeddable Implementations
ERC20Impl
total_supply(self)balance_of(self, account)allowance(self, owner, spender)transfer(self, recipient, amount)transfer_from(self, sender, recipient, amount)approve(self, spender, amount)
ERC20MetadataImpl
ERC20CamelOnlyImpl
ERC20PermitImpl
permit(self, owner, spender, amount, deadline, signature)nonces(self, owner)DOMAIN_SEPARATOR(self)
SNIP12MetadataExternalImpl
Internal implementations
InternalImpl
initializer(self, name, symbol)mint(self, recipient, amount)burn(self, account, amount)update(self, from, to, amount)_transfer(self, sender, recipient, amount)_approve(self, owner, spender, amount)_spend_allowance(self, owner, spender, amount)
Events
Hooks
Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC20Component is expected to provide an implementation of the ERC20HooksTrait. For basic token contracts, an empty implementation with no logic must be provided.
You can use openzeppelin_token::erc20::ERC20HooksEmptyImpl which is already available as part of the library for this purpose.
before_update(ref self: ContractState, from: ContractAddress, recipient: ContractAddress, amount: u256) hook
Function executed at the beginning of the update function prior to any other logic.
after_update(ref self: ContractState, from: ContractAddress, recipient: ContractAddress, amount: u256) hook
Function executed at the end of the update function.
Embeddable functions
total_supply(@self: ContractState) → u256 external
See IERC20::total_supply.
balance_of(@self: ContractState, account: ContractAddress) → u256 external
See IERC20::balance_of.
allowance(@self: ContractState, owner: ContractAddress, spender: ContractAddress) → u256 external
See IERC20::allowance.
transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) → bool external
See IERC20::transfer.
Requirements:
recipientcannot be the zero address.- The caller must have a balance of at least
amount.
transfer_from(ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256) → bool external
Requirements:
sendercannot be the zero address.sendermust have a balance of at leastamount.recipientcannot be the zero address.- The caller must have allowance for
sender's tokens of at leastamount.
approve(ref self: ContractState, spender: ContractAddress, amount: u256) → bool external
See IERC20::approve.
Requirements:
spendercannot be the zero address.
name() → ByteArray external
See IERC20Metadata::name.
symbol() → ByteArray external
decimals() → u8 external
totalSupply(self: @ContractState) → u256 external
See IERC20::total_supply.
Supports the Cairo v0 convention of writing external methods in camelCase as discussed here.
balanceOf(self: @ContractState, account: ContractAddress) → u256 external
See IERC20::balance_of.
Supports the Cairo v0 convention of writing external methods in camelCase as discussed here.
transferFrom(ref self: ContractState, sender: ContractAddress, recipient: ContractAddress) → bool external
Supports the Cairo v0 convention of writing external methods in camelCase as discussed here.
permit(ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, deadline: u64, signature: Span<felt252>) → bool external
Sets amount as the allowance of spender over owner's tokens after validating the signature.
Requirements:
owneris a deployed account contract.spenderis not the zero address.deadlineis not a timestamp in the past.signatureis a valid signature that can be validated with a call toowneraccount.signaturemust use the current nonce of theowner.
Emits an Approval event. Every successful call increases `owner’s nonce by one.
nonces(self: @ContractState, owner: ContractAddress) → felt252 external
Returns the current nonce of owner. A nonce value must be included whenever a signature for permit call is generated.
DOMAIN_SEPARATOR(self: @ContractState) → felt252 external
Returns the domain separator used in generating a message hash for permit signature. The domain hashing logic follows the SNIP12 standard.
snip12_metadata(self: @ContractState) → (felt252, felt252) external
Returns the domain name and version used to generate the message hash for permit signature.
The returned tuple contains:
t.0: The name used in the SNIP12Metadata implementation.t.1: The version used in the SNIP12Metadata implementation.
Internal functions
initializer(ref self: ContractState, name: ByteArray, symbol: ByteArray) internal
Initializes the contract by setting the token name and symbol. This should be used inside of the contract’s constructor.
mint(ref self: ContractState, recipient: ContractAddress, amount: u256) internal
Creates an amount number of tokens and assigns them to recipient.
Emits a Transfer event with from being the zero address.
Requirements:
recipientcannot be the zero address.
burn(ref self: ContractState, account: ContractAddress, amount: u256) internal
Destroys amount number of tokens from account.
Emits a Transfer event with to set to the zero address.
Requirements:
accountcannot be the zero address.
update(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256) internal
Transfers an amount of tokens from from to to, or alternatively mints (or burns) if from (or to) is the zero address.
This function can be extended using the ERC20HooksTrait, to add functionality before and/or after the transfer, mint, or burn.
Emits a Transfer event.
_transfer(ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256) internal
Moves amount of tokens from from to to.
This internal function does not check for access permissions but can be useful as a building block, for example to implement automatic token fees, slashing mechanisms, etc.
Emits a Transfer event.
Requirements:
fromcannot be the zero address.tocannot be the zero address.frommust have a balance of at leastamount.
_approve(ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256) internal
Sets amount as the allowance of spender over owner's tokens.
This internal function does not check for access permissions but can be useful as a building block, for example to implement automatic allowances on behalf of other addresses.
Emits an Approval event.
Requirements:
ownercannot be the zero address.spendercannot be the zero address.
_spend_allowance(ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256) internal
Updates owner's allowance for spender based on spent amount.
This internal function does not update the allowance value in the case of infinite allowance.
Possibly emits an Approval event.
Events
Transfer(from: ContractAddress, to: ContractAddress, value: u256) event
See IERC20::Transfer.
Approval(owner: ContractAddress, spender: ContractAddress, value: u256) event
See IERC20::Approval.
Extensions
ERC4626Component
use openzeppelin_token::erc20::extensions::erc4626::ERC4626Component;Extension of ERC20 that implements the IERC4626 interface which allows the minting and burning of "shares" in exchange for an underlying "asset." The component leverages traits to configure fees, limits, and decimals.
constants
functions
Hooks
FeeConfigTrait
calculate_deposit_fee(self, assets, shares)calculate_mint_fee(self, assets, shares)calculate_withdraw_fee(self, assets, shares)calculate_redeem_fee(self, assets, shares)
LimitConfigTrait
deposit_limit(self, receiver)mint_limit(self, receiver)withdraw_limit(self, owner)redeem_limit(self, owner)
ERC4626HooksTrait
before_deposit(self, caller, receiver, assets, shares, fee)after_deposit(self, caller, receiver, assets, shares, fee)before_withdraw(self, caller, receiver, owner, assets, shares, fee)after_withdraw(self, caller, receiver, owner, assets, shares, fee)
AssetsManagementTrait
Embeddable Implementations
ERC4626Impl
asset(self)total_assets(self)convert_to_shares(self, assets)convert_to_assets(self, shares)max_deposit(self, receiver)preview_deposit(self, assets)deposit(self, assets, receiver)max_mint(self, receiver)preview_mint(self, shares)mint(self, shares, receiver)max_withdraw(self, owner)preview_withdraw(self, assets)withdraw(self, assets, receiver, owner)max_redeem(self, owner)preview_redeem(self, shares)redeem(self, shares, receiver, owner)
ERC20Impl
total_supply(self)balance_of(self, account)allowance(self, owner, spender)transfer(self, recipient, amount)transfer_from(self, sender, recipient, amount)approve(self, spender, amount)
ERC4626MetadataImpl
Internal functions
InternalImpl
initializer(self, asset_address)_deposit(self, caller, receiver, assets, shares)_withdraw(self, caller, receiver, owner, assets, shares)_convert_to_shares(self, assets, rounding)_convert_to_assets(self, shares, rounding)
Immutable Config
UNDERLYING_DECIMALS: u128 constant
Should match the underlying asset’s decimals. The default value is 18.
DECIMALS_OFFSET: u128 constant
Corresponds to the representational offset between UNDERLYING_DECIMALS and the vault decimals. The greater the offset, the more expensive it is for attackers to execute an inflation attack.
validate() internal
Validates the given implementation of the contract’s configuration.
Requirements:
UNDERLYING_DECIMALS+DECIMALS_OFFSETcannot exceed 255 (max u8).
This function is called by the contract’s initializer.
Hooks
Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC4626Component is expected to provide an implementation of the ERC4626HooksTrait. For basic token contracts, an empty implementation with no logic must be provided.
You can use openzeppelin_token::erc20::extensions::erc4626::ERC4626EmptyHooks which is already available as part of the library for this purpose.
FeeConfigTrait
The logic for calculating entry and exit fees is expected to be defined at the contract level. Defaults to no entry or exit fees.
The FeeConfigTrait hooks directly into the preview methods of the ERC4626 component. The preview methods must return as close to the exact amount of shares or assets as possible if the actual (previewed) operation occurred in the same transaction (according to EIP-4626 spec). All operations use their corresponding preview method as the value of assets or shares being moved to or from the user. The fees calculated in FeeConfigTrait are used to adjust the final asset and share amounts used in both the preview and the actual operations.
To transfer fees, this trait needs to be coordinated with ERC4626Component::ERC4626Hooks.
See implementation examples:
- Contract charging fees in assets: ERC4626AssetsFeesMock
- Contract charging fees in shares: ERC4626SharesFeesMock
calculate_deposit_fee(self: @ContractState, assets: u256, shares: u256) → Option<Fee> hook
Calculates the entry fee for a deposit during preview_deposit. The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the after_deposit hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be minted to the fee recipient.
calculate_mint_fee(self: @ContractState, assets: u256, shares: u256) → Option<Fee> hook
Calculates the entry fee for a mint during preview_mint. The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the after_deposit hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be minted to the fee recipient.
calculate_withdraw_fee(self: @ContractState, assets: u256, shares: u256) → Option<Fee> hook
Calculates the exit fee for a withdraw during preview_withdraw. The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the before_withdraw hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be transferred from the owner to the fee recipient.
calculate_redeem_fee(self: @ContractState, assets: u256, shares: u256) → Option<Fee> hook
Calculates the exit fee for a redeem during preview_redeem. The returned fee affects the final asset and share amounts. Fees are not transferred automatically and must be handled in the before_withdraw hook: asset fees should be transferred from the vault’s management to the fee recipient, while share fees should be transferred from the owner to the fee recipient.
LimitConfigTrait
Sets limits to the target exchange type and is expected to be defined at the contract level. These limits correspond directly to the max_<OPERATION> i.e. deposit_limit → max_deposit.
The EIP-4626 spec states that the max_<OPERATION> methods must take into account all global and user-specific limits. If an operation is disabled (even temporarily), the corresponding limit MUST be 0 and MUST NOT panic.
deposit_limit(ref self: ContractState, receiver: ContractAddress) → Option<u256> hook
The max deposit allowed.
Defaults (Option::None) to 2 ** 256 - 1.
mint_limit(ref self: ContractState, receiver: ContractAddress) → Option<u256> hook
The max mint allowed.
Defaults (Option::None) to 2 ** 256 - 1.
withdraw_limit(ref self: ContractState, owner: ContractAddress) → Option<u256> hook
The max withdraw allowed.
Defaults (Option::None) to the full asset balance of owner converted from shares.
redeem_limit(ref self: ContractState, owner: ContractAddress) → Option<u256> hook
The max redeem allowed.
Defaults (Option::None) to the full asset balance of owner.
ERC4626HooksTrait
Allows contracts to hook logic into deposit and withdraw transactions. This is where contracts can transfer fees.
ERC4626 preview methods must be inclusive of any entry or exit fees. Fees are calculated using FeeConfigTrait methods and automatically adjust the final asset and share amounts. Fee transfers are handled in ERC4626HooksTrait methods.
Special care must be taken when calling external contracts in these hooks. In that case, consider implementing reentrancy protections. For example, in the withdraw flow, the withdraw_limit is checked before the before_withdraw hook is invoked. If this hook performs a reentrant call that invokes withdraw again, the subsequent check on withdraw_limit will be done before the first withdrawal’s core logic (e.g., burning shares and transferring assets) is executed. This could lead to bypassing withdrawal constraints or draining funds.
See the ERC4626AssetsFeesMock and ERC4626SharesFeesMock examples.
before_deposit(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, assets: u256, shares: u256, fee: Option<Fee>) hook
Hooks into _deposit.
Executes logic before transferring assets and minting shares. The fee is calculated via FeeConfigTrait. Assets and shares represent the actual amounts the user will spend and receive, respectively. Asset fees are included in assets; share fees are excluded from shares.
after_deposit(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, assets: u256, shares: u256, fee: Option<Fee>) hook
Hooks into _deposit.
Executes logic after transferring assets and minting shares. The fee is calculated via FeeConfigTrait. Assets and shares represent the actual amounts the user will spend and receive, respectively. Asset fees are included in assets; share fees are excluded from shares.
before_withdraw(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256, fee: Option<Fee>) hook
Hooks into _withdraw.
Executes logic before burning shares and transferring assets. The fee is calculated via FeeConfigTrait. Assets and shares represent the actual amounts the user will receive and spend, respectively. Asset fees are excluded from assets; share fees are included in shares.
after_withdraw(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256, fee: Option<Fee>) hook
Hooks into _withdraw.
Executes logic after burning shares and transferring assets. The fee is calculated via FeeConfigTrait. Assets and shares represent the actual amounts the user will receive and spend, respectively. Asset fees are excluded from assets; share fees are included in shares.
AssetsManagementTrait
Defines how the ERC4626 vault manages its underlying assets. This trait provides the core asset management functionality for the vault, abstracting the actual storage and transfer mechanisms. It enables two primary implementation patterns:
- Self-managed assets: The vault contract holds assets directly on its own address. This is the default behavior provided by
ERC4626SelfAssetsManagementimplementation. - External vault: Assets are managed by an external contract, allowing for more complex asset management strategies. The exact implementation is expected to be defined by the contract implementing the ERC4626 component.
The trait methods are called during deposit, withdrawal, and total assets calculations, ensuring that the vault’s share pricing remains accurate regardless of the underlying asset management strategy.
Implementations must ensure that get_total_assets returns the actual amount of assets that can be withdrawn by users. Inaccurate reporting can lead to incorrect share valuations and potential economic attacks.
See implementation examples:
- Self-managed vault: ERC4626SelfAssetsManagement.
- External vault: ERC4626ExternalAssetsManagement.
get_total_assets(self: @ContractState) → u256 hook
Returns the total amount of underlying assets under the vault’s management. Used for share price calculations and determining the vault’s total value.
This method should return the actual amount of assets that the vault controls and that can be used to satisfy withdrawal requests. For self-managed vaults, this is typically the vault contract’s token balance. For external vaults, this should include any assets deposited in external protocols, minus any that are locked or unredeemable.
The accuracy of this method is critical for proper vault operation: - Overreporting can lead to share dilution and user losses. - Underreporting can lead to share inflation and potential economic attacks.
transfer_assets_in(ref self: ContractState, from: ContractAddress, assets: u256) hook
Transfers assets from an external address into the vault’s management. Called during deposit and mint operations.
This method should handle the actual transfer of underlying assets from the from address into the vault’s control. For self-managed vaults, this typically means transferring tokens to the vault contract’s address. For external vaults, this might involve transferring into an external contract.
Requirements:
- MUST transfer exactly
assetsamount of the underlying token. - SHOULD revert if the transfer fails or insufficient allowance/balance.
transfer_assets_out(ref self: ContractState, to: ContractAddress, assets: u256) hook
Transfers assets from the vault’s management to an external address. Called during withdraw and redeem operations.
This method should handle the actual transfer of underlying assets from the vault’s control to the to address. For self-managed vaults, this typically means transferring tokens from the vault contract’s address. For external vaults, this might involve withdrawing from an external contract first.
Requirements:
- MUST transfer exactly
assetsamount of the underlying token. - SHOULD revert if insufficient assets are available or transfer fails.
Embeddable functions
asset(self: @ContractState) → ContractAddress external
Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
total_assets(self: @ContractState) → u256 external
Returns the total amount of the underlying asset that is “managed” by Vault.
convert_to_shares(self: @ContractState, assets: u256) → u256 external
Returns the amount of shares that the Vault would exchange for the amount of assets provided irrespective of slippage or fees.
As per the EIP-4626 spec, this may panic only if there’s an overflow from an unreasonably large input.
convert_to_assets(self: @ContractState, shares: u256) → u256 external
Returns the amount of assets that the Vault would exchange for the amount of shares provided irrespective of slippage or fees.
As per the EIP-4626 spec, this may panic only if there’s an overflow from an unreasonably large input.
max_deposit(self: @ContractState, receiver: ContractAddress) → u256 external
Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, through a deposit call.
The default max deposit value is 2 ** 256 - 1.
This can be changed in the implementing contract by defining custom logic in LimitConfigTrait::deposit_limit.
preview_deposit(self: @ContractState, assets: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given current on-chain conditions.
The default deposit preview value is the full amount of shares. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in FeeConfigTrait::calculate_deposit_fee.
This method must be inclusive of entry fees to be compliant with the EIP-4626 spec.
deposit(ref self: ContractState, assets: u256, receiver: ContractAddress) → u256 external
Mints Vault shares to receiver by depositing exactly assets of underlying tokens. Returns the amount of newly-minted shares.
Requirements:
assetsis less than or equal to the max deposit amount forreceiver.
Emits a Deposit event.
max_mint(self: @ContractState, receiver: ContractAddress) → u256 external
Returns the maximum amount of the Vault shares that can be minted for receiver through a mint call.
The default max mint value is 2 ** 256 - 1.
This can be changed in the implementing contract by defining custom logic in LimitConfigTrait::mint_limit.
preview_mint(self: @ContractState, shares: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given current on-chain conditions.
The default mint preview value is the full amount of assets. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in FeeConfigTrait::calculate_mint_fee.
This method must be inclusive of entry fees to be compliant with the EIP-4626 spec.
mint(self: @ContractState, shares: u256, receiver: ContractAddress) → u256 external
Mints exactly Vault shares to receiver by depositing amount of underlying tokens. Returns the amount deposited assets.
Requirements:
sharesis less than or equal to the max shares amount forreceiver.
Emits a Deposit event.
max_withdraw(self: @ContractState, owner: ContractAddress) → u256 external
Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a withdraw call.
The default max withdraw value is the full balance of assets for owner (converted from shares). This can be changed in the implementing contract by defining custom logic in LimitConfigTrait::withdraw_limit.
With customized limits, the maximum withdraw amount will either be the custom limit itself or owner's total asset balance, whichever value is less.
preview_withdraw(self: @ContractState, assets: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, given current on-chain conditions.
The default withdraw preview value is the full amount of shares. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in FeeConfigTrait::calculate_withdraw_fee.
This method must be inclusive of exit fees to be compliant with the EIP-4626 spec.
withdraw(self: @ContractState, assets: u256, receiver: ContractAddress, owner: ContractAddress) → u256 external
Burns shares from owner and sends exactly assets of underlying tokens to receiver.
Requirements:
assetsis less than or equal to the max withdraw amount ofowner.
Emits a Withdraw event.
max_redeem(self: @ContractState, owner: ContractAddress) → u256 external
Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a redeem call.
The default max redeem value is the full balance of assets for owner. This can be changed in the implementing contract by defining custom logic in LimitConfigTrait::redeem_limit.
With customized limits, the maximum redeem amount will either be the custom limit itself or owner's total asset balance, whichever value is less.
preview_redeem(self: @ContractState, shares: u256) → u256 external
Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, given current on-chain conditions.
The default redeem preview value is the full amount of assets. This can be changed to account for fees, for example, in the implementing contract by defining custom logic in FeeConfigTrait::calculate_redeem_fee.
This method must be inclusive of exit fees to be compliant with the EIP-4626 spec.
redeem(self: @ContractState, shares: u256, receiver: ContractAddress, owner: ContractAddress) → u256 external
Burns exactly shares from owner and sends assets of underlying tokens to receiver.
Requirements:
sharesis less than or equal to the max redeem amount ofowner.
Emits a Withdraw event.
name(self: @ContractState) → ByteArray external
Returns the name of the token.
symbol(self: @ContractState) → ByteArray external
Returns the ticker symbol of the token, usually a shorter version of the name.
decimals(self: @ContractState) → u8 external
Returns the cumulative number of decimals which includes both UNDERLYING_DECIMALS and OFFSET_DECIMALS. Both of which must be defined in the ImmutableConfig inside the implementing contract.
Internal functions
initializer(ref self: ContractState, asset_address: ContractAddress) internal
Validates the ImmutableConfig constants and sets the asset_address to the vault. This should be set in the contract’s constructor.
Requirements:
asset_addresscannot be the zero address.
_deposit(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, assets: u256, shares: u256) internal
Internal logic for deposit and mint.
Transfers assets from caller to the Vault contract then mints shares to receiver. Fees can be transferred in the ERC4626Hooks::after_deposit hook which is executed after assets are transferred and shares are minted.
Requirements:
- ERC20::transfer_from must return true.
Emits two ERC20::Transfer events (ERC20::mint and ERC20::transfer_from).
Emits a Deposit event.
_withdraw(ref self: ContractState, caller: ContractAddress, receiver: ContractAddress, owner: ContractAddress, assets: u256, shares: u256) internal
Internal logic for withdraw and redeem.
Burns shares from owner and then transfers assets to receiver. Fees can be transferred in the ERC4626Hooks::before_withdraw hook which is executed before shares are burned and assets are transferred.
Requirements:
- ERC20::transfer must return true.
Emits two ERC20::Transfer events (ERC20::burn and ERC20::transfer).
Emits a Withdraw event.
_convert_to_shares(self: @ContractState, assets: u256, rounding: Rounding) -> u256 internal
Internal conversion function (from assets to shares) with support for rounding direction.
_convert_to_assets(self: @ContractState, shares: u256, rounding: Rounding) -> u256 internal
Internal conversion function (from shares to assets) with support for rounding direction.
Presets
ERC20Upgradeable
use openzeppelin_presets::ERC20Upgradeable;Upgradeable ERC20 contract leveraging ERC20Component with a fixed-supply mechanism for token distribution.
0x07802658d99373a4002434cbdc8897d1936c6b1beea48af0cc3b5574707f8d92
Constructor
Embedded Implementations
ERC20MixinImpl
OwnableMixinImpl
External Functions
Constructor
constructor(ref self: ContractState, name: ByteArray, symbol: ByteArray, fixed_supply: u256, recipient: ContractAddress, owner: ContractAddress) constructor
Sets the name and symbol and mints fixed_supply tokens to recipient. Assigns owner as the contract owner with permissions to upgrade.
External functions
upgrade(ref self: ContractState, new_class_hash: ClassHash) external
Upgrades the contract to a new implementation given by new_class_hash.
Requirements:
- The caller is the contract owner.
new_class_hashcannot be zero.