Interfaces and Dispatchers
This section describes the interfaces OpenZeppelin Contracts for Cairo offer, and explains the design choices behind them.
Interfaces can be found in the module tree under the interface
submodule, such as token::erc20::interface
. For example:
use openzeppelin_token::erc20::interface::IERC20;
or
use openzeppelin_token::erc20::interface::ERC20ABI;
For simplicity, we’ll use ERC20 as example but the same concepts apply to other modules. |
Interface traits
The library offers three types of traits to implement or interact with contracts:
Standard traits
These are associated with a predefined interface such as a standard. This includes only the functions defined in the interface, and is the standard way to interact with a compliant contract.
#[starknet::interface]
pub trait IERC20<TState> {
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
}
ABI traits
They describe a contract’s complete interface. This is useful to interface with a preset contract offered by this library, such as the ERC20 preset that includes functions from different traits such as IERC20
and IERC20Camel
.
The library offers an ABI trait for most components, providing all external function signatures even when most of the time all of them don’t need to be implemented at the same time. This can be helpful when interacting with a contract implementing the component, instead of defining a new dispatcher. |
#[starknet::interface]
pub trait ERC20ABI<TState> {
// IERC20
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
// IERC20Metadata
fn name(self: @TState) -> ByteArray;
fn symbol(self: @TState) -> ByteArray;
fn decimals(self: @TState) -> u8;
// IERC20CamelOnly
fn totalSupply(self: @TState) -> u256;
fn balanceOf(self: @TState, account: ContractAddress) -> u256;
fn transferFrom(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
}
Dispatcher traits
Traits annotated with #[starknet::interface]
automatically generate a dispatcher that can be used to interact with contracts that implement the given interface. They can be imported by appending the Dispatcher
and DispatcherTrait
suffixes to the trait name, like this:
use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
Other types of dispatchers are also auto-generated from the annotated trait. See the Interacting with another contract section of the Cairo book for more information.
In the example, the IERC20Dispatcher is the one used to interact with contracts, but the
IERC20DispatcherTrait needs to be in scope for the functions to be available.
|
Dual interfaces
camelCase functions are deprecated and maintained only for Backwards Compatibility.
It’s recommended to only use snake_case interfaces with contracts and components. The camelCase functions will be removed in
future versions.
|
Following the Great Interface Migration plan, we added snake_case
functions to all of our preexisting camelCase
contracts with the goal of eventually dropping support for the latter.
In short, the library offers two types of interfaces and utilities to handle them:
-
camelCase
interfaces, which are the ones we’ve been using so far. -
snake_case
interfaces, which are the ones we’re migrating to.
This means that currently most of our contracts implement dual interfaces. For example, the ERC20 preset contract exposes transferFrom
, transfer_from
, balanceOf
, balance_of
, etc.
Dual interfaces are available for all external functions present in previous versions of OpenZeppelin Contracts for Cairo (v0.6.1 and below). |
IERC20
The default version of the ERC20 interface trait exposes snake_case
functions:
#[starknet::interface]
pub trait IERC20<TState> {
fn name(self: @TState) -> ByteArray;
fn symbol(self: @TState) -> ByteArray;
fn decimals(self: @TState) -> u8;
fn total_supply(self: @TState) -> u256;
fn balance_of(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transfer_from(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
}
IERC20Camel
On top of that, the library also offers a camelCase
version of the same interface:
#[starknet::interface]
pub trait IERC20Camel<TState> {
fn name(self: @TState) -> ByteArray;
fn symbol(self: @TState) -> ByteArray;
fn decimals(self: @TState) -> u8;
fn totalSupply(self: @TState) -> u256;
fn balanceOf(self: @TState, account: ContractAddress) -> u256;
fn allowance(self: @TState, owner: ContractAddress, spender: ContractAddress) -> u256;
fn transfer(ref self: TState, recipient: ContractAddress, amount: u256) -> bool;
fn transferFrom(
ref self: TState, sender: ContractAddress, recipient: ContractAddress, amount: u256
) -> bool;
fn approve(ref self: TState, spender: ContractAddress, amount: u256) -> bool;
}