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() → 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

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

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() → 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_deposit if 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 mint call in the same transaction. I.e. IERC4626::mint should return the same or fewer assets as preview_mint if 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_withdraw if 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) if owner is 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

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

balance_of(@self: ContractState, account: ContractAddress) → u256 external

allowance(@self: ContractState, owner: ContractAddress, spender: ContractAddress) → u256 external

transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) → bool external

Requirements:

  • recipient cannot 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:

  • sender cannot be the zero address.

  • sender must have a balance of at least amount.

  • recipient cannot be the zero address.

  • The caller must have allowance for sender's tokens of at least amount.

approve(ref self: ContractState, spender: ContractAddress, amount: u256) → bool external

Requirements:

  • spender cannot be the zero address.

name() → ByteArray external

symbol() → ByteArray external

decimals() → u8 external

totalSupply(self: @ContractState) → u256 external

Supports the Cairo v0 convention of writing external methods in camelCase as discussed here.

balanceOf(self: @ContractState, account: ContractAddress) → u256 external

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:

  • owner is a deployed account contract.

  • spender is not the zero address.

  • deadline is not a timestamp in the past.

  • signature is a valid signature that can be validated with a call to owner account.

  • signature must use the current nonce of the owner.

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:

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:

  • recipient cannot 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:

  • account cannot 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:

  • from cannot be the zero address.

  • to cannot be the zero address.

  • from must have a balance of at least amount.

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

  • owner cannot be the zero address.

  • spender cannot 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

Approval(owner: ContractAddress, spender: ContractAddress, value: u256) event

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.

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_OFFSET cannot 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:

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

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:

  1. Self-managed assets: The vault contract holds assets directly on its own address. This is the default behavior provided by ERC4626SelfAssetsManagement implementation.

  2. 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:

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 assets amount 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 assets amount 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:

  • assets is less than or equal to the max deposit amount for receiver.

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:

  • shares is less than or equal to the max shares amount for receiver.

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:

  • assets is less than or equal to the max withdraw amount of owner.

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:

  • shares is less than or equal to the max redeem amount of owner.

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_address cannot 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:

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:

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

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_hash cannot be zero.