From 551e9d7ffdd8f8cf68297761121bd6b4f9eec9bf Mon Sep 17 00:00:00 2001 From: immrsd Date: Fri, 19 Dec 2025 19:32:16 +0300 Subject: [PATCH 01/11] Copy 2.x folder for v3.0.0 --- content/contracts-cairo/3.x/access.mdx | 513 +++ content/contracts-cairo/3.x/accounts.mdx | 504 ++ content/contracts-cairo/3.x/api/access.mdx | 867 ++++ content/contracts-cairo/3.x/api/account.mdx | 842 ++++ content/contracts-cairo/3.x/api/erc1155.mdx | 783 ++++ content/contracts-cairo/3.x/api/erc20.mdx | 1580 +++++++ content/contracts-cairo/3.x/api/erc721.mdx | 1154 +++++ content/contracts-cairo/3.x/api/finance.mdx | 328 ++ .../contracts-cairo/3.x/api/governance.mdx | 4059 +++++++++++++++++ .../contracts-cairo/3.x/api/introspection.mdx | 100 + .../contracts-cairo/3.x/api/merkle-tree.mdx | 185 + content/contracts-cairo/3.x/api/security.mdx | 202 + content/contracts-cairo/3.x/api/testing.mdx | 10 + .../contracts-cairo/3.x/api/token_common.mdx | 469 ++ content/contracts-cairo/3.x/api/udc.mdx | 85 + content/contracts-cairo/3.x/api/upgrades.mdx | 128 + content/contracts-cairo/3.x/api/utilities.mdx | 381 ++ .../3.x/backwards-compatibility.mdx | 35 + content/contracts-cairo/3.x/components.mdx | 667 +++ content/contracts-cairo/3.x/erc1155.mdx | 239 + content/contracts-cairo/3.x/erc20.mdx | 252 + content/contracts-cairo/3.x/erc4626.mdx | 433 ++ content/contracts-cairo/3.x/erc721.mdx | 213 + content/contracts-cairo/3.x/finance.mdx | 216 + .../3.x/governance/governor.mdx | 443 ++ .../3.x/governance/multisig.mdx | 150 + .../3.x/governance/timelock.mdx | 198 + .../contracts-cairo/3.x/governance/votes.mdx | 222 + .../contracts-cairo/3.x/guides/deploy-udc.mdx | 273 ++ .../contracts-cairo/3.x/guides/deployment.mdx | 40 + .../3.x/guides/erc20-permit.mdx | 63 + .../3.x/guides/erc20-supply.mdx | 148 + .../3.x/guides/interfaces-and-dispatchers.mdx | 163 + content/contracts-cairo/3.x/guides/snip12.mdx | 344 ++ content/contracts-cairo/3.x/index.mdx | 122 + content/contracts-cairo/3.x/introspection.mdx | 137 + content/contracts-cairo/3.x/macros.mdx | 15 + .../contracts-cairo/3.x/macros/type_hash.mdx | 193 + .../3.x/macros/with_components.mdx | 133 + content/contracts-cairo/3.x/presets.mdx | 141 + content/contracts-cairo/3.x/security.mdx | 218 + content/contracts-cairo/3.x/udc.mdx | 114 + content/contracts-cairo/3.x/upgrades.mdx | 127 + .../contracts-cairo/3.x/utils/constants.js | 19 + .../contracts-cairo/3.x/utils/replacements.ts | 10 + content/contracts-cairo/3.x/wizard.mdx | 12 + content/contracts-cairo/latest-versions.js | 2 +- 47 files changed, 17531 insertions(+), 1 deletion(-) create mode 100644 content/contracts-cairo/3.x/access.mdx create mode 100644 content/contracts-cairo/3.x/accounts.mdx create mode 100644 content/contracts-cairo/3.x/api/access.mdx create mode 100644 content/contracts-cairo/3.x/api/account.mdx create mode 100644 content/contracts-cairo/3.x/api/erc1155.mdx create mode 100644 content/contracts-cairo/3.x/api/erc20.mdx create mode 100644 content/contracts-cairo/3.x/api/erc721.mdx create mode 100644 content/contracts-cairo/3.x/api/finance.mdx create mode 100644 content/contracts-cairo/3.x/api/governance.mdx create mode 100644 content/contracts-cairo/3.x/api/introspection.mdx create mode 100644 content/contracts-cairo/3.x/api/merkle-tree.mdx create mode 100644 content/contracts-cairo/3.x/api/security.mdx create mode 100644 content/contracts-cairo/3.x/api/testing.mdx create mode 100644 content/contracts-cairo/3.x/api/token_common.mdx create mode 100644 content/contracts-cairo/3.x/api/udc.mdx create mode 100644 content/contracts-cairo/3.x/api/upgrades.mdx create mode 100644 content/contracts-cairo/3.x/api/utilities.mdx create mode 100644 content/contracts-cairo/3.x/backwards-compatibility.mdx create mode 100644 content/contracts-cairo/3.x/components.mdx create mode 100644 content/contracts-cairo/3.x/erc1155.mdx create mode 100644 content/contracts-cairo/3.x/erc20.mdx create mode 100644 content/contracts-cairo/3.x/erc4626.mdx create mode 100644 content/contracts-cairo/3.x/erc721.mdx create mode 100644 content/contracts-cairo/3.x/finance.mdx create mode 100644 content/contracts-cairo/3.x/governance/governor.mdx create mode 100644 content/contracts-cairo/3.x/governance/multisig.mdx create mode 100644 content/contracts-cairo/3.x/governance/timelock.mdx create mode 100644 content/contracts-cairo/3.x/governance/votes.mdx create mode 100644 content/contracts-cairo/3.x/guides/deploy-udc.mdx create mode 100644 content/contracts-cairo/3.x/guides/deployment.mdx create mode 100644 content/contracts-cairo/3.x/guides/erc20-permit.mdx create mode 100644 content/contracts-cairo/3.x/guides/erc20-supply.mdx create mode 100644 content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx create mode 100644 content/contracts-cairo/3.x/guides/snip12.mdx create mode 100644 content/contracts-cairo/3.x/index.mdx create mode 100644 content/contracts-cairo/3.x/introspection.mdx create mode 100644 content/contracts-cairo/3.x/macros.mdx create mode 100644 content/contracts-cairo/3.x/macros/type_hash.mdx create mode 100644 content/contracts-cairo/3.x/macros/with_components.mdx create mode 100644 content/contracts-cairo/3.x/presets.mdx create mode 100644 content/contracts-cairo/3.x/security.mdx create mode 100644 content/contracts-cairo/3.x/udc.mdx create mode 100644 content/contracts-cairo/3.x/upgrades.mdx create mode 100644 content/contracts-cairo/3.x/utils/constants.js create mode 100644 content/contracts-cairo/3.x/utils/replacements.ts create mode 100644 content/contracts-cairo/3.x/wizard.mdx diff --git a/content/contracts-cairo/3.x/access.mdx b/content/contracts-cairo/3.x/access.mdx new file mode 100644 index 00000000..3573209e --- /dev/null +++ b/content/contracts-cairo/3.x/access.mdx @@ -0,0 +1,513 @@ +--- +title: Access +--- + +Access control--that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. +The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. +It is therefore critical to understand how you implement it, lest someone else +[steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/). + +## Ownership and `Ownable` + +The most common and basic form of access control is the concept of ownership: there’s an account that is the `owner` +of a contract and can do administrative tasks on it. +This approach is perfectly reasonable for contracts that have a single administrative user. + +OpenZeppelin Contracts for Cairo provides [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent) for implementing ownership in your contracts. + +### Usage + +Integrating this component into a contract first requires assigning an owner. +The implementing contract’s constructor should set the initial owner by passing the owner’s address to Ownable’s +[`initializer`](/contracts-cairo/2.x/api/access#OwnableComponent-initializer) like this: + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_access::ownable::OwnableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + + // Ownable Mixin + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl InternalImpl = OwnableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event + } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + // Set the initial owner of the contract + self.ownable.initializer(owner); + } + + (...) +} +``` + +To restrict a function’s access to the owner only, add in the `assert_only_owner` method: + +```rust +#[starknet::contract] +mod MyContract { + (...) + + #[external(v0)] + fn only_owner_allowed(ref self: ContractState) { + // This function can only be called by the owner + self.ownable.assert_only_owner(); + + (...) + } +} +``` + +### Interface + +This is the full interface of the `OwnableMixinImpl` implementation: + +```rust +#[starknet::interface] +pub trait OwnableABI { + // IOwnable + fn owner() -> ContractAddress; + fn transfer_ownership(new_owner: ContractAddress); + fn renounce_ownership(); + + // IOwnableCamelOnly + fn transferOwnership(newOwner: ContractAddress); + fn renounceOwnership(); +} +``` + +Ownable also lets you: + +* `transfer_ownership` from the owner account to a new one, and +* `renounce_ownership` for the owner to relinquish this administrative privilege, a common pattern +after an initial stage with centralized administration is over. + + +Removing the owner altogether will mean that administrative tasks that are protected by `assert_only_owner` +will no longer be callable! + + +### Two step transfer + +The component also offers a more robust way of transferring ownership via the +[OwnableTwoStepImpl](/contracts-cairo/2.x/api/access#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) implementation. A two step transfer mechanism helps +to prevent unintended and irreversible owner transfers. Simply replace the `OwnableMixinImpl` +with its respective two step variant: + +```rust +#[abi(embed_v0)] +impl OwnableTwoStepMixinImpl = OwnableComponent::OwnableTwoStepMixinImpl; +``` + +#### Interface + +This is the full interface of the two step `OwnableTwoStepMixinImpl` implementation: + +```rust +#[starknet::interface] +pub trait OwnableTwoStepABI { + // IOwnableTwoStep + fn owner() -> ContractAddress; + fn pending_owner() -> ContractAddress; + fn accept_ownership(); + fn transfer_ownership(new_owner: ContractAddress); + fn renounce_ownership(); + + // IOwnableTwoStepCamelOnly + fn pendingOwner() -> ContractAddress; + fn acceptOwnership(); + fn transferOwnership(newOwner: ContractAddress); + fn renounceOwnership(); +} +``` + +## Role-Based `AccessControl` + +While the simplicity of ownership can be useful for simple systems or quick prototyping, different levels of +authorization are often needed. You may want for an account to have permission to ban users from a system, but not +create new tokens. [Role-Based Access Control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) offers +flexibility in this regard. + +In essence, we will be defining multiple roles, each allowed to perform different sets of actions. +An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for +instead of simply using [`assert_only_owner`](/contracts-cairo/2.x/api/access#OwnableComponent-assert_only_owner). This check can be enforced through [`assert_only_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-assert_only_role). +Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. + +Most software uses access control systems that are role-based: some users are regular users, some may be supervisors +or managers, and a few will often have administrative privileges. + +### Usage + +For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and +check if an account has that role. See [Creating role identifiers](#creating-role-identifiers) for information +on creating identifiers. + +Here’s a simple example of implementing [AccessControl](/contracts-cairo/2.x/api/access#AccessControlComponent) on a portion of an ERC20 token contract which defines +and sets a 'minter' role: + +```rust +const MINTER_ROLE: felt252 = selector!("MINTER_ROLE"); + +#[starknet::contract] +mod MyContract { + use openzeppelin_access::accesscontrol::AccessControlComponent; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; + use starknet::ContractAddress; + use super::MINTER_ROLE; + + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // AccessControl + #[abi(embed_v0)] + impl AccessControlImpl = + AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // ERC20 + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + accesscontrol: AccessControlComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + erc20: ERC20Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + ERC20Event: ERC20Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + initial_supply: u256, + recipient: ContractAddress, + minter: ContractAddress + ) { + // ERC20-related initialization + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, initial_supply); + + // AccessControl-related initialization + self.accesscontrol.initializer(); + self.accesscontrol._grant_role(MINTER_ROLE, minter); + } + + /// This function can only be called by a minter. + #[external(v0)] + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + self.accesscontrol.assert_only_role(MINTER_ROLE); + self.erc20.mint(recipient, amount); + } +} +``` + + +Make sure you fully understand how [AccessControl](/contracts-cairo/2.x/api/access#AccessControlComponent) works before +using it on your system, or copy-pasting the examples from this guide. + + +While clear and explicit, this isn’t anything we wouldn’t have been able to achieve with +[Ownable](/contracts-cairo/2.x/api/access#OwnableComponent). Where [AccessControl](/contracts-cairo/2.x/api/access#AccessControlComponent) shines the most is in scenarios where granular +permissions are required, which can be implemented by defining _multiple_ roles. + +Let’s augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens: + +```rust +const MINTER_ROLE: felt252 = selector!("MINTER_ROLE"); +const BURNER_ROLE: felt252 = selector!("BURNER_ROLE"); + +#[starknet::contract] +mod MyContract { + use openzeppelin_access::accesscontrol::AccessControlComponent; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; + use starknet::ContractAddress; + use super::{MINTER_ROLE, BURNER_ROLE}; + + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // AccessControl + #[abi(embed_v0)] + impl AccessControlImpl = + AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // ERC20 + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + accesscontrol: AccessControlComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + erc20: ERC20Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + ERC20Event: ERC20Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + initial_supply: u256, + recipient: ContractAddress, + minter: ContractAddress, + burner: ContractAddress + ) { + // ERC20-related initialization + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, initial_supply); + + // AccessControl-related initialization + self.accesscontrol.initializer(); + self.accesscontrol._grant_role(MINTER_ROLE, minter); + self.accesscontrol._grant_role(BURNER_ROLE, burner); + } + + /// This function can only be called by a minter. + #[external(v0)] + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + self.accesscontrol.assert_only_role(MINTER_ROLE); + self.erc20.mint(recipient, amount); + } + + /// This function can only be called by a burner. + #[external(v0)] + fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { + self.accesscontrol.assert_only_role(BURNER_ROLE); + self.erc20.burn(account, amount); + } +} +``` + +So clean! +By splitting concerns this way, more granular levels of permission may be implemented than were possible with the +simpler ownership approach to access control. Limiting what each component of a system is able to do is known +as the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), and is a good +security practice. Note that each account may still have more than one role, if so desired. + +### Granting and revoking roles + +The ERC20 token example above uses [`_grant_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-_grant_role), +an `internal` function that is useful when programmatically assigning +roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts? + +By default, **accounts with a role cannot grant it or revoke it from other accounts**: all having a role does is making +the [`assert_only_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-assert_only_role) check pass. To grant and revoke roles dynamically, you will need help from the role’s _admin_. + +Every role has an associated admin role, which grants permission to call the +[`grant_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-grant_role) and +[`revoke_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-revoke_role) functions. +A role can be granted or revoked by using these if the calling account has the corresponding admin role. +Multiple roles may have the same admin role to make management easier. +A role’s admin can even be the same role itself, which would cause accounts with that role to be able +to also grant and revoke it. + +This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also +provides an easy way to manage simpler applications. `AccessControl` includes a special role with the role identifier +of `0`, called `DEFAULT_ADMIN_ROLE`, which acts as the **default admin role for all roles**. +An account with this role will be able to manage any other role, unless +[`set_role_admin`](/contracts-cairo/2.x/api/access#AccessControlComponent-set_role_admin) is used to select a new admin role. + +Let’s take a look at the ERC20 token example, this time taking advantage of the default admin role: + +```rust +const MINTER_ROLE: felt252 = selector!("MINTER_ROLE"); +const BURNER_ROLE: felt252 = selector!("BURNER_ROLE"); + +#[starknet::contract] +mod MyContract { + use openzeppelin_access::accesscontrol::AccessControlComponent; + use openzeppelin_access::accesscontrol::DEFAULT_ADMIN_ROLE; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; + use starknet::ContractAddress; + use super::{MINTER_ROLE, BURNER_ROLE}; + + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // AccessControl + #[abi(embed_v0)] + impl AccessControlImpl = + AccessControlComponent::AccessControlImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // ERC20 + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + (...) + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + initial_supply: u256, + recipient: ContractAddress, + admin: ContractAddress + ) { + // ERC20-related initialization + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, initial_supply); + + // AccessControl-related initialization + self.accesscontrol.initializer(); + self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, admin); + } + + /// This function can only be called by a minter. + #[external(v0)] + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + self.accesscontrol.assert_only_role(MINTER_ROLE); + self.erc20.mint(recipient, amount); + } + + /// This function can only be called by a burner. + #[external(v0)] + fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { + self.accesscontrol.assert_only_role(BURNER_ROLE); + self.erc20.burn(account, amount); + } +} +``` + + +The `grant_role` and `revoke_role` functions are automatically exposed as `external` functions +from the `AccessControlImpl` by leveraging the `#[abi(embed_v0)]` annotation. + + +Note that, unlike the previous examples, no accounts are granted the 'minter' or 'burner' roles. +However, because those roles' admin role is the default admin role, and that role was granted to the 'admin', that +same account can call `grant_role` to give minting or burning permission, and `revoke_role` to remove it. + +Dynamic role allocation is often a desirable property, for example in systems where trust in a participant may vary +over time. It can also be used to support use cases such as [KYC](https://en.wikipedia.org/wiki/Know_your_customer), +where the list of role-bearers may not be known up-front, or may be prohibitively expensive to include in a single transaction. + +### Creating role identifiers + +In the Solidity implementation of AccessControl, contracts generally refer to the +[keccak256 hash](https://docs.soliditylang.org/en/latest/units-and-global-variables.html?highlight=keccak256#mathematical-and-cryptographic-functions) +of a role as the role identifier. + +For example: + +```rust +bytes32 public constant SOME_ROLE = keccak256("SOME_ROLE") +``` + +These identifiers take up 32 bytes (256 bits). + +Cairo field elements (`felt252`) store a maximum of 252 bits. +With this discrepancy, this library maintains an agnostic stance on how contracts should create identifiers. +Some ideas to consider: + +* Use [sn_keccak](https://docs.starknet.io/learn/protocol/cryptography#starknet-keccak) instead. +* Use Cairo friendly hashing algorithms like Poseidon, which are implemented in the +[Cairo corelib](https://github.com/starkware-libs/cairo/blob/main/corelib/src/poseidon.cairo). + + +The `selector!` macro can be used to compute [sn_keccak](https://docs.starknet.io/learn/protocol/cryptography#starknet-keccak) in Cairo. + + +### Interface + +This is the full interface of the `AccessControlMixinImpl` implementation: + +```rust +#[starknet::interface] +pub trait AccessControlABI { + // IAccessControl + fn has_role(role: felt252, account: ContractAddress) -> bool; + fn get_role_admin(role: felt252) -> felt252; + fn grant_role(role: felt252, account: ContractAddress); + fn revoke_role(role: felt252, account: ContractAddress); + fn renounce_role(role: felt252, account: ContractAddress); + + // IAccessControlCamel + fn hasRole(role: felt252, account: ContractAddress) -> bool; + fn getRoleAdmin(role: felt252) -> felt252; + fn grantRole(role: felt252, account: ContractAddress); + fn revokeRole(role: felt252, account: ContractAddress); + fn renounceRole(role: felt252, account: ContractAddress); + + // ISRC5 + fn supports_interface(interface_id: felt252) -> bool; +} +``` + +`AccessControl` also lets you `renounce_role` from the calling account. +The method expects an account as input as an extra security measure, to ensure you are +not renouncing a role from an unintended account. diff --git a/content/contracts-cairo/3.x/accounts.mdx b/content/contracts-cairo/3.x/accounts.mdx new file mode 100644 index 00000000..580037b8 --- /dev/null +++ b/content/contracts-cairo/3.x/accounts.mdx @@ -0,0 +1,504 @@ +--- +title: Accounts +--- + +Unlike Ethereum where accounts are derived from a private key, all Starknet accounts are contracts. This means there’s no Externally Owned Account (EOA) +concept on Starknet. + +Instead, the network features native account abstraction and signature validation happens at the contract level. + +For a general overview of account abstraction, see +[Starknet’s documentation](https://docs.starknet.io/learn/protocol/accounts). +A more detailed discussion on the topic can be found in +[Starknet Shaman’s forum](https://community.starknet.io/t/starknet-account-abstraction-model-part-1/781). + + +For detailed information on the usage and implementation check the [API Reference](/contracts-cairo/2.x/api/account) section. + + +## What is an account? + +Accounts in Starknet are smart contracts, and so they can be deployed and interacted +with like any other contract, and can be extended to implement any custom logic. However, an account is a special type +of contract that is used to validate and execute transactions. For this reason, it must implement a set of entrypoints +that the protocol uses for this execution flow. The [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) proposal defines a standard interface for accounts, +supporting this execution flow and interoperability with DApps in the ecosystem. + +### ISRC6 Interface + +```rust +/// Represents a call to a target contract function. +struct Call { + to: ContractAddress, + selector: felt252, + calldata: Span +} + +/// Standard Account Interface +#[starknet::interface] +pub trait ISRC6 { + /// Executes a transaction through the account. + fn __execute__(calls: Array); + + /// Asserts whether the transaction is valid to be executed. + fn __validate__(calls: Array) -> felt252; + + /// Asserts whether a given signature for a given hash is valid. + fn is_valid_signature(hash: felt252, signature: Array) -> felt252; +} +``` + + +The `calldata` member of the `Call` struct in the accounts has been updated to `Span` for optimization +purposes, but the interface ID remains the same for backwards compatibility. This inconsistency will be fixed in future releases. + + +[SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) adds the `is_valid_signature` method. This method is not used by the protocol, but it’s useful for +DApps to verify the validity of signatures, supporting features like Sign In with Starknet. + +SNIP-6 also defines that compliant accounts must implement the SRC5 interface following [SNIP-5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md), as +a mechanism for detecting whether a contract is an account or not through introspection. + +### ISRC5 Interface + +```rust +/// Standard Interface Detection +#[starknet::interface] +pub trait ISRC5 { + /// Queries if a contract implements a given interface. + fn supports_interface(interface_id: felt252) -> bool; +} +``` + +[SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) compliant accounts must return `true` when queried for the ISRC6 interface ID. + +Even though these interfaces are not enforced by the protocol, it’s recommended to implement them for enabling +interoperability with the ecosystem. + +### Protocol-level methods + +The Starknet protocol uses a few entrypoints for abstracting the accounts. We already mentioned the first two +as part of the ISRC6 interface, and both are required for enabling accounts to be used for executing transactions. The rest are optional: + +1. `__validate__` verifies the validity of the transaction to be executed. This is usually used to validate signatures, +but the entrypoint implementation can be customized to feature any validation mechanism [with some limitations](https://docs.starknet.io/learn/protocol/accounts#limitations). +2. `__execute__` executes the transaction if the validation is successful. +3. `__validate_declare__` optional entrypoint similar to `__validate__` but for transactions +meant to declare other contracts. +4. `__validate_deploy__` optional entrypoint similar to `__validate__` but meant for [counterfactual deployments](./guides/deployment). + + +Although these entrypoints are available to the protocol for its regular transaction flow, they can also be called like any other method. + + +## Starknet Account + +Starknet native account abstraction pattern allows for the creation of custom accounts with different validation schemes, but +usually most account implementations validate transactions using the [Stark curve](https://docs.starknet.io/learn/protocol/cryptography#the-stark-curve) which is the most efficient way +of validating signatures since it is a STARK-friendly curve. + +OpenZeppelin Contracts for Cairo provides [AccountComponent](/contracts-cairo/2.x/api/account#AccountComponent) for implementing this validation scheme. + +### Usage + +Constructing an account contract requires integrating both [AccountComponent](/contracts-cairo/2.x/api/account#AccountComponent) and [SRC5Component](/contracts-cairo/2.x/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: + +```rust +#[starknet::contract(account)] +mod MyAccount { + use openzeppelin_account::AccountComponent; + use openzeppelin_introspection::src5::SRC5Component; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // Account Mixin + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + impl AccountInternalImpl = AccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + } +} +``` + +### Interface + +This is the full interface of the `AccountMixinImpl` implementation: + +```rust +#[starknet::interface] +pub trait AccountABI { + // ISRC6 + fn __execute__(calls: Array); + fn __validate__(calls: Array) -> felt252; + fn is_valid_signature(hash: felt252, signature: Array) -> felt252; + + // ISRC5 + fn supports_interface(interface_id: felt252) -> bool; + + // IDeclarer + fn __validate_declare__(class_hash: felt252) -> felt252; + + // IDeployable + fn __validate_deploy__( + class_hash: felt252, contract_address_salt: felt252, public_key: felt252 + ) -> felt252; + + // IPublicKey + fn get_public_key() -> felt252; + fn set_public_key(new_public_key: felt252, signature: Span); + + // ISRC6CamelOnly + fn isValidSignature(hash: felt252, signature: Array) -> felt252; + + // IPublicKeyCamel + fn getPublicKey() -> felt252; + fn setPublicKey(newPublicKey: felt252, signature: Span); +} +``` + +## Ethereum Account + +Besides the Stark-curve account, OpenZeppelin Contracts for Cairo also offers Ethereum-flavored accounts that use the [secp256k1](https://en.bitcoin.it/wiki/Secp256k1) curve for signature validation. +For this the [EthAccountComponent](/contracts-cairo/2.x/api/account#EthAccountComponent) must be used. + +### Usage + +Constructing a secp256k1 account contract also requires integrating both [EthAccountComponent](/contracts-cairo/2.x/api/account#EthAccountComponent) and [SRC5Component](/contracts-cairo/2.x/api/introspection#SRC5Component). +The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. +Here’s an example of a basic contract: + +```rust +#[starknet::contract(account)] +mod MyEthAccount { + use openzeppelin_account::EthAccountComponent; + use openzeppelin_account::interface::EthPublicKey; + use openzeppelin_introspection::src5::SRC5Component; + use starknet::ClassHash; + + component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // EthAccount Mixin + #[abi(embed_v0)] + impl EthAccountMixinImpl = + EthAccountComponent::EthAccountMixinImpl; + impl EthAccountInternalImpl = EthAccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + eth_account: EthAccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + EthAccountEvent: EthAccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: EthPublicKey) { + self.eth_account.initializer(public_key); + } +} +``` + +### Interface + +This is the full interface of the `EthAccountMixinImpl` implementation: + +```rust +#[starknet::interface] +pub trait EthAccountABI { + // ISRC6 + fn __execute__(calls: Array); + fn __validate__(calls: Array) -> felt252; + fn is_valid_signature(hash: felt252, signature: Array) -> felt252; + + // ISRC5 + fn supports_interface(interface_id: felt252) -> bool; + + // IDeclarer + fn __validate_declare__(class_hash: felt252) -> felt252; + + // IEthDeployable + fn __validate_deploy__( + class_hash: felt252, contract_address_salt: felt252, public_key: EthPublicKey + ) -> felt252; + + // IEthPublicKey + fn get_public_key() -> EthPublicKey; + fn set_public_key(new_public_key: EthPublicKey, signature: Span); + + // ISRC6CamelOnly + fn isValidSignature(hash: felt252, signature: Array) -> felt252; + + // IEthPublicKeyCamel + fn getPublicKey() -> EthPublicKey; + fn setPublicKey(newPublicKey: EthPublicKey, signature: Span); +} + +``` + +## Deploying an account + +In Starknet there are two ways of deploying smart contracts: using the `deploy_syscall` and doing +counterfactual deployments. +The former can be easily done with the [Universal Deployer Contract (UDC)](./udc), a contract that +wraps and exposes the `deploy_syscall` to provide arbitrary deployments through regular contract calls. +But if you don’t have an account to invoke it, you will probably want to use the latter. + +To do counterfactual deployments, you need to implement another protocol-level entrypoint named +`__validate_deploy__`. Check the [counterfactual deployments](./guides/deployment) guide to learn how. + +## Sending transactions + +Let’s now explore how to send transactions through these accounts. + +### Starknet Account + +First, let’s take the example account we created before and deploy it: + +```rust +#[starknet::contract(account)] +mod MyAccount { + use openzeppelin_account::AccountComponent; + use openzeppelin_introspection::src5::SRC5Component; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // Account Mixin + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + impl AccountInternalImpl = AccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + } +} +``` + +To deploy the account variant, compile the contract and declare the class hash because custom accounts are likely not declared. +This means that you’ll need an account already deployed. + +Next, create the account JSON with Starknet Foundry’s [custom account setup](https://foundry-rs.github.io/starknet-foundry/starknet/account.html#custom-account-contract) and include the `--class-hash` flag with the declared class hash. +The flag enables custom account variants. + + +The following examples use `sncast` [v0.23.0](https://github.com/foundry-rs/starknet-foundry/releases/tag/v0.23.0). + + +```bash +$ sncast \ + --url http://127.0.0.1:5050 \ + account create \ + --name my-custom-account \ + --class-hash 0x123456... +``` + +This command will output the precomputed contract address and the recommended `max-fee`. +To counterfactually deploy the account, send funds to the address and then deploy the custom account. + +```bash +$ sncast \ + --url http://127.0.0.1:5050 \ + account deploy \ + --name my-custom-account +``` + +Once the account is deployed, set the `--account` flag with the custom account name to send transactions from that account. + +```bash +$ sncast \ + --account my-custom-account \ + --url http://127.0.0.1:5050 \ + invoke \ + --contract-address 0x123... \ + --function "some_function" \ + --calldata 1 2 3 +``` + +### Ethereum Account + +First, let’s take the example account we created before and deploy it: + +```rust +#[starknet::contract(account)] +mod MyEthAccount { + use openzeppelin_account::EthAccountComponent; + use openzeppelin_account::interface::EthPublicKey; + use openzeppelin_introspection::src5::SRC5Component; + + component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // EthAccount Mixin + #[abi(embed_v0)] + impl EthAccountMixinImpl = + EthAccountComponent::EthAccountMixinImpl; + impl EthAccountInternalImpl = EthAccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + eth_account: EthAccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + EthAccountEvent: EthAccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: EthPublicKey) { + self.eth_account.initializer(public_key); + } +} +``` + +Special tooling is required in order to deploy and send transactions with an Ethereum-flavored account contract. +The following examples utilize the [StarknetJS](https://www.starknetjs.com/) library. + +Compile and declare the contract on the target network. +Next, precompute the EthAccount contract address using the declared class hash. + + +The following examples use unreleased features from StarknetJS (`starknetjs@next`) at commit [d002baea0abc1de3ac6e87a671f3dec3757437b3](https://github.com/starknet-io/starknet.js/commit/d002baea0abc1de3ac6e87a671f3dec3757437b3). + + +```javascript +import * as dotenv from 'dotenv'; +import { CallData, EthSigner, hash } from 'starknet'; +import { ABI as ETH_ABI } from '../abis/eth_account.js'; +dotenv.config(); + +const ethSigner = new EthSigner(process.env.ETH_PRIVATE_KEY); +const ethPubKey = await ethSigner.getPubKey(); +const ethAccountClassHash = ''; +const ethCallData = new CallData(ETH_ABI); +const ethAccountConstructorCalldata = ethCallData.compile('constructor', { + public_key: ethPubKey +}) +const salt = '0x12345'; +const deployerAddress = '0x0'; +const ethContractAddress = hash.calculateContractAddressFromHash( + salt, + ethAccountClassHash, + ethAccountConstructorCalldata, + deployerAddress +); +console.log('Pre-calculated EthAccount address: ', ethContractAddress); +``` + +Send funds to the pre-calculated EthAccount address and deploy the contract. + +```javascript +import * as dotenv from 'dotenv'; +import { Account, CallData, EthSigner, RpcProvider, stark } from 'starknet'; +import { ABI as ETH_ABI } from '../abis/eth_account.js'; +dotenv.config(); + +const provider = new RpcProvider({ nodeUrl: process.env.API_URL }); +const ethSigner = new EthSigner(process.env.ETH_PRIVATE_KEY); +const ethPubKey = await ethSigner.getPubKey(); +const ethAccountAddress = '' +const ethAccount = new Account(provider, ethAccountAddress, ethSigner); + +const ethAccountClassHash = '' +const ethCallData = new CallData(ETH_ABI); +const ethAccountConstructorCalldata = ethCallData.compile('constructor', { + public_key: ethPubKey +}) +const salt = '0x12345'; +const deployPayload = { + classHash: ethAccountClassHash, + constructorCalldata: ethAccountConstructorCalldata, + addressSalt: salt, +}; + +const { suggestedMaxFee: feeDeploy } = await ethAccount.estimateAccountDeployFee(deployPayload); +const { transaction_hash, contract_address } = await ethAccount.deployAccount( + deployPayload, + { maxFee: stark.estimatedFeeToMaxFee(feeDeploy, 100) } +); +await provider.waitForTransaction(transaction_hash); +console.log('EthAccount deployed at: ', contract_address); +``` + +Once deployed, connect the EthAccount instance to the target contract which enables calls to come from the EthAccount. +Here’s what an ERC20 transfer from an EthAccount looks like. + +```javascript +import * as dotenv from 'dotenv'; +import { Account, RpcProvider, Contract, EthSigner } from 'starknet'; +dotenv.config(); + +const provider = new RpcProvider({ nodeUrl: process.env.API_URL }); +const ethSigner = new EthSigner(process.env.ETH_PRIVATE_KEY); +const ethAccountAddress = '' +const ethAccount = new Account(provider, ethAccountAddress, ethSigner); + +const erc20 = new Contract(compiledErc20.abi, erc20Address, provider); + +erc20.connect(ethAccount); + +const transferCall = erc20.populate('transfer', { + recipient: recipient.address, + amount: 50n +}); +const tx = await erc20.transfer( + transferCall.calldata, { maxFee: 900_000_000_000_000 } +); +await provider.waitForTransaction(tx.transaction_hash); +``` diff --git a/content/contracts-cairo/3.x/api/access.mdx b/content/contracts-cairo/3.x/api/access.mdx new file mode 100644 index 00000000..71fd194c --- /dev/null +++ b/content/contracts-cairo/3.x/api/access.mdx @@ -0,0 +1,867 @@ +--- +title: Access Control +--- + +This crate provides ways to restrict who can access the functions of a contract or when they can do it. + +- [Ownable](#OwnableComponent) is a simple mechanism with a single "owner" role that can be assigned to a single account. This mechanism can be useful in simple scenarios, but fine grained access needs are likely to outgrow it. +- [AccessControl](#AccessControlComponent) provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. + +## Interfaces + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +### `IAccessControl` [toc] [#IAccessControl] + + +```rust +use openzeppelin_access::accesscontrol::interface::IAccessControl; +``` + +External interface of AccessControl. + +[SRC5 ID](./introspection#ISRC5) + +```text +0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751 +``` + +Functions + +- [`has_role(role, account)`](#IAccessControl-has_role) +- [`get_role_admin(role)`](#IAccessControl-get_role_admin) +- [`grant_role(role, account)`](#IAccessControl-grant_role) +- [`revoke_role(role, account)`](#IAccessControl-revoke_role) +- [`renounce_role(role, account)`](#IAccessControl-renounce_role) + +Events + +- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#IAccessControl-RoleAdminChanged) +- [`RoleGranted(role, account, sender)`](#IAccessControl-RoleGranted) +- [`RoleRevoked(role, account, sender)`](#IAccessControl-RoleRevoked) + + +#### Functions [!toc] [#IAccessControl-Functions] + + +Returns whether `account` can act as `role`. + + + +Returns the admin role that controls `role`. See [grant\_role](#IAccessControl-grant_role) and [revoke\_role](#IAccessControl-revoke_role). + +To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). + + + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a [RoleGranted](#IAccessControl-RoleGranted) event. + +Requirements: + +- the caller must have `role`'s admin role. + + + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must have `role`'s admin role. + + + +Revokes `role` from the calling account. + +Roles are often managed via [grant\_role](#IAccessControl-grant_role) and [revoke\_role](#IAccessControl-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must be `account`. + + +#### Events [!toc] [#IAccessControl-Events] + + +Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` + +`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite [RoleAdminChanged](#IAccessControl-RoleAdminChanged) not being emitted signaling this. + + + +Emitted when `account` is granted `role`. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role` is called from the constructor. + + + +Emitted when `account` is revoked `role`. + +`sender` is the account that originated the contract call: + +- if using `revoke_role`, it is the admin role bearer. +- if using `renounce_role`, it is the role bearer (i.e. `account`). + + +### `IAccessControlWithDelay` [toc] [#IAccessControlWithDelay] + + +```rust +use openzeppelin_access::accesscontrol::interface::IAccessControlWithDelay; +``` + +External interface for the extended `AccessControlWithDelay` functionality. + +Functions + +- [`get_role_status(role, account)`](#IAccessControlWithDelay-get_role_status) +- [`grant_role_with_delay(role, account, delay)`](#IAccessControlWithDelay-grant_role_with_delay) + +Events + +- [`RoleGrantedWithDelay(role, account, sender, delay)`](#IAccessControlWithDelay-RoleGrantedWithDelay) + +#### Functions [!toc] [#IAccessControlWithDelay-Functions] + + +Returns the account’s status for the given role. The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + + + +Attempts to grant `role` to `account` with the specified activation delay. + +Requirements: + +- The caller must have `role`'s admin role. +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. + + +#### Events [!toc] [#IAccessControlWithDelay-Events] + + +Emitted when `account` is granted `role` with a delay. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if [\_grant\_role\_with\_delay](#AccessControlComponent-_grant_role_with_delay) is called from the constructor. + + +## Core + +### `OwnableComponent` [toc] [#OwnableComponent] + + +```rust +use openzeppelin_access::ownable::OwnableComponent; +``` + +`Ownable` provides a basic access control mechanism where an account (an owner) can be granted exclusive access to specific functions. + +This module includes the internal `assert_only_owner` to restrict a function to be used only by the owner. + +[Embeddable Mixin Implementations](../components#mixins) + +#### OwnableMixinImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableMixinImpl] + +- [`OwnableImpl`](#OwnableComponent-Embeddable-Impls-OwnableImpl) +- [`OwnableCamelOnlyImpl`](#OwnableComponent-Embeddable-Impls-OwnableCamelOnlyImpl) + +#### OwnableTwoStepMixinImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableTwoStepMixinImpl] + +- [`OwnableTwoStepImpl`](#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) +- [`OwnableTwoStepCamelOnlyImpl`](#OwnableComponent-Embeddable-Impls-OwnableTwoStepCamelOnlyImpl) + +Embeddable Implementations + +#### OwnableImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableImpl] + +- [`owner(self)`](#OwnableComponent-owner) +- [`transfer_ownership(self, new_owner)`](#OwnableComponent-transfer_ownership) +- [`renounce_ownership(self)`](#OwnableComponent-renounce_ownership) + +#### OwnableTwoStepImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl] + +- [`owner(self)`](#OwnableComponent-two-step-owner) +- [`pending_owner(self)`](#OwnableComponent-two-step-pending_owner) +- [`accept_ownership(self)`](#OwnableComponent-two-step-accept_ownership) +- [`transfer_ownership(self, new_owner)`](#OwnableComponent-two-step-transfer_ownership) +- [`renounce_ownership(self)`](#OwnableComponent-two-step-renounce_ownership) + +#### OwnableCamelOnlyImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableCamelOnlyImpl] + +- [`transferOwnership(self, newOwner)`](#OwnableComponent-transferOwnership) +- [`renounceOwnership(self)`](#OwnableComponent-renounceOwnership) + +#### OwnableTwoStepCamelOnlyImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableTwoStepCamelOnlyImpl] + +- [`pendingOwner(self)`](#OwnableComponent-two-step-pendingOwner) +- [`acceptOwnership(self)`](#OwnableComponent-two-step-acceptOwnership) +- [`transferOwnership(self, new_owner)`](#OwnableComponent-two-step-transferOwnership) +- [`renounceOwnership(self)`](#OwnableComponent-two-step-renounceOwnership) + +Internal Implementations + +#### InternalImpl [!toc] [#OwnableComponent-InternalImpl] + +- [`initializer(self, owner)`](#OwnableComponent-initializer) +- [`assert_only_owner(self)`](#OwnableComponent-assert_only_owner) +- [`_transfer_ownership(self, new_owner)`](#OwnableComponent-_transfer_ownership) +- [`_propose_owner(self, new_owner)`](#OwnableComponent-_propose_owner) + +Events + +- [`OwnershipTransferStarted(previous_owner, new_owner)`](#OwnableComponent-OwnershipTransferStarted) +- [`OwnershipTransferred(previous_owner, new_owner)`](#OwnableComponent-OwnershipTransferred) + +#### Embeddable functions [!toc] [#OwnableComponent-Embeddable-Functions] + + +Returns the address of the current owner. + + + +Transfers ownership of the contract to a new account (`new_owner`). Can only be called by the current owner. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + + + +Leaves the contract without owner. It will not be possible to call `assert_only_owner` functions anymore. Can only be called by the current owner. + + +Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. + + + +#### Embeddable functions (two step transfer) [!toc] [#OwnableComponent-Embeddable-Functions-Two-Step] + + +Returns the address of the current owner. + + + +Returns the address of the pending owner. + + + +Transfers ownership of the contract to the pending owner. Can only be called by the pending owner. Resets pending owner to zero address. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + + + +Starts the two step ownership transfer process, by setting the pending owner. Setting `new_owner` to the zero address is allowed, this can be used to cancel an initiated ownership transfer. + +Can only be called by the current owner. + +Emits an [OwnershipTransferStarted](#OwnableComponent-OwnershipTransferStarted) event. + + + +Leaves the contract without owner. It will not be possible to call `assert_only_owner` functions anymore. Can only be called by the current owner. + + +Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. + + + + +See [transfer\_ownership](#OwnableComponent-transfer_ownership). + + + +See [renounce\_ownership](#OwnableComponent-renounce_ownership). + + + +See [pending\_owner](#OwnableComponent-two-step-pending_owner). + + + +See [accept\_ownership](#OwnableComponent-two-step-accept_ownership). + + + +See [transfer\_ownership](#OwnableComponent-two-step-transfer_ownership). + + + +See [renounce\_ownership](#OwnableComponent-two-step-renounce_ownership). + + +#### Internal functions [!toc] [#OwnableComponent-Internal-Functions] + + +Initializes the contract and sets `owner` as the initial owner. + +Requirements: + +- `owner` cannot be the zero address. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + + + +Panics if called by any account other than the owner. + + + +Transfers ownership of the contract to a new account (`new_owner`). Internal function without access restriction. + +Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. + + + +Sets a new pending owner in a two step transfer. + +Internal function without access restriction. + +Emits an [OwnershipTransferStarted](#OwnableComponent-OwnershipTransferStarted) event. + + +#### Events [!toc] [#OwnableComponent-Events] + + +Emitted when the pending owner is updated. + + + +Emitted when the ownership is transferred. + + +### `AccessControlComponent` [toc] [#AccessControlComponent] + + +```rust +use openzeppelin_access::accesscontrol::AccessControlComponent; +``` + +Component that allows contracts to implement role-based access control mechanisms. Roles are referred to by their `felt252` identifier: + +```rust +const MY_ROLE: felt252 = selector!("MY_ROLE"); +``` + +Roles can be used to represent a set of permissions. To restrict access to a function call, use [`assert_only_role`](#AccessControlComponent-assert_only_role): + +```rust +(...) + +#[external(v0)] +fn foo(ref self: ContractState) { + self.accesscontrol.assert_only_role(MY_ROLE); + + // Do something +} +``` + +Roles can be granted and revoked dynamically via the [grant\_role](#AccessControlComponent-grant_role), [grant\_role\_with\_delay](#IAccessControlWithDelay-grant_role_with_delay) and [revoke\_role](#AccessControlComponent-revoke_role) functions. Each role has an associated admin role, and only accounts that have a role’s admin role can call [grant\_role](#AccessControlComponent-grant_role), [grant\_role\_with\_delay](#IAccessControlWithDelay-grant_role_with_delay) and [revoke\_role](#AccessControlComponent-revoke_role). + +By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means that only accounts with this role will be able to grant or revoke other roles. More complex role relationships can be created by using [set\_role\_admin](#AccessControlComponent-set_role_admin). + + +The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to grant and revoke this role. Extra precautions should be taken to secure accounts that have been granted it. + + +[Embeddable Mixin Implementations](../components#mixins) + +#### AccessControlMixinImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlMixinImpl] + +- [`AccessControlImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlImpl) +- [`AccessControlCamelImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlCamelImpl) +- [`AccessControlWithDelayImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlWithDelayImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +#### AccessControlImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlImpl] + +- [`has_role(self, role, account)`](#AccessControlComponent-has_role) +- [`get_role_admin(self, role)`](#AccessControlComponent-get_role_admin) +- [`grant_role(self, role, account)`](#AccessControlComponent-grant_role) +- [`revoke_role(self, role, account)`](#AccessControlComponent-revoke_role) +- [`renounce_role(self, role, account)`](#AccessControlComponent-renounce_role) + +#### AccessControlCamelImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlCamelImpl] + +- [`hasRole(self, role, account)`](#AccessControlComponent-hasRole) +- [`getRoleAdmin(self, role)`](#AccessControlComponent-getRoleAdmin) +- [`grantRole(self, role, account)`](#AccessControlComponent-grantRole) +- [`revokeRole(self, role, account)`](#AccessControlComponent-revokeRole) +- [`renounceRole(self, role, account)`](#AccessControlComponent-renounceRole) + +#### AccessControlWithDelayImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlWithDelayImpl] + +- [`get_role_status(self, role, account)`](#AccessControlComponent-get_role_status) +- [`grant_role_with_delay(self, role, account, delay)`](#AccessControlComponent-grant_role_with_delay) + +#### SRC5Impl [!toc] [#AccessControlComponent-Embeddable-Impls-SRC5Impl] + +- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) + +Internal Implementations + +#### InternalImpl [!toc] [#AccessControlComponent-InternalImpl] + +- [`initializer(self)`](#AccessControlComponent-initializer) +- [`assert_only_role(self, role)`](#AccessControlComponent-assert_only_role) +- [`is_role_effective(self, role, account)`](#AccessControlComponent-is_role_effective) +- [`resolve_role_status(self, role, account)`](#AccessControlComponent-resolve_role_status) +- [`is_role_granted(self, role, account)`](#AccessControlComponent-is_role_granted) +- [`set_role_admin(self, role, admin_role)`](#AccessControlComponent-set_role_admin) +- [`_grant_role(self, role, account)`](#AccessControlComponent-_grant_role) +- [`_grant_role_with_delay(self, role, account, delay)`](#AccessControlComponent-_grant_role_with_delay) +- [`_revoke_role(self, role, account)`](#AccessControlComponent-_revoke_role) + +Events + +#### IAccessControl [!toc] [#AccessControlComponent-Events-IAccessControl] + +- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#AccessControlComponent-RoleAdminChanged) +- [`RoleGranted(role, account, sender)`](#AccessControlComponent-RoleGranted) +- [`RoleRevoked(role, account, sender)`](#AccessControlComponent-RoleRevoked) + +#### IAccessControlWithDelay [!toc] [#AccessControlComponent-Events-IAccessControlWithDelay] + +- [`RoleGrantedWithDelay(role, account, sender, delay)`](#AccessControlComponent-RoleGrantedWithDelay) + +#### Embeddable functions [!toc] [#AccessControlComponent-Embeddable-Functions] + + +Returns whether `account` can act as `role`. + + + +Returns the admin role that controls `role`. See [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). + +To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). + + + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + + + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a [RoleGranted](#IAccessControl-RoleGranted) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleGranted](#IAccessControl-RoleGranted) event. + + + +Attempts to grant `role` to `account` with the specified activation delay. + +Requirements: + +- The caller must have \`role’s admin role. +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. + + + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. + + + +Revokes `role` from the calling account. + +Roles are often managed via [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been revoked `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. + +Requirements: + +- the caller must be `account`. + +May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. + + + +See [ISRC5::supports\_interface](./introspection#ISRC5-supports_interface). + + + +See [has\_role](#AccessControlComponent-has_role). + + + +See [get\_role\_admin](#AccessControlComponent-get_role_admin). + + + +See [grant\_role](#AccessControlComponent-grant_role). + + + +See [revoke\_role](#AccessControlComponent-revoke_role). + + + +See [renounce\_role](#AccessControlComponent-renounce_role). + + +#### Internal functions [!toc] [#AccessControlComponent-Internal-Functions] + + +Initializes the contract by registering the [IAccessControl](#IAccessControl) interface ID. + + + +Validates that the caller can act as the given role. Otherwise it panics. + + + +Returns whether the account can act as the given role. + +The account can act as the role if it is active and the `effective_from` time is before or equal to the current time. + + +If the `effective_from` timepoint is 0, the role is effective immediately. This is backwards compatible with implementations that didn’t use delays but a single boolean flag. + + + + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + + + +Returns whether the account has the given role granted. + + +The account may not be able to act as the role yet, if a delay was set and has not passed yet. Use `is_role_effective` to check if the account can act as the role. + + + + +Sets `admin_role` as `role`'s admin role. + +Internal function without access restriction. + +Emits a [RoleAdminChanged](#IAccessControl-RoleAdminChanged) event. + + + +Attempts to grant `role` to `account`. The function does nothing if `role` is already effective for `account`. If `role` has been granted to `account`, but is not yet active due to a time delay, the delay is removed and `role` becomes effective immediately. + +Internal function without access restriction. + +May emit a [RoleGranted](#IAccessControl-RoleGranted) event. + + + +Attempts to grant `role` to `account` with the specified activation delay. + +The role will become effective after the given delay has passed. If the role is already active (`Effective`) for the account, the function will panic. If the role has been granted but is not yet active (being in the `Delayed` state), the existing delay will be overwritten with the new `delay`. + +Internal function without access restriction. + +Requirements: + +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. + + + +Revokes `role` from `account`. + +Internal function without access restriction. + +May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. + + +#### Events [!toc] [#AccessControlComponent-Events] + + +See [IAccessControl::RoleAdminChanged](#IAccessControl-RoleAdminChanged). + + + +See [IAccessControl::RoleGranted](#IAccessControl-RoleGranted). + + + +See [IAccessControlWithDelay::RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay). + + + +See [IAccessControl::RoleRevoked](#IAccessControl-RoleRevoked). + diff --git a/content/contracts-cairo/3.x/api/account.mdx b/content/contracts-cairo/3.x/api/account.mdx new file mode 100644 index 00000000..159d9ddd --- /dev/null +++ b/content/contracts-cairo/3.x/api/account.mdx @@ -0,0 +1,842 @@ +--- +title: Account +--- + +This crate provides components to implement account contracts that can be used for interacting with the network. + +## Interfaces + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +### `ISRC6` [toc] [#ISRC6] + + +```rust +use openzeppelin_account::interface::ISRC6; +``` + +Interface of the SRC6 Standard Account as defined in the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md). + +[SRC5 ID](./introspection#ISRC5) + +```text +0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd +``` + +Functions + +- [`__execute__(calls)`](#ISRC6-__execute__) +- [`__validate__(calls)`](#ISRC6-__validate__) +- [`is_valid_signature(hash, signature)`](#ISRC6-is_valid_signature) + +#### Functions [!toc] [#ISRC6-Functions] + + +Executes the list of calls as a transaction after validation. + +The `Call` struct is defined in [corelib](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/account.cairo#L3). + + + +Validates a transaction before execution. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + + +Validates whether a signature is valid or not for the given message hash. + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + +### `ISRC9_V2` [toc] [#ISRC9_V2] + + +```rust +use openzeppelin_account::extensions::src9::ISRC9_V2; +``` + +Interface of the SRC9 Standard as defined in the [SNIP-9](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md). + +[SRC5 ID](./introspection#ISRC5) + +```text +0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872 +``` + +Functions + +- [`execute_from_outside_v2(outside_execution, signature)`](#ISRC9_V2-execute_from_outside_v2) +- [`is_valid_outside_execution_nonce(nonce)`](#ISRC9_V2-is_valid_outside_execution_nonce) + +#### Functions [!toc] [#ISRC9_V2-Functions] + + +Allows anyone to submit a transaction on behalf of the account as long as they have the relevant signatures. + +This method allows reentrancy. A call to `__execute__` or `execute_from_outside_v2` can trigger another nested transaction to `execute_from_outside_v2` thus the implementation MUST verify that the provided `signature` matches the hash of `outside_execution` and that `nonce` was not already used. + +The implementation should expect version to be set to 2 in the domain separator. + +Arguments: + +- `outside_execution` - The parameters of the transaction to execute. +- `signature` - A valid signature on the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message encoding of `outside_execution`. + + + +Get the status of a given nonce. `true` if the nonce is available to use. + + +## Core + +### `AccountComponent` [toc] [#AccountComponent] + + +```rust +use openzeppelin_account::AccountComponent; +``` + +Account component implementing [`ISRC6`](#ISRC6) for signatures over the [Starknet curve](https://docs.starknet.io/learn/protocol/cryptography#the-stark-curve). + +Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. + +[Embeddable Mixin Implementations](../components#mixins) + +#### AccountMixinImpl [!toc] [#AccountComponent-Embeddable-Impls-AccountMixinImpl] + +- [`SRC6Impl`](#AccountComponent-Embeddable-Impls-SRC6Impl) +- [`DeclarerImpl`](#AccountComponent-Embeddable-Impls-DeclarerImpl) +- [`DeployableImpl`](#AccountComponent-Embeddable-Impls-DeployableImpl) +- [`PublicKeyImpl`](#AccountComponent-Embeddable-Impls-PublicKeyImpl) +- [`SRC6CamelOnlyImpl`](#AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl) +- [`PublicKeyCamelImpl`](#AccountComponent-Embeddable-Impls-PublicKeyCamelImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +#### SRC6Impl [!toc] [#AccountComponent-Embeddable-Impls-SRC6Impl] + +- [`__execute__(self, calls)`](#AccountComponent-__execute__) +- [`__validate__(self, calls)`](#AccountComponent-__validate__) +- [`is_valid_signature(self, hash, signature)`](#AccountComponent-is_valid_signature) + +#### DeclarerImpl [!toc] [#AccountComponent-Embeddable-Impls-DeclarerImpl] + +- [`__validate_declare__(self, class_hash)`](#AccountComponent-__validate_declare__) + +#### DeployableImpl [!toc] [#AccountComponent-Embeddable-Impls-DeployableImpl] + +- [`__validate_deploy__(self, hash, signature)`](#AccountComponent-__validate_deploy__) + +#### PublicKeyImpl [!toc] [#AccountComponent-Embeddable-Impls-PublicKeyImpl] + +- [`get_public_key(self)`](#AccountComponent-get_public_key) +- [`set_public_key(self, new_public_key, signature)`](#AccountComponent-set_public_key) + +#### SRC6CamelOnlyImpl [!toc] [#AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] + +- [`isValidSignature(self, hash, signature)`](#AccountComponent-isValidSignature) + +#### PublicKeyCamelImpl [!toc] [#AccountComponent-Embeddable-Impls-PublicKeyCamelImpl] + +- [`getPublicKey(self)`](#AccountComponent-getPublicKey) +- [`setPublicKey(self, newPublicKey, signature)`](#AccountComponent-setPublicKey) + +#### SRC5Impl [!toc] [#AccountComponent-Embeddable-Impls-SRC5Impl] + +- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) + +Internal Implementations + +#### InternalImpl [!toc] [#AccountComponent-InternalImpl] + +- [`initializer(self, public_key)`](#AccountComponent-initializer) +- [`assert_only_self(self)`](#AccountComponent-assert_only_self) +- [`assert_valid_new_owner(self, current_owner, new_owner, signature)`](#AccountComponent-assert_valid_new_owner) +- [`validate_transaction(self)`](#AccountComponent-validate_transaction) +- [`_set_public_key(self, new_public_key)`](#AccountComponent-_set_public_key) +- [`_is_valid_signature(self, hash, signature)`](#AccountComponent-_is_valid_signature) + +Events + +- [`OwnerAdded(new_owner_guid)`](#AccountComponent-OwnerAdded) +- [`OwnerRemoved(removed_owner_guid)`](#AccountComponent-OwnerRemoved) + +#### Embeddable functions [!toc] [#AccountComponent-Embeddable-Functions] + + +See [ISRC6::\_\_execute\_\_](#ISRC6-__execute__). + + + +See [ISRC6::\_\_validate\_\_](#ISRC6-__validate__). + + + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + + + +Validates a [`Declare` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#declare-v3). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + + +Validates a [`DeployAccount` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#deploy-account-v3). See [Counterfactual Deployments](../guides/deployment). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + + +Returns the current public key of the account. + + + +Sets a new public key for the account. Only accessible by the account calling itself through `__execute__`. + +Requirements: + +- The caller must be the contract itself. +- The signature must be valid for the new owner. + +Emits both an [OwnerRemoved](#AccountComponent-OwnerRemoved) and an [OwnerAdded](#AccountComponent-OwnerAdded) event. + +The message to be signed is computed in Cairo as follows: + +```javascript +let message_hash = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(get_contract_address()) + .update_with(current_owner) + .finalize(); +``` + + + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + + + +See [get\_public\_key](#AccountComponent-get_public_key). + + + +See [set\_public\_key](#AccountComponent-set_public_key). + + +#### Internal functions [!toc] [#AccountComponent-Internal-Functions] + + +Initializes the account with the given public key, and registers the `ISRC6` interface ID. + +Emits an [OwnerAdded](#AccountComponent-OwnerAdded) event. + + + +Validates that the caller is the account itself. Otherwise it reverts. + + + +Validates that `new_owner` accepted the ownership of the contract through a signature. + +Requirements: + +- `signature` must be valid for the new owner. + +This function assumes that `current_owner` is the current owner of the contract, and does not validate this assumption. + + + +Validates a transaction signature from the [global context](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/info.cairo#L61). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + + +Set the public key without validating the caller. + +Emits an [OwnerAdded](#AccountComponent-OwnerAdded) event. + +The usage of this method outside the `set_public_key` function is discouraged. + + + +Validates the provided `signature` for the `hash`, using the account's current public key. + + +#### Events [!toc] [#AccountComponent-Events] + + +Emitted when a `public_key` is added. + + + +Emitted when a `public_key` is removed. + + +### `EthAccountComponent` [toc] [#EthAccountComponent] + + +```rust +use openzeppelin_account::eth_account::EthAccountComponent; +``` + +Account component implementing [`ISRC6`](#ISRC6) for signatures over the [Secp256k1 curve](https://en.bitcoin.it/wiki/Secp256k1). + +Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. + +The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Point`. + +[Embeddable Mixin Implementations](../components#mixins) + +#### EthAccountMixinImpl [!toc] [#EthAccountComponent-Embeddable-Impls-EthAccountMixinImpl] + +- [`SRC6Impl`](#EthAccountComponent-Embeddable-Impls-SRC6Impl) +- [`DeclarerImpl`](#EthAccountComponent-Embeddable-Impls-DeclarerImpl) +- [`DeployableImpl`](#EthAccountComponent-Embeddable-Impls-DeployableImpl) +- [`PublicKeyImpl`](#EthAccountComponent-Embeddable-Impls-PublicKeyImpl) +- [`SRC6CamelOnlyImpl`](#EthAccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl) +- [`PublicKeyCamelImpl`](#EthAccountComponent-Embeddable-Impls-PublicKeyCamelImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +#### SRC6Impl [!toc] [#EthAccountComponent-Embeddable-Impls-SRC6Impl] + +- [`__execute__(self, calls)`](#EthAccountComponent-__execute__) +- [`__validate__(self, calls)`](#EthAccountComponent-__validate__) +- [`is_valid_signature(self, hash, signature)`](#EthAccountComponent-is_valid_signature) + +#### DeclarerImpl [!toc] [#EthAccountComponent-Embeddable-Impls-DeclarerImpl] + +- [`__validate_declare__(self, class_hash)`](#EthAccountComponent-__validate_declare__) + +#### DeployableImpl [!toc] [#EthAccountComponent-Embeddable-Impls-DeployableImpl] + +- [`__validate_deploy__(self, hash, signature)`](#EthAccountComponent-__validate_deploy__) + +#### PublicKeyImpl [!toc] [#EthAccountComponent-Embeddable-Impls-PublicKeyImpl] + +- [`get_public_key(self)`](#EthAccountComponent-get_public_key) +- [`set_public_key(self, new_public_key, signature)`](#EthAccountComponent-set_public_key) + +#### SRC6CamelOnlyImpl [!toc] [#EthAccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] + +- [`isValidSignature(self, hash, signature)`](#EthAccountComponent-isValidSignature) + +#### PublicKeyCamelImpl [!toc] [#EthAccountComponent-Embeddable-Impls-PublicKeyCamelImpl] + +- [`getPublicKey(self)`](#EthAccountComponent-getPublicKey) +- [`setPublicKey(self, newPublicKey, signature)`](#EthAccountComponent-setPublicKey) + +#### SRC5Impl [!toc] [#EthAccountComponent-Embeddable-Impls-SRC5Impl] + +- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) + +Internal Implementations + +#### InternalImpl [!toc] [#EthAccountComponent-InternalImpl] + +- [`initializer(self, public_key)`](#EthAccountComponent-initializer) +- [`assert_only_self(self)`](#EthAccountComponent-assert_only_self) +- [`assert_valid_new_owner(self, current_owner, new_owner, signature)`](#EthAccountComponent-assert_valid_new_owner) +- [`validate_transaction(self)`](#EthAccountComponent-validate_transaction) +- [`_set_public_key(self, new_public_key)`](#EthAccountComponent-_set_public_key) +- [`_is_valid_signature(self, hash, signature)`](#EthAccountComponent-_is_valid_signature) + +Events + +- [`OwnerAdded(new_owner_guid)`](#EthAccountComponent-OwnerAdded) +- [`OwnerRemoved(removed_owner_guid)`](#EthAccountComponent-OwnerRemoved) + +#### Embeddable functions [!toc] [#EthAccountComponent-Embeddable-Functions] + + +See [ISRC6::\_\_execute\_\_](#ISRC6-__execute__). + + + +See [ISRC6::\_\_validate\_\_](#ISRC6-__validate__). + + + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + + + +Validates a [`Declare` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#declare-v3). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + + +Validates a [`DeployAccount` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#deploy-account-v3). See [Counterfactual Deployments](../guides/deployment). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + + +Returns the current public key of the account. + + + +Sets a new public key for the account. Only accessible by the account calling itself through `__execute__`. + +Requirements: + +- The caller must be the contract itself. +- The signature must be valid for the new owner. + +Emits both an [OwnerRemoved](#EthAccountComponent-OwnerRemoved) and an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. + +The message to be signed is computed in Cairo as follows: + +```javascript +let message_hash = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(get_contract_address()) + .update_with(current_owner.get_coordinates().unwrap_syscall()) + .finalize(); +``` + + + +See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). + + + +See [get\_public\_key](#EthAccountComponent-get_public_key). + + + +See [set\_public\_key](#EthAccountComponent-set_public_key). + + +#### Internal functions [!toc] [#EthAccountComponent-Internal-Functions] + + +Initializes the account with the given public key, and registers the `ISRC6` interface ID. + +Emits an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. + + + +Validates that the caller is the account itself. Otherwise it reverts. + + + +Validates that `new_owner` accepted the ownership of the contract through a signature. + +Requirements: + +- The signature must be valid for the `new_owner`. + +This function assumes that `current_owner` is the current owner of the contract, and does not validate this assumption. + + + +Validates a transaction signature from the [global context](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/info.cairo#L61). + +Returns the short string `'VALID'` if valid, otherwise it reverts. + + + +Set the public key without validating the caller. + +Emits an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. + +The usage of this method outside the `set_public_key` function is discouraged. + + + +Validates the provided `signature` for the `hash`, using the account's current public key. + + +#### Events [!toc] [#EthAccountComponent-Events] + +The `guid` is computed as the hash of the public key, using the poseidon hash function. + + +Emitted when a `public_key` is added. + + + +Emitted when a `public_key` is removed. + + +## Extensions + +### `SRC9Component` [toc] [#SRC9Component] + + +```rust +use openzeppelin_account::extensions::SRC9Component; +``` + +OutsideExecution component implementing [`ISRC9_V2`](#ISRC9_V2). + +This component is signature-agnostic, meaning it can be integrated into any account contract, as long as the account implements the ISRC6 interface. + +Embeddable Implementations + +#### OutsideExecutionV2Impl [!toc] [#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl] + +- [`execute_from_outside_v2(self, outside_execution, signature)`](#SRC9Component-execute_from_outside_v2) +- [`is_valid_outside_execution_nonce(self, nonce)`](#SRC9Component-is_valid_outside_execution_nonce) + +Internal Implementations + +#### InternalImpl [!toc] [#SRC9Component-InternalImpl] + +- [`initializer(self)`](#SRC9Component-initializer) + +#### Embeddable functions [!toc] [#SRC9Component-Embeddable-Functions] + + +Allows anyone to submit a transaction on behalf of the account as long as they have the relevant signatures. + +This method allows reentrancy. A call to `__execute__` or `execute_from_outside_v2` can trigger another nested transaction to `execute_from_outside_v2`. This implementation verifies that the provided `signature` matches the hash of `outside_execution` and that `nonce` was not already used. + +Arguments: + +- `outside_execution` - The parameters of the transaction to execute. +- `signature` - A valid signature on the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message encoding of `outside_execution`. + +Requirements: + +- The caller must be the `outside_execution.caller` unless 'ANY\_CALLER' is used. +- The current time must be within the `outside_execution.execute_after` and `outside_execution.execute_before` span. +- The `outside_execution.nonce` must not be used before. +- The `signature` must be valid. + + + +Returns the status of a given nonce. `true` if the nonce is available to use. + + +#### Internal functions [!toc] [#SRC9Component-Internal-Functions] + + +Initializes the account by registering the `ISRC9_V2` interface ID. + + +## Presets + +### `AccountUpgradeable` [toc] [#AccountUpgradeable] + + +```rust +use openzeppelin_presets::AccountUpgradeable; +``` + +Upgradeable account which can change its public key and declare, deploy, or call contracts. Supports outside execution by implementing [SRC9](#SRC9Component). + +[Sierra class hash](../presets) + +```text +{{AccountUpgradeableClassHash}} +``` + +Constructor + +- [`constructor(self, public_key)`](#AccountUpgradeable-constructor) + +Embedded Implementations + +AccountComponent + +- [`AccountMixinImpl`](#AccountComponent-Embeddable-Mixin-Impl) + +SRC9Component + +- [`OutsideExecutionV2Impl`](#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#AccountUpgradeable-upgrade) + +#### Constructor [!toc] [#AccountUpgradeable-Constructor] + + +Sets the account `public_key` and registers the interfaces the contract supports. + + +#### External functions [!toc] [#AccountUpgradeable-External-Functions] + + +Upgrades the contract to a new implementation given by `new_class_hash`. + +Requirements: + +- The caller is the account contract itself. +- `new_class_hash` cannot be zero. + + +### `EthAccountUpgradeable` [toc] [#EthAccountUpgradeable] + + +```rust +use openzeppelin_presets::EthAccountUpgradeable; +``` + +Upgradeable account which can change its public key and declare, deploy, or call contracts, using Ethereum signing keys. Supports outside execution by implementing [SRC9](#SRC9Component). + +The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Point`. + +[Sierra class hash](../presets) + +```text +{{EthAccountUpgradeableClassHash}} +``` + +Constructor + +- [`constructor(self, public_key)`](#EthAccountUpgradeable-constructor) + +Embedded Implementations + +EthAccountComponent + +- [`EthAccountMixinImpl`](#EthAccountComponent-Embeddable-Mixin-Impl) + +SRC9Component + +- [`OutsideExecutionV2Impl`](#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#EthAccountUpgradeable-upgrade) + +#### Constructor [!toc] [#EthAccountUpgradeable-Constructor] + + +Sets the account `public_key` and registers the interfaces the contract supports. + + +#### External functions [!toc] [#EthAccountUpgradeable-External-Functions] + + +Upgrades the contract to a new implementation given by `new_class_hash`. + +Requirements: + +- The caller is the account contract itself. +- `new_class_hash` cannot be zero. + diff --git a/content/contracts-cairo/3.x/api/erc1155.mdx b/content/contracts-cairo/3.x/api/erc1155.mdx new file mode 100644 index 00000000..8526aaea --- /dev/null +++ b/content/contracts-cairo/3.x/api/erc1155.mdx @@ -0,0 +1,783 @@ +--- +title: ERC1155 +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This module provides interfaces, presets, and utilities related to ERC1155 contracts. + +For an overview of ERC1155, read our [ERC1155 guide](../erc1155). + +## Interfaces + +### `IERC1155` [toc] [#IERC1155] + + + +```rust +use openzeppelin_token::erc1155::interface::IERC1155; +``` + +Interface of the IERC1155 standard as defined in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155). + +[SRC5 ID](./introspection#ISRC5) + +```text +0x6114a8f75559e1b39fcba08ce02961a1aa082d9256a158dd3e64964e4b1b52 +``` + +Functions + +- [`balance_of(account, token_id)`](#IERC1155-balance_of) +- [`balance_of_batch(accounts, token_ids)`](#IERC1155-balance_of_batch) +- [`safe_transfer_from(from, to, token_id, value, data)`](#IERC1155-safe_transfer_from) +- [`safe_batch_transfer_from(from, to, token_ids, values, data)`](#IERC1155-safe_batch_transfer_from) +- [`set_approval_for_all(operator, approved)`](#IERC1155-set_approval_for_all) +- [`is_approved_for_all(owner, operator)`](#IERC1155-is_approved_for_all) + +Events + +- [`TransferSingle(operator, from, to, id, value)`](#IERC1155-TransferSingle) +- [`TransferBatch(operator, from, to, ids, values)`](#IERC1155-TransferBatch) +- [`ApprovalForAll(owner, operator, approved)`](#IERC1155-ApprovalForAll) +- [`URI(value, id)`](#IERC1155-URI) + +#### Functions [!toc] [#functions] + + +Returns the amount of `token_id` tokens owned by `account`. + + + +Returns a list of balances derived from the `accounts` and `token_ids` pairs. + + + +Transfers ownership of `value` amount of `token_id` from `from` if `to` is either `IERC1155Receiver` or an account. + +`data` is additional data, it has no specified format and it is passed to `to`. + +Emits a [TransferSingle](#IERC1155-TransferSingle) event. + + + +Transfers ownership of `token_ids` and `values` pairs from `from` if `to` is either `IERC1155Receiver` or an account. + +`data` is additional data, it has no specified format and it is passed to `to`. + +Emits a [TransferBatch](#IERC1155-TransferBatch) event. + + + +Enables or disables approval for `operator` to manage all of the caller's assets. + +Emits an [ApprovalForAll](#IERC1155-ApprovalForAll) event. + + + +Queries if `operator` is an authorized operator for `owner`. + + +#### Events [!toc] [#events] + + +Emitted when `value` amount of `id` token is transferred from `from` to `to` through `operator`. + + + +Emitted when a batch of `values` amount of `ids` tokens are transferred from `from` to `to` through `operator`. + + + +Emitted when `owner` enables or disables `operator` to manage all of the owner's assets. + + + +Emitted when the token URI is updated to `value` for the `id` token. + + +### `IERC1155MetadataURI` [toc] [#IERC1155MetadataURI] + + + +```rust +use openzeppelin_token::erc1155::interface::IERC1155MetadataURI; +``` + +Interface for the optional metadata function in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155#metadata). + +[SRC5 ID](./introspection#ISRC5) + +```text +0xcabe2400d5fe509e1735ba9bad205ba5f3ca6e062da406f72f113feb889ef7 +``` + +Functions + +- [`uri(token_id)`](#IERC1155MetadataURI-uri) + +#### Functions [!toc] [#functions_2] + + +Returns the Uniform Resource Identifier (URI) for the `token_id` token. + + +### `IERC1155Receiver` [toc] [#IERC1155Receiver] + + + +```rust +use openzeppelin_token::erc1155::interface::IERC1155Receiver; +``` + +Interface for contracts that support receiving token transfers from `ERC1155` contracts. + +[SRC5 ID](./introspection#ISRC5) + +```text +0x15e8665b5af20040c3af1670509df02eb916375cdf7d8cbaf7bd553a257515e +``` + +Functions + +- [`on_erc1155_received(operator, from, token_id, value, data)`](#IERC1155Receiver-on_erc1155_received) +- [`on_erc1155_batch_received(operator, from, token_ids, values, data)`](#IERC1155Receiver-on_erc1155_batch_received) + +#### Functions [!toc] [#functions_3] + + +This function is called whenever an ERC1155 `token_id` token is transferred to this `IERC1155Receiver` implementer via [IERC1155::safe\_transfer\_from](#IERC1155-safe_transfer_from) by `operator` from `from`. + + + +This function is called whenever multiple ERC1155 `token_ids` tokens are transferred to this `IERC1155Receiver` implementer via [IERC1155::safe\_batch\_transfer\_from](#IERC1155-safe_batch_transfer_from) by `operator` from `from`. + + +## [](#core)Core + +### `ERC1155Component` [toc] [#ERC1155Component] + + + +```rust +use openzeppelin_token::erc1155::ERC1155Component; +``` + +ERC1155 component implementing [IERC1155](#IERC1155) and [IERC1155MetadataURI](#IERC1155MetadataURI). + +Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. + +See [Hooks](#ERC1155Component-Hooks) to understand how are hooks used. + +#### Hooks [!toc] [#ERC1155Component-Hooks] + +#### ERC1155HooksTrait [!toc] [#ERC1155Component-ERC1155HooksTrait] + +- [`before_update(self, from, to, token_ids, values)`](#ERC1155Component-before_update) +- [`after_update(self, from, to, token_ids, values)`](#ERC1155Component-after_update) + +[Embeddable Mixin Implementations](../components#mixins) + +#### ERC1155MixinImpl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155MixinImpl] + +- [`ERC1155Impl`](#ERC1155Component-Embeddable-Impls-ERC1155Impl) +- [`ERC1155MetadataURIImpl`](#ERC1155Component-Embeddable-Impls-ERC1155MetadataURIImpl) +- [`ERC1155CamelImpl`](#ERC1155Component-Embeddable-Impls-ERC1155CamelImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls-SRC5Impl) + +Embeddable Implementations + +#### ERC1155Impl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155Impl] + +- [`balance_of(self, account, token_id)`](#ERC1155Component-balance_of) +- [`balance_of_batch(self, accounts, token_ids)`](#ERC1155Component-balance_of_batch) +- [`safe_transfer_from(self, from, to, token_id, value, data)`](#ERC1155Component-safe_transfer_from) +- [`safe_batch_transfer_from(self, from, to, token_ids, values, data)`](#ERC1155Component-safe_batch_transfer_from) +- [`set_approval_for_all(self, operator, approved)`](#ERC1155Component-set_approval_for_all) +- [`is_approved_for_all(self, owner, operator)`](#ERC1155Component-is_approved_for_all) + +#### ERC1155MetadataURIImpl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155MetadataURIImpl] + +- [`uri(self, token_id)`](#ERC1155Component-uri) + +#### ERC1155CamelImpl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155CamelImpl] + +- [`balanceOf(self, account, tokenId)`](#ERC1155Component-balanceOf) +- [`balanceOfBatch(self, accounts, tokenIds)`](#ERC1155Component-balanceOfBatch) +- [`safeTransferFrom(self, from, to, tokenId, value, data)`](#ERC1155Component-safeTransferFrom) +- [`safeBatchTransferFrom(self, from, to, tokenIds, values, data)`](#ERC1155Component-safeBatchTransferFrom) +- [`setApprovalForAll(self, operator, approved)`](#ERC1155Component-setApprovalForAll) +- [`isApprovedForAll(self, owner, operator)`](#ERC1155Component-isApprovedForAll) + +Internal Functions + +#### InternalImpl [!toc] [#ERC1155Component-InternalImpl] + +- [`initializer(self, base_uri)`](#ERC1155Component-initializer) +- [`initializer_no_metadata(self)`](#ERC1155Component-initializer_no_metadata) +- [`mint_with_acceptance_check(self, to, token_id, value, data)`](#ERC1155Component-mint_with_acceptance_check) +- [`batch_mint_with_acceptance_check(self, to, token_ids, values, data)`](#ERC1155Component-batch_mint_with_acceptance_check) +- [`burn(self, from, token_id, value)`](#ERC1155Component-burn) +- [`batch_burn(self, from, token_ids, values)`](#ERC1155Component-batch_burn) +- [`update_with_acceptance_check(self, from, to, token_ids, values, data)`](#ERC1155Component-update_with_acceptance_check) +- [`update(self, from, to, token_ids, values)`](#ERC1155Component-update) +- [`_set_base_uri(self, base_uri)`](#ERC1155Component-_set_base_uri) + +Events + +IERC1155 + +- [`TransferSingle(operator, from, to, id, value)`](#ERC1155Component-TransferSingle) +- [`TransferBatch(operator, from, to, ids, values)`](#ERC1155Component-TransferBatch) +- [`ApprovalForAll(owner, operator, approved)`](#ERC1155Component-ApprovalForAll) +- [`URI(value, id)`](#ERC1155Component-URI) + +Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC1155Component is expected to provide an implementation of the ERC1155HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. + +You can use `openzeppelin_token::erc1155::ERC1155HooksEmptyImpl` which is already available as part of the library for this purpose. + + +Function executed at the beginning of the [update](#ERC1155Component-update) function prior to any other logic. + + + +Function executed at the end of the [update](#ERC1155Component-update) function. + + +#### Embeddable functions [!toc] [#embeddable_functions] + + +Returns the amount of `token_id` tokens owned by `account`. + + + +Returns a list of balances derived from the `accounts` and `token_ids` pairs. + +Requirements: + +- `token_ids` and `accounts` must have the same length. + + + +Transfers ownership of `value` amount of `token_id` from `from` if `to` is either an account or `IERC1155Receiver`. + +`data` is additional data, it has no specified format and it is passed to `to`. + +This function can potentially allow a reentrancy attack when transferring tokens to an untrusted contract, when invoking `on_ERC1155_received` on the receiver. Ensure to follow the checks-effects-interactions pattern and consider employing reentrancy guards when interacting with untrusted contracts. + +Requirements: + +- Caller is either approved or the `token_id` owner. +- `from` is not the zero address. +- `to` is not the zero address. +- If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_received` and return the required magic value. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. + + + +Transfers ownership of `values` and `token_ids` pairs from `from` if `to` is either an account or `IERC1155Receiver`. + +`data` is additional data, it has no specified format and it is passed to `to`. + +This function can potentially allow a reentrancy attack when transferring tokens to an untrusted contract, when invoking `on_ERC1155_batch_received` on the receiver. Ensure to follow the checks-effects-interactions pattern and consider employing reentrancy guards when interacting with untrusted contracts. + +Requirements: + +- Caller is either approved or the `token_id` owner. +- `from` is not the zero address. +- `to` is not the zero address. +- `token_ids` and `values` must have the same length. +- If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` and return the acceptance magic value. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. + + + +Enables or disables approval for `operator` to manage all of the callers assets. + +Requirements: + +- `operator` cannot be the caller. + +Emits an [ApprovalForAll](#ERC1155Component-ApprovalForAll) event. + + + +Queries if `operator` is an authorized operator for `owner`. + + + + +This implementation returns the same URI for **all** token types. It relies on the token type ID substitution mechanism [specified in the EIP](https://eips.ethereum.org/EIPS/eip-1155#metadata). + +Clients calling this function must replace the `id` substring with the actual token type ID. + + + +See [ERC1155Component::balance\_of](#ERC1155Component-balance_of). + + + +See [ERC1155Component::balance\_of\_batch](#ERC1155Component-balance_of_batch). + + + +See [ERC1155Component::safe\_transfer\_from](#ERC1155Component-safe_transfer_from). + + + +See [ERC1155Component::safe\_batch\_transfer\_from](#ERC1155Component-safe_batch_transfer_from). + + + +See [ERC1155Component::set\_approval\_for\_all](#ERC1155Component-set_approval_for_all). + + + +See [ERC1155Component::is\_approved\_for\_all](#ERC1155Component-is_approved_for_all). + + +#### Internal functions [!toc] [#internal_functions] + + +Initializes the contract by setting the token's base URI as `base_uri`, and registering the supported interfaces. This should only be used inside the contract's constructor. + +Most ERC1155 contracts expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface which is what this initializer is meant to support. If the contract DOES NOT expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface, meaning tokens do not have a URI, the contract must instead use [initializer\_no\_metadata](#ERC1155Component-initializer_no_metadata) in the constructor. Failure to abide by these instructions can lead to unexpected issues especially with UIs. + + + +Initializes the contract with no metadata by registering only the IERC1155 interface. + +This initializer should ONLY be used during construction in the very specific instance when the contract does NOT expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface. Initializing a contract with this initializer means that tokens will not have a URI. + + + +Creates a `value` amount of tokens of type `token_id`, and assigns them to `to`. + +Requirements: + +- `to` cannot be the zero address. +- If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_received` and return the acceptance magic value. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. + + + +Batched version of [mint\_with\_acceptance\_check](#ERC1155Component-mint_with_acceptance_check). + +Requirements: + +- `to` cannot be the zero address. +- `token_ids` and `values` must have the same length. +- If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` and return the acceptance magic value. + +Emits a [TransferBatch](#ERC1155Component-TransferBatch) event. + + + +Destroys a `value` amount of tokens of type `token_id` from `from`. + +Requirements: + +- `from` cannot be the zero address. +- `from` must have at least `value` amount of tokens of type `token_id`. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. + + + +Batched version of [burn](#ERC1155Component-burn). + +Requirements: + +- `from` cannot be the zero address. +- `from` must have at least `value` amount of tokens of type `token_id`. +- `token_ids` and `values` must have the same length. + +Emits a [TransferBatch](#ERC1155Component-TransferBatch) event. + + + +Version of `update` that performs the token acceptance check by calling `onERC1155Received` or `onERC1155BatchReceived` in the receiver if it implements `IERC1155Receiver`, otherwise by checking if it is an account. + +Requirements: + +- `to` is either an account contract or supports the `IERC1155Receiver` interface. +- `token_ids` and `values` must have the same length. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. + + + +Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from` (or `to`) is the zero address. + +Requirements: + +- `token_ids` and `values` must have the same length. + +Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. + +This function can be extended using the [ERC1155HooksTrait](#ERC1155Component-ERC1155HooksTrait), to add functionality before and/or after the transfer, mint, or burn. + +The ERC1155 acceptance check is not performed in this function. See [update\_with\_acceptance\_check](#ERC1155Component-update_with_acceptance_check) instead. + + + +Sets a new URI for all token types, by relying on the token type ID substitution mechanism [specified in the EIP](https://eips.ethereum.org/EIPS/eip-1155#metadata). + +By this mechanism, any occurrence of the `id` substring in either the URI or any of the values in the JSON file at said URI will be replaced by clients with the token type ID. + +For example, the `https://token-cdn-domain/\id\.json` URI would be interpreted by clients as `https://token-cdn-domain/000000000000...000000000000004cce0.json` for token type ID `0x4cce0`. + +Because these URIs cannot be meaningfully represented by the `URI` event, this function emits no events. + + +#### Events [!toc] [#events_2] + + +See [IERC1155::TransferSingle](#IERC1155-TransferSingle). + + + +See [IERC1155::TransferBatch](#IERC1155-TransferBatch). + + + +See [IERC1155::ApprovalForAll](#IERC1155-ApprovalForAll). + + + + +See [IERC1155::URI](#IERC1155-URI). + + +### `ERC1155ReceiverComponent` [toc] [#ERC1155ReceiverComponent] + + + +```rust +use openzeppelin_token::erc1155::ERC1155ReceiverComponent; +``` + +ERC1155Receiver component implementing [IERC1155Receiver](#IERC1155Receiver). + +Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. + +[Embeddable Mixin Implementations](../components#mixins) + +#### ERC1155MixinImpl [!toc] [#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155MixinImpl] + +- [`ERC1155ReceiverImpl`](#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverImpl) +- [`ERC1155ReceiverCamelImpl`](#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverCamelImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls-SRC5Impl) + +Embeddable Implementations + +#### ERC1155ReceiverImpl [!toc] [#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverImpl] + +- [`on_erc1155_received(self, operator, from, token_id, value, data)`](#ERC1155ReceiverComponent-on_erc1155_received) +- [`on_erc1155_batch_received(self, operator, from, token_ids, values, data)`](#ERC1155ReceiverComponent-on_erc1155_batch_received) + +#### ERC1155ReceiverCamelImpl [!toc] [#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverCamelImpl] + +- [`onERC1155Received(self, operator, from, tokenId, value, data)`](#ERC1155ReceiverComponent-onERC1155Received) +- [`onERC1155BatchReceived(self, operator, from, tokenIds, values, data)`](#ERC1155ReceiverComponent-onERC1155BatchReceived) + +Internal Functions + +#### InternalImpl [!toc] [#ERC1155ReceiverComponent-InternalImpl] + +- [`initializer(self)`](#ERC1155ReceiverComponent-initializer) + +#### Embeddable functions [!toc] [#embeddable_functions_2] + + +Returns the `IERC1155Receiver` interface ID. + + + +Returns the `IERC1155Receiver` interface ID. + + + +See [ERC1155ReceiverComponent::on\_erc1155\_received](#ERC1155ReceiverComponent-on_erc1155_received). + + + +See [ERC1155ReceiverComponent::on\_erc1155\_batch\_received](#ERC1155ReceiverComponent-on_erc1155_batch_received). + + +#### Internal functions [!toc] [#internal_functions_2] + + +Registers the `IERC1155Receiver` interface ID as supported through introspection. + + +## [](#presets)Presets + +### `ERC1155Upgradeable` [toc] [#ERC1155Upgradeable] + + + +```rust +use openzeppelin_presets::ERC1155; +``` + +Upgradeable ERC1155 contract leveraging [ERC1155Component](#ERC1155Component). + +[Sierra class hash](../presets) + +```text +{{ERC1155UpgradeableClassHash}} +``` + +Constructor + +- [`constructor(self, base_uri, recipient, token_ids, values, owner)`](#ERC1155Upgradeable-constructor) + +Embedded Implementations + +ERC1155Component + +- [`ERC1155MixinImpl`](#ERC1155Component-Embeddable-Mixin-Impl) + +OwnableMixinImpl + +- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#ERC1155Upgradeable-upgrade) + +#### Constructor [!toc] [#ERC1155Upgradeable-constructor-section] + + +Sets the `base_uri` for all tokens and registers the supported interfaces. Mints the `values` for `token_ids` tokens to `recipient`. Assigns `owner` as the contract owner with permissions to upgrade. + +Requirements: + +- `to` is either an account contract (supporting ISRC6) or supports the `IERC1155Receiver` interface. +- `token_ids` and `values` must have the same length. + + +#### External Functions [!toc] [#ERC1155Upgradeable-external-functions] + + +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. + diff --git a/content/contracts-cairo/3.x/api/erc20.mdx b/content/contracts-cairo/3.x/api/erc20.mdx new file mode 100644 index 00000000..08678751 --- /dev/null +++ b/content/contracts-cairo/3.x/api/erc20.mdx @@ -0,0 +1,1580 @@ +--- +title: ERC20 +--- + +This module provides interfaces, presets, and utilities related to ERC20 contracts. + +For an overview of ERC20, read our [ERC20 guide](../erc20). + +## Interfaces + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +### `IERC20` [toc] [#IERC20] + + +```rust +use openzeppelin_token::erc20::interface::IERC20; +``` + +Interface of the IERC20 standard as defined in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). + +Functions + +- [`total_supply()`](#IERC20-total_supply) +- [`balance_of(account)`](#IERC20-balance_of) +- [`allowance(owner, spender)`](#IERC20-allowance) +- [`transfer(recipient, amount)`](#IERC20-transfer) +- [`transfer_from(sender, recipient, amount)`](#IERC20-transfer_from) +- [`approve(spender, amount)`](#IERC20-approve) + +Events + +- [`Transfer(from, to, value)`](#IERC20-Transfer) +- [`Approval(owner, spender, value)`](#IERC20-Approval) + +#### Functions [!toc] [#IERC20-Functions] + + +Returns the amount of tokens in existence. + + + +Returns the amount of tokens owned by `account`. + + + +Returns the remaining number of tokens that `spender` is allowed to spend on behalf of `owner` through [transfer\_from](#transfer_from). This is zero by default. + +This value changes when [approve](#IERC20-approve) or [transfer\_from](#IERC20-transfer_from) are called. + + + +Moves `amount` tokens from the caller's token balance to `to`. Returns `true` on success, reverts otherwise. + +Emits a [Transfer](#IERC20-Transfer) event. + + + +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](#IERC20-Transfer) event. + + + +Sets `amount` as the allowance of `spender` over the caller's tokens. Returns `true` on success, reverts otherwise. + +Emits an [Approval](#ERC20-Approval) event. + + +#### Events [!toc] [#IERC20-Events] + + +Emitted when `value` tokens are moved from one address (`from`) to another (`to`). + +Note that `value` may be zero. + + + +Emitted when the allowance of a `spender` for an `owner` is set. `value` is the new allowance. + + +### `IERC20Metadata` [toc] [#IERC20Metadata] + + +```rust +use openzeppelin_token::erc20::interface::IERC20Metadata; +``` + +Interface for the optional metadata functions in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). + +Functions + +- [`name()`](#IERC20Metadata-name) +- [`symbol()`](#IERC20Metadata-symbol) +- [`decimals()`](#IERC20Metadata-decimals) + +#### Functions [!toc] [#IERC20Metadata-Functions] + + +Returns the name of the token. + + + +Returns the ticker symbol of the token. + + + +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](../erc20#customizing_decimals). + +This information is only used for *display* purposes: it in no way affects any of the arithmetic of the contract. + + +### `IERC20Permit` [toc] [#IERC20Permit] + + +```rust +use openzeppelin_token::erc20::interface::IERC20Permit; +``` + +Interface of the ERC20Permit standard to support gasless token approvals as defined in [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612). + +Functions + +- [`permit(owner, spender, amount, deadline, signature)`](#IERC20Permit-permit) +- [`nonces(owner)`](#IERC20Permit-nonces) +- [`DOMAIN_SEPARATOR()`](#IERC20Permit-DOMAIN_SEPARATOR) + +#### Functions [!toc] [#IERC20Permit-Functions] + + +Sets `amount` as the allowance of `spender` over `owner`'s tokens after validating the signature. + + + +Returns the current nonce of `owner`. A nonce value must be included whenever a signature for `permit` call is generated. + + + +Returns the domain separator used in generating a message hash for `permit` signature. The domain hashing logic follows the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard. + + +### `IERC4626` [toc] [#IERC4626] + + +```rust +use openzeppelin_token::erc20::extensions::erc4626::interface::IERC4626; +``` + +Interface of the IERC4626 standard as defined in [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626). + +Functions + +- [`asset()`](#IERC4626-asset) +- [`total_assets()`](#IERC4626-total_assets) +- [`convert_to_shares(assets)`](#IERC4626-convert_to_shares) +- [`convert_to_assets(shares)`](#IERC4626-convert_to_assets) +- [`max_deposit(receiver)`](#IERC4626-max_deposit) +- [`preview_deposit(assets)`](#IERC4626-preview_deposit) +- [`deposit(assets, receiver)`](#IERC4626-deposit) +- [`max_mint(receiver)`](#IERC4626-max_mint) +- [`preview_mint(shares)`](#IERC4626-preview_mint) +- [`mint(shares, receiver)`](#IERC4626-mint) +- [`max_withdraw(owner)`](#IERC4626-max_withdraw) +- [`preview_withdraw(assets)`](#IERC4626-preview_withdraw) +- [`withdraw(assets, receiver, owner)`](#IERC4626-withdraw) +- [`max_redeem(owner)`](#IERC4626-max_redeem) +- [`preview_redeem(shares)`](#IERC4626-preview_redeem) +- [`redeem(shares, receiver, owner)`](#IERC4626-redeem) + +Events + +- [`Deposit(sender, owner, assets, shares)`](#IERC4626-Deposit) +- [`Withdraw(sender, receiver, owner, assets, shares)`](#IERC4626-Withdraw) + +#### Functions [!toc] [#IERC4626-Functions] + + +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. + + + +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. + + + +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. + + + +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. + + + +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. + + + +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](#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](#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](#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. + + + +Mints Vault shares to `receiver` by depositing exactly amount of `assets`. + +Requirements: + +- MUST emit the [IERC4626::Deposit](#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. + + + +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. + + + +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](#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](#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](#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. + + + +Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. + +Requirements: + +- MUST emit the [IERC4626::Deposit](#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. + + + +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. + + + +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](#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](#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](#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. + + + +Burns shares from owner and sends exactly assets of underlying tokens to receiver. + +Requirements: + +- MUST emit the [IERC4626::Withdraw](#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. + + + +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. + + + +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](#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](#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](#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. + + + +Burns exactly shares from owner and sends assets of underlying tokens to receiver. + +Requirements: + +- MUST emit the [IERC4626::Withdraw](#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 [!toc] [#IERC4626-Events] + + +Emitted when `sender` exchanges `assets` for `shares` and transfers those `shares` to `owner`. + + + +Emitted when `sender` exchanges `shares`, owned by `owner`, for `assets` and transfers those `assets` to `receiver`. + + +## Core + +### `ERC20Component` [toc] [#ERC20Component] + + +```rust +use openzeppelin_token::erc20::ERC20Component; +``` + +ERC20 component extending [IERC20](#IERC20) and [IERC20Metadata](#IERC20Metadata). + +See [Hooks](#ERC20Component-Hooks) to understand how are hooks used. + +Hooks + +#### ERC20HooksTrait [!toc] [#ERC20Component-ERC20HooksTrait] + +- [`before_update(self, from, recipient, amount)`](#ERC20Component-before_update) +- [`after_update(self, from, recipient, amount)`](#ERC20Component-after_update) + +[Embeddable Mixin Implementations](../components#mixins) + +#### ERC20MixinImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20MixinImpl] + +- [`ERC20Impl`](#ERC20Component-Embeddable-Impls-ERC20Impl) +- [`ERC20MetadataImpl`](#ERC20Component-Embeddable-Impls-ERC20MetadataImpl) +- [`ERC20CamelOnlyImpl`](#ERC20Component-Embeddable-Impls-ERC20CamelOnlyImpl) + +Embeddable Implementations + +#### ERC20Impl [!toc] [#ERC20Component-Embeddable-Impls-ERC20Impl] + +- [`total_supply(self)`](#ERC20Component-total_supply) +- [`balance_of(self, account)`](#ERC20Component-balance_of) +- [`allowance(self, owner, spender)`](#ERC20Component-allowance) +- [`transfer(self, recipient, amount)`](#ERC20Component-transfer) +- [`transfer_from(self, sender, recipient, amount)`](#ERC20Component-transfer_from) +- [`approve(self, spender, amount)`](#ERC20Component-approve) + +#### ERC20MetadataImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20MetadataImpl] + +- [`name(self)`](#ERC20Component-name) +- [`symbol(self)`](#ERC20Component-symbol) +- [`decimals(self)`](#ERC20Component-decimals) + +#### ERC20CamelOnlyImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20CamelOnlyImpl] + +- [`totalSupply(self)`](#ERC20Component-totalSupply) +- [`balanceOf(self, account)`](#ERC20Component-balanceOf) +- [`transferFrom(self, sender, recipient, amount)`](#ERC20Component-transferFrom) + +#### ERC20PermitImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20PermitImpl] + +- [`permit(self, owner, spender, amount, deadline, signature)`](#ERC20Component-permit) +- [`nonces(self, owner)`](#ERC20Component-nonces) +- [`DOMAIN_SEPARATOR(self)`](#ERC20Component-DOMAIN_SEPARATOR) + +#### SNIP12MetadataExternalImpl [!toc] [#ERC20Component-Embeddable-Impls-SNIP12MetadataExternalImpl] + +- [`snip12_metadata(self)`](#ERC20Component-snip12_metadata) + +Internal implementations + +#### InternalImpl [!toc] [#ERC20Component-InternalImpl] + +- [`initializer(self, name, symbol)`](#ERC20Component-initializer) +- [`mint(self, recipient, amount)`](#ERC20Component-mint) +- [`burn(self, account, amount)`](#ERC20Component-burn) +- [`update(self, from, to, amount)`](#ERC20Component-update) +- [`_transfer(self, sender, recipient, amount)`](#ERC20Component-_transfer) +- [`_approve(self, owner, spender, amount)`](#ERC20Component-_approve) +- [`_spend_allowance(self, owner, spender, amount)`](#ERC20Component-_spend_allowance) + +Events + +- [`Transfer(from, to, value)`](#ERC20Component-Transfer) +- [`Approval(owner, spender, value)`](#ERC20Component-Approval) + +#### Hooks [!toc] [#ERC20Component-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. + + +Function executed at the beginning of the [update](#ERC20Component-update) function prior to any other logic. + + + +Function executed at the end of the [update](#ERC20Component-update) function. + + +#### Embeddable functions [!toc] [#ERC20Component-Embeddable-Functions] + + +See [IERC20::total\_supply](#IERC20-total_supply). + + + +See [IERC20::balance\_of](#IERC20-balance_of). + + + +See [IERC20::allowance](#IERC20-allowance). + + + +See [IERC20::transfer](#IERC20-transfer). + +Requirements: + +- `recipient` cannot be the zero address. +- The caller must have a balance of at least `amount`. + + + +See [IERC20::transfer\_from](#IERC20-transfer_from). + +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`. + + + +See [IERC20::approve](#IERC20-approve). + +Requirements: + +- `spender` cannot be the zero address. + + + +See [IERC20Metadata::name](#IERC20Metadata-name). + + + +See [IERC20Metadata::symbol](#IERC20Metadata-symbol). + + + +See [IERC20Metadata::decimals](#IERC20Metadata-decimals). + + + +See [IERC20::total\_supply](#IERC20-total_supply). + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). + + + +See [IERC20::balance\_of](#IERC20-balance_of). + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). + + + +See [IERC20::transfer\_from](#IERC20-transfer_from). + +Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). + + + +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](#ERC20-Approval) event. Every successful call increases \`owner's nonce by one. + + + +Returns the current nonce of `owner`. A nonce value must be included whenever a signature for `permit` call is generated. + + + +Returns the domain separator used in generating a message hash for `permit` signature. The domain hashing logic follows the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard. + + + +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](./utilities#snip12) implementation. +- `t.1`: The version used in the [SNIP12Metadata](./utilities#snip12) implementation. + + +#### Internal functions [!toc] [#ERC20Component-Internal-Functions] + + +Initializes the contract by setting the token name and symbol. This should be used inside of the contract's constructor. + + + +Creates an `amount` number of tokens and assigns them to `recipient`. + +Emits a [Transfer](#ERC20Component-Transfer) event with `from` being the zero address. + +Requirements: + +- `recipient` cannot be the zero address. + + + +Destroys `amount` number of tokens from `account`. + +Emits a [Transfer](#ERC20Component-Transfer) event with `to` set to the zero address. + +Requirements: + +- `account` cannot be the zero address. + + + +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](#ERC20Component-ERC20HooksTrait), to add functionality before and/or after the transfer, mint, or burn. + +Emits a [Transfer](#ERC20Component-Transfer) event. + + + +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](#ERC20Component-Transfer) event. + +Requirements: + +- `from` cannot be the zero address. +- `to` cannot be the zero address. +- `from` must have a balance of at least `amount`. + + + +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](#ERC20Component-Approval) event. + +Requirements: + +- `owner` cannot be the zero address. +- `spender` cannot be the zero address. + + + +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](#ERC20Component-Approval) event. + + +#### Events [!toc] [#ERC20Component-Events] + + +See [IERC20::Transfer](#IERC20-Transfer). + + + +See [IERC20::Approval](#IERC20-Approval). + + +## Extensions + +### `ERC4626Component` [toc] [#ERC4626Component] + + +```rust +use openzeppelin_token::erc20::extensions::erc4626::ERC4626Component; +``` + +Extension of ERC20 that implements the [IERC4626](#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 Component Config](../components#immutable-config) + +#### constants [!toc] [#ERC4626Component-constants] + +- [`UNDERLYING_DECIMALS`](#ERC4626Component-IC-UNDERLYING_DECIMALS) +- [`DECIMALS_OFFSET`](#ERC4626Component-IC-DECIMALS_OFFSET) + +#### functions [!toc] [#ERC4626Component-functions] + +- [`validate()`](#ERC4626Component-IC-validate) + +Hooks + +#### FeeConfigTrait [!toc] [#ERC4626Component-FeeConfigTrait] + +- [`calculate_deposit_fee(self, assets, shares)`](#ERC4626Component-calculate_deposit_fee) +- [`calculate_mint_fee(self, assets, shares)`](#ERC4626Component-calculate_mint_fee) +- [`calculate_withdraw_fee(self, assets, shares)`](#ERC4626Component-calculate_withdraw_fee) +- [`calculate_redeem_fee(self, assets, shares)`](#ERC4626Component-calculate_redeem_fee) + +#### LimitConfigTrait [!toc] [#ERC4626Component-LimitConfigTrait] + +- [`deposit_limit(self, receiver)`](#ERC4626Component-deposit_limit) +- [`mint_limit(self, receiver)`](#ERC4626Component-mint_limit) +- [`withdraw_limit(self, owner)`](#ERC4626Component-withdraw_limit) +- [`redeem_limit(self, owner)`](#ERC4626Component-redeem_limit) + +#### ERC4626HooksTrait [!toc] [#ERC4626Component-ERC4626HooksTrait] + +- [`before_deposit(self, caller, receiver, assets, shares, fee)`](#ERC4626Component-before_deposit) +- [`after_deposit(self, caller, receiver, assets, shares, fee)`](#ERC4626Component-after_deposit) +- [`before_withdraw(self, caller, receiver, owner, assets, shares, fee)`](#ERC4626Component-before_withdraw) +- [`after_withdraw(self, caller, receiver, owner, assets, shares, fee)`](#ERC4626Component-after_withdraw) + +#### AssetsManagementTrait [!toc] [#ERC4626Component-AssetsManagementTrait] + +- [`get_total_assets(self)`](#ERC4626Component-get_total_assets) +- [`transfer_assets_in(self, from, assets)`](#ERC4626Component-transfer_assets_in) +- [`transfer_assets_out(self, to, assets)`](#ERC4626Component-transfer_assets_out) + +Embeddable Implementations + +#### ERC4626Impl [!toc] [#ERC4626Component-Embeddable-Impls-ERC4626Impl] + +- [`asset(self)`](#ERC4626Component-asset) +- [`total_assets(self)`](#ERC4626Component-total_assets) +- [`convert_to_shares(self, assets)`](#ERC4626Component-convert_to_shares) +- [`convert_to_assets(self, shares)`](#ERC4626Component-convert_to_assets) +- [`max_deposit(self, receiver)`](#ERC4626Component-max_deposit) +- [`preview_deposit(self, assets)`](#ERC4626Component-preview_deposit) +- [`deposit(self, assets, receiver)`](#ERC4626Component-deposit) +- [`max_mint(self, receiver)`](#ERC4626Component-max_mint) +- [`preview_mint(self, shares)`](#ERC4626Component-preview_mint) +- [`mint(self, shares, receiver)`](#ERC4626Component-mint) +- [`max_withdraw(self, owner)`](#ERC4626Component-max_withdraw) +- [`preview_withdraw(self, assets)`](#ERC4626Component-preview_withdraw) +- [`withdraw(self, assets, receiver, owner)`](#ERC4626Component-withdraw) +- [`max_redeem(self, owner)`](#ERC4626Component-max_redeem) +- [`preview_redeem(self, shares)`](#ERC4626Component-preview_redeem) +- [`redeem(self, shares, receiver, owner)`](#ERC4626Component-redeem) + +#### ERC20Impl [!toc] [#ERC4626Component-Embeddable-Impls-ERC20Impl] + +- [`total_supply(self)`](#ERC20Component-total_supply) +- [`balance_of(self, account)`](#ERC20Component-balance_of) +- [`allowance(self, owner, spender)`](#ERC20Component-allowance) +- [`transfer(self, recipient, amount)`](#ERC20Component-transfer) +- [`transfer_from(self, sender, recipient, amount)`](#ERC20Component-transfer_from) +- [`approve(self, spender, amount)`](#ERC20Component-approve) + +#### ERC4626MetadataImpl [!toc] [#ERC4626Component-Embeddable-Impls-ERC4626MetadataImpl] + +- [`name(self)`](#ERC4626Component-name) +- [`symbol(self)`](#ERC4626Component-symbol) +- [`decimals(self)`](#ERC4626Component-decimals) + +Internal functions + +#### InternalImpl [!toc] [#ERC4626Component-InternalImpl] + +- [`initializer(self, asset_address)`](#ERC4626Component-initializer) +- [`_deposit(self, caller, receiver, assets, shares)`](#ERC4626Component-_deposit) +- [`_withdraw(self, caller, receiver, owner, assets, shares)`](#ERC4626Component-_withdraw) +- [`_convert_to_shares(self, assets, rounding)`](#ERC4626Component-_convert_to_shares) +- [`_convert_to_assets(self, shares, rounding)`](#ERC4626Component-_convert_to_assets) + +#### Immutable Config [!toc] [#ERC4626Component-Immutable-Config] + + +Should match the underlying asset's decimals. The default value is `18`. + + + +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. + + + +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 [!toc] [#ERC4626Component-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 [!toc] [#ERC4626Component-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](https://eips.ethereum.org/EIPS/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](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L253) +- Contract charging fees in shares: [ERC4626SharesFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L426) + + +Calculates the entry fee for a deposit during [preview\_deposit](#ERC4626Component-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](#ERC4626Component-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. + + + +Calculates the entry fee for a mint during [preview\_mint](#ERC4626Component-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](#ERC4626Component-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. + + + +Calculates the exit fee for a withdraw during [preview\_withdraw](#ERC4626Component-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](#ERC4626Component-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. + + + +Calculates the exit fee for a redeem during [preview\_redeem](#ERC4626Component-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](#ERC4626Component-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 [!toc] [#ERC4626Component-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_` i.e. `deposit_limit` → `max_deposit`. + +The [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec states that the `max_` 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. + + +The max deposit allowed. + +Defaults (`Option::None`) to 2 \** 256 - 1. + + + +The max mint allowed. + +Defaults (`Option::None`) to 2 \** 256 - 1. + + + +The max withdraw allowed. + +Defaults (`Option::None`) to the full asset balance of `owner` converted from shares. + + + +The max redeem allowed. + +Defaults (`Option::None`) to the full asset balance of `owner`. + + +#### ERC4626HooksTrait [!toc] [#ERC4626Component-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](#ERC4626Component-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](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L253) and [ERC4626SharesFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L426) examples. + + +Hooks into [\_deposit](#ERC4626Component-_deposit). + +Executes logic before transferring assets and minting shares. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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. + + + +Hooks into [\_deposit](#ERC4626Component-_deposit). + +Executes logic after transferring assets and minting shares. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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. + + + +Hooks into [\_withdraw](#ERC4626Component-_withdraw). + +Executes logic before burning shares and transferring assets. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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. + + + +Hooks into [\_withdraw](#ERC4626Component-_withdraw). + +Executes logic after burning shares and transferring assets. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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 [!toc] [#ERC4626Component-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: + +- Self-managed vault: [ERC4626SelfAssetsManagement](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/token/src/erc20/extensions/erc4626/erc4626.cairo#L760). +- External vault: [ERC4626ExternalAssetsManagement](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L92). + + +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. + + + +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. + + + +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 [!toc] [#ERC4626Component-Embeddable-Functions] + + +Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. + + + +Returns the total amount of the underlying asset that is "managed" by Vault. + + + +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](https://eips.ethereum.org/EIPS/eip-4626) spec, this may panic *only* if there's an overflow from an unreasonably large input. + + + +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](https://eips.ethereum.org/EIPS/eip-4626) spec, this may panic *only* if there's an overflow from an unreasonably large input. + + + +Returns the maximum amount of the underlying asset that can be deposited into the Vault for the `receiver`, through a [deposit](#ERC4626Component-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](#ERC4626Component-deposit_limit). + + + +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](#ERC4626Component-calculate_deposit_fee). + +This method must be inclusive of entry fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + + + +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](#IERC4626-Deposit) event. + + + +Returns the maximum amount of the Vault shares that can be minted for `receiver` through a [mint](#ERC4626Component-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](#ERC4626Component-mint_limit). + + + +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](#ERC4626Component-calculate_mint_fee). + +This method must be inclusive of entry fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + + + +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](#IERC4626-Deposit) event. + + + +Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a [withdraw](#ERC4626Component-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](#ERC4626Component-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. + + + +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](#ERC4626Component-calculate_withdraw_fee). + +This method must be inclusive of exit fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + + + +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](#IERC4626-Withdraw) event. + + + +Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a [redeem](#ERC4626Component-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](#ERC4626Component-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. + + + +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](#ERC4626Component-calculate_redeem_fee). + +This method must be inclusive of exit fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. + + + +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](#IERC4626-Withdraw) event. + + + +Returns the name of the token. + + + +Returns the ticker symbol of the token, usually a shorter version of the name. + + + +Returns the cumulative number of decimals which includes both `UNDERLYING_DECIMALS` and `OFFSET_DECIMALS`. Both of which must be defined in the [ImmutableConfig](#ERC4626Component-Immutable-Config) inside the implementing contract. + + +#### Internal functions [!toc] [#ERC4626Component-Internal-Functions] + + +Validates the [ImmutableConfig](#ERC4626Component-Immutable-Config) 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. + + + +Internal logic for [deposit](#ERC4626Component-deposit) and [mint](#ERC4626Component-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](#ERC20Component-transfer_from) must return true. + +Emits two [ERC20::Transfer](#ERC20Component-Transfer) events (`ERC20::mint` and `ERC20::transfer_from`). + +Emits a [Deposit](#IERC4626-Deposit) event. + + + +Internal logic for [withdraw](#ERC4626Component-withdraw) and [redeem](#ERC4626Component-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](#ERC20Component-transfer) must return true. + +Emits two [ERC20::Transfer](#ERC20Component-Transfer) events (`ERC20::burn` and `ERC20::transfer`). + +Emits a [Withdraw](#IERC4626-Withdraw) event. + + + +Internal conversion function (from assets to shares) with support for `rounding` direction. + + + +Internal conversion function (from shares to assets) with support for `rounding` direction. + + +## Presets + +### `ERC20Upgradeable` [toc] [#ERC20Upgradeable] + + +```rust +use openzeppelin_presets::ERC20Upgradeable; +``` + +Upgradeable ERC20 contract leveraging [ERC20Component](#ERC20Component) with a fixed-supply mechanism for token distribution. + +[Sierra class hash](../presets) + +```text +{{ERC20UpgradeableClassHash}} +``` + +Constructor + +- [`constructor(self, name, symbol, fixed_supply, recipient, owner)`](#ERC20Upgradeable-constructor) + +Embedded Implementations + +#### ERC20MixinImpl [!toc] [#ERC20Upgradeable-Embedded-Impls-ERC20MixinImpl] + +- [`ERC20MixinImpl`](#ERC20Component-Embeddable-Mixin-Impl) + +#### OwnableMixinImpl [!toc] [#ERC20Upgradeable-Embedded-Impls-OwnableMixinImpl] + +- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#ERC20Upgradeable-upgrade) + +#### Constructor [!toc] [#ERC20Upgradeable-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 [!toc] [#ERC20Upgradeable-External-Functions] + + +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. + diff --git a/content/contracts-cairo/3.x/api/erc721.mdx b/content/contracts-cairo/3.x/api/erc721.mdx new file mode 100644 index 00000000..9436efa2 --- /dev/null +++ b/content/contracts-cairo/3.x/api/erc721.mdx @@ -0,0 +1,1154 @@ +--- +title: ERC721 +--- + +This module provides interfaces, presets, and utilities related to ERC721 contracts. + +For an overview of ERC721, read our [ERC721 guide](../erc721). + +## Interfaces + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +### `IERC721` [toc] [#IERC721] + + +```rust +use openzeppelin_token::erc721::interface::IERC721; +``` + +Interface of the IERC721 standard as defined in [EIP721](https://eips.ethereum.org/EIPS/eip-721). + +[SRC5 ID](./introspection#ISRC5) + +```text +0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 +``` + +Functions + +- [`balance_of(account)`](#IERC721-balance_of) +- [`owner_of(token_id)`](#IERC721-owner_of) +- [`safe_transfer_from(from, to, token_id, data)`](#IERC721-safe_transfer_from) +- [`transfer_from(from, to, token_id)`](#IERC721-transfer_from) +- [`approve(to, token_id)`](#IERC721-approve) +- [`set_approval_for_all(operator, approved)`](#IERC721-set_approval_for_all) +- [`get_approved(token_id)`](#IERC721-get_approved) +- [`is_approved_for_all(owner, operator)`](#IERC721-is_approved_for_all) + +Events + +- [`Approval(owner, approved, token_id)`](#IERC721-Approval) +- [`ApprovalForAll(owner, operator, approved)`](#IERC721-ApprovalForAll) +- [`Transfer(from, to, token_id)`](#IERC721-Transfer) + +#### Functions [!toc] [#IERC721-Functions] + + +Returns the number of NFTs owned by `account`. + + + +Returns the owner address of `token_id`. + + + +Transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. For information regarding how contracts communicate their awareness of the ERC721 protocol, see [Receiving Tokens](../erc721#receiving_tokens). + +Emits a [Transfer](#IERC721-Transfer) event. + + + +Transfer ownership of `token_id` from `from` to `to`. + +Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. Usage of [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from) prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. + +Emits a [Transfer](#IERC721-Transfer) event. + + + +Change or reaffirm the approved address for an NFT. + +Emits an [Approval](#IERC721-Approval) event. + + + +Enable or disable approval for `operator` to manage all of the caller's assets. + +Emits an [ApprovalForAll](#IERC721-ApprovalForAll) event. + + + +Returns the address approved for `token_id`. + + + +Query if `operator` is an authorized operator for `owner`. + + +#### Events [!toc] [#IERC721-Events] + + +Emitted when `owner` enables `approved` to manage the `token_id` token. + + + +Emitted when `owner` enables or disables `operator` to manage the `token_id` token. + + + +Emitted when `token_id` token is transferred from `from` to `to`. + + +### `IERC721Metadata` [toc] [#IERC721Metadata] + + +```rust +use openzeppelin_token::erc721::interface::IERC721Metadata; +``` + +Interface for the optional metadata functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). + +[SRC5 ID](./introspection#ISRC5) + +```text +0xabbcd595a567dce909050a1038e055daccb3c42af06f0add544fa90ee91f25 +``` + +Functions + +- [`name()`](#IERC721Metadata-name) +- [`symbol()`](#IERC721Metadata-symbol) +- [`token_uri(token_id)`](#IERC721Metadata-token_uri) + +#### Functions [!toc] [#IERC721Metadata-Functions] + + +Returns the NFT name. + + + +Returns the NFT ticker symbol. + + + +Returns the Uniform Resource Identifier (URI) for the `token_id` token. If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. + + +### `IERC721Receiver` [toc] [#IERC721Receiver] + + +```rust +use openzeppelin_token::erc721::interface::IERC721Receiver; +``` + +Interface for contracts that support receiving `safe_transfer_from` transfers. + +[SRC5 ID](./introspection#ISRC5) + +```text +0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc +``` + +Functions + +- [`on_erc721_received(operator, from, token_id, data)`](#IERC721Receiver-on_erc721_received) + +#### Functions [!toc] [#IERC721Receiver-Functions] + + +Whenever an IERC721 `token_id` token is transferred to this non-account contract via [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from) by `operator` from `from`, this function is called. + + +### `IERC721Enumerable` [toc] [#IERC721Enumerable] + + +Interface for the optional enumerable functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). + +[SRC5 ID](./introspection#ISRC5) + +```text +0x16bc0f502eeaf65ce0b3acb5eea656e2f26979ce6750e8502a82f377e538c87 +``` + +Functions + +- [`total_supply()`](#IERC721Enumerable-total_supply) +- [`token_by_index(index)`](#IERC721Enumerable-token_by_index) +- [`token_of_owner_by_index(owner, index)`](#IERC721Enumerable-token_of_owner_by_index) + +#### Functions [!toc] [#IERC721Enumerable-Functions] + + +Returns the total amount of tokens stored by the contract. + + + +Returns a token id at a given `index` of all the tokens stored by the contract. Use along with [IERC721Enumerable::total\_supply](#IERC721Enumerable-total_supply) to enumerate all tokens. + + + +Returns the token id owned by `owner` at a given `index` of its token list. Use along with [IERC721::balance\_of](#IERC721-balance_of) to enumerate all of `owner`'s tokens. + + +## Core + +### `ERC721Component` [toc] [#ERC721Component] + + +```rust +use openzeppelin_token::erc721::ERC721Component; +``` + +ERC721 component implementing [IERC721](#IERC721) and [IERC721Metadata](#IERC721Metadata). + +Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. + +See [Hooks](#ERC721Component-Hooks) to understand how are hooks used. + +Hooks + +#### ERC721HooksTrait [!toc] [#ERC721Component-ERC721HooksTrait] + +- [`before_update(self, to, token_id, auth)`](#ERC721Component-before_update) +- [`after_update(self, to, token_id, auth)`](#ERC721Component-after_update) + +[Embeddable Mixin Implementations](../components#mixins) + +#### ERC721MixinImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721MixinImpl] + +- [`ERC721Impl`](#ERC721Component-Embeddable-Impls-ERC721Impl) +- [`ERC721MetadataImpl`](#ERC721Component-Embeddable-Impls-ERC721MetadataImpl) +- [`ERC721CamelOnlyImpl`](#ERC721Component-Embeddable-Impls-ERC721CamelOnlyImpl) +- [`ERC721MetadataCamelOnlyImpl`](#ERC721Component-Embeddable-Impls-ERC721MetadataCamelOnlyImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +#### ERC721Impl [!toc] [#ERC721Component-Embeddable-Impls-ERC721Impl] + +- [`balance_of(self, account)`](#ERC721Component-balance_of) +- [`owner_of(self, token_id)`](#ERC721Component-owner_of) +- [`safe_transfer_from(self, from, to, token_id, data)`](#ERC721Component-safe_transfer_from) +- [`transfer_from(self, from, to, token_id)`](#ERC721Component-transfer_from) +- [`approve(self, to, token_id)`](#ERC721Component-approve) +- [`set_approval_for_all(self, operator, approved)`](#ERC721Component-set_approval_for_all) +- [`get_approved(self, token_id)`](#ERC721Component-get_approved) +- [`is_approved_for_all(self, owner, operator)`](#ERC721Component-is_approved_for_all) + +#### ERC721MetadataImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721MetadataImpl] + +- [`name(self)`](#ERC721Component-name) +- [`symbol(self)`](#ERC721Component-symbol) +- [`token_uri(self, token_id)`](#ERC721Component-token_uri) + +#### ERC721CamelOnlyImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721CamelOnlyImpl] + +- [`balanceOf(self, account)`](#ERC721Component-balanceOf) +- [`ownerOf(self, tokenId)`](#ERC721Component-ownerOf) +- [`safeTransferFrom(self, from, to, tokenId, data)`](#ERC721Component-safeTransferFrom) +- [`transferFrom(self, from, to, tokenId)`](#ERC721Component-transferFrom) +- [`setApprovalForAll(self, operator, approved)`](#ERC721Component-setApprovalForAll) +- [`getApproved(self, tokenId)`](#ERC721Component-getApproved) +- [`isApprovedForAll(self, owner, operator)`](#ERC721Component-isApprovedForAll) + +#### ERC721MetadataCamelOnlyImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721MetadataCamelOnlyImpl] + +- [`tokenURI(self, tokenId)`](#ERC721Component-tokenURI) + +#### SRC5Impl [!toc] [#ERC721Component-Embeddable-Impls-SRC5Impl] + +- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) + +Internal functions + +#### InternalImpl [!toc] [#ERC721Component-InternalImpl] + +- [`initializer(self, name, symbol, base_uri)`](#ERC721Component-initializer) +- [`initializer_no_metadata(self)`](#ERC721Component-initializer_no_metadata) +- [`exists(self, token_id)`](#ERC721Component-exists) +- [`transfer(self, from, to, token_id)`](#ERC721Component-transfer) +- [`mint(self, to, token_id)`](#ERC721Component-mint) +- [`safe_transfer(self, from, to, token_id, data)`](#ERC721Component-safe_transfer) +- [`safe_mint(self, to, token_id, data)`](#ERC721Component-safe_mint) +- [`burn(self, token_id)`](#ERC721Component-burn) +- [`update(self, to, token_id, auth)`](#ERC721Component-update) +- [`_owner_of(self, token_id)`](#ERC721Component-_owner_of) +- [`_require_owned(self, token_id)`](#ERC721Component-_require_owned) +- [`_approve(self, to, token_id, auth)`](#ERC721Component-_approve) +- [`_approve_with_optional_event(self, to, token_id, auth, emit_event)`](#ERC721Component-_approve_with_optional_event) +- [`_set_approval_for_all(self, owner, operator, approved)`](#ERC721Component-_set_approval_for_all) +- [`_set_base_uri(self, base_uri)`](#ERC721Component-_set_base_uri) +- [`_base_uri(self)`](#ERC721Component-_base_uri) +- [`_is_authorized(self, owner, spender, token_id)`](#ERC721Component-_is_authorized) +- [`_check_authorized(self, owner, spender, token_id)`](#ERC721Component-_check_authorized) + +Events + +IERC721 + +- [`Approval(owner, approved, token_id)`](#ERC721Component-Approval) +- [`ApprovalForAll(owner, operator, approved)`](#ERC721Component-ApprovalForAll) +- [`Transfer(from, to, token_id)`](#ERC721Component-Transfer) + +#### Hooks [!toc] [#ERC721Component-Hooks] + +Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC721Component is expected to provide an implementation of the ERC721HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. + +You can use `openzeppelin_token::erc721::ERC721HooksEmptyImpl` which is already available as part of the library for this purpose. + + +Function executed at the beginning of the [update](#ERC721Component-update) function prior to any other logic. + + + +Function executed at the end of the [update](#ERC721Component-update) function. + + +#### [](#embeddable_functions)Embeddable functions [!toc] [#embeddable_functions] + + +See [IERC721::balance\_of](#IERC721-balance_of). + + + +See [IERC721::owner\_of](#IERC721-owner_of). + +Requirements: + +- `token_id` exists. + + + +See [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from). + +Requirements: + +- Caller is either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. +- `to` is either an account contract or supports the [IERC721Receiver](#IERC721Receiver) interface. + + + +See [IERC721::transfer\_from](#IERC721-transfer_from). + +Requirements: + +- Caller either approved or the `token_id` owner. +- `to` is not the zero address. +- `from` is not the zero address. +- `token_id` exists. + + + +See [IERC721::approve](#IERC721-approve). + +Requirements: + +- The caller is either an approved operator or the `token_id` owner. +- `to` cannot be the token owner or the zero address. +- `token_id` exists. + + + +See [IERC721::set\_approval\_for\_all](#IERC721-set_approval_for_all). + +Requirements: + +- `operator` is not the zero address. + + + +See [IERC721::get\_approved](#IERC721-get_approved). + +Requirements: + +- `token_id` exists. + + + +See [IERC721::is\_approved\_for\_all](#IERC721-is_approved_for_all). + + + +See [IERC721Metadata::name](#IERC721Metadata-name). + + + +See [IERC721Metadata::symbol](#IERC721Metadata-symbol). + + + +Returns the Uniform Resource Identifier (URI) for the `token_id` token. If a base URI is set, the resulting URI for each token will be the concatenation of the base URI and the token ID. For example, the base URI `https://token-cdn-domain/` would be returned as `https://token-cdn-domain/123` for token ID `123`. + +If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. + + + +See [ERC721Component::balance\_of](#ERC721Component-balance_of). + + + +See [ERC721Component::owner\_of](#ERC721Component-owner_of). + + + +See [ERC721Component::safe\_transfer\_from](#ERC721Component-safe_transfer_from). + + + +See [ERC721Component::transfer\_from](#ERC721Component-transfer_from). + + + +See [ERC721Component::set\_approval\_for\_all](#ERC721Component-set_approval_for_all). + + + +See [ERC721Component::get\_approved](#ERC721Component-get_approved). + + + +See [ERC721Component::is\_approved\_for\_all](#ERC721Component-is_approved_for_all). + + + +See [ERC721Component::token\_uri](#ERC721Component-token_uri). + + +#### [](#internal_functions)Internal functions [!toc] [#internal_functions] + + +Initializes the contract by setting the token name and symbol. This should be used inside the contract's constructor. + +Most ERC721 contracts expose the [IERC721Metadata](#IERC721Metadata) interface which is what this initializer is meant to support. If the contract DOES NOT expose the [IERC721Metadata](#IERC721Metadata) interface, meaning the token does not have a name, symbol, or URI, the contract must instead use [initializer\_no\_metadata](#ERC721Component-initializer_no_metadata) in the constructor. Failure to abide by these instructions can lead to unexpected issues especially with UIs. + + + +Initializes the contract with no metadata by registering only the IERC721 interface. + +This initializer should ONLY be used during construction in the very specific instance when the contract does NOT expose the [IERC721Metadata](#IERC721Metadata) interface. Initializing a contract with this initializer means that tokens will not have a name, symbol, or URI. + + + +Internal function that returns whether `token_id` exists. + +Tokens start existing when they are minted ([mint](#ERC721-mint)), and stop existing when they are burned ([burn](#ERC721-burn)). + + + +Transfers `token_id` from `from` to `to`. + +Internal function without access restriction. + +This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + +Requirements: + +- `to` is not the zero address. +- `from` is the token owner. +- `token_id` exists. + +Emits a [Transfer](#IERC721-Transfer) event. + + + +Mints `token_id` and transfers it to `to`. Internal function without access restriction. + +This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + +Requirements: + +- `to` is not the zero address. +- `token_id` does not exist. + +Emits a [Transfer](#IERC721-Transfer) event. + + + +Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. + +`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. + +This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + +Requirements: + +- `to` cannot be the zero address. +- `from` must be the token owner. +- `token_id` exists. +- `to` is either an account contract or supports the `IERC721Receiver` interface. + +Emits a [Transfer](#IERC721-Transfer) event. + + + +Mints `token_id` if `to` is either an account or `IERC721Receiver`. + +`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. + +This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + +Requirements: + +- `token_id` does not exist. +- `to` is either an account contract or supports the `IERC721Receiver` interface. + +Emits a [Transfer](#IERC721-Transfer) event. + + + +Destroys `token_id`. The approval is cleared when the token is burned. + +This internal function does not check if the caller is authorized to operate on the token. + +Requirements: + +- `token_id` exists. + +Emits a [Transfer](#IERC721-Transfer) event. + + + +Transfers `token_id` from its current owner to `to`, or alternatively mints (or burns) if the current owner (or `to`) is the zero address. Returns the owner of the `token_id` before the update. + +The `auth` argument is optional. If the value passed is non-zero, then this function will check that `auth` is either the owner of the token, or approved to operate on the token (by the owner). + +Emits a [Transfer](#IERC721-Transfer) event. + +This function can be extended using the `ERC721HooksTrait`, to add functionality before and/or after the transfer, mint, or burn. + + + +Internal function that returns the owner address of `token_id`. + + + +Version of [\_owner\_of](#ERC721Component-_owner_of) that panics if owner is the zero address. + + + +Approve `to` to operate on `token_id` + +The `auth` argument is optional. If the value passed is non-zero, then this function will check that `auth` is either the owner of the token, or approved to operate on all tokens held by this owner. + +Emits an [Approval](#IERC721-Approval) event. + + + +Variant of [\_approve](#ERC721Component-_approve) with an optional flag to enable or disable the `Approval` event. The event is not emitted in the context of transfers. + +If `auth` is zero and `emit_event` is false, this function will not check that the token exists. + +Requirements: + +- if `auth` is non-zero, it must be either the owner of the token or approved to operate on all of its tokens. + +May emit an [Approval](#IERC721-Approval) event. + + + +Enables or disables approval for `operator` to manage all of the `owner` assets. + +Requirements: + +- `operator` is not the zero address. + +Emits an [Approval](#IERC721-Approval) event. + + + +Internal function that sets the `base_uri`. + + + +Base URI for computing [token\_uri](#IERC721Metadata-token_uri). + +If set, the resulting URI for each token will be the concatenation of the base URI and the token ID. Returns an empty `ByteArray` if not set. + + + +Returns whether `spender` is allowed to manage `owner`'s tokens, or `token_id` in particular (ignoring whether it is owned by `owner`). + +This function assumes that `owner` is the actual owner of `token_id` and does not verify this assumption. + + + +Checks if `spender` can operate on `token_id`, assuming the provided `owner` is the actual owner. + +Requirements: + +- `owner` cannot be the zero address. +- `spender` cannot be the zero address. +- `spender` must be the owner of `token_id` or be approved to operate on it. + +This function assumes that `owner` is the actual owner of `token_id` and does not verify this assumption. + + +#### [](#events_2)Events [!toc] [#events_2] + + +See [IERC721::Approval](#IERC721-Approval). + + + +See [IERC721::ApprovalForAll](#IERC721-ApprovalForAll). + + + +See [IERC721::Transfer](#IERC721-Transfer). + + +### `ERC721ReceiverComponent` [toc] [#ERC721ReceiverComponent] + + +```rust +use openzeppelin_token::erc721::ERC721ReceiverComponent; +``` + +ERC721Receiver component implementing [IERC721Receiver](#IERC721Receiver). + +Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. + +[Embeddable Mixin Implementations](../components#mixins) + +#### ERCReceiverMixinImpl [!toc] [#ERC721ReceiverComponent-Embeddable-Impls-ERCReceiverMixinImpl] + +- [`ERC721ReceiverImpl`](#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverImpl) +- [`ERC721ReceiverCamelImpl`](#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverCamelImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +#### ERC721ReceiverImpl [!toc] [#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverImpl] + +- [`on_erc721_received(self, operator, from, token_id, data)`](#ERC721ReceiverComponent-on_erc721_received) + +#### ERC721ReceiverCamelImpl [!toc] [#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverCamelImpl] + +- [`onERC721Received(self, operator, from, tokenId, data)`](#ERC721ReceiverComponent-onERC721Received) + +Internal Functions + +#### InternalImpl [!toc] [#ERC721ReceiverComponent-InternalImpl] + +- [`initializer(self)`](#ERC721ReceiverComponent-initializer) + +#### [](#embeddable_functions_2)Embeddable functions [!toc] [#embeddable_functions_2] + + +Returns the `IERC721Receiver` interface ID. + + + +See [ERC721ReceiverComponent::on\_erc721\_received](#ERC721ReceiverComponent-on_erc721_received). + + +#### [](#internal_functions_2)Internal functions [!toc] [#internal_functions_2] + + +Registers the `IERC721Receiver` interface ID as supported through introspection. + + +## Extensions + +### `ERC721EnumerableComponent` [toc] [#ERC721EnumerableComponent] + + +```rust +use openzeppelin_token::erc721::extensions::ERC721EnumerableComponent; +``` + +Extension of ERC721 as defined in the EIP that adds enumerability of all the token ids in the contract as well as all token ids owned by each account. This extension allows contracts to publish their entire list of NFTs and make them discoverable. + +Implementing [ERC721Component](#ERC721Component) is a requirement for this component to be implemented. + +To properly track token ids, this extension requires that the [ERC721EnumerableComponent::before\_update](#ERC721EnumerableComponent-before_update) function is called before every transfer, mint, or burn operation. For this, the [ERC721HooksTrait::before\_update](#ERC721Component-before_update) hook must be used. Here's how the hook should be implemented in a contract: + +```[ +#[starknet::contract] +mod ERC721EnumerableContract { + (...) + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: ERC721EnumerableComponent, storage: erc721_enumerable, event: ERC721EnumerableEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + impl ERC721HooksImpl of ERC721Component::ERC721HooksTrait { + fn before_update( + ref self: ERC721Component::ComponentState, + to: ContractAddress, + token_id: u256, + auth: ContractAddress + ) { + let mut contract_state = self.get_contract_mut(); + contract_state.erc721_enumerable.before_update(to, token_id); + } + } +} +``` + +Embeddable Implementations + +#### ERC721EnumerableImpl [!toc] [#ERC721EnumerableComponent-Embeddable-Impls-ERC721EnumerableImpl] + +- [`total_supply(self)`](#ERC721EnumerableComponent-total_supply) +- [`token_by_index(self, index)`](#ERC721EnumerableComponent-token_by_index) +- [`token_of_owner_by_index(self, address, index)`](#ERC721EnumerableComponent-token_of_owner_by_index) + +Internal functions + +#### InternalImpl [!toc] [#ERC721EnumerableComponent-InternalImpl] + +- [`initializer(self)`](#ERC721EnumerableComponent-initializer) +- [`before_update(self, to, token_id)`](#ERC721EnumerableComponent-before_update) +- [`all_tokens_of_owner(self, owner)`](#ERC721EnumerableComponent-all_tokens_of_owner) +- [`_add_token_to_owner_enumeration(self, to, token_id)`](#ERC721EnumerableComponent-_add_token_to_owner_enumeration) +- [`_add_token_to_all_tokens_enumeration(self, token_id)`](#ERC721EnumerableComponent-_add_token_to_all_tokens_enumeration) +- [`_remove_token_from_owner_enumeration(self, from, token_id)`](#ERC721EnumerableComponent-_remove_token_from_owner_enumeration) +- [`_remove_token_from_all_tokens_enumeration(self, token_id)`](#ERC721EnumerableComponent-_remove_token_from_all_tokens_enumeration) + +#### [](#ERC721EnumerableComponent-Embeddable-functions)Embeddable functions [!toc] [#ERC721EnumerableComponent-Embeddable-functions] + + +Returns the current amount of votes that `account` has. + + + +See [IERC721Enumerable::token\_by\_index](#IERC721Enumerable-token_by_index). + +Requirements: + +- `index` is less than the total token supply. + + + +See [IERC721Enumerable::token\_of\_owner\_by\_index](#IERC721Enumerable-token_of_owner_by_index). + +Requirements: + +- `index` is less than `owner`'s token balance. +- `owner` is not the zero address. + + +#### [](#ERC721EnumerableComponent-Internal-functions)Internal functions [!toc] [#ERC721EnumerableComponent-Internal-functions] + + +Registers the `IERC721Enumerable` interface ID as supported through introspection. + + + +Updates the ownership and token-tracking data structures. + +When a token is minted (or burned), `token_id` is added to (or removed from) the token-tracking structures. + +When a token is transferred, minted, or burned, the ownership-tracking data structures reflect the change in ownership of `token_id`. + +This must be added to the implementing contract's [ERC721HooksTrait::before\_update](#ERC721Component-before_update) hook. + + + +Returns a list of all token ids owned by the specified `owner`. This function provides a more efficient alternative to calling `ERC721::balance_of` and iterating through tokens with `ERC721Enumerable::token_of_owner_by_index`. + +Requirements: + +- `owner` is not the zero address. + + + +Adds token to this extension's ownership-tracking data structures. + + + +Adds token to this extension's token-tracking data structures. + + + +Removes a token from this extension's ownership-tracking data structures. + +This has 0(1) time complexity but alters the indexed order of owned tokens by swapping `token_id` and the index thereof with the last token id and the index thereof e.g. removing `1` from `[1, 2, 3, 4]` results in `[4, 2, 3]`. + + + +Removes `token_id` from this extension's token-tracking data structures. + +This has 0(1) time complexity but alters the indexed order by swapping `token_id` and the index thereof with the last token id and the index thereof e.g. removing `1` from `[1, 2, 3, 4]` results in `[4, 2, 3]`. + + +## Presets + +### `ERC721Upgradeable` [toc] [#ERC721Upgradeable] + + +```rust +use openzeppelin_presets::ERC721Upgradeable; +``` + +Upgradeable ERC721 contract leveraging [ERC721Component](#ERC721Component). + +[Sierra class hash](../presets) + +```text +{{ERC721UpgradeableClassHash}} +``` + +Constructor + +- [`constructor(self, name, symbol, recipient, token_ids, base_uri, owner)`](#ERC721Upgradeable-constructor) + +Embedded Implementations + +ERC721MixinImpl + +- [`ERC721MixinImpl`](#ERC721Component-Embeddable-Mixin-Impl) + +OwnableMixinImpl + +- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) + +External Functions + +- [`upgrade(self, new_class_hash)`](#ERC721Upgradeable-upgrade) + +#### [](#ERC721Upgradeable-constructor-section)Constructor [!toc] [#ERC721Upgradeable-constructor-section] + + +Sets the `name` and `symbol`. Mints `token_ids` tokens to `recipient` and sets the `base_uri`. Assigns `owner` as the contract owner with permissions to upgrade. + + +#### [](#ERC721Upgradeable-external-functions)External functions [!toc] [#ERC721Upgradeable-external-functions] + + +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. + diff --git a/content/contracts-cairo/3.x/api/finance.mdx b/content/contracts-cairo/3.x/api/finance.mdx new file mode 100644 index 00000000..a41e0c64 --- /dev/null +++ b/content/contracts-cairo/3.x/api/finance.mdx @@ -0,0 +1,328 @@ +--- +title: Finance +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This crate includes primitives for financial systems. + +## Interfaces + +### `IVesting` [toc] [#IVesting] + + + +```rust +use openzeppelin_finance::vesting::interface::IVesting; +``` + +Common interface for contracts implementing the vesting functionality. + +Functions + +- [`start()`](#IVesting-start) +- [`cliff()`](#IVesting-cliff) +- [`duration()`](#IVesting-duration) +- [`end()`](#IVesting-end) +- [`released(token)`](#IVesting-released) +- [`releasable(token)`](#IVesting-releasable) +- [`vested_amount(token, timestamp)`](#IVesting-vested_amount) +- [`release(token)`](#IVesting-release) + +Events + +- [`AmountReleased(token, amount)`](#IVesting-AmountReleased) + +#### Functions [!toc] [#IVesting-Functions] + + +Returns the timestamp marking the beginning of the vesting period. + + + +Returns the timestamp marking the end of the cliff period. + + + +Returns the total duration of the vesting period. + + + +Returns the timestamp marking the end of the vesting period. + + + +Returns the already released amount for a given `token`. + + + +Returns the amount of a given `token` that can be released at the time of the call. + + + +Returns the total vested amount of a specified `token` at a given `timestamp`. + + + +Releases the amount of a given `token` that has already vested and returns that amount. + +May emit an [AmountReleased](#IVesting-AmountReleased) event. + + +#### Events [!toc] [#IVesting-Events] + + +Emitted when vested tokens are released to the beneficiary. + + +## [](#vesting)Vesting + +### `VestingComponent` [toc] [#VestingComponent] + + + +```rust +use openzeppelin_finance::vesting::VestingComponent; +``` + +Vesting component implementing the [`IVesting`](#IVesting) interface. + +Vesting Schedule Trait Implementations + +#### functions [!toc] [#VestingComponent-Vesting-Schedule-functions] + +- [`calculate_vested_amount(self, token, total_allocation, timestamp, start, duration, cliff)`](#VestingComponent-calculate_vested_amount) + +Embeddable Implementations + +#### VestingImpl [!toc] [#VestingComponent-Embeddable-Impls-VestingImpl] + +- [`start(self)`](#VestingComponent-start) +- [`cliff(self)`](#VestingComponent-cliff) +- [`duration(self)`](#VestingComponent-duration) +- [`end(self)`](#VestingComponent-end) +- [`released(self, token)`](#VestingComponent-released) +- [`releasable(self, token)`](#VestingComponent-releasable) +- [`vested_amount(self, token, timestamp)`](#VestingComponent-vested_amount) +- [`release(self, token)`](#VestingComponent-release) + +Internal implementations + +#### InternalImpl [!toc] [#VestingComponent-InternalImpl] + +- [`initializer(self, start, duration, cliff_duration)`](#VestingComponent-initializer) +- [`resolve_vested_amount(self, token, timestamp)`](#VestingComponent-resolve_vested_amount) + +A trait that defines the logic for calculating the vested amount based on a given timestamp. + +You can read more about the trait's purpose and how to use it [here](../finance#vesting-schedule). + + +Calculates and returns the vested amount at a given `timestamp` based on the core vesting parameters. + + +#### Functions [!toc] [#VestingComponent-Functions] + + +Returns the timestamp marking the beginning of the vesting period. + + + +Returns the timestamp marking the end of the cliff period. + + + +Returns the total duration of the vesting period. + + + +Returns the timestamp marking the end of the vesting period. + + + +Returns the already released amount for a given `token`. + + + +Returns the amount of a given `token` that can be released at the time of the call. + + + +Returns the total vested amount of a specified `token` at a given `timestamp`. + + + +Releases the amount of a given `token` that has already vested and returns that amount. + +If the releasable amount is zero, this function won't emit the event or attempt to transfer the tokens. + +Requirements: + +- `transfer` call to the `token` must return `true` indicating a successful transfer. + +May emit an [AmountReleased](#IVesting-AmountReleased) event. + + +#### Internal functions [!toc] [#VestingComponent-Internal-Functions] + + +Initializes the component by setting the vesting `start`, `duration` and `cliff_duration`. To prevent reinitialization, this should only be used inside of a contract's constructor. + +Requirements: + +- `cliff_duration` must be less than or equal to `duration`. + + + +Returns the vested amount that's calculated using the [VestingSchedule](#VestingComponent-Vesting-Schedule) trait implementation. + + +### `LinearVestingSchedule` [toc] [#LinearVestingSchedule] + + + +```rust +use openzeppelin_finance::vesting::LinearVestingSchedule; +``` + +Defines the logic for calculating the vested amount, incorporating a cliff period. It returns 0 before the cliff ends. After the cliff period, the vested amount returned is directly proportional to the time passed since the start of the vesting schedule. + +## [](#presets)Presets + +### `VestingWallet` [toc] [#VestingWallet] + + + +```rust +use openzeppelin::presets::VestingWallet; +``` + +A non-upgradable contract leveraging [VestingComponent](#VestingComponent) and [OwnableComponent](./access#OwnableComponent). + +The contract is intentionally designed to be non-upgradable to ensure that neither the vesting initiator nor the vesting beneficiary can modify the vesting schedule without the consent of the other party. + +[Sierra class hash](../presets) + +```text +{{VestingWalletClassHash}} +``` + +Constructor + +- [`constructor(self, beneficiary, start, duration, cliff_duration)`](#VestingWallet-constructor) + +Embedded Implementations + +VestingComponent + +- [`VestingImpl`](#VestingComponent-Embeddable-Impls-VestingImpl) + +OwnableComponent + +- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) + +#### Constructor [!toc] [#VestingWallet-constructor-section] + + +Initializes the vesting component by setting the vesting `start`, `duration` and `cliff_duration`. Assigns `beneficiary` as the contract owner and the vesting beneficiary. + +Requirements: + +- `cliff_duration` must be less than or equal to `duration`. + diff --git a/content/contracts-cairo/3.x/api/governance.mdx b/content/contracts-cairo/3.x/api/governance.mdx new file mode 100644 index 00000000..f0bd318f --- /dev/null +++ b/content/contracts-cairo/3.x/api/governance.mdx @@ -0,0 +1,4059 @@ +--- +title: Governance +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This crate includes primitives for on-chain governance. + +## Interfaces + +### `IGovernor` [toc] [#IGovernor] + + + +```rust +use openzeppelin_governance::governor::interface::IGovernor; +``` + +Interface of a governor contract. + +[SRC5 ID](./introspection#ISRC5) + +```text +0x1100a1f8546595b5bd75a6cd8fcc5b015370655e66f275963321c5cd0357ac9 +``` + +Functions + +- [`name()`](#IGovernor-name) +- [`version()`](#IGovernor-version) +- [`COUNTING_MODE()`](#IGovernor-COUNTING_MODE) +- [`hash_proposal(calls, description_hash)`](#IGovernor-hash_proposal) +- [`state(proposal_id)`](#IGovernor-state) +- [`proposal_threshold()`](#IGovernor-proposal_threshold) +- [`proposal_snapshot(proposal_id)`](#IGovernor-proposal_snapshot) +- [`proposal_deadline(proposal_id)`](#IGovernor-proposal_deadline) +- [`proposal_proposer(proposal_id)`](#IGovernor-proposal_proposer) +- [`proposal_eta(proposal_id)`](#IGovernor-proposal_eta) +- [`proposal_needs_queuing(proposal_id)`](#IGovernor-proposal_needs_queuing) +- [`voting_delay()`](#IGovernor-voting_delay) +- [`voting_period()`](#IGovernor-voting_period) +- [`quorum(timepoint)`](#IGovernor-quorum) +- [`get_votes(account, timepoint)`](#IGovernor-get_votes) +- [`get_votes_with_params(account, timepoint, params)`](#IGovernor-get_votes_with_params) +- [`has_voted(proposal_id, account)`](#IGovernor-has_voted) +- [`propose(calls, description)`](#IGovernor-propose) +- [`queue(calls, description_hash)`](#IGovernor-queue) +- [`execute(calls, description_hash)`](#IGovernor-execute) +- [`cancel(proposal_id, description_hash)`](#IGovernor-cancel) +- [`cast_vote(proposal_id, support)`](#IGovernor-cast_vote) +- [`cast_vote_with_reason(proposal_id, support, reason)`](#IGovernor-cast_vote_with_reason) +- [`cast_vote_with_reason_and_params(proposal_id, support, reason, params)`](#IGovernor-cast_vote_with_reason_and_params) +- [`cast_vote_by_sig(proposal_id, support, reason, signature)`](#IGovernor-cast_vote_by_sig) +- [`cast_vote_with_reason_and_params_by_sig(proposal_id, support, reason, params, signature)`](#IGovernor-cast_vote_with_reason_and_params_by_sig) +- [`nonces(voter)`](#IGovernor-nonces) +- [`relay(call)`](#IGovernor-relay) + +Events + +- [`ProposalCreated(proposal_id, proposer, calls, signatures, vote_start, vote_end, description)`](#IGovernor-ProposalCreated) +- [`ProposalQueued(proposal_id, eta_seconds)`](#IGovernor-ProposalQueued) +- [`ProposalExecuted(proposal_id)`](#IGovernor-ProposalExecuted) +- [`ProposalCanceled(proposal_id)`](#IGovernor-ProposalCanceled) +- [`VoteCast(voter, proposal_id, support, weight, reason)`](#IGovernor-VoteCast) +- [`VoteCastWithParams(voter, proposal_id, support, weight, reason, params)`](#IGovernor-VoteCastWithParams) + +#### Functions [!toc] [#IGovernor-Functions] + + +Name of the governor instance (used in building the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + + + +Version of the governor instance (used in building [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + + + +A description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. + +There are 2 standard keys: `support` and `quorum`. + +- `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. +- `quorum=bravo` means that only For votes are counted towards quorum. +- `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. + +If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique name that describes the behavior. For example: + +- `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. +- `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. + + +The string can be decoded by the standard [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) JavaScript class. + + + + +Hashing function used to (re)build the proposal id from the proposal details. + + + +Returns the state of a proposal, given its id. + + + +The number of votes required in order for a voter to become a proposer. + + + +Timepoint used to retrieve user's votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. + + + +Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. + + + +The account that created a proposal. + + + +The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn't use the governor clock, and instead relies on the executor's clock which may be different. In most cases this will be a timestamp. + + + +Whether a proposal needs to be queued before execution. This indicates if the proposal needs to go through a timelock. + + + +Delay between when a proposal is created and when the vote starts. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + + + +Delay between when a vote starts and when it ends. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +The `voting_delay` can delay the start of the vote. This must be considered when setting the voting duration compared to the voting delay. + +This value is stored when the proposal is submitted so that possible changes to the value do not affect proposals that have already been submitted. + + + +Minimum number of votes required for a proposal to be successful. + +The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows the quorum to scale depending on values such as the total supply of a token at this timepoint. + + + +Returns the voting power of an `account` at a specific `timepoint`. + +This can be implemented in a number of ways, for example by reading the delegated balance from one (or multiple) `ERC20Votes` tokens. + + + +Returns the voting power of an `account` at a specific `timepoint`, given additional encoded parameters. + + + +Returns whether an `account` has cast a vote on a proposal. + + + +Creates a new proposal. Vote starts after a delay specified by `voting_delay` and lasts for a duration specified by `voting_period`. + +The state of the Governor and targets may change between the proposal creation and its execution. This may be the result of third party actions on the targeted contracts, or other governor proposals. For example, the balance of this contract could be updated or its access control permissions may be modified, possibly compromising the proposal's ability to execute successfully (e.g. the governor doesn't have enough value to cover a proposal with multiple transfers). + +Returns the id of the proposal. + + + +Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing is not necessary, this function may revert. + +Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached. + +Returns the id of the proposal. + + + +Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the deadline to be reached. Depending on the governor it might also be required that the proposal was queued and that some delay passed. + +Some modules can modify the requirements for execution, for example by adding an additional timelock (See `timelock_controller`). + +Returns the id of the proposal. + + + +Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e. before the vote starts. + +Returns the id of the proposal. + + + +Cast a vote on a proposal. + +Returns the weight of the vote. + + + +Cast a vote on a proposal with a `reason`. + +Returns the weight of the vote. + + + +Cast a vote on a proposal with a reason and additional encoded parameters. + +Returns the weight of the vote. + + + +Cast a vote on a proposal using the voter's signature. + +Returns the weight of the vote. + + + +Cast a vote on a proposal with a reason and additional encoded parameters using the `voter`'s signature. + +Returns the weight of the vote. + + + +Returns the next unused nonce for an address. + + + +Relays a transaction or function call to an arbitrary target. + +In cases where the governance executor is some contract other than the governor itself, like when using a timelock, this function can be invoked in a governance proposal to recover tokens that were sent to the governor contract by mistake. + +If the executor is simply the governor itself, use of `relay` is redundant. + + +#### Events [!toc] [#IGovernor-Events] + + +Emitted when a proposal is created. + + + +Emitted when a proposal is queued. + + + +Emitted when a proposal is executed. + + + +Emitted when a proposal is canceled. + + + +Emitted when a vote is cast. + + + +Emitted when a vote is cast with params. + + +### `IMultisig` [toc] [#IMultisig] + + + +```rust +use openzeppelin_governance::multisig::interface::IMultisig; +``` + +Interface of a multisig contract. + +Functions + +- [`get_quorum()`](#IMultisig-get_quorum) +- [`is_signer(signer)`](#IMultisig-is_signer) +- [`get_signers()`](#IMultisig-get_signers) +- [`is_confirmed(id)`](#IMultisig-is_confirmed) +- [`is_confirmed_by(id, signer)`](#IMultisig-is_confirmed_by) +- [`is_executed(id)`](#IMultisig-is_executed) +- [`get_submitted_block(id)`](#IMultisig-get_submitted_block) +- [`get_transaction_state(id)`](#IMultisig-get_transaction_state) +- [`get_transaction_confirmations(id)`](#IMultisig-get_transaction_confirmations) +- [`hash_transaction(to, selector, calldata, salt)`](#IMultisig-hash_transaction) +- [`hash_transaction_batch(calls, salt)`](#IMultisig-hash_transaction_batch) +- [`add_signers(new_quorum, signers_to_add)`](#IMultisig-add_signers) +- [`remove_signers(new_quorum, signers_to_remove)`](#IMultisig-remove_signers) +- [`replace_signer(signer_to_remove, signer_to_add)`](#IMultisig-replace_signer) +- [`change_quorum(new_quorum)`](#IMultisig-change_quorum) +- [`submit_transaction(to, selector, calldata, salt)`](#IMultisig-submit_transaction) +- [`submit_transaction_batch(calls, salt)`](#IMultisig-submit_transaction_batch) +- [`confirm_transaction(id)`](#IMultisig-confirm_transaction) +- [`revoke_confirmation(id)`](#IMultisig-revoke_confirmation) +- [`execute_transaction(to, selector, calldata, salt)`](#IMultisig-execute_transaction) +- [`execute_transaction_batch(calls, salt)`](#IMultisig-execute_transaction_batch) + +Events + +- [`SignerAdded(signer)`](#IMultisig-SignerAdded) +- [`SignerRemoved(signer)`](#IMultisig-SignerRemoved) +- [`QuorumUpdated(old_quorum, new_quorum)`](#IMultisig-QuorumUpdated) +- [`TransactionSubmitted(id, signer)`](#IMultisig-TransactionSubmitted) +- [`TransactionConfirmed(id, signer)`](#IMultisig-TransactionConfirmed) +- [`ConfirmationRevoked(id, signer)`](#IMultisig-ConfirmationRevoked) +- [`TransactionExecuted(id)`](#IMultisig-TransactionExecuted) +- [`CallSalt(id, salt)`](#IMultisig-CallSalt) + +#### Functions [!toc] [#IMultisig-Functions] + + +Returns the current quorum value. The quorum is the minimum number of confirmations required to approve a transaction. + + + +Returns whether the given `signer` is registered. Only registered signers can submit, confirm, or execute transactions. + + + +Returns the list of all current signers. + + + +Returns whether the transaction with the given `id` has been confirmed. + + + +Returns whether the transaction with the given `id` has been confirmed by the specified `signer`. + + + +Returns whether the transaction with the given `id` has been executed. + + + +Returns the block number when the transaction with the given `id` was submitted. + + + +Returns the current state of the transaction with the given `id`. + + + +Returns the number of confirmations from registered signers for the transaction with the specified `id`. + + + +Returns the computed identifier of a transaction containing a single call. + + + +Returns the computed identifier of a transaction containing a batch of calls. + + + +Adds new signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after addition. + +Emits a [SignerAdded](#IMultisig-SignerAdded) event for each signer added. + +Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. + + + +Removes signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after removal. + +Emits a [SignerRemoved](#IMultisig-SignerRemoved) event for each signer removed. + +Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. + + + +Replaces an existing signer with a new signer. + +Requirements: + +- The caller must be the contract itself. +- `signer_to_remove` must be an existing signer. +- `signer_to_add` must not be an existing signer. + +Emits a [SignerRemoved](#IMultisig-SignerRemoved) event for the removed signer. + +Emits a [SignerAdded](#IMultisig-SignerAdded) event for the new signer. + + + +Updates the quorum value to `new_quorum` if it differs from the current quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be non-zero. +- `new_quorum` must be less than or equal to the total number of signers. + +Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. + + + +Submits a new transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#IMultisig-TransactionSubmitted) event. + +Emits a [CallSalt](#IMultisig-CallSalt) event if `salt` is not zero. + + + + +Submits a new batch transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#IMultisig-TransactionSubmitted) event. + +Emits a [CallSalt](#IMultisig-CallSalt) event if `salt` is not zero. + + + +Confirms a transaction with the given `id`. + +Requirements: + +- The caller must be a registered signer. +- The transaction must exist and not be executed. +- The caller must not have already confirmed the transaction. + +Emits a [TransactionConfirmed](#IMultisig-TransactionConfirmed) event. + + + +Revokes a previous confirmation for a transaction with the given `id`. + +Requirements: + +- The transaction must exist and not be executed. +- The caller must have previously confirmed the transaction. + +Emits a [ConfirmationRevoked](#IMultisig-ConfirmationRevoked) event. + + + +Executes a confirmed transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#IMultisig-TransactionExecuted) event. + + + +Executes a confirmed batch transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#IMultisig-TransactionExecuted) event. + + +#### Events [!toc] [#IMultisig-Events] + + +Emitted when a new `signer` is added. + + + +Emitted when a `signer` is removed. + + + +Emitted when the `quorum` value is updated. + + + +Emitted when a new transaction is submitted by a `signer`. + + + +Emitted when a transaction is confirmed by a `signer`. + + + +Emitted when a `signer` revokes his confirmation. + + + +Emitted when a transaction is executed. + + + +Emitted when a new transaction is submitted with non-zero salt. + + +### `ITimelock` [toc] [#ITimelock] + + + +```rust +use openzeppelin_governance::timelock::interface::ITimelock; +``` + +Interface of a timelock contract. + +Functions + +- [`is_operation(id)`](#ITimelock-is_operation) +- [`is_operation_pending(id)`](#ITimelock-is_operation_pending) +- [`is_operation_ready(id)`](#ITimelock-is_operation_ready) +- [`is_operation_done(id)`](#ITimelock-is_operation_done) +- [`get_timestamp(id)`](#ITimelock-get_timestamp) +- [`get_operation_state(id)`](#ITimelock-get_operation_state) +- [`get_min_delay()`](#ITimelock-get_min_delay) +- [`hash_operation(call, predecessor, salt)`](#ITimelock-hash_operation) +- [`hash_operation_batch(calls, predecessor, salt)`](#ITimelock-hash_operation_batch) +- [`schedule(call, predecessor, salt, delay)`](#ITimelock-schedule) +- [`schedule_batch(calls, predecessor, salt, delay)`](#ITimelock-schedule_batch) +- [`cancel(id)`](#ITimelock-cancel) +- [`execute(call, predecessor, salt)`](#ITimelock-execute) +- [`execute_batch(calls, predecessor, salt)`](#ITimelock-execute_batch) +- [`update_delay(new_delay)`](#ITimelock-update_delay) + +Events + +- [`CallScheduled(id, index, call, predecessor, delay)`](#ITimelock-CallScheduled) +- [`CallExecuted(id, index, call)`](#ITimelock-CallExecuted) +- [`CallSalt(id, salt)`](#ITimelock-CallSalt) +- [`CallCancelled(id)`](#ITimelock-CallCancelled) +- [`MinDelayChanged(old_duration, new_duration)`](#ITimelock-MinDelayChanged) + +#### Functions [!toc] [#ITimelock-Functions] + + +Returns whether `id` corresponds to a registered operation. This includes the OperationStates: `Waiting`, `Ready`, and `Done`. + + + +Returns whether the `id` OperationState is pending or not. Note that a pending operation may be either `Waiting` or `Ready`. + + + +Returns whether the `id` OperationState is `Ready` or not. + + + +Returns whether the `id` OperationState is `Done` or not. + + + +Returns the timestamp at which `id` becomes `Ready`. + +`0` means the OperationState is `Unset` and `1` means the OperationState is `Done`. + + + + +Returns the current state of the operation with the given `id`. + +The possible states are: + +- `Unset`: the operation has not been scheduled or has been canceled. +- `Waiting`: the operation has been scheduled and is pending the scheduled delay. +- `Ready`: the timer has expired, and the operation is eligible for execution. +- `Done`: the operation has been executed. + + + +Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls `update_delay`. + + + +Returns the identifier of an operation containing a single transaction. + + + +Returns the identifier of an operation containing a batch of transactions. + + + +Schedule an operation containing a single transaction. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. + +Emits [CallScheduled](#ITimelock-CallScheduled) event. Emits [CallSalt](#ITimelock-CallSalt) event if `salt` is not zero. + + + +Schedule an operation containing a batch of transactions. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. + +Emits one [CallScheduled](#ITimelock-CallScheduled) event for each transaction in the batch. Emits [CallSalt](#ITimelock-CallSalt) event if `salt` is not zero. + + + +Cancels an operation. A canceled operation returns to `Unset` OperationState. + +Requirements: + +- The caller must have the `CANCELLER_ROLE` role. +- `id` must be a pending operation. + +Emits a [CallCancelled](#ITimelock-CallCancelled) event. + + + +Execute a (Ready) operation containing a single Call. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#ITimelock-CallExecuted) event. + +This function can reenter, but it doesn't pose a risk because [`_after_call(self: @ContractState, id: felt252)` internal](#TimelockControllerComponent-_after_call) checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + + + +Execute a (Ready) operation containing a batch of Calls. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#ITimelock-CallExecuted) event for each Call. + +This function can reenter, but it doesn't pose a risk because `_after_call` checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + + + +Changes the minimum timelock duration for future operations. + +Requirements: + +- The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function. + +Emits a [MinDelayChanged](#ITimelock-MinDelayChanged) event. + + +#### Events [!toc] [#ITimelock-Events] + + +Emitted when `call` is scheduled as part of operation `id`. + + + +Emitted when `call` is performed as part of operation `id`. + + + +Emitted when a new proposal is scheduled with non-zero salt. + + + +Emitted when operation `id` is cancelled. + + + +Emitted when the minimum delay for future operations is modified. + + +### `IVotes` [toc] [#IVotes] + + + +```rust +use openzeppelin_governance::votes::interface::IVotes; +``` + +Common interface for Votes-enabled contracts. + +Functions + +- [`get_votes(account)`](#IVotes-get_votes) +- [`get_past_votes(account, timepoint)`](#IVotes-get_past_votes) +- [`get_past_total_supply(timepoint)`](#IVotes-get_past_total_supply) +- [`delegates(account)`](#IVotes-delegates) +- [`delegate(delegatee)`](#IVotes-delegate) +- [`delegate_by_sig(delegator, delegatee, nonce, expiry, signature)`](#IVotes-delegate_by_sig) +- [`clock()`](#IVotes-clock) +- [`CLOCK_MODE()`](#IVotes-CLOCK_MODE) + +#### Functions [!toc] [#IVotes-Functions] + + +Returns the current amount of votes that `account` has. + + + +Returns the amount of votes that `account` had at a specific moment in the past. + + + +Returns the total supply of votes available at a specific moment in the past. + +This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote. + + + +Returns the delegate that `account` has chosen. + + + +Delegates votes from the sender to `delegatee`. + + + +Delegates votes from `delegator` to `delegatee` through a [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message signature validation. + + + +Returns the current timepoint determined by the contract's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + + + +Returns a description of the clock the contract is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + + +## [](#governor)Governor + +This modular system of Governor components allows the deployment of easily customizable on-chain voting protocols. + +For a walkthrough of how to implement a Governor, check the [Governor](../governance/governor) page. + +### `GovernorComponent` [toc] [#GovernorComponent] + + + +```rust +use openzeppelin_governance::governor::GovernorComponent; +``` + +Core of the governance system. + +The extension traits presented below are what make the GovernorComponent a modular and configurable system. The embeddable and internal implementations depends on these trait. They can be implemented locally in the contract, or through the provided library [component extensions](#governor_extensions). + +Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. + +Extensions traits + +#### GovernorSettingsTrait [!toc] [#GovernorComponent-GovernorSettingsTrait] + +- [`voting_delay(self)`](#GovernorComponent-GovernorSettingsTrait-voting_delay) +- [`voting_period(self)`](#GovernorComponent-GovernorSettingsTrait-voting_period) +- [`proposal_threshold(self)`](#GovernorComponent-GovernorSettingsTrait-proposal_threshold) + +#### GovernorQuorumTrait [!toc] [#GovernorComponent-GovernorQuorumTrait] + +- [`quorum(self, timepoint)`](#GovernorComponent-GovernorQuorumTrait-quorum) + +#### GovernorCountingTrait [!toc] [#GovernorComponent-GovernorCountingTrait] + +- [`counting_mode(self)`](#GovernorComponent-GovernorCountingTrait-counting_mode) +- [`count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorComponent-GovernorCountingTrait-count_vote) +- [`has_voted(self, proposal_id, account)`](#GovernorComponent-GovernorCountingTrait-has_voted) +- [`quorum_reached(self, proposal_id)`](#GovernorComponent-GovernorCountingTrait-quorum_reached) +- [`vote_succeeded(self, proposal_id)`](#GovernorComponent-GovernorCountingTrait-vote_succeeded) + +#### GovernorVotesTrait [!toc] [#GovernorComponent-GovernorVotesTrait] + +- [`clock(self)`](#GovernorComponent-GovernorVotesTrait-clock) +- [`CLOCK_MODE(self)`](#GovernorComponent-GovernorVotesTrait-CLOCK_MODE) +- [`get_votes(self, account, timepoint, params)`](#GovernorComponent-GovernorVotesTrait-get_votes) + +#### GovernorExecutionTrait [!toc] [#GovernorComponent-GovernorExecutionTrait] + +- [`state(self, proposal_id)`](#GovernorComponent-GovernorExecutionTrait-state) +- [`executor(self)`](#GovernorComponent-GovernorExecutionTrait-executor) +- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorComponent-GovernorExecutionTrait-execute_operations) +- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorComponent-GovernorExecutionTrait-queue_operations) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorComponent-GovernorExecutionTrait-proposal_needs_queuing) +- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorComponent-GovernorExecutionTrait-cancel_operations) + +Embeddable Implementations + +#### GovernorImpl [!toc] [#GovernorComponent-GovernorImpl] + +- [`name(self)`](#GovernorComponent-name) +- [`version(self)`](#GovernorComponent-version) +- [`COUNTING_MODE(self)`](#GovernorComponent-COUNTING_MODE) +- [`hash_proposal(self, calls, description_hash)`](#GovernorComponent-hash_proposal) +- [`state(self, proposal_id)`](#GovernorComponent-state) +- [`proposal_threshold(self)`](#GovernorComponent-proposal_threshold) +- [`proposal_snapshot(self, proposal_id)`](#GovernorComponent-proposal_snapshot) +- [`proposal_deadline(self, proposal_id)`](#GovernorComponent-proposal_deadline) +- [`proposal_proposer(self, proposal_id)`](#GovernorComponent-proposal_proposer) +- [`proposal_eta(self, proposal_id)`](#GovernorComponent-proposal_eta) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorComponent-proposal_needs_queuing) +- [`voting_delay(self)`](#GovernorComponent-voting_delay) +- [`voting_period(self)`](#GovernorComponent-voting_period) +- [`quorum(self, timepoint)`](#GovernorComponent-quorum) +- [`get_votes(self, account, timepoint)`](#GovernorComponent-get_votes) +- [`get_votes_with_params(self, account, timepoint, params)`](#GovernorComponent-get_votes_with_params) +- [`has_voted(self, proposal_id, account)`](#GovernorComponent-has_voted) +- [`propose(self, calls, description)`](#GovernorComponent-propose) +- [`queue(self, calls, description_hash)`](#GovernorComponent-queue) +- [`execute(self, calls, description_hash)`](#GovernorComponent-execute) +- [`cancel(self, proposal_id, description_hash)`](#GovernorComponent-cancel) +- [`cast_vote(self, proposal_id, support)`](#GovernorComponent-cast_vote) +- [`cast_vote_with_reason(self, proposal_id, support, reason)`](#GovernorComponent-cast_vote_with_reason) +- [`cast_vote_with_reason_and_params(self, proposal_id, support, reason, params)`](#GovernorComponent-cast_vote_with_reason_and_params) +- [`cast_vote_by_sig(self, proposal_id, support, reason, signature)`](#GovernorComponent-cast_vote_by_sig) +- [`cast_vote_with_reason_and_params_by_sig(self, proposal_id, support, reason, params, signature)`](#GovernorComponent-cast_vote_with_reason_and_params_by_sig) +- [`nonces(self, voter)`](#GovernorComponent-nonces) +- [`relay(self, call)`](#GovernorComponent-relay) + +Internal Implementations + +#### InternalImpl [!toc] [#GovernorComponent-InternalImpl] + +- [`initializer(self)`](#GovernorComponent-initializer) +- [`get_proposal(self, proposal_id)`](#GovernorComponent-get_proposal) +- [`is_valid_description_for_proposer(self, proposer, description)`](#GovernorComponent-is_valid_description_for_proposer) +- [`_hash_proposal(self, calls, description_hash)`](#GovernorComponent-_hash_proposal) +- [`_proposal_snapshot(self, proposal_id)`](#GovernorComponent-_proposal_snapshot) +- [`_proposal_deadline(self, proposal_id)`](#GovernorComponent-_proposal_deadline) +- [`_proposal_proposer(self, proposal_id)`](#GovernorComponent-_proposal_proposer) +- [`_proposal_eta(self, proposal_id)`](#GovernorComponent-_proposal_eta) + +#### InternalExtendedImpl [!toc] [#GovernorComponent-InternalExtendedImpl] + +- [`assert_only_governance(self)`](#GovernorComponent-assert_only_governance) +- [`validate_state(self, proposal_id, allowed_states)`](#GovernorComponent-validate_state) +- [`use_nonce(self, voter)`](#GovernorComponent-use_nonce) +- [`_get_votes(self, account, timepoint, params)`](#GovernorComponent-_get_votes) +- [`_proposal_threshold(self)`](#GovernorComponent-_proposal_threshold) +- [`_state(self, proposal_id)`](#GovernorComponent-_state) +- [`_propose(self, calls, description, proposer)`](#GovernorComponent-_propose) +- [`_cancel(self, proposal_id, description_hash)`](#GovernorComponent-_cancel) +- [`_count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorComponent-_count_vote) +- [`_cast_vote(self, proposal_id, voter, support, reason, params)`](#GovernorComponent-_cast_vote) + +Events + +- [`ProposalCreated(proposal_id, proposer, calls, signatures, vote_start, vote_end, description)`](#GovernorComponent-ProposalCreated) +- [`ProposalQueued(proposal_id)`](#GovernorComponent-ProposalQueued) +- [`ProposalExecuted(proposal_id)`](#GovernorComponent-ProposalExecuted) +- [`ProposalCanceled(proposal_id)`](#GovernorComponent-ProposalCanceled) +- [`VoteCast(voter, proposal_id, support, weight, reason)`](#GovernorComponent-VoteCast) +- [`VoteCastWithParams(voter, proposal_id, support, weight, reason, params)`](#GovernorComponent-VoteCastWithParams) + +#### Extensions traits functions [!toc] [#GovernorComponent-Extensions-Traits] + + +Must return the delay, in number of timepoints, between when the proposal is created and when the vote starts. This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + + + +Must return the delay, in number of timepoints, between the vote start and vote end. + + + +Must return the minimum number of votes that an account must have to create a proposal. + + + +Must return the minimum number of votes required for a proposal to succeed. + + + +Must return a description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. See [COUNTING\_MODE](#GovernorComponent-COUNTING_MODE) for more details. + + + +Must register a vote for `proposal_id` by `account` with a given `support`, voting `weight` and voting `params`. + +Support is generic and can represent various things depending on the voting system used. + + + +Must return whether an account has cast a vote on a proposal. + + + +Must return whether the minimum quorum has been reached for a proposal. + + + +Must return whether a proposal has succeeded or not. + + + +Returns the current timepoint determined by the governor's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + + + +Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + + + +Must return the voting power of an account at a specific timepoint with the given parameters. + + + +Must return the state of a proposal at the current time. + +The state can be either: + +- `Pending`: The proposal does not exist yet. +- `Active`: The proposal is active. +- `Canceled`: The proposal has been canceled. +- `Defeated`: The proposal has been defeated. +- `Succeeded`: The proposal has succeeded. +- `Queued`: The proposal has been queued. +- `Executed`: The proposal has been executed. + + + +Must return the address through which the governor executes action. Should be used to specify whether the module execute actions through another contract such as a timelock. + +MUST be the governor itself, or an instance of TimelockController with the governor as the only proposer, canceller, and executor. + +When the executor is not the governor itself (i.e. a timelock), it can call functions that are restricted with the `assert_only_governance` guard, and also potentially execute transactions on behalf of the governor. Because of this, this module is designed to work with the TimelockController as the unique potential external executor. + + + + +Execution mechanism. Can be used to modify the way operations are executed (for example adding a vault/timelock). + + + +Queuing mechanism. Can be used to modify the way queuing is performed (for example adding a vault/timelock). + +Requirements: + +- Must return a timestamp that describes the expected ETA for execution. If the returned value is 0, the core will consider queueing did not succeed, and the public `queue` function will revert. + + + +Must return whether proposals need to be queued before execution. This usually indicates if the proposal needs to go through a timelock. + + + +Cancel mechanism. Can be used to modify the way canceling is performed (for example adding a vault/timelock). + + +#### Embeddable functions [!toc] [#GovernorComponent-Embeddable-Functions] + + +Name of the governor instance (used in building the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + + + +Version of the governor instance (used in building [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). + + + +A description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. + +There are 2 standard keys: `support` and `quorum`. + +- `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. +- `quorum=bravo` means that only For votes are counted towards quorum. +- `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. + +If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique name that describes the behavior. For example: + +- `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. +- `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. + +The string can be decoded by the standard [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) JavaScript class. + + + + +Hashing function used to (re)build the proposal id from the proposal details. + + + +Returns the state of a proposal, given its id. + + + +The number of votes required in order for a voter to become a proposer. + + + +Timepoint used to retrieve user's votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. + + + +Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. + + + +The account that created a proposal. + + + +The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn't use the governor clock, and instead relies on the executor's clock which may be different. In most cases this will be a timestamp. + + + +Whether a proposal needs to be queued before execution. This indicates if the proposal needs to go through a timelock. + + + +Delay between when a proposal is created and when the vote starts. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + + + +Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. + +The `voting_delay` can delay the start of the vote. This must be considered when setting the voting duration compared to the voting delay. + +This value is stored when the proposal is submitted so that possible changes to the value do not affect proposals that have already been submitted. + + + + +Minimum number of votes required for a proposal to be successful. + +The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows the quorum to scale depending on values such as the total supply of a token at this timepoint. + + + +Returns the voting power of an `account` at a specific `timepoint`. + +This can be implemented in a number of ways, for example by reading the delegated balance from one (or multiple) `ERC20Votes` tokens. + + + +Returns the voting power of an account at a specific timepoint, given additional encoded parameters. + + + +Returns whether an account has cast a vote on a proposal. + + + +Creates a new proposal. Voting starts after the delay specified by `voting_delay` and lasts for a duration specified by `voting_period`. Returns the id of the proposal. + +This function has opt-in frontrunning protection, described in `is_valid_description_for_proposer`. + +The state of the Governor and targets may change between the proposal creation and its execution. This may be the result of third party actions on the targeted contracts, or other governor proposals. For example, the balance of this contract could be updated or its access control permissions may be modified, possibly compromising the proposal's ability to execute successfully (e.g. the governor doesn't have enough value to cover a proposal with multiple transfers). + +Requirements: + +- The proposer must be authorized to submit the proposal. +- The proposer must have enough votes to submit the proposal if `proposal_threshold` is greater than zero. +- The proposal must not already exist. + +Emits a [ProposalCreated](#GovernorComponent-ProposalCreated) event. + + + +Queues a proposal. Some governors require this step to be performed before execution can happen. If queuing is not necessary, this function may revert. Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached. + +Returns the id of the proposal. + +Requirements: + +- The proposal must be in the `Succeeded` state. +- The queue operation must return a non-zero ETA. + +Emits a [ProposalQueued](#GovernorComponent-ProposalQueued) event. + + + + +Executes a successful proposal. This requires the quorum to be reached, the vote to be successful, and the deadline to be reached. Depending on the governor it might also be required that the proposal was queued and that some delay passed. + +Some modules can modify the requirements for execution, for example by adding an additional timelock (See `timelock_controller`). + +Returns the id of the proposal. + +Requirements: + +- The proposal must be in the `Succeeded` or `Queued` state. + +Emits a [ProposalExecuted](#GovernorComponent-ProposalExecuted) event. + + + +Cancels a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e. before the vote starts. + +Returns the id of the proposal. + +Requirements: + +- The proposal must be in the `Pending` state. +- The caller must be the proposer of the proposal. + +Emits a [ProposalCanceled](#GovernorComponent-ProposalCanceled) event. + + + +Cast a vote. + +Requirements: + +- The proposal must be active. + +Emits a [VoteCast](#GovernorComponent-VoteCast) event. + + + +Cast a vote with a `reason`. + +Requirements: + +- The proposal must be active. + +Emits a [VoteCast](#GovernorComponent-VoteCast) event. + + + +Cast a vote with a `reason` and additional serialized `params`. + +Requirements: + +- The proposal must be active. + +Emits either: + +- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. +- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. + + + +Cast a vote using the `voter`'s signature. + +Requirements: + +- The proposal must be active. +- The nonce in the signed message must match the account's current nonce. +- `voter` must implement `SRC6::is_valid_signature`. +- `signature` must be valid for the message hash. + +Emits a [VoteCast](#GovernorComponent-VoteCast) event. + + + +Cast a vote with a `reason` and additional serialized `params` using the `voter`'s signature. + +Requirements: + +- The proposal must be active. +- The nonce in the signed message must match the account's current nonce. +- `voter` must implement `SRC6::is_valid_signature`. +- `signature` must be valid for the message hash. + +Emits either: + +- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. +- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. + + + +Returns the next unused nonce for an address. + + + +Relays a transaction or function call to an arbitrary target. + +In cases where the governance executor is some contract other than the governor itself, like when using a timelock, this function can be invoked in a governance proposal to recover tokens that were sent to the governor contract by mistake. + +If the executor is simply the governor itself, use of `relay` is redundant. + + +#### Internal functions [!toc] [#GovernorComponent-Internal-Functions] + + +Initializes the contract by registering the supported interface id. + + + +Returns the proposal object given its id. + + + +Checks if the proposer is authorized to submit a proposal with the given description. + +If the proposal description ends with `#proposer=0x???`, where `0x???` is an address written as a hex string (case insensitive), then the submission of this proposal will only be authorized to said address. + +This is used for frontrunning protection. By adding this pattern at the end of their proposal, one can ensure that no other address can submit the same proposal. An attacker would have to either remove or change that part, which would result in a different proposal id. + +In Starknet, the Sequencer ensures the order of transactions, but frontrunning can still be achieved by nodes, and potentially other actors in the future with sequencer decentralization. + +If the description does not match this pattern, it is unrestricted and anyone can submit it. This includes: + +- If the `0x???` part is not a valid hex string. +- If the `0x???` part is a valid hex string, but does not contain exactly 64 hex digits. +- If it ends with the expected suffix followed by newlines or other whitespace. +- If it ends with some other similar suffix, e.g. `#other=abc`. +- If it does not end with any such suffix. + + + +Returns the proposal id computed from the given parameters. + +The proposal id is computed as a Pedersen hash of: + +- The array of calls being proposed +- The description hash + + + +Timepoint used to retrieve user's votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. + + + +Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. + + + +The account that created a proposal. + + + +The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn't use the governor clock, and instead relies on the executor's clock which may be different. In most cases this will be a timestamp. + + + +Asserts that the caller is the governance executor. + +When the executor is not the governor itself (i.e. a timelock), it can call functions that are restricted with this modifier, and also potentially execute transactions on behalf of the governor. Because of this, this module is designed to work with the TimelockController as the unique potential external executor. The timelock MUST have the governor as the only proposer, canceller, and executor. + + + +Validates that a proposal is in the expected state. Otherwise it panics. + + + +Consumes a nonce, returns the current value, and increments nonce. + + + +Internal wrapper for `GovernorVotesTrait::get_votes`. + + + +Internal wrapper for `GovernorProposeTrait::proposal_threshold`. + + + +Returns the state of a proposal, given its id. + +Requirements: + +- The proposal must exist. + + + +Internal propose mechanism. Returns the proposal id. + +Requirements: + +- The proposal must not already exist. + +Emits a [ProposalCreated](#GovernorComponent-ProposalCreated) event. + + + +Internal cancel mechanism with minimal restrictions. + +A proposal can be cancelled in any state other than Canceled or Executed. + +Once cancelled, a proposal can't be re-submitted. + + + +Internal wrapper for `GovernorCountingTrait::count_vote`. + + + +Internal vote-casting mechanism. + +Checks that the vote is pending and that it has not been cast yet. This function retrieves the voting weight using `get_votes` and then calls the `_count_vote` internal function. + +Emits either: + +- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. +- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. + + +#### Events [!toc] [#GovernorComponent-Events] + + +Emitted when a proposal is created. + + + +Emitted when a proposal is queued. + + + +Emitted when a proposal is executed. + + + +Emitted when a proposal is canceled. + + + +Emitted when a vote is cast. + + + +Emitted when a vote is cast with params. + + +## [](#governor_extensions)Governor extensions + +The Governor component can (and must) be extended by implementing the [extensions traits](#GovernorComponent-Extensions-Traits-Traits) to add the desired functionality. This can be achieved by directly implementing the traits on your contract, or by using a set of ready-to-use extensions provided by the library, which are presented below. + +### `GovernorCoreExecutionComponent` [toc] [#GovernorCoreExecutionComponent] + + + +```rust +use openzeppelin_governance::governor::extensions::GovernorCoreExecutionComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) providing an execution mechanism directly through the Governor itself. For a timelocked execution mechanism, see [GovernorTimelockExecutionComponent](#GovernorTimelockExecutionComponent). + +Extension traits implementations + +#### GovernorExecution [!toc] [#GovernorCoreExecutionComponent-GovernorExecution] + +- [`state(self, proposal_id)`](#GovernorCoreExecutionComponent-state) +- [`executor(self)`](#GovernorCoreExecutionComponent-executor) +- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorCoreExecutionComponent-execute_operations) +- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorCoreExecutionComponent-queue_operations) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorCoreExecutionComponent-proposal_needs_queuing) +- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorCoreExecutionComponent-cancel_operations) + +#### Extension traits functions [!toc] [#GovernorCoreExecutionComponent-Extension-Traits-Functions] + + +Returns the state of a proposal given its id. + +Requirements: + +- The proposal must exist. + + + +Returns the executor address. + +In this case, it returns the governor contract address since execution is performed directly through it. + + + +Executes the proposal's operations directly through the governor contract. + + + +In this implementation, queuing is not required so it returns 0. + + + +In this implementation, it always returns false. + + + +Cancels a proposal's operations. + + +### `GovernorCountingSimpleComponent` [toc] [#GovernorCountingSimpleComponent] + + + +```rust +use openzeppelin_governance::governor::extensions::GovernorCountingSimpleComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for simple vote counting with three options. + +Extension traits implementations + +#### GovernorCounting [!toc] [#GovernorCountingSimpleComponent-GovernorCounting] + +- [`counting_mode(self)`](#GovernorCountingSimpleComponent-counting_mode) +- [`count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorCountingSimpleComponent-count_vote) +- [`has_voted(self, proposal_id, account)`](#GovernorCountingSimpleComponent-has_voted) +- [`quorum_reached(self, proposal_id)`](#GovernorCountingSimpleComponent-quorum_reached) +- [`vote_succeeded(self, proposal_id)`](#GovernorCountingSimpleComponent-vote_succeeded) + +#### Extension traits functions [!toc] [#GovernorCountingSimpleComponent-Extension-Traits-Functions] + + +Returns `"support=bravo&quorum=for,abstain"`. + +- `support=bravo` indicates that the support follows the Governor Bravo format where voters can vote For, Against, or Abstain +- `quorum=for,abstain` indicates that both For and Abstain votes count toward quorum + + + +Records a vote for a proposal. + +The support value follows the `VoteType` enum (0=Against, 1=For, 2=Abstain). + +Returns the weight that was counted. + + + +Returns whether an account has cast a vote on a proposal. + + + + +Returns whether a proposal has reached quorum. + +In this implementation, both For and Abstain votes count toward quorum. + + + +Returns whether a proposal has succeeded. + +In this implementation, the For votes must be strictly greater than Against votes. + + +### `GovernorSettingsComponent` [toc] [#GovernorSettingsComponent] + + + +```rust +use openzeppelin_governance::governor::extensions::GovernorSettingsComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for settings that are updatable through governance. + +Extension traits implementations + +#### GovernorSettings [!toc] [#GovernorSettingsComponent-GovernorSettings] + +- [`voting_delay(self)`](#GovernorSettingsComponent-voting_delay) +- [`voting_period(self)`](#GovernorSettingsComponent-voting_period) +- [`proposal_threshold(self)`](#GovernorSettingsComponent-proposal_threshold) + +Embeddable implementations + +#### GovernorSettingsAdminImpl [!toc] [#GovernorSettingsComponent-GovernorSettingsAdminImpl] + +- [`set_voting_delay(self, new_voting_delay)`](#GovernorSettingsComponent-set_voting_delay) +- [`set_voting_period(self, new_voting_period)`](#GovernorSettingsComponent-set_voting_period) +- [`set_proposal_threshold(self, new_proposal_threshold)`](#GovernorSettingsComponent-set_proposal_threshold) + +Internal implementations + +#### InternalImpl [!toc] [#GovernorSettingsComponent-InternalImpl] + +- [`initializer(self, new_voting_delay, new_voting_period, new_proposal_threshold)`](#GovernorSettingsComponent-initializer) +- [`assert_only_governance(self)`](#GovernorSettingsComponent-assert_only_governance) +- [`_set_voting_delay(self, new_voting_delay)`](#GovernorSettingsComponent-_set_voting_delay) +- [`_set_voting_period(self, new_voting_period)`](#GovernorSettingsComponent-_set_voting_period) +- [`_set_proposal_threshold(self, new_proposal_threshold)`](#GovernorSettingsComponent-_set_proposal_threshold) + +Events + +- [`VotingDelayUpdated(old_voting_delay, new_voting_delay)`](#GovernorSettingsComponent-VotingDelayUpdated) +- [`VotingPeriodUpdated(old_voting_period, new_voting_period)`](#GovernorSettingsComponent-VotingPeriodUpdated) +- [`ProposalThresholdUpdated(old_proposal_threshold, new_proposal_threshold)`](#GovernorSettingsComponent-ProposalThresholdUpdated) + +#### Extension traits functions [!toc] [#GovernorSettingsComponent-ExtensionTraitsFunctions] + + +Returns the delay, between when a proposal is created and when voting starts. + + + +Returns the time period, during which votes can be cast. + + + +Returns the minimum number of votes required for an account to create a proposal. + + +#### Embeddable functions [!toc] [#GovernorSettingsComponent-EmbeddableFunctions] + + +Sets the voting delay. + +Requirements: + +- Caller must be the governance executor. + +This function does not emit an event if the new voting delay is the same as the old one. + +May emit a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated) event. + + + +Sets the voting period. + +This function does not emit an event if the new voting period is the same as the old one. + +Requirements: + +- Caller must be the governance executor. +- `new_voting_period` must be greater than 0. + +May emit a [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated) event. + + + +Sets the proposal threshold. + +This function does not emit an event if the new proposal threshold is the same as the old one. + +Requirements: + +- Caller must be the governance executor. + +May emit a [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. + + +#### Internal functions [!toc] [#GovernorSettingsComponent-InternalFunctions] + + +Initializes the component by setting the default values. + +Requirements: + +- `new_voting_period` must be greater than 0. + +Emits a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated), [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated), and [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. + + + +Asserts that the caller is the governance executor. + + + +Internal function to update the voting delay. + +This function does not emit an event if the new voting delay is the same as the old one. + +May emit a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated) event. + + + +Internal function to update the voting period. + +Requirements: + +- `new_voting_period` must be greater than 0. + +This function does not emit an event if the new voting period is the same as the old one. + +May emit a [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated) event. + + + +Internal function to update the proposal threshold. + +This function does not emit an event if the new proposal threshold is the same as the old one. + +May emit a [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. + + +#### Events [!toc] [#GovernorSettingsComponent-Events] + + +Emitted when the voting delay is updated. + + + +Emitted when the voting period is updated. + + + +Emitted when the proposal threshold is updated. + + +### `GovernorVotesComponent` [toc] [#GovernorVotesComponent] + + + +```rust +use openzeppelin_governance::governor::extensions::GovernorVotesComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for voting weight extraction from a token with the [IVotes](#IVotes) extension. + +Extension traits implementations + +#### GovernorVotes [!toc] [#GovernorVotesComponent-GovernorVotes] + +- [`clock(self)`](#GovernorVotesComponent-clock) +- [`CLOCK_MODE(self)`](#GovernorVotesComponent-CLOCK_MODE) +- [`get_votes(self, account, timepoint, params)`](#GovernorVotesComponent-get_votes) + +Embeddable implementations + +#### VotesTokenImpl [!toc] [#GovernorVotesComponent-VotesTokenImpl] + +- [`token(self)`](#GovernorVotesComponent-token) + +Internal implementations + +#### InternalImpl [!toc] [#GovernorVotesComponent-InternalImpl] + +- [`initializer(self, votes_token)`](#GovernorVotesComponent-initializer) + +#### Extension traits functions [!toc] [#GovernorVotesComponent-ExtensionTraitsFunctions] + + +Returns the current timepoint determined by the governor's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + + + +Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + + + +Returns the voting power of `account` at a specific `timepoint` using the votes token. + + +#### Embeddable functions [!toc] [#GovernorVotesComponent-EmbeddableFunctions] + + +Returns the votes token that voting power is sourced from. + + +#### Internal functions [!toc] [#GovernorVotesComponent-InternalFunctions] + + +Initializes the component by setting the votes token. + +Requirements: + +- `votes_token` must not be zero. + + +### `GovernorVotesQuorumFractionComponent` [toc] [#GovernorVotesQuorumFractionComponent] + + + +```rust +use openzeppelin_governance::governor::extensions::GovernorVotesQuorumFractionComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) for voting weight extraction from a token with the [IVotes](#IVotes) extension and a quorum expressed as a fraction of the total supply. + +Extension traits implementations + +#### GovernorQuorum [!toc] [#GovernorVotesQuorumFractionComponent-GovernorQuorum] + +- [`quorum(self, timepoint)`](#GovernorVotesQuorumFractionComponent-quorum) + +#### GovernorVotes [!toc] [#GovernorVotesQuorumFractionComponent-GovernorVotes] + +- [`clock(self)`](#GovernorVotesQuorumFractionComponent-clock) +- [`CLOCK_MODE(self)`](#GovernorVotesQuorumFractionComponent-CLOCK_MODE) +- [`get_votes(self, account, timepoint, params)`](#GovernorVotesQuorumFractionComponent-get_votes) + +Embeddable implementations + +#### QuorumFractionImpl [!toc] [#GovernorVotesQuorumFractionComponent-QuorumFractionImpl] + +- [`token(self)`](#GovernorVotesQuorumFractionComponent-token) +- [`current_quorum_numerator(self)`](#GovernorVotesQuorumFractionComponent-current_quorum_numerator) +- [`quorum_numerator(self, timepoint)`](#GovernorVotesQuorumFractionComponent-quorum_numerator) +- [`quorum_denominator(self)`](#GovernorVotesQuorumFractionComponent-quorum_denominator) + +Internal implementations + +#### InternalImpl [!toc] [#GovernorVotesQuorumFractionComponent-InternalImpl] + +- [`initializer(self, votes_token, quorum_numerator)`](#GovernorVotesQuorumFractionComponent-initializer) +- [`update_quorum_numerator(self, new_quorum_numerator)`](#GovernorVotesQuorumFractionComponent-update_quorum_numerator) + +Events + +- [`QuorumNumeratorUpdated(old_quorum_numerator, new_quorum_numerator)`](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) + +#### Extension traits functions [!toc] [#GovernorVotesQuorumFractionComponent-ExtensionTraitsFunctions] + + +It is computed as a percentage of the votes token total supply at a given `timepoint` in the past. + + + +Returns the current timepoint determined by the governor's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + + + +Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + + + +Returns the voting power of `account` at a specific `timepoint` using the votes token. + + +#### Embeddable functions [!toc] [#GovernorVotesQuorumFractionComponent-EmbeddableFunctions] + + +Returns the address of the votes token used for voting power extraction. + + + +Returns the current quorum numerator value. + + + +Returns the quorum numerator value at a specific `timepoint` in the past. + + + +Returns the quorum denominator value. + + +#### Internal functions [!toc] [#GovernorVotesQuorumFractionComponent-InternalFunctions] + + +Initializes the component by setting the votes token and the initial quorum numerator value. + +Requirements: + +- `votes_token` must not be zero. +- `quorum_numerator` must be less than `quorum_denominator`. + +Emits a [QuorumNumeratorUpdated](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) event. + + + +Updates the quorum numerator. + +This function does not emit an event if the new quorum numerator is the same as the old one. + +Requirements: + +- `new_quorum_numerator` must be less than `quorum_denominator`. + +May emit a [QuorumNumeratorUpdated](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) event. + + +#### Events [!toc] [#GovernorVotesQuorumFractionComponent-Events] + + +Emitted when the quorum numerator is updated. + + +### `GovernorTimelockExecutionComponent` [toc] [#GovernorTimelockExecutionComponent] + + + +```rust +use openzeppelin_governance::governor::extensions::GovernorTimelockExecutionComponent; +``` + +Extension of [GovernorComponent](#GovernorComponent) that binds the execution process to an instance of a contract implementing [TimelockControllerComponent](#TimelockControllerComponent). This adds a delay, enforced by the timelock to all successful proposals (in addition to the voting duration). + +The Governor needs the [PROPOSER, EXECUTOR, and CANCELLER roles](../governance/timelock#roles) to work properly. + +Using this model means the proposal will be operated by the timelock and not by the governor. Thus, the assets and permissions must be attached to the timelock. Any asset sent to the governor will be inaccessible from a proposal, unless executed via `Governor::relay`. + +Setting up the timelock to have additional proposers or cancellers besides the governor is very risky, as it grants them the ability to: 1) execute operations as the timelock, and thus possibly performing operations or accessing funds that are expected to only be accessible through a vote, and 2) block governance proposals that have been approved by the voters, effectively executing a Denial of Service attack. + +Extension traits implementations + +#### GovernorExecution [!toc] [#GovernorTimelockExecutionComponent-GovernorExecution] + +- [`state(self, proposal_id)`](#GovernorTimelockExecutionComponent-state) +- [`executor(self)`](#GovernorTimelockExecutionComponent-executor) +- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorTimelockExecutionComponent-execute_operations) +- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorTimelockExecutionComponent-queue_operations) +- [`proposal_needs_queuing(self, proposal_id)`](#GovernorTimelockExecutionComponent-proposal_needs_queuing) +- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorTimelockExecutionComponent-cancel_operations) + +Embeddable implementations + +#### TimelockedImpl [!toc] [#GovernorTimelockExecutionComponent-TimelockedImpl] + +- [`timelock(self)`](#GovernorTimelockExecutionComponent-timelock) +- [`get_timelock_id(self, proposal_id)`](#GovernorTimelockExecutionComponent-get_timelock_id) +- [`update_timelock(self, new_timelock)`](#GovernorTimelockExecutionComponent-update_timelock) + +Internal implementations + +#### InternalImpl [!toc] [#GovernorTimelockExecutionComponent-InternalImpl] + +- [`initializer(self, timelock_controller)`](#GovernorTimelockExecutionComponent-initializer) +- [`assert_only_governance(self)`](#GovernorTimelockExecutionComponent-assert_only_governance) +- [`timelock_salt(self, description_hash)`](#GovernorTimelockExecutionComponent-timelock_salt) +- [`get_timelock_dispatcher(self)`](#GovernorTimelockExecutionComponent-get_timelock_dispatcher) +- [`_update_timelock(self, new_timelock)`](#GovernorTimelockExecutionComponent-_update_timelock) + +Events + +- [`TimelockUpdated(old_timelock, new_timelock)`](#GovernorTimelockExecutionComponent-TimelockUpdated) + +#### Extension traits functions [!toc] [#GovernorTimelockExecutionComponent-ExtensionTraitsFunctions] + + +Returns the state of a proposal given its id. + +Requirements: + +- The proposal must exist. + + + +Returns the executor address. + +In this module, the executor is the timelock controller. + + + +Runs the already queued proposal through the timelock. + + + +Queue a proposal to the timelock. + +Returns the eta for the execution of the queued proposal. + + + +In this implementation, it always returns true. + + + +Cancels the timelocked proposal if it has already been queued. + + +#### Embeddable functions [!toc] [#GovernorTimelockExecutionComponent-EmbeddableFunctions] + + +Returns the timelock controller address. + + + +Returns the timelock proposal id for a given proposal id. + + + +Updates the associated timelock. + +Requirements: + +- The caller must be the governance. + +Emits a [TimelockUpdated](#GovernorTimelockExecutionComponent-TimelockUpdated) event. + + +#### Internal functions [!toc] [#GovernorTimelockExecutionComponent-InternalFunctions] + + +Initializes the timelock controller. + +Requirements: + +- The timelock must not be the zero address. + + + +Ensures the caller is the executor (the timelock controller in this case). + + + +Computes the `TimelockController` operation salt as the XOR of the governor address and `description_hash`. + +It is computed with the governor address itself to avoid collisions across governor instances using the same timelock. + + + +Returns a dispatcher for interacting with the timelock controller. + + + +Internal function to update the timelock controller address. + +Emits a [TimelockUpdated](#GovernorTimelockExecutionComponent-TimelockUpdated) event. + + +#### Events [!toc] [#GovernorTimelockExecutionComponent-Events] + + +Emitted when the timelock controller is updated. + + +## [](#multisig)Multisig + +A Multisig module enhances security and decentralization by requiring multiple signers to approve and execute transactions. Features include configurable quorum, signer management, and self-administration, ensuring collective decision-making and transparency for critical operations. + +### `MultisigComponent` [toc] [#MultisigComponent] + + + +```rust +use openzeppelin_governance::multisig::MultisigComponent; +``` + +Component that implements [IMultisig](#IMultisig) and provides functionality for multisignature wallets, including transaction management, quorum handling, and signer operations. + +Embeddable Implementations + +#### MultisigImpl [!toc] [#MultisigComponent-MultisigImpl] + +- [`get_quorum(self)`](#MultisigComponent-get_quorum) +- [`is_signer(self, signer)`](#MultisigComponent-is_signer) +- [`get_signers(self)`](#MultisigComponent-get_signers) +- [`is_confirmed(self, id)`](#MultisigComponent-is_confirmed) +- [`is_confirmed_by(self, id, signer)`](#MultisigComponent-is_confirmed_by) +- [`is_executed(self, id)`](#MultisigComponent-is_executed) +- [`get_submitted_block(self, id)`](#MultisigComponent-get_submitted_block) +- [`get_transaction_state(self, id)`](#MultisigComponent-get_transaction_state) +- [`get_transaction_confirmations(self, id)`](#MultisigComponent-get_transaction_confirmations) +- [`hash_transaction(self, to, selector, calldata, salt)`](#MultisigComponent-hash_transaction) +- [`hash_transaction_batch(self, calls, salt)`](#MultisigComponent-hash_transaction_batch) +- [`add_signers(ref self, new_quorum, signers_to_add)`](#MultisigComponent-add_signers) +- [`remove_signers(ref self, new_quorum, signers_to_remove)`](#MultisigComponent-remove_signers) +- [`replace_signer(ref self, signer_to_remove, signer_to_add)`](#MultisigComponent-replace_signer) +- [`change_quorum(ref self, new_quorum)`](#MultisigComponent-change_quorum) +- [`submit_transaction(ref self, to, selector, calldata, salt)`](#MultisigComponent-submit_transaction) +- [`submit_transaction_batch(ref self, calls, salt)`](#MultisigComponent-submit_transaction_batch) +- [`confirm_transaction(ref self, id)`](#MultisigComponent-confirm_transaction) +- [`revoke_confirmation(ref self, id)`](#MultisigComponent-revoke_confirmation) +- [`execute_transaction(ref self, to, selector, calldata, salt)`](#MultisigComponent-execute_transaction) +- [`execute_transaction_batch(ref self, calls, salt)`](#MultisigComponent-execute_transaction_batch) + +Internal Implementations + +#### InternalImpl [!toc] [#MultisigComponent-InternalImpl] + +- [`initializer(ref self, quorum, signers)`](#MultisigComponent-initializer) +- [`resolve_tx_state(self, id)`](#MultisigComponent-resolve_tx_state) +- [`assert_one_of_signers(self, caller)`](#MultisigComponent-assert_one_of_signers) +- [`assert_tx_exists(self, id)`](#MultisigComponent-assert_tx_exists) +- [`assert_only_self(self)`](#MultisigComponent-assert_only_self) +- [`_add_signers(ref self, new_quorum, signers_to_add)`](#MultisigComponent-_add_signers) +- [`_remove_signers(ref self, new_quorum, signers_to_remove)`](#MultisigComponent-_remove_signers) +- [`_replace_signer(ref self, signer_to_remove, signer_to_add)`](#MultisigComponent-_replace_signer) +- [`_change_quorum(ref self, new_quorum)`](#MultisigComponent-_change_quorum) + +Events + +- [`SignerAdded(signer)`](#MultisigComponent-SignerAdded) +- [`SignerRemoved(signer)`](#MultisigComponent-SignerRemoved) +- [`QuorumUpdated(old_quorum, new_quorum)`](#MultisigComponent-QuorumUpdated) +- [`TransactionSubmitted(id, signer)`](#MultisigComponent-TransactionSubmitted) +- [`TransactionConfirmed(id, signer)`](#MultisigComponent-TransactionConfirmed) +- [`ConfirmationRevoked(id, signer)`](#MultisigComponent-ConfirmationRevoked) +- [`TransactionExecuted(id)`](#MultisigComponent-TransactionExecuted) +- [`CallSalt(id, salt)`](#MultisigComponent-CallSalt) + +#### Embeddable functions [!toc] [#MultisigComponent-EmbeddableFunctions] + + +Returns the current quorum value. + + + +Checks if a given `signer` is registered. + + + +Returns a list of all current signers. + + + +Returns whether the transaction with the given `id` has been confirmed. A confirmed transaction has received the required number of confirmations (quorum). + + + +Returns whether the transaction with the given `id` has been confirmed by the specified `signer`. + + + +Returns whether the transaction with the given `id` has been executed. + + + +Returns the block number when the transaction with the given `id` was submitted. + + + +Returns the current state of the transaction with the given `id`. + +The possible states are: + +- `NotFound`: the transaction does not exist. +- `Pending`: the transaction exists but hasn't reached the required confirmations. +- `Confirmed`: the transaction has reached the required confirmations but hasn't been executed. +- `Executed`: the transaction has been executed. + + + +Returns the number of confirmations from registered signers for the transaction with the specified `id`. + + + +Returns the computed identifier of a transaction containing a single call. + + + +Returns the computed identifier of a transaction containing a batch of calls. + + + +Adds new signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after addition. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each signer added. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + + + +Removes signers and updates the quorum. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be less than or equal to the total number of signers after removal. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for each signer removed. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + + + +Replaces an existing signer with a new signer. + +Requirements: + +- The caller must be the contract itself. +- `signer_to_remove` must be an existing signer. +- `signer_to_add` must not be an existing signer. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for the removed signer. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for the new signer. + + + +Updates the quorum value to `new_quorum`. + +Requirements: + +- The caller must be the contract itself. +- `new_quorum` must be non-zero. +- `new_quorum` must be less than or equal to the total number of signers. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + + + +Submits a new transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#MultisigComponent-TransactionSubmitted) event. + +Emits a [CallSalt](#MultisigComponent-CallSalt) event if `salt` is not zero. + + + +Submits a new batch transaction for confirmation. + +Requirements: + +- The caller must be a registered signer. +- The transaction must not have been submitted before. + +Emits a [TransactionSubmitted](#MultisigComponent-TransactionSubmitted) event. + +Emits a [CallSalt](#MultisigComponent-CallSalt) event if `salt` is not zero. + + + +Confirms a transaction with the given `id`. + +Requirements: + +- The caller must be a registered signer. +- The transaction must exist and not be executed. +- The caller must not have already confirmed the transaction. + +Emits a [TransactionConfirmed](#MultisigComponent-TransactionConfirmed) event. + + + +Revokes a previous confirmation for a transaction with the given `id`. + +Requirements: + +- The transaction must exist and not be executed. +- The caller must have previously confirmed the transaction. + +Emits a [ConfirmationRevoked](#MultisigComponent-ConfirmationRevoked) event. + + + +Executes a confirmed transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#MultisigComponent-TransactionExecuted) event. + + + +Executes a confirmed batch transaction. + +Requirements: + +- The caller must be a registered signer. +- The transaction must be confirmed and not yet executed. + +Emits a [TransactionExecuted](#MultisigComponent-TransactionExecuted) event. + + +#### Internal functions [!toc] [#MultisigComponent-InternalFunctions] + + +Initializes the Multisig component with the initial `quorum` and `signers`. This function must be called during contract initialization to set up the initial state. + +Requirements: + +- `quorum` must be non-zero and less than or equal to the number of `signers`. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each signer added. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event. + + + +Resolves and returns the current state of the transaction with the given `id`. + +The possible states are: + +- `NotFound`: the transaction does not exist. +- `Pending`: the transaction exists but hasn't reached the required confirmations. +- `Confirmed`: the transaction has reached the required confirmations but hasn't been executed. +- `Executed`: the transaction has been executed. + + + +Asserts that the `caller` is one of the registered signers. + +Requirements: + +- The `caller` must be a registered signer. + + + +Asserts that a transaction with the given `id` exists. + +Requirements: + +- The transaction with the given `id` must have been submitted. + + + +Asserts that the caller is the contract itself. + +Requirements: + +- The caller must be the contract's own address. + + + +Adds new signers and updates the quorum. + +Requirements: + +- Each signer address must be non-zero. +- `new_quorum` must be non-zero and less than or equal to the total number of signers after addition. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each new signer added. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + + + +Removes existing signers and updates the quorum. + +Requirements: + +- `new_quorum` must be non-zero and less than or equal to the total number of signers after removal. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for each signer removed. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + + + +Replaces an existing signer with a new signer. + +Requirements: + +- `signer_to_remove` must be an existing signer. +- `signer_to_add` must not be an existing signer. +- `signer_to_add` must be a non-zero address. + +Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for the removed signer. + +Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for the new signer. + + + +Updates the quorum value to `new_quorum` if it differs from the current quorum. + +Requirements: + +- `new_quorum` must be non-zero. +- `new_quorum` must be less than or equal to the total number of signers. + +Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. + + +#### Events [!toc] [#MultisigComponent-Events] + + +Emitted when a new `signer` is added. + + + +Emitted when a `signer` is removed. + + + +Emitted when the `quorum` value is updated. + + + +Emitted when a new transaction is submitted by a `signer`. + + + +Emitted when a transaction is confirmed by a `signer`. + + + +Emitted when a `signer` revokes his confirmation. + + + +Emitted when a transaction is executed. + + + +Emitted when a new transaction is submitted with non-zero salt. + + +## [](#timelock)Timelock + +In a governance system, `TimelockControllerComponent` is in charge of introducing a delay between a proposal and its execution. + +### `TimelockControllerComponent` [toc] [#TimelockControllerComponent] + + + +```rust +use openzeppelin_governance::timelock::TimelockControllerComponent; +``` + +Component that implements [ITimelock](#ITimelock) and enables the implementing contract to act as a timelock controller. + +[Embeddable Mixin Implementations](../components#mixins) + +#### TimelockMixinImpl [!toc] [#TimelockControllerComponent-TimelockMixinImpl] + +- [`TimelockImpl`](#TimelockControllerComponent-Embeddable-Impls-TimelockImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) +- [`AccessControlImpl`](./access#AccessControlComponent-Embeddable-Impls) +- [`AccessControlCamelImpl`](./access#AccessControlComponent-Embeddable-Impls) + +Embeddable Implementations + +#### TimelockImpl [!toc] [#TimelockControllerComponent-TimelockImpl] + +- [`is_operation(self, id)`](#TimelockControllerComponent-is_operation) +- [`is_operation_pending(self, id)`](#TimelockControllerComponent-is_operation_pending) +- [`is_operation_ready(self, id)`](#TimelockControllerComponent-is_operation_ready) +- [`is_operation_done(self, id)`](#TimelockControllerComponent-is_operation_done) +- [`get_timestamp(self, id)`](#TimelockControllerComponent-get_timestamp) +- [`get_operation_state(self, id)`](#TimelockControllerComponent-get_operation_state) +- [`get_min_delay(self)`](#TimelockControllerComponent-get_min_delay) +- [`hash_operation(self, call, predecessor, salt)`](#TimelockControllerComponent-hash_operation) +- [`hash_operation_batch(self, calls, predecessor, salt)`](#TimelockControllerComponent-hash_operation_batch) +- [`schedule(self, call, predecessor, salt, delay)`](#TimelockControllerComponent-schedule) +- [`schedule_batch(self, calls, predecessor, salt, delay)`](#TimelockControllerComponent-schedule_batch) +- [`cancel(self, id)`](#TimelockControllerComponent-cancel) +- [`execute(self, call, predecessor, salt)`](#TimelockControllerComponent-execute) +- [`execute_batch(self, calls, predecessor, salt)`](#TimelockControllerComponent-execute_batch) +- [`update_delay(self, new_delay)`](#TimelockControllerComponent-update_delay) + +#### SRC5Impl [!toc] [#TimelockControllerComponent-SRC5Impl] + +- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) + +#### AccessControlImpl [!toc] [#TimelockControllerComponent-AccessControlImpl] + +- [`has_role(self, role, account)`](./access#IAccessControl-has_role) +- [`get_role_admin(self, role)`](./access#IAccessControl-get_role_admin) +- [`grant_role(self, role, account)`](./access#IAccessControl-grant_role) +- [`revoke_role(self, role, account)`](./access#IAccessControl-revoke_role) +- [`renounce_role(self, role, account)`](./access#IAccessControl-renounce_role) + +#### AccessControlCamelImpl [!toc] [#TimelockControllerComponent-AccessControlCamelImpl] + +- [`hasRole(self, role, account)`](./access#IAccessControl-hasRole) +- [`getRoleAdmin(self, role)`](./access#IAccessControl-getRoleAdmin) +- [`grantRole(self, role, account)`](./access#IAccessControl-grantRole) +- [`revokeRole(self, role, account)`](./access#IAccessControl-revokeRole) +- [`renounceRole(self, role, account)`](./access#IAccessControl-renounceRole) + +Internal Implementations + +#### InternalImpl [!toc] [#TimelockControllerComponent-InternalImpl] + +- [`initializer(self, min_delay, proposers, executors, admin)`](#TimelockControllerComponent-initializer) +- [`assert_only_role(self, role)`](#TimelockControllerComponent-assert_only_role) +- [`assert_only_role_or_open_role(self, role)`](#TimelockControllerComponent-assert_only_role_or_open_role) +- [`assert_only_self(self)`](#TimelockControllerComponent-assert_only_self) +- [`_before_call(self, id, predecessor)`](#TimelockControllerComponent-_before_call) +- [`_after_call(self, id)`](#TimelockControllerComponent-_after_call) +- [`_schedule(self, id, delay)`](#TimelockControllerComponent-_schedule) +- [`_execute(self, call)`](#TimelockControllerComponent-_execute) + +Events + +- [`CallScheduled(id, index, call, predecessor, delay)`](#TimelockControllerComponent-CallScheduled) +- [`CallExecuted(id, index, call)`](#TimelockControllerComponent-CallExecuted) +- [`CallSalt(id, salt)`](#TimelockControllerComponent-CallSalt) +- [`CallCancelled(id)`](#TimelockControllerComponent-CallCancelled) +- [`MinDelayChanged(old_duration, new_duration)`](#TimelockControllerComponent-MinDelayChanged) + +#### Embeddable functions [!toc] [#TimelockControllerComponent-EmbeddableFunctions] + + +Returns whether `id` corresponds to a registered operation. This includes the OperationStates: `Waiting`, `Ready`, and `Done`. + + + +Returns whether the `id` OperationState is pending or not. Note that a pending operation may be either `Waiting` or `Ready`. + + + +Returns whether the `id` OperationState is `Ready` or not. + + + +Returns whether the `id` OperationState is `Done` or not. + + + +Returns the timestamp at which `id` becomes `Ready`. + +`0` means the OperationState is `Unset` and `1` means the OperationState is `Done`. + + + +Returns the current state of the operation with the given `id`. + +The possible states are: + +- `Unset`: the operation has not been scheduled or has been canceled. +- `Waiting`: the operation has been scheduled and is pending the scheduled delay. +- `Ready`: the timer has expired, and the operation is eligible for execution. +- `Done`: the operation has been executed. + + + +Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls `update_delay`. + + + +Returns the identifier of an operation containing a single transaction. + + + +Returns the identifier of an operation containing a batch of transactions. + + + +Schedule an operation containing a single transaction. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. +- The proposal must not already exist. +- `delay` must be greater than or equal to the min delay. + +Emits [CallScheduled](#TimelockControllerComponent-CallScheduled) event. Emits [CallSalt](#TimelockControllerComponent-CallSalt) event if `salt` is not zero. + + + +Schedule an operation containing a batch of transactions. + +Requirements: + +- The caller must have the `PROPOSER_ROLE` role. +- The proposal must not already exist. +- `delay` must be greater than or equal to the min delay. + +Emits one [CallScheduled](#TimelockControllerComponent-CallScheduled) event for each transaction in the batch. Emits [CallSalt](#TimelockControllerComponent-CallSalt) event if `salt` is not zero. + + + +Cancels an operation. A canceled operation returns to `Unset` OperationState. + +Requirements: + +- The caller must have the `CANCELLER_ROLE` role. +- `id` must be a pending operation. + +Emits a [CallCancelled](#TimelockControllerComponent-CallCancelled) event. + + + +Execute a (Ready) operation containing a single Call. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#TimelockControllerComponent-CallExecuted) event. + +This function can reenter, but it doesn't pose a risk because [`_after_call(self: @ContractState, id: felt252)` internal](#TimelockControllerComponent-_after_call) checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + + + +Execute a (Ready) operation containing a batch of Calls. + +Requirements: + +- Caller must have `EXECUTOR_ROLE`. +- `id` must be in Ready OperationState. +- `predecessor` must either be `0` or in Done OperationState. + +Emits a [CallExecuted](#TimelockControllerComponent-CallExecuted) event for each Call. + +This function can reenter, but it doesn't pose a risk because `_after_call` checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. + + + +Changes the minimum timelock duration for future operations. + +Requirements: + +- The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function. + +Emits a [MinDelayChanged](#TimelockControllerComponent-MinDelayChanged) event. + + +#### Internal functions [!toc] [#TimelockControllerComponent-InternalFunctions] + + +Initializes the contract by registering support for SRC5 and AccessControl. + +This function also configures the contract with the following parameters: + +- `min_delay`: initial minimum delay in seconds for operations. +- `proposers`: accounts to be granted proposer and canceller roles. +- `executors`: accounts to be granted executor role. +- `admin`: optional account to be granted admin role; disable with zero address. + +The optional admin can aid with initial configuration of roles after deployment without being subject to delay, but this role should be subsequently renounced in favor of administration through timelocked proposals. + +Emits two [IAccessControl::RoleGranted](./access#IAccessControl-RoleGranted) events for each account in `proposers` with `PROPOSER_ROLE` and `CANCELLER_ROLE` roles. + +Emits a [IAccessControl::RoleGranted](./access#IAccessControl-RoleGranted) event for each account in `executors` with `EXECUTOR_ROLE` role. + +May emit a [IAccessControl::RoleGranted](./access#IAccessControl-RoleGranted) event for `admin` with `DEFAULT_ADMIN_ROLE` role (if `admin` is not zero). + +Emits [MinDelayChanged](#TimelockControllerComponent-MinDelayChanged) event. + + + +Validates that the caller has the given `role`. Otherwise it panics. + + + +Validates that the caller has the given `role`. If `role` is granted to the zero address, then this is considered an open role which allows anyone to be the caller. + + + +Validates that the caller is the timelock contract itself. Otherwise it panics. + + + +Private function that checks before execution of an operation's calls. + +Requirements: + +- `id` must be in the `Ready` OperationState. +- `predecessor` must either be zero or be in the `Done` OperationState. + + + +Private function that checks after execution of an operation's calls and sets the OperationState of `id` to `Done`. + +Requirements: + +- `id` must be in the Ready OperationState. + + + +Private function that schedules an operation that is to become valid after a given `delay`. + + + +Private function that executes an operation's calls. + + +#### Events [!toc] [#TimelockControllerComponent-Events] + + +Emitted when `call` is scheduled as part of operation `id`. + + + +Emitted when `call` is performed as part of operation `id`. + + + +Emitted when a new proposal is scheduled with non-zero salt. + + + +Emitted when operation `id` is cancelled. + + + +Emitted when the minimum delay for future operations is modified. + + +## [](#votes)Votes + +The `VotesComponent` provides a flexible system for tracking and delegating voting power. This system allows users to delegate their voting power to other addresses, enabling more active participation in governance. + +### `VotesComponent` [toc] [#VotesComponent] + + + +```rust +use openzeppelin_governance::votes::VotesComponent; +``` + +Component that implements the [IVotes](#IVotes) interface and provides a flexible system for tracking and delegating voting power. + +By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + +When using this module, your contract must implement the [VotingUnitsTrait](#VotingUnitsTrait). For convenience, this is done automatically for `ERC20` and `ERC721` tokens. + +Voting Units Trait Implementations + +#### ERC20VotesImpl [!toc] [#VotesComponent-ERC20VotesImpl] + +- [`get_voting_units(self, account)`](#VotesComponent-ERC20VotesImpl-get_voting_units) + +#### ERC721VotesImpl [!toc] [#VotesComponent-ERC721VotesImpl] + +- [`get_voting_units(self, account)`](#VotesComponent-ERC721VotesImpl-get_voting_units) + +Embeddable Implementations + +#### VotesImpl [!toc] [#VotesComponent-VotesImpl] + +- [`get_votes(self, account)`](#VotesComponent-get_votes) +- [`get_past_votes(self, account, timepoint)`](#VotesComponent-get_past_votes) +- [`get_past_total_supply(self, timepoint)`](#VotesComponent-get_past_total_supply) +- [`delegates(self, account)`](#VotesComponent-delegates) +- [`delegate(self, delegatee)`](#VotesComponent-delegate) +- [`delegate_by_sig(self, delegator, delegatee, nonce, expiry, signature)`](#VotesComponent-delegate_by_sig) +- [`clock(self)`](#VotesComponent-clock) +- [`CLOCK_MODE(self)`](#VotesComponent-CLOCK_MODE) + +Internal implementations + +#### InternalImpl [!toc] [#VotesComponent-InternalImpl] + +- [`get_total_supply(self)`](#VotesComponent-get_total_supply) +- [`move_delegate_votes(self, from, to, amount)`](#VotesComponent-move_delegate_votes) +- [`transfer_voting_units(self, from, to, amount)`](#VotesComponent-transfer_voting_units) +- [`num_checkpoints(self, account)`](#VotesComponent-num_checkpoints) +- [`checkpoints(self, account, pos)`](#VotesComponent-checkpoints) +- [`_delegate(self, account, delegatee)`](#VotesComponent-_delegate) + +Events + +- [`DelegateChanged(delegator, from_delegate, to_delegate)`](#VotesComponent-DelegateChanged) +- [`DelegateVotesChanged(delegate, previous_votes, new_votes)`](#VotesComponent-DelegateVotesChanged) + + +Returns the number of voting units for a given account. + +This implementation is specific to ERC20 tokens, where the balance of tokens directly represents the number of voting units. + +This implementation will work out of the box if the ERC20 component is implemented in the final contract. + +This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting. + + + +Returns the number of voting units for a given account. + +This implementation is specific to ERC721 tokens, where each token represents one voting unit. The function returns the balance of ERC721 tokens for the specified account. + +This implementation will work out of the box if the ERC721 component is implemented in the final contract. + +This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting. + + +#### Embeddable functions [!toc] [#VotesComponent-EmbeddableFunctions] + + +Returns the current amount of votes that `account` has. + + + +Returns the amount of votes that `account` had at a specific moment in the past. + +Requirements: + +- `timepoint` must be in the past. + + + +Returns the total supply of votes available at a specific moment in the past. + +This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote. + +Requirements: + +- `timepoint` must be in the past. + + + +Returns the delegate that `account` has chosen. + + + +Delegates votes from the sender to `delegatee`. + +Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + + + +Delegates votes from `delegator` to `delegatee` through a [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message signature validation. + +Requirements: + +- `expiry` must not be in the past. +- `nonce` must match the account's current nonce. +- `delegator` must implement `SRC6::is_valid_signature`. +- `signature` should be valid for the message hash. + +Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + + + +Returns the current timepoint determined by the contract's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). + +Requirements: + +- This function MUST always be non-decreasing. + + + +Returns a description of the clock the contract is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + + +#### Internal functions [!toc] [#VotesComponent-InternalFunctions] + + +Returns the current total supply of votes. + + + +Moves delegated votes from one delegate to another. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + + + +Transfers, mints, or burns voting units. + +To register a mint, `from` should be zero. To register a burn, `to` should be zero. Total supply of voting units will be adjusted with mints and burns. + +If voting units are based on an underlying transferable asset (like a token), you must call this function every time the asset is transferred to keep the internal voting power accounting in sync. For ERC20 and ERC721 tokens, this is typically handled using hooks. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + + + +Returns the number of checkpoints for `account`. + + + +Returns the `pos`-th checkpoint for `account`. + + + +Delegates all of `account`'s voting units to `delegatee`. + +Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. + +May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. + + +#### Events [!toc] [#VotesComponent-Events] + + +Emitted when an account changes their delegate. + + + +Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. + + +### `VotingUnitsTrait` [toc] [#VotingUnitsTrait] + + + +```rust +pub trait VotingUnitsTrait { + fn get_voting_units(self: @TState, account: ContractAddress) -> u256; +} +``` + +A trait that must be implemented when integrating [VotesComponent](#VotesComponent) into a contract. It offers a mechanism to retrieve the number of voting units for a given account at the current time. + +Functions + +- [`get_voting_units(self, account)`](#VotingUnitsTrait-get_voting_units) + +#### Functions [!toc] [#VotingUnitsTrait-Functions] + + +Returns the number of voting units for a given account. For ERC20, this is typically the token balance. For ERC721, this is typically the number of tokens owned. + +While any formula can be used as a measure of voting units, the internal vote accounting of the contract may be compromised if voting units are transferred in any external flow by following a different formula. +For example, when implementing the hook for ERC20, the number of voting units transferred should match the formula given by the `get_voting_units` implementation. + diff --git a/content/contracts-cairo/3.x/api/introspection.mdx b/content/contracts-cairo/3.x/api/introspection.mdx new file mode 100644 index 00000000..a0bb6158 --- /dev/null +++ b/content/contracts-cairo/3.x/api/introspection.mdx @@ -0,0 +1,100 @@ +--- +title: Introspection +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This crate handles [type introspection](https://en.wikipedia.org/wiki/Type_introspection) of contracts. In other words, it examines which functions can be called on a given contract. This is referred to as the contract's interface. + +## Interfaces + +### `ISRC5` [toc] [#ISRC5] + + + +```rust +use openzeppelin_introspection::interface::ISRC5; +``` + +Interface of the SRC5 Introspection Standard as defined in [SNIP-5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md). + +[SRC5 ID](#ISRC5) + +```text +0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055 +``` + +Functions + +- [`supports_interface(interface_id)`](#ISRC5-supports_interface) + +#### Functions [!toc] [#ISRC5-Functions] + + +Checks whether the contract implements the given interface. + +Check [Computing the Interface ID](../introspection#computing-the-interface-id) for more information on how to compute this ID. + + +## [](#core)Core + +### `SRC5Component` [toc] [#SRC5Component] + + + +```rust +use openzeppelin_introspection::src5::SRC5Component; +``` + +SRC5 component extending [`ISRC5`](#ISRC5). + +Embeddable Implementations + +#### SRC5Impl [!toc] [#SRC5Component-SRC5Impl] + +- [`supports_interface(self, interface_id)`](#SRC5Component-supports_interface) + +Internal Implementations + +#### InternalImpl [!toc] [#SRC5Component-InternalImpl] + +- [`register_interface(self, interface_id)`](#SRC5Component-register_interface) +- [`deregister_interface(self, interface_id)`](#SRC5Component-deregister_interface) + +#### Embeddable functions [!toc] [#SRC5Component-EmbeddableFunctions] + + +See [`ISRC5::supports_interface`](#ISRC5-supports_interface). + + +#### Internal functions [!toc] [#SRC5Component-InternalFunctions] + + +Registers support for the given `interface_id`. + + + +Deregisters support for the given `interface_id`. + diff --git a/content/contracts-cairo/3.x/api/merkle-tree.mdx b/content/contracts-cairo/3.x/api/merkle-tree.mdx new file mode 100644 index 00000000..5772e073 --- /dev/null +++ b/content/contracts-cairo/3.x/api/merkle-tree.mdx @@ -0,0 +1,185 @@ +--- +title: Merkle Tree +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This crate provides a set of utilities for verifying Merkle Tree proofs on-chain. The tree and the proofs can be generated using this [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). + +This module provides: + +- `verify` - can prove that some value is part of a Merkle tree. +- `verify_multi_proof` - can prove multiple values are part of a Merkle tree. + +`openzeppelin_merkle_tree` doesn’t have dependencies outside of `corelib`, and can be used in projects that are not Starknet-related. + +To use it as a standalone package, you can add it in your `Scarb.toml` as follows: + +`openzeppelin_merkle_tree = "3.0.0-alpha.1"` + +## [](#modules)Modules + +### [](#merkle_proof)`merkle_proof` [toc] [#merkle_proof] + + + +```rust +use openzeppelin_merkle_tree::merkle_proof; +``` + +These functions deal with verification of Merkle Tree proofs. + +The tree and the proofs can be generated using this [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). You will find a quickstart guide in the readme. + +You should avoid using leaf values that are two felt252 values long prior to hashing, or use a hash function other than the one used to hash internal nodes for hashing leaves. This is because the concatenation of a sorted pair of internal nodes in the Merkle tree could be reinterpreted as a leaf value. The JavaScript library generates Merkle trees that are safe against this attack out of the box. + +Functions + +- [`verify(proof, root, leaf)`](#merkle_proof-verify) +- [`verify_pedersen(proof, root, leaf)`](#merkle_proof-verify_pedersen) +- [`verify_poseidon(proof, root, leaf)`](#merkle_proof-verify_poseidon) +- [`process_proof(proof, leaf)`](#merkle_proof-process_proof) +- [`verify_multi_proof(proof, proof_flags, root, leaves)`](#merkle_proof-verify_multi_proof) +- [`process_multi_proof(proof, proof_flags, leaf)`](#merkle_proof-process_multi_proof) + +#### [](#merkle_proof-Functions)Functions [!toc] + + +Returns true if a `leaf` can be proved to be a part of a Merkle tree defined by `root`. + +For this, a `proof` must be provided, containing sibling hashes on the branch from the leaf to the root of the tree. + +Each pair of leaves and each pair of pre-images are assumed to be sorted. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + +`verify_pedersen` and `verify_poseidon` already include the corresponding `Hasher` implementations. + + + +Version of `verify` using Pedersen as the hashing function. + + + +Version of `verify` using Poseidon as the hashing function. + + + +Returns the rebuilt hash obtained by traversing a Merkle tree up from `leaf` using `proof`. + +A `proof` is valid if and only if the rebuilt hash matches the root of the tree. + +When processing the proof, the pairs of leaves & pre-images are assumed to be sorted. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + + + +Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by `root`, according to `proof` and `proof_flags` as described in `process_multi_proof`. + +The `leaves` must be validated independently. + +Not all Merkle trees admit multiproofs. See `process_multi_proof` for details. + +Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `true`. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + + + +Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. + +The reconstruction proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another leaf/inner node or a proof sibling node, depending on whether each `proof_flags` item is true or false respectively. + +Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: + +1. The tree is complete (but not necessarily perfect). +2. The leaves to be proven are in the opposite order than they are in the tree. (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). + +The *empty set* (i.e. the case where `proof.len() == 1 && leaves.len() == 0`) is considered a no-op, and therefore a valid multiproof (i.e. it returns `proof.at(0)`). Consider disallowing this case if you're not validating the leaves elsewhere. + +This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. + + +### [](#hashes)`hashes` [toc] [#hashes] + + + +```rust +use openzeppelin_merkle_tree::hashes; +``` + +Module providing the trait and default implementations for the commutative hash functions used in [`merkle_proof`](#merkle_proof). + +The `PedersenCHasher` implementation matches the default node hashing function used in the [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). + +Traits + +- [`CommutativeHasher`](#hashes-CommutativeHasher) + +Impls + +- [`PedersenCHasher`](#hashes-PedersenCHasher) +- [`PoseidonCHasher`](#hashes-PoseidonCHasher) + +#### [](#hashes-Traits)Traits [!toc] + + +Declares a commutative hash function with the following signature: + +`commutative_hash(a: felt252, b: felt252) → felt252;` + +which computes a commutative hash of a sorted pair of felt252 values. + +This is usually implemented as an extension of a non-commutative hash function, like Pedersen or Poseidon, returning the hash of the concatenation of the two values by first sorting them. + +Frequently used when working with merkle proofs. + +The `commutative_hash` function MUST follow the invariant that `commutative_hash(a, b) == commutative_hash(b, a)`. + + +#### [](#hashes-Impls)Impls [!toc] + + +Implementation of the `CommutativeHasher` trait which computes the Pedersen hash of chaining the two input values with the len (2), sorting the pair first. + + + +Implementation of the `CommutativeHasher` trait which computes the Poseidon hash of the concatenation of two values, sorting the pair first. + diff --git a/content/contracts-cairo/3.x/api/security.mdx b/content/contracts-cairo/3.x/api/security.mdx new file mode 100644 index 00000000..12e3a01f --- /dev/null +++ b/content/contracts-cairo/3.x/api/security.mdx @@ -0,0 +1,202 @@ +--- +title: Security +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This crate provides components to handle common security-related tasks. + +## [](#initializable)Initializable + +### [](#InitializableComponent)`InitializableComponent` [toc] [#InitializableComponent] + + + +```rust +use openzeppelin_security::InitializableComponent; +``` + +Component enabling one-time initialization for contracts. + +Embeddable Implementations + +InitializableImpl + +- [`is_initialized(self)`](#InitializableComponent-is_initialized) + +Internal Implementations + +InternalImpl + +- [`initialize(self)`](#InitializableComponent-initialize) + +#### [](#InitializableComponent-Embeddable-Functions)Embeddable functions [!toc] + + +Returns whether the contract has been initialized. + + +#### [](#InitializableComponent-Internal-Functions)Internal functions [!toc] + + +Initializes the contract. Can only be called once. + +Requirements: + +- the contract must not have been initialized before. + + +## [](#pausable)Pausable + +### [](#PausableComponent)`PausableComponent` [toc] [#PausableComponent] + + + +```rust +use openzeppelin_security::PausableComponent; +``` + +Component to implement an emergency stop mechanism. + +Embeddable Implementations + +PausableImpl + +- [`is_paused(self)`](#PausableComponent-is_paused) + +Internal Implementations + +InternalImpl + +- [`assert_not_paused(self)`](#PausableComponent-assert_not_paused) +- [`assert_paused(self)`](#PausableComponent-assert_paused) +- [`pause(self)`](#PausableComponent-pause) +- [`unpause(self)`](#PausableComponent-unpause) + +Events + +- [`Paused(account)`](#PausableComponent-Paused) +- [`Unpaused(account)`](#PausableComponent-Unpaused) + +#### [](#PausableComponent-Embeddable-Functions)Embeddable functions [!toc] + + +Returns whether the contract is currently paused. + + +#### [](#PausableComponent-Internal-Functions)Internal functions [!toc] + + +Panics if the contract is paused. + + + +Panics if the contract is not paused. + + + +Pauses the contract. + +Requirements: + +- the contract must not be paused. + +Emits a [Paused](#PausableComponent-Paused) event. + + + +Unpauses the contract. + +Requirements: + +- the contract must be paused. + +Emits an [Unpaused](#PausableComponent-Unpaused) event. + + +#### [](#PausableComponent-Events)Events [!toc] + + +Emitted when the contract is paused by `account`. + + + +Emitted when the contract is unpaused by `account`. + + +## [](#reentrancyguard)ReentrancyGuard + +### [](#ReentrancyGuardComponent)`ReentrancyGuardComponent` [toc] [#ReentrancyGuardComponent] + + + +```rust +use openzeppelin_security::ReentrancyGuardComponent; +``` + +Component to help prevent reentrant calls. + +Internal Implementations + +InternalImpl + +- [`start(self)`](#ReentrancyGuardComponent-start) +- [`end(self)`](#ReentrancyGuardComponent-end) + +#### [](#ReentrancyGuardComponent-Internal-Functions)Internal functions [!toc] + + +Prevents a contract's function from calling itself or another protected function, directly or indirectly. + +Requirements: + +- the guard must not be currently enabled. + + + +Removes the reentrant guard. + diff --git a/content/contracts-cairo/3.x/api/testing.mdx b/content/contracts-cairo/3.x/api/testing.mdx new file mode 100644 index 00000000..7a9b3cc6 --- /dev/null +++ b/content/contracts-cairo/3.x/api/testing.mdx @@ -0,0 +1,10 @@ +--- +title: Testing +--- + + +The `openzeppelin_testing` package version is now decoupled from the `cairo-contracts` version. + + +You can find the documentation for the `openzeppelin_testing` package in the README for the corresponding version in the +[scarb registry](https://scarbs.xyz/packages/openzeppelin_testing). diff --git a/content/contracts-cairo/3.x/api/token_common.mdx b/content/contracts-cairo/3.x/api/token_common.mdx new file mode 100644 index 00000000..3be2e45f --- /dev/null +++ b/content/contracts-cairo/3.x/api/token_common.mdx @@ -0,0 +1,469 @@ +--- +title: Common (Token) +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This module provides extensions and utilities that are common to multiple token standards. + +## Interfaces + +### [](#IERC2981)`IERC2981` [toc] [#IERC2981] + + + +```rust +use openzeppelin_token::common::erc2981::interface::IERC2981; +``` + +[SRC5 ID](./introspection#ISRC5) + +```text +0x2d3414e45a8700c29f119a54b9f11dca0e29e06ddcb214018fc37340e165ed6 +``` + +Interface of the ERC2981 standard as defined in [EIP-2981](https://eips.ethereum.org/EIPS/eip-2981). + +Functions + +- [`royalty_info(token_id, sale_price)`](#IERC2981-royalty_info) + +#### [](#IERC2981-Functions)Functions [!toc] + + +Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of exchange. The royalty amount is denominated and must be paid in that same unit of exchange. + + +### [](#IERC2981Info)`IERC2981Info` [toc] [#IERC2981Info] + + + +```rust +use openzeppelin_token::common::erc2981::interface::IERC2981Info; +``` + +Interface providing external read functions for discovering the state of ERC2981 component. + +Functions + +- [`default_royalty()`](#IERC2981Info-default_royalty) +- [`token_royalty(token_id)`](#IERC2981Info-token_royalty) + +#### [](#IERC2981Info-Functions)Functions [!toc] + + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + + + +Returns the royalty information specific to a token. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + + +### [](#IERC2981Admin)`IERC2981Admin` [toc] [#IERC2981Admin] + + + +```rust +use openzeppelin_token::common::erc2981::interface::IERC2981Admin; +``` + +Interface providing external admin functions for managing the settings of ERC2981 component. + +Functions + +- [`set_default_royalty(receiver, fee_numerator)`](#IERC2981Admin-set_default_royalty) +- [`delete_default_royalty()`](#IERC2981Admin-delete_default_royalty) +- [`set_token_royalty(token_id, receiver, fee_numerator)`](#IERC2981Admin-set_token_royalty) +- [`reset_token_royalty(token_id)`](#IERC2981Admin-reset_token_royalty) + +#### [](#IERC2981Admin-Functions)Functions [!toc] + + +Sets the royalty information that all ids in this contract will default to. + + + +Sets the default royalty percentage and receiver to zero. + + + +Sets the royalty information for a specific token id that takes precedence over the global default. + + + +Resets royalty information for the token id back to unset. + + +## [](#erc2981)ERC2981 + +### [](#ERC2981Component)`ERC2981Component` [toc] [#ERC2981Component] + + + +```rust +use openzeppelin_token::common::erc2981::ERC2981Component; +``` + +ERC2981 component extending [IERC2981](#IERC2981). + +[Immutable Component Config](../components#immutable-config) + +constants + +- [`FEE_DENOMINATOR`](#ERC2981Component-IC-FEE_DENOMINATOR) + +functions + +- [`validate()`](#ERC2981Component-IC-validate) + +Embeddable Implementations + +ERC2981Impl + +- [`royalty_info(self, token_id, sale_price)`](#ERC2981Component-royalty_info) + +ERC2981InfoImpl + +- [`default_royalty(self)`](#ERC2981InfoImpl-default_royalty) +- [`token_royalty(self, token_id)`](#ERC2981InfoImpl-token_royalty) + +ERC2981AdminOwnableImpl + +- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminOwnableImpl-set_default_royalty) +- [`delete_default_royalty(self)`](#ERC2981AdminOwnableImpl-delete_default_royalty) +- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminOwnableImpl-set_token_royalty) +- [`reset_token_royalty(self, token_id)`](#ERC2981AdminOwnableImpl-reset_token_royalty) + +ERC2981AdminAccessControlImpl + +- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminAccessControlImpl-set_default_royalty) +- [`delete_default_royalty(self)`](#ERC2981AdminAccessControlImpl-delete_default_royalty) +- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminAccessControlImpl-set_token_royalty) +- [`reset_token_royalty(self, token_id)`](#ERC2981AdminAccessControlImpl-reset_token_royalty) + +Internal implementations + +InternalImpl + +- [`initializer(self, default_receiver, default_royalty_fraction)`](#ERC2981Component-initializer) +- [`_default_royalty(self)`](#ERC2981Component-_default_royalty) +- [`_set_default_royalty(self, receiver, fee_numerator)`](#ERC2981Component-_set_default_royalty) +- [`_delete_default_royalty(self)`](#ERC2981Component-_delete_default_royalty) +- [`_token_royalty(self, token_id)`](#ERC2981Component-_token_royalty) +- [`_set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981Component-_set_token_royalty) +- [`_reset_token_royalty(self, token_id)`](#ERC2981Component-_reset_token_royalty) + +#### [](#ERC2981Component-Immutable-Config)Immutable Config constants [!toc] + + +The denominator with which to interpret the fee set in `_set_token_royalty` and `_set_default_royalty` as a fraction of the sale price. + + + +Validates the given implementation of the contract's configuration. + +Requirements: + +- `FEE_DENOMINATOR` must be greater than 0. + +This function is called by the contract's initializer. + + +#### [](#ERC2981Component-Embeddable-functions)Embeddable functions [!toc] + + +Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of exchange. The royalty amount is denominated and should be paid in that same unit of exchange. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The amount of royalty payment. + + + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + + + +Returns the royalty information specific to a token. If no specific royalty information is set for the token, the default is returned. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + + +#### [](#ERC2981Component-ERC2981AdminOwnableImpl)ERC2981AdminOwnableImpl [!toc] + +Provides admin functions for managing royalty settings that are restricted to be called only by the contract's owner. Requires the contract to implement [OwnableComponent](./access#OwnableComponent). + + +Sets the royalty information that all ids in this contract will default to. + +Requirements: + +- The caller is the contract owner. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Sets the default royalty percentage and receiver to zero. + +Requirements: + +- The caller is the contract owner. + + + +Sets the royalty information for a specific token id that takes precedence over the global default. + +Requirements: + +- The caller is the contract owner. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Resets royalty information for the token id back to unset. + +Requirements: + +- The caller is the contract owner. + + +#### [](#ERC2981Component-ERC2981AdminAccessControlImpl)ERC2981AdminAccessControlImpl [!toc] + +Provides admin functions for managing royalty settings that require `ROYALTY_ADMIN_ROLE` to be granted to the caller. Requires the contract to implement [AccessControlComponent](./access#AccessControlComponent). + + +Role for the admin responsible for managing royalty settings. + + + +Sets the royalty information that all ids in this contract will default to. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Sets the default royalty percentage and receiver to zero. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. + + + +Sets the royalty information for a specific token id that takes precedence over the global default. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Resets royalty information for the token id back to unset. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. + + +#### [](#ERC2981Component-Internal-functions)Internal functions [!toc] + + +Initializes the contract by setting the default royalty and registering the supported interface. + +Requirements: + +- `default_receiver` cannot be the zero address. +- `default_royalty_fraction` cannot be greater than the fee denominator. +- The fee denominator must be greater than 0. + +The fee denominator is set by the contract using the [Immutable Component Config](../components#immutable-config). + + + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + + + +Sets the royalty information that all ids in this contract will default to. + +Requirements: + +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Sets the default royalty percentage and receiver to zero. + + + +Returns the royalty information that all ids in this contract will default to. + +The returned tuple contains: + +- `t.0`: The receiver of the royalty payment. +- `t.1`: The numerator of the royalty fraction. +- `t.2`: The denominator of the royalty fraction. + + + +Sets the royalty information for a specific token id that takes precedence over the global default. + +Requirements: + +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Resets royalty information for the token id back to unset. + diff --git a/content/contracts-cairo/3.x/api/udc.mdx b/content/contracts-cairo/3.x/api/udc.mdx new file mode 100644 index 00000000..28bc000a --- /dev/null +++ b/content/contracts-cairo/3.x/api/udc.mdx @@ -0,0 +1,85 @@ +--- +title: Universal Deployer +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +Reference of the Universal Deployer Contract (UDC) interface and preset. + +## Interfaces + +### [](#IUniversalDeployer)`IUniversalDeployer` [toc] [#IUniversalDeployer] + + + +```rust +use openzeppelin_utils::interfaces::IUniversalDeployer; +``` + +Functions + +- [`deploy_contract(class_hash, salt, not_from_zero, calldata)`](#IUniversalDeployer-deploy_contract) + +Events + +- [`ContractDeployed(address, deployer, not_from_zero, class_hash, calldata, salt)`](#IUniversalDeployer-ContractDeployed) + +#### [](#IUniversalDeployer-Functions)Functions [!toc] + + +Deploys a contract through the Universal Deployer Contract. + + +#### [](#IUniversalDeployer-Events)Events [!toc] + + +Emitted when `deployer` deploys a contract through the Universal Deployer Contract. + + +## [](#presets)Presets + +### [](#UniversalDeployer)`UniversalDeployer` [toc] [#UniversalDeployer] + + + +```rust +use openzeppelin_presets::UniversalDeployer; +``` + +The standard Universal Deployer Contract. + +[Sierra class hash](../presets) + +```text +{{UniversalDeployerClassHash}} +``` + +Embedded Implementations + +UniversalDeployerImpl + +- [`deploy_contract(self, address, deployer, not_from_zero, class_hash, calldata, salt)`](#UniversalDeployer-deploy_contract) + +#### [](#UniversalDeployer-External-functions)External functions [!toc] + + +Deploys a contract through the Universal Deployer Contract. + +When `not_from_zero` is `true`, `salt` is hashed with the caller address and the modified salt is passed to the inner `deploy_syscall`. This type of deployment is [origin-dependent](../udc#origin-dependent). + +When `not_from_zero` is `false`, the deployment type is [origin-independent](../udc#origin-independent). + +Emits an [ContractDeployed](#IUniversalDeployer-ContractDeployed) event. + diff --git a/content/contracts-cairo/3.x/api/upgrades.mdx b/content/contracts-cairo/3.x/api/upgrades.mdx new file mode 100644 index 00000000..9780fde5 --- /dev/null +++ b/content/contracts-cairo/3.x/api/upgrades.mdx @@ -0,0 +1,128 @@ +--- +title: Upgrades +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This crate provides interfaces and utilities related to upgradeability. + +## Interfaces + +### [](#IUpgradeable)`IUpgradeable` [toc] [#IUpgradeable] + + + +```rust +use openzeppelin_upgrades::interface::IUpgradeable; +``` + +Interface of an upgradeable contract. + +Functions + +- [`upgrade(new_class_hash)`](#IUpgradeable-upgrade) + +#### [](#IUpgradeable-Functions)Functions [!toc] + + +Upgrades the contract code by updating its [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/). + +This function is usually protected by an [Access Control](../access) mechanism. + + +### [](#IUpgradeAndCall)`IUpgradeAndCall` [toc] [#IUpgradeAndCall] + + + +```rust +use openzeppelin_upgrades::interface::IUpgradeAndCall; +``` + +Interface for an upgradeable contract that couples an upgrade with a function call in the upgraded context. + +Functions + +- [`upgrade_and_call(new_class_hash, selector, calldata)`](#IUpgradeAndCall-upgrade_and_call) + +#### [](#IUpgradeAndCall-Functions)Functions [!toc] + + +Upgrades the contract code by updating its [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/) and calls `selector` with the upgraded context. + +This function is usually protected by an [Access Control](../access) mechanism. + + +## [](#core)Core + +### [](#UpgradeableComponent)`UpgradeableComponent` [toc] [#UpgradeableComponent] + + + +```rust +use openzeppelin_upgrades::upgradeable::UpgradeableComponent; +``` + +Upgradeable component. + +Internal Implementations + +InternalImpl + +- [`upgrade(self, new_class_hash)`](#UpgradeableComponent-upgrade) +- [`upgrade_and_call(self, new_class_hash, selector, calldata)`](#UpgradeableComponent-upgrade_and_call) + +Events + +- [`Upgraded(class_hash)`](#UpgradeableComponent-Upgraded) + +#### [](#UpgradeableComponent-Internal-Functions)Internal Functions [!toc] + + +Upgrades the contract by updating the contract [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/). + +Requirements: + +- `new_class_hash` must be different from zero. + +Emits an [Upgraded](#UpgradeableComponent-Upgraded) event. + + + +Replaces the contract's class hash with `new_class_hash` and then calls `selector` from the upgraded context. This function returns the unwrapped `call_contract_syscall` return value(s), if available, of the `selector` call. + +Requirements: + +- `new_class_hash` must be different from zero. + +The function call comes from the upgraded contract itself and not the account. + +A similar behavior to `upgrade_and_call` can also be achieved with a list of calls from an account since the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) account standard supports multicall. An account can execute a list of calls with [upgrade](#IUpgradeable-upgrade) being the first element in the list and the extra function call as the second. With this approach, the calls will execute from the account's context and can't be front-ran. + +Emits an [Upgraded](#UpgradeableComponent-Upgraded) event. + + +#### [](#UpgradeableComponent-Events)Events [!toc] + + +Emitted when the [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/) is upgraded. + diff --git a/content/contracts-cairo/3.x/api/utilities.mdx b/content/contracts-cairo/3.x/api/utilities.mdx new file mode 100644 index 00000000..5288699b --- /dev/null +++ b/content/contracts-cairo/3.x/api/utilities.mdx @@ -0,0 +1,381 @@ +--- +title: Utilities +--- + +import { UMBRELLA_VERSION } from "../utils/constants.js"; + +This crate provides miscellaneous components and libraries containing utility functions to handle common tasks. + +## Core + +### `utils` [toc] [#utils] + + + +```rust +use openzeppelin_utils; +``` + +Module containing core utilities of the library. + +Members + +Inner modules + +- [`cryptography`](#utils-cryptography) +- [`deployments`](#utils-deployments) +- [`math`](#utils-math) +- [`contract_clock`](#utils-contract_clock) +- [`serde`](#utils-serde) + +#### Inner modules [!toc] [#utils-Inner-Modules] + + +See [`openzeppelin_utils::cryptography`](#cryptography). + + + +See [`openzeppelin_utils::deployments`](#deployments). + + + +See [`openzeppelin_utils::math`](#math). + + + +See [`openzeppelin_utils::contract_clock`](#contract_clock). + + + +See [`openzeppelin_utils::serde`](#serde). + + +### `cryptography` [toc] [#cryptography] + + + +```rust +use openzeppelin_utils::cryptography; +``` + +Module containing utilities related to cryptography. + +Members + +Inner modules + +- [`nonces`](#cryptography-nonces) +- [`snip12`](#cryptography-snip12) + +#### Inner modules [!toc] [#cryptography-Inner-Modules] + + +See [`openzeppelin_utils::cryptography::nonces::NoncesComponent`](#NoncesComponent). + + + +See [`openzeppelin_utils::cryptography::snip12`](#snip12). + + +### `deployments` [toc] [#deployments] + + + +```rust +use openzeppelin_utils::deployments; +``` + +Module containing utility functions for calculating contract addresses through [deploy\_syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) and the [Universal Deployer Contract](../udc) (UDC). + +Members + +Structs + +- [`DeployerInfo(caller_address, udc_address)`](#deployments-DeployerInfo) + +Functions + +- [`calculate_contract_address_from_deploy_syscall(salt, class_hash, constructor_calldata, deployer_address)`](#deployments-calculate_contract_address_from_deploy_syscall) +- [`compute_hash_on_elements(data)`](#deployments-compute_hash_on_elements) +- [`calculate_contract_address_from_udc(salt, class_hash, constructor_calldata, deployer_info)`](#deployments-calculate_contract_address_from_udc) + +#### Structs [!toc] [#deployments-Structs] + + +Struct containing arguments necessary in [utils::calculate\_contract\_address\_from\_udc](#deployments-calculate_contract_address_from_udc) for origin-dependent deployment calculations. + + +#### Functions [!toc] [#deployments-Functions] + + +Returns the contract address when passing the given arguments to [deploy\_syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall). + + + +Creates a Pedersen hash chain with the elements of `data` and returns the finalized hash. + + + +Returns the calculated contract address for UDC deployments. + +Origin-independent deployments (deployed from zero) should pass `Option::None` as `deployer_info`. + +Origin-dependent deployments hash `salt` with `caller_address` (member of [DeployerInfo](#deployments-DeployerInfo)) and pass the hashed salt to the inner [deploy\_syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) as the `contract_address_salt` argument. + + +### `math` [toc] [#math] + + + +```rust +use openzeppelin_utils::math; +``` + +Module containing math utilities. + +Members + +Functions + +- [`average(a, b)`](#math-average) + +#### Functions [!toc] [#math-Functions] + + +Returns the average of two unsigned integers. The result is rounded down. + +`T` is a generic value matching different numeric implementations. + + +### `contract_clock` [toc] [#contract_clock] + + +```rust +use openzeppelin_utils::contract_clock; +``` + +Module providing a trait for the [EIP-6372](https://eips.ethereum.org/EIPS/eip-6372) standard along with default clock implementations based on either block number or block timestamp. + +Traits + +- [`ERC6372Clock`](#ERC6372Clock) + +Implementations + +- [`ERC6372BlockNumberClock`](#contract_clock-ERC6372BlockNumberClock) +- [`ERC6372TimestampClock`](#contract_clock-ERC6372TimestampClock) + +#### `ERC6372Clock` [toc] [#ERC6372Clock] + + +```rust +use openzeppelin_utils::contract_clock::ERC6372Clock; +``` + +A trait for the [EIP-6372](https://eips.ethereum.org/EIPS/eip-6372) standard that allows flexible internal clock implementation — based on block timestamp, block number, or a custom logic. + +Functions + +- [`clock()`](#ERC6372Clock-clock) +- [`CLOCK_MODE()`](#ERC6372Clock-CLOCK_MODE) + +#### Functions [!toc] [#ERC6372Clock-Functions] + + +Returns the current timepoint determined by the contract's operational mode, intended for use in time-sensitive logic. + +Requirements: + +- This function MUST always be non-decreasing. + + + +Returns a description of the clock the contract is operating in. + +Requirements: + +- The output MUST be formatted like a URL query string, decodable in standard JavaScript. + + +#### Implementations [!toc] [#contract_clock-Impls] + + +Implementation of the `ERC6372Clock` trait that uses the block number as its clock reference. + + + +Implementation of the `ERC6372Clock` trait that uses the block timestamp as its clock reference. + + +### `serde` [toc] [#serde] + + + +```rust +use openzeppelin_utils::serde; +``` + +Module containing utilities related to serialization and deserialization of Cairo data structures. + +Members + +Traits + +- [`SerializedAppend`](#serde-SerializedAppend) + +#### Traits [!toc] [#serde-Traits] + + +Importing this trait allows the ability to append a serialized representation of a Cairo data structure already implementing the `Serde` trait to a `felt252` buffer. + +Usage example: + +```rust +use openzeppelin_utils::serde::SerializedAppend; +use starknet::ContractAddress; + +fn to_calldata(recipient: ContractAddress, amount: u256) -> Array { + let mut calldata = array![]; + calldata.append_serde(recipient); + calldata.append_serde(amount); + calldata +} +``` + +Note that the `append_serde` method is automatically available for arrays of felts, and it accepts any data structure that implements the `Serde` trait. + + +## Cryptography [#cryptography-toc] + +### `NoncesComponent` [toc] [#NoncesComponent] + + + +```rust +use openzeppelin_utils::cryptography::nonces::NoncesComponent; +``` + +This component provides a simple mechanism for handling incremental nonces for a set of addresses. It is commonly used to prevent replay attacks when contracts accept signatures as input. + +Embeddable Implementations + +NoncesImpl + +- [`nonces(self, owner)`](#NoncesComponent-nonces) + +Internal Implementations + +InternalImpl + +- [`use_nonce(self, owner)`](#NoncesComponent-use_nonce) +- [`use_checked_nonce(self, owner, nonce)`](#NoncesComponent-use_checked_nonce) + +#### Embeddable functions [!toc] [#NoncesComponent-Embeddable-Functions] + + +Returns the next unused nonce for an `owner`. + + +#### Internal functions [!toc] [#NoncesComponent-Internal-Functions] + + +Consumes a nonce, returns the current value, and increments nonce. + +For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be decremented or reset. This guarantees that the nonce never overflows. + + + +Same as `use_nonce` but checking that `nonce` is the next valid one for `owner`. + + +### `snip12` [toc] [#snip12] + + + +```rust +use openzeppelin_utils::snip12; +``` + +Supports on-chain generation of message hashes compliant with [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md). + +For a full walkthrough on how to use this module, see the [SNIP12 and Typed Messages](../guides/snip12) guide. diff --git a/content/contracts-cairo/3.x/backwards-compatibility.mdx b/content/contracts-cairo/3.x/backwards-compatibility.mdx new file mode 100644 index 00000000..3c63bfcd --- /dev/null +++ b/content/contracts-cairo/3.x/backwards-compatibility.mdx @@ -0,0 +1,35 @@ +--- +title: Backwards Compatibility +--- + +OpenZeppelin Contracts uses semantic versioning to communicate backwards compatibility of its API and storage layout. Patch and minor updates will generally be backwards compatible, with rare exceptions as detailed below. Major updates should be assumed incompatible with previous releases. On this page, we provide details about these guarantees. + +Bear in mind that while releasing versions, we treat minors as majors and patches as minors, in accordance with semantic versioning. This means that `v2.1.0` could be adding features to `v2.0.0`, while `v3.0.0` would be considered a breaking release. + +## API + +In backwards compatible releases, all changes should be either additions or modifications to internal implementation details. Most code should continue to compile and behave as expected. The exceptions to this rule are listed below. + +### Security + +Infrequently, a patch or minor update will remove or change an API in a breaking way but only if the previous API is considered insecure. These breaking changes will be noted in the changelog and release notes, and published along with a security advisory. + +### Errors + +The specific error format and data that is included with reverts should not be assumed stable unless otherwise specified. + +### Major releases + +Major releases should be assumed incompatible. Nevertheless, the external interfaces of contracts will remain compatible if they are standardized, or if the maintainers judge that changing them would cause significant strain on the ecosystem. + +An important aspect that major releases may break is "upgrade compatibility", in particular storage layout compatibility. It will never be safe for a live contract to upgrade from one major release to another. + +In the case of breaking "upgrade compatibility", an entry to the changelog will be added listing those breaking changes. + +## Storage layout + +Patch updates will always preserve storage layout compatibility, and after `1.0.0` minors will too. This means that a live contract can be upgraded from one minor to another without corrupting the storage layout. In some cases it may be necessary to initialize new state variables when upgrading, although we expect this to be infrequent. + +## Cairo version + +The minimum Cairo version required to compile the contracts will remain unchanged for patch updates, but it may change for minors. diff --git a/content/contracts-cairo/3.x/components.mdx b/content/contracts-cairo/3.x/components.mdx new file mode 100644 index 00000000..5266792a --- /dev/null +++ b/content/contracts-cairo/3.x/components.mdx @@ -0,0 +1,667 @@ +--- +title: Components +--- + +The following documentation provides reasoning and examples on how to use Contracts for Cairo components. + +Starknet components are separate modules that contain storage, events, and implementations that can be integrated into a contract. +Components themselves cannot be declared or deployed. +Another way to think of components is that they are abstract modules that must be instantiated. + +[shamans_post]: https://community.starknet.io/t/cairo-components/101136#components-1 +[cairo_book]: https://book.cairo-lang.org/ch103-02-00-composability-and-components.html + + +For more information on the construction and design of Starknet components, see the [Starknet Shamans post][shamans_post] and the [Cairo book][cairo_book]. + + +## Building a contract + +### Setup + +The contract should first import the component and declare it with the `component!` macro: + +```rust +#[starknet::contract] +mod MyContract { + // Import the component + use openzeppelin_security::InitializableComponent; + + // Declare the component + component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); +} +``` + +The `path` argument should be the imported component itself (in this case, [InitializableComponent](./security#initializable)). +The `storage` and `event` arguments are the variable names that will be set in the `Storage` struct and `Event` enum, respectively. +Note that even if the component doesn’t define any events, the compiler will still create an empty event enum inside the component module. + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_security::InitializableComponent; + + component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); + + #[storage] + struct Storage { + #[substorage(v0)] + initializable: InitializableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + InitializableEvent: InitializableComponent::Event + } +} +``` + +The `#[substorage(v0)]` attribute must be included for each component in the `Storage` trait. +This allows the contract to have indirect access to the component’s storage. +See [Accessing component storage](#accessing-component-storage) for more on this. + +The `#[flat]` attribute for events in the `Event` enum, however, is not required. +For component events, the first key in the event log is the component ID. +Flattening the component event removes it, leaving the event ID as the first key. + +### Implementations + +Components come with granular implementations of different interfaces. +This allows contracts to integrate only the implementations that they’ll use and avoid unnecessary bloat. +Integrating an implementation looks like this: + +```rust +mod MyContract { + use openzeppelin_security::InitializableComponent; + + component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); + + (...) + + // Gives the contract access to the implementation methods + impl InitializableImpl = + InitializableComponent::InitializableImpl; +} +``` + +Defining an `impl` gives the contract access to the methods within the implementation from the component. +For example, `is_initialized` is defined in the `InitializableImpl`. +A function on the contract level can expose it like this: + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_security::InitializableComponent; + + component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); + + (...) + + impl InitializableImpl = + InitializableComponent::InitializableImpl; + + #[external(v0)] + fn is_initialized(ref self: ContractState) -> bool { + self.initializable.is_initialized() + } +} +``` + +While there’s nothing wrong with manually exposing methods like in the previous example, this process can be tedious for implementations with many methods. +Fortunately, a contract can embed implementations which will expose all of the methods of the implementation. +To embed an implementation, add the `#[abi(embed_v0)]` attribute above the `impl`: + +```rust +#[starknet::contract] +mod MyContract { + (...) + + // This attribute exposes the methods of the `impl` + #[abi(embed_v0)] + impl InitializableImpl = + InitializableComponent::InitializableImpl; +} +``` + +`InitializableImpl` defines the `is_initialized` method in the component. +By adding the embed attribute, `is_initialized` becomes a contract entrypoint for `MyContract`. + + +Embeddable implementations, when available in this library’s components, are segregated from the internal component implementation which makes it easier to safely expose. +Components also separate granular implementations from [mixin](#mixins) implementations. +The API documentation design reflects these groupings. +See [ERC20Component](/contracts-cairo/2.x/api/erc20#erc20component) as an example which includes: + +* **Embeddable Mixin Implementation** +* **Embeddable Implementations** +* **Internal Implementations** +* **Events** + + + +### Mixins + +Mixins are impls made of a combination of smaller, more specific impls. +While separating components into granular implementations offers flexibility, +integrating components with many implementations can appear crowded especially if the contract uses all of them. +Mixins simplify this by allowing contracts to embed groups of implementations with a single directive. + +Compare the following code blocks to see the benefit of using a mixin when creating an account contract. + +#### Account without mixin + +```rust +component!(path: AccountComponent, storage: account, event: AccountEvent); +component!(path: SRC5Component, storage: src5, event: SRC5Event); + +#[abi(embed_v0)] +impl SRC6Impl = AccountComponent::SRC6Impl; +#[abi(embed_v0)] +impl DeclarerImpl = AccountComponent::DeclarerImpl; +#[abi(embed_v0)] +impl DeployableImpl = AccountComponent::DeployableImpl; +#[abi(embed_v0)] +impl PublicKeyImpl = AccountComponent::PublicKeyImpl; +#[abi(embed_v0)] +impl SRC6CamelOnlyImpl = AccountComponent::SRC6CamelOnlyImpl; +#[abi(embed_v0)] +impl PublicKeyCamelImpl = AccountComponent::PublicKeyCamelImpl; +impl AccountInternalImpl = AccountComponent::InternalImpl; + +#[abi(embed_v0)] +impl SRC5Impl = SRC5Component::SRC5Impl; +``` + +#### Account with mixin + +```rust +component!(path: AccountComponent, storage: account, event: AccountEvent); +component!(path: SRC5Component, storage: src5, event: SRC5Event); + +#[abi(embed_v0)] +impl AccountMixinImpl = AccountComponent::AccountMixinImpl; +impl AccountInternalImpl = AccountComponent::InternalImpl; +``` + +The rest of the setup for the contract, however, does not change. +This means that component dependencies must still be included in the `Storage` struct and `Event` enum. +Here’s a full example of an account contract that embeds the `AccountMixinImpl`: + +```rust +#[starknet::contract] +mod Account { + use openzeppelin_account::AccountComponent; + use openzeppelin_introspection::src5::SRC5Component; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // This embeds all of the methods from the many AccountComponent implementations + // and also includes `supports_interface` from `SRC5Impl` + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + impl AccountInternalImpl = AccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + } +} +``` + +### Initializers + + +Failing to use a component’s `initializer` can result in irreparable contract deployments. + +Always read the API Reference documentation for each integrated component. + + + +Some components require some sort of setup upon construction. +Usually, this would be a job for a constructor; however, components themselves cannot implement constructors. +Components instead offer ``initializer``s within their `InternalImpl` to call from the contract’s constructor. +Let’s look at how a contract would integrate [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent): + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_access::ownable::OwnableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + + // Instantiate `InternalImpl` to give the contract access to the `initializer` + impl InternalImpl = OwnableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event + } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + // Invoke ownable's `initializer` + self.ownable.initializer(owner); + } +} +``` + +### Immutable Config + +While initializers help set up the component’s initial state, some require configuration that may be defined +as constants, saving gas by avoiding the necessity of reading from storage each time the variable needs to be used. The +Immutable Component Config pattern helps with this matter by allowing the implementing contract to define a set of +constants declared in the component, customizing its functionality. + + +The Immutable Component Config standard is defined in the SRC-107. + + +Here’s an example of how to use the Immutable Component Config pattern with the [ERC2981Component](/contracts-cairo/2.x/api/token_common#erc2981component): + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::common::erc2981::ERC2981Component; + use starknet::contract_address_const; + + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // Instantiate `InternalImpl` to give the contract access to the `initializer` + impl InternalImpl = ERC2981Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc2981: ERC2981Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC2981Event: ERC2981Component::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + // Define the immutable config + pub impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig { + const FEE_DENOMINATOR: u128 = 10_000; + } + + #[constructor] + fn constructor(ref self: ContractState) { + let default_receiver = contract_address_const::<'RECEIVER'>(); + let default_royalty_fraction = 1000; + // Invoke erc2981's `initializer` + self.erc2981.initializer(default_receiver, default_royalty_fraction); + } +} +``` + +#### Default config + +Sometimes, components implementing the Immutable Component Config pattern provide a default configuration that can be +directly used without implementing the `ImmutableConfig` trait locally. When provided, this implementation will be named +`DefaultConfig` and will be available in the same module containing the component, as a sibling. + +In the following example, the `DefaultConfig` trait is used to define the `FEE_DENOMINATOR` config constant. + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_introspection::src5::SRC5Component; + // Bring the DefaultConfig trait into scope + use openzeppelin_token::common::erc2981::{ERC2981Component, DefaultConfig}; + use starknet::contract_address_const; + + component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + // Instantiate `InternalImpl` to give the contract access to the `initializer` + impl InternalImpl = ERC2981Component::InternalImpl; + + #[storage] + struct Storage { + (...) + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + (...) + } + + #[constructor] + fn constructor(ref self: ContractState) { + let default_receiver = contract_address_const::<'RECEIVER'>(); + let default_royalty_fraction = 1000; + // Invoke erc2981's `initializer` + self.erc2981.initializer(default_receiver, default_royalty_fraction); + } +} +``` + +#### `validate` function + +The `ImmutableConfig` trait may also include a `validate` function with a default implementation, which +asserts that the configuration is correct, and must not be overridden by the implementing contract. For more information +on how to use this function, refer to the [validate section of the SRC-107](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-107.md#validate-function). + +### Dependencies + +Some components include dependencies of other components. +Contracts that integrate components with dependencies must also include the component dependency. +For instance, [AccessControlComponent](/contracts-cairo/2.x/api/access#accesscontrolcomponent) depends on [SRC5Component](/contracts-cairo/2.x/api/introspection#src5component). +Creating a contract with `AccessControlComponent` should look like this: + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_access::accesscontrol::AccessControlComponent; + use openzeppelin_introspection::src5::SRC5Component; + + component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // AccessControl + #[abi(embed_v0)] + impl AccessControlImpl = + AccessControlComponent::AccessControlImpl; + #[abi(embed_v0)] + impl AccessControlCamelImpl = + AccessControlComponent::AccessControlCamelImpl; + impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + accesscontrol: AccessControlComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + (...) +} +``` + +## Customization + + +Customizing implementations and accessing component storage can potentially corrupt the state, bypass security checks, and undermine the component logic. + +**Exercise extreme caution**. See [Security](#security). + + + +### Hooks + +Hooks are entrypoints to the business logic of a token component that are accessible at the contract level. +This allows contracts to insert additional behaviors before and/or after token transfers (including mints and burns). +Prior to hooks, extending functionality required contracts to create [custom implementations](#custom-implementations). + +All token components include a generic hooks trait that include empty default functions. +When creating a token contract, the using contract must create an implementation of the hooks trait. +Suppose an ERC20 contract wanted to include Pausable functionality on token transfers. +The following snippet leverages the `before_update` hook to include this behavior. + +```rust +#[starknet::contract] +mod MyToken { + use openzeppelin_security::pausable::PausableComponent::InternalTrait; + use openzeppelin_security::pausable::PausableComponent; + use openzeppelin_token::erc20::{ERC20Component, DefaultConfig}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: PausableComponent, storage: pausable, event: PausableEvent); + + // ERC20 Mixin + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[abi(embed_v0)] + impl PausableImpl = PausableComponent::PausableImpl; + impl PausableInternalImpl = PausableComponent::InternalImpl; + + // Create the hooks implementation + impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait { + // Occurs before token transfers + fn before_update( + ref self: ERC20Component::ComponentState, + from: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) { + // Access local state from component state + let contract_state = self.get_contract(); + // Call function from integrated component + contract_state.pausable.assert_not_paused(); + } + + // Omitting the `after_update` hook because the default behavior + // is already implemented in the trait + } + + (...) +} +``` + +Notice that the `self` parameter expects a component state type. +Instead of passing the component state, the using contract’s state can be passed which simplifies the syntax. +The hook then moves the scope up with the Cairo-generated `get_contract` through the `HasComponent` trait (as illustrated with ERC20Component in this example). +From here, the hook can access the using contract’s integrated components, storage, and implementations. + +Be advised that even if a token contract does not require hooks, the hooks trait must still be implemented. +The using contract may instantiate an empty impl of the trait; +however, the Contracts for Cairo library already provides the instantiated impl to abstract this away from contracts. +The using contract just needs to bring the implementation into scope like this: + +```rust +#[starknet::contract] +mod MyToken { + use openzeppelin_token::erc20::{ERC20Component, DefaultConfig}; + use openzeppelin_token::erc20::ERC20HooksEmptyImpl; + + (...) +} +``` + + +For a more in-depth guide on hooks, see [Extending Cairo Contracts with Hooks](https://fleming-andrew.medium.com/extending-cairo-contracts-with-hooks-c3ca21d1d6b8). + + +### Custom implementations + +There are instances where a contract requires different or amended behaviors from a component implementation. +In these scenarios, a contract must create a custom implementation of the interface. +Let’s break down a pausable ERC20 contract to see what that looks like. +Here’s the setup: + +```rust +#[starknet::contract] +mod ERC20Pausable { + use openzeppelin_security::pausable::PausableComponent; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; + // Import the ERC20 interfaces to create custom implementations + use openzeppelin_token::erc20::interface::{IERC20, IERC20CamelOnly}; + use starknet::ContractAddress; + + component!(path: PausableComponent, storage: pausable, event: PausableEvent); + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + #[abi(embed_v0)] + impl PausableImpl = PausableComponent::PausableImpl; + impl PausableInternalImpl = PausableComponent::InternalImpl; + + // `ERC20MetadataImpl` can keep the embed directive because the implementation + // will not change + #[abi(embed_v0)] + impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; + // Do not add the embed directive to these implementations because + // these will be customized + impl ERC20Impl = ERC20Component::ERC20Impl; + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; + + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + (...) +} +``` + +The first thing to notice is that the contract imports the interfaces of the implementations that will be customized. +These will be used in the next code example. + +Next, the contract includes the [ERC20Component](/contracts-cairo/2.x/api/erc20#erc20component) implementations; however, `ERC20Impl` and `ERC20CamelOnlyImpl` are **not** embedded. +Instead, we want to expose our custom implementation of an interface. +The following example shows the pausable logic integrated into the ERC20 implementations: + +```rust +#[starknet::contract] +mod ERC20Pausable { + (...) + + // Custom ERC20 implementation + #[abi(embed_v0)] + impl CustomERC20Impl of IERC20 { + fn transfer( + ref self: ContractState, recipient: ContractAddress, amount: u256 + ) -> bool { + // Add the custom logic + self.pausable.assert_not_paused(); + // Add the original implementation method from `IERC20Impl` + self.erc20.transfer(recipient, amount) + } + + fn total_supply(self: @ContractState) -> u256 { + // This method's behavior does not change from the component + // implementation, but this method must still be defined. + // Simply add the original implementation method from `IERC20Impl` + self.erc20.total_supply() + } + + (...) + } + + // Custom ERC20CamelOnly implementation + #[abi(embed_v0)] + impl CustomERC20CamelOnlyImpl of IERC20CamelOnly { + fn totalSupply(self: @ContractState) -> u256 { + self.erc20.total_supply() + } + + fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { + self.erc20.balance_of(account) + } + + fn transferFrom( + ref self: ContractState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) -> bool { + self.pausable.assert_not_paused(); + self.erc20.transfer_from(sender, recipient, amount) + } + } +} +``` + +Notice that in the `CustomERC20Impl`, the `transfer` method integrates `pausable.assert_not_paused` as well as `erc20.transfer` from `PausableImpl` and `ERC20Impl` respectively. +This is why the contract defined the `ERC20Impl` from the component in the previous example. + +Creating a custom implementation of an interface must define **all** methods from that interface. +This is true even if the behavior of a method does not change from the component implementation (as `total_supply` exemplifies in this example). + +### Accessing component storage + +There may be cases where the contract must read or write to an integrated component’s storage. +To do so, use the same syntax as calling an implementation method except replace the name of the method with the storage variable like this: + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_security::InitializableComponent; + + component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); + + #[storage] + struct Storage { + #[substorage(v0)] + initializable: InitializableComponent::Storage + } + + (...) + + fn write_to_comp_storage(ref self: ContractState) { + self.initializable.Initializable_initialized.write(true); + } + + fn read_from_comp_storage(self: @ContractState) -> bool { + self.initializable.Initializable_initialized.read() + } +} +``` + +## Security + +The maintainers of OpenZeppelin Contracts for Cairo are mainly concerned with the correctness and security of the code as published in the library. + +Customizing implementations and manipulating the component state may break some important assumptions and introduce vulnerabilities. +While we try to ensure the components remain secure in the face of a wide range of potential customizations, this is done in a best-effort manner. +Any and all customizations to the component logic should be carefully reviewed and checked against the source code of the component they are customizing so as to fully understand their impact and guarantee their security. diff --git a/content/contracts-cairo/3.x/erc1155.mdx b/content/contracts-cairo/3.x/erc1155.mdx new file mode 100644 index 00000000..d3e05494 --- /dev/null +++ b/content/contracts-cairo/3.x/erc1155.mdx @@ -0,0 +1,239 @@ +--- +title: ERC1155 +--- + +The ERC1155 multi-token standard is a specification for [fungibility-agnostic](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens) token contracts. +The ERC1155 library implements an approximation of [EIP-1155](https://eips.ethereum.org/EIPS/eip-1155) in Cairo for StarkNet. + +## Multi Token Standard + +The distinctive feature of ERC1155 is that it uses a single smart contract to represent multiple tokens at once. This +is why its [balance_of](/contracts-cairo/2.x/api/erc1155#IERC1155-balance_of) function differs from ERC20’s and ERC777’s: it has an additional ID argument for the +identifier of the token that you want to query the balance of. + +This is similar to how ERC721 does things, but in that standard a token ID has no concept of balance: each token is +non-fungible and exists or doesn’t. The ERC721 [balance_of](/contracts-cairo/2.x/api/erc721#IERC721-balance_of) function refers to how many different tokens an account +has, not how many of each. On the other hand, in ERC1155 accounts have a distinct balance for each token ID, and +non-fungible tokens are implemented by simply minting a single one of them. + +This approach leads to massive gas savings for projects that require multiple tokens. Instead of deploying a new +contract for each token type, a single ERC1155 token contract can hold the entire system state, reducing deployment +costs and complexity. + +## Usage + +Using Contracts for Cairo, constructing an ERC1155 contract requires integrating both `ERC1155Component` and `SRC5Component`. +The contract should also set up the constructor to initialize the token’s URI and interface support. +Here’s an example of a basic contract: + +```rust +#[starknet::contract] +mod MyERC1155 { + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC1155 Mixin + #[abi(embed_v0)] + impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl; + impl ERC1155InternalImpl = ERC1155Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc1155: ERC1155Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC1155Event: ERC1155Component::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + token_uri: ByteArray, + recipient: ContractAddress, + token_ids: Span, + values: Span + ) { + self.erc1155.initializer(token_uri); + self + .erc1155 + .batch_mint_with_acceptance_check(recipient, token_ids, values, array![].span()); + } +} +``` + +## Interface + +The following interface represents the full ABI of the Contracts for Cairo [ERC1155Component](/contracts-cairo/2.x/api/erc1155#ERC1155Component). +The interface includes the [IERC1155](/contracts-cairo/2.x/api/erc1155#IERC1155) standard interface and the optional [IERC1155MetadataURI](/contracts-cairo/2.x/api/erc1155#IERC1155MetadataURI) interface together with [ISRC5](/contracts-cairo/2.x/api/introspection#ISRC5). + +To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. + +```rust +#[starknet::interface] +pub trait ERC1155ABI { + // IERC1155 + fn balance_of(account: ContractAddress, token_id: u256) -> u256; + fn balance_of_batch( + accounts: Span, token_ids: Span + ) -> Span; + fn safe_transfer_from( + from: ContractAddress, + to: ContractAddress, + token_id: u256, + value: u256, + data: Span + ); + fn safe_batch_transfer_from( + from: ContractAddress, + to: ContractAddress, + token_ids: Span, + values: Span, + data: Span + ); + fn is_approved_for_all( + owner: ContractAddress, operator: ContractAddress + ) -> bool; + fn set_approval_for_all(operator: ContractAddress, approved: bool); + + // IERC1155MetadataURI + fn uri(token_id: u256) -> ByteArray; + + // ISRC5 + fn supports_interface(interface_id: felt252) -> bool; + + // IERC1155Camel + fn balanceOf(account: ContractAddress, tokenId: u256) -> u256; + fn balanceOfBatch( + accounts: Span, tokenIds: Span + ) -> Span; + fn safeTransferFrom( + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + value: u256, + data: Span + ); + fn safeBatchTransferFrom( + from: ContractAddress, + to: ContractAddress, + tokenIds: Span, + values: Span, + data: Span + ); + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; + fn setApprovalForAll(operator: ContractAddress, approved: bool); +} +``` + +## ERC1155 Compatibility + +Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC1155 standard but some differences can still be found, such as: + +* The optional `data` argument in both `safe_transfer_from` and `safe_batch_transfer_from` is implemented as `Span`. +* `IERC1155Receiver` compliant contracts must implement SRC5 and register the `IERC1155Receiver` interface ID. +* `IERC1155Receiver::on_erc1155_received` must return that interface ID on success. + +## Batch operations + +Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, [balance_of_batch](/contracts-cairo/2.x/api/erc1155#IERC1155-balance_of_batch) and [safe_batch_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_batch_transfer_from), that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive. We also have [safe_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_transfer_from) for non-batch operations. + +In the spirit of the standard, we’ve also included batch operations in the non-standard functions, such as +[batch_mint_with_acceptance_check](/contracts-cairo/2.x/api/erc1155#ERC1155Component-batch_mint_with_acceptance_check). + + +While [safe_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_batch_transfer_from) prevent loss by checking the receiver can handle the +tokens, this yields execution to the receiver which can result in a [reentrant call](./security#reentrancy-guard). + + +## Receiving tokens + +In order to be sure a non-account contract can safely accept ERC1155 tokens, the contract must implement the `IERC1155Receiver` interface. +The recipient contract must also implement the [SRC5](./introspection#src5) interface which supports interface introspection. + +### IERC1155Receiver + +```rust +#[starknet::interface] +pub trait IERC1155Receiver { + fn on_erc1155_received( + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + value: u256, + data: Span + ) -> felt252; + fn on_erc1155_batch_received( + operator: ContractAddress, + from: ContractAddress, + token_ids: Span, + values: Span, + data: Span + ) -> felt252; +} +``` + +Implementing the `IERC1155Receiver` interface exposes the [on_erc1155_received](/contracts-cairo/2.x/api/erc1155#IERC1155Receiver-on_erc1155_received) and [on_erc1155_batch_received](/contracts-cairo/2.x/api/erc1155#IERC1155Receiver-on_erc1155_batch_received) methods. +When [safe_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_batch_transfer_from) are called, they invoke the recipient contract’s `on_erc1155_received` or `on_erc1155_batch_received` methods respectively which **must** return the [IERC1155Receiver interface ID](/contracts-cairo/2.x/api/erc1155#IERC1155Receiver). +Otherwise, the transaction will fail. + + +For information on how to calculate interface IDs, see [Computing the interface ID](./introspection#computing-the-interface-id). + + +### Creating a token receiver contract + +The Contracts for Cairo ERC1155ReceiverComponent already returns the correct interface ID for safe token transfers. +To integrate the `IERC1155Receiver` interface into a contract, simply include the ABI embed directive to the implementations and add the `initializer` in the contract’s constructor. +Here’s an example of a simple token receiver contract: + +```rust +#[starknet::contract] +mod MyTokenReceiver { + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc1155::ERC1155ReceiverComponent; + use starknet::ContractAddress; + + component!(path: ERC1155ReceiverComponent, storage: erc1155_receiver, event: ERC1155ReceiverEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC1155Receiver Mixin + #[abi(embed_v0)] + impl ERC1155ReceiverMixinImpl = ERC1155ReceiverComponent::ERC1155ReceiverMixinImpl; + impl ERC1155ReceiverInternalImpl = ERC1155ReceiverComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc1155_receiver: ERC1155ReceiverComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC1155ReceiverEvent: ERC1155ReceiverComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.erc1155_receiver.initializer(); + } +} +``` diff --git a/content/contracts-cairo/3.x/erc20.mdx b/content/contracts-cairo/3.x/erc20.mdx new file mode 100644 index 00000000..601d4902 --- /dev/null +++ b/content/contracts-cairo/3.x/erc20.mdx @@ -0,0 +1,252 @@ +--- +title: ERC20 +--- + +The ERC20 token standard is a specification for [fungible tokens](https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens), a type of token where all the units are exactly equal to each other. +`token::erc20::ERC20Component` provides an approximation of [EIP-20](https://eips.ethereum.org/EIPS/eip-20) in Cairo for Starknet. + + +Prior to [Contracts v0.7.0](https://github.com/OpenZeppelin/cairo-contracts/releases/tag/v0.7.0), ERC20 contracts store and read `decimals` from storage; however, this implementation returns a static `18`. +If upgrading an older ERC20 contract that has a decimals value other than `18`, the upgraded contract **must** use a custom `decimals` implementation. +See the [Customizing decimals](#customizing-decimals) guide. + + +## Usage + +Using Contracts for Cairo, constructing an ERC20 contract requires setting up the constructor and instantiating the token implementation. +Here’s what that looks like: + +```rust +#[starknet::contract] +mod MyToken { + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // ERC20 Mixin + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc20: ERC20Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + initial_supply: u256, + recipient: ContractAddress + ) { + let name = "MyToken"; + let symbol = "MTK"; + + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, initial_supply); + } +} +``` + +`MyToken` integrates both the `ERC20Impl` and `ERC20MetadataImpl` with the embed directive which marks the implementations as external in the contract. +While the `ERC20MetadataImpl` is optional, it’s generally recommended to include it because the vast majority of ERC20 tokens provide the metadata methods. +The above example also includes the `ERC20InternalImpl` instance. +This allows the contract’s constructor to initialize the contract and create an initial supply of tokens. + + +For a more complete guide on ERC20 token mechanisms, see [Creating ERC20 Supply](./guides/erc20-supply). + + +## Interface + +The following interface represents the full ABI of the Contracts for Cairo [ERC20Component](/contracts-cairo/2.x/api/erc20#ERC20Component). +The interface includes the [IERC20](/contracts-cairo/2.x/api/erc20#IERC20) standard interface as well as the optional [IERC20Metadata](/contracts-cairo/2.x/api/erc20#IERC20Metadata). + +To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes an implementation of the interface written in camelCase. + +```rust +#[starknet::interface] +pub trait ERC20ABI { + // IERC20 + fn total_supply() -> u256; + fn balance_of(account: ContractAddress) -> u256; + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; + fn approve(spender: ContractAddress, amount: u256) -> bool; + + // IERC20Metadata + fn name() -> ByteArray; + fn symbol() -> ByteArray; + fn decimals() -> u8; + + // IERC20Camel + fn totalSupply() -> u256; + fn balanceOf(account: ContractAddress) -> u256; + fn transferFrom( + sender: ContractAddress, recipient: ContractAddress, amount: u256 + ) -> bool; +} +``` + +## ERC20 compatibility + +Although Starknet is not EVM compatible, this component aims to be as close as possible to the ERC20 token standard. +Some notable differences, however, can still be found, such as: + +* The `ByteArray` type is used to represent strings in Cairo. +* The component offers a [dual interface](./guides/interfaces-and-dispatchers#dual-interfaces) which supports both snake_case and camelCase methods, as opposed to just camelCase in Solidity. +* `transfer`, `transfer_from` and `approve` will never return anything different from `true` because they will revert on any error. +* Function selectors are calculated differently between [Cairo](https://github.com/starkware-libs/cairo/blob/7dd34f6c57b7baf5cd5a30c15e00af39cb26f7e1/crates/cairo-lang-starknet/src/contract.rs#L39-L48) and [Solidity](https://solidity-by-example.org/function-selector/). + +## Customizing decimals + +Cairo, like Solidity, does not support [floating-point numbers](https://en.wikipedia.org//wiki/Floating-point_arithmetic). +To get around this limitation, ERC20 token contracts may offer a `decimals` field which communicates to outside interfaces (wallets, exchanges, etc.) how the token should be displayed. +For instance, suppose a token had a `decimals` value of `3` and the total token supply was `1234`. +An outside interface would display the token supply as `1.234`. +In the actual contract, however, the supply would still be the integer `1234`. +In other words, **the decimals field in no way changes the actual arithmetic** because all operations are still performed on integers. + +Most contracts use `18` decimals and this was even proposed to be compulsory (see the [EIP discussion](https://github.com/ethereum/EIPs/issues/724)). + +### The static approach (SRC-107) + +The Contracts for Cairo `ERC20` component leverages SRC-107 to allow for a static and configurable number of decimals. +To use the default `18` decimals, you can use the `DefaultConfig` implementation by just importing it: + +```rust +#[starknet::contract] +mod MyToken { + // Importing the DefaultConfig implementation would make decimals 18 by default. + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + (...) +} +``` + +To customize this value, you can implement the ImmutableConfig trait locally in the contract. +The following example shows how to set the decimals to `6`: + +```rust +mod MyToken { + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + (...) + + // Custom implementation of the ERC20Component ImmutableConfig. + impl ERC20ImmutableConfig of ERC20Component::ImmutableConfig { + const DECIMALS: u8 = 6; + } +} +``` + +### The storage approach + +For more complex scenarios, such as a factory deploying multiple tokens with differing values for decimals, a flexible solution might be appropriate. + + +Note that we are not using the MixinImpl or the DefaultConfig in this case, since we need to customize the IERC20Metadata implementation. + + +```rust +#[starknet::contract] +mod MyToken { + use openzeppelin_token::erc20::interface as interface; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc20: ERC20Component::Storage, + // The decimals value is stored locally + decimals: u8, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event, + } + + #[constructor] + fn constructor( + ref self: ContractState, decimals: u8, initial_supply: u256, recipient: ContractAddress, + ) { + // Call the internal function that writes decimals to storage + self._set_decimals(decimals); + + // Initialize ERC20 + let name = "MyToken"; + let symbol = "MTK"; + + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, initial_supply); + } + + #[abi(embed_v0)] + impl ERC20CustomMetadataImpl of interface::IERC20Metadata { + fn name(self: @ContractState) -> ByteArray { + self.erc20.ERC20_name.read() + } + + fn symbol(self: @ContractState) -> ByteArray { + self.erc20.ERC20_symbol.read() + } + + fn decimals(self: @ContractState) -> u8 { + self.decimals.read() + } + } + + #[generate_trait] + impl InternalImpl of InternalTrait { + fn _set_decimals(ref self: ContractState, decimals: u8) { + self.decimals.write(decimals); + } + } +} +``` + +This contract expects a `decimals` argument in the constructor and uses an internal function to write the decimals to storage. +Note that the `decimals` state variable must be defined in the contract’s storage because this variable does not exist in the component offered by OpenZeppelin Contracts for Cairo. +It’s important to include a custom ERC20 metadata implementation and NOT use the Contracts for Cairo `ERC20MetadataImpl` in this specific case since the `decimals` method will always return `18`. diff --git a/content/contracts-cairo/3.x/erc4626.mdx b/content/contracts-cairo/3.x/erc4626.mdx new file mode 100644 index 00000000..e22f162e --- /dev/null +++ b/content/contracts-cairo/3.x/erc4626.mdx @@ -0,0 +1,433 @@ +--- +title: ERC4626 +--- + +[ERC4626](https://eips.ethereum.org/EIPS/eip-4626) is an extension of [ERC20](./erc20) that proposes a standard interface for token vaults. +This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), +which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault. + +We provide a base component of ERC4626 which is designed to allow developers to easily re-configure the vault’s behavior, using traits and hooks, while +staying compliant. In this guide, we will discuss some security considerations that affect ERC4626. We will also discuss common customizations of the vault. + +## Security concern: Inflation attack + +### Visualizing the vault + +In exchange for the assets deposited into an ERC4626 vault, a user receives shares. These shares can later be burned to redeem the corresponding underlying assets. +The number of shares a user gets depends on the amount of assets they put in and on the exchange rate of the vault. This exchange rate is defined by the +current liquidity held by the vault. + +* If a vault has 100 tokens to back 200 shares, then each share is worth 0.5 assets. +* If a vault has 200 tokens to back 100 shares, then each share is worth 2.0 assets. + +In other words, the exchange rate can be defined as the slope of the line that passes through the origin and the current number of assets and shares in the vault. +Deposits and withdrawals move the vault in this line. + +![Exchange rates in linear scale](/erc4626-rate-linear.png) + +When plotted in log-log scale, the rate is defined similarly, but appears differently (because the point (0,0) is infinitely far away). Rates are represented by "diagonal" lines with different offsets. + +![Exchange rates in logarithmic scale](/erc4626-rate-loglog.png) + +In such a representation, widely different rates can be clearly visible in the same graph. This wouldn’t be the case in linear scale. + +![More exchange rates in logarithmic scale](/erc4626-rate-loglogext.png) + +### The attack + +When depositing tokens, the number of shares a user gets is rounded towards zero. This rounding takes away value from the user in favor of +the vault (i.e. in favor of all the current shareholders). This rounding is often negligible because of the amount at stake. If you deposit 1e9 shares +worth of tokens, the rounding will have you lose at most 0.0000001% of your deposit. However if you deposit 10 shares worth of tokens, you could +lose 10% of your deposit. Even worse, if you deposit less than 1 share worth of tokens, you will receive 0 shares, effectively making a donation. + +For a given amount of assets, the more shares you receive the safer you are. If you want to limit your losses to at most 1%, you need to receive at least 100 shares. + +![Depositing assets](/erc4626-deposit.png) + +In the figure we can see that for a given deposit of 500 assets, the number of shares we get and the corresponding rounding losses depend on the exchange rate. +If the exchange rate is that of the orange curve, we are getting less than a share, so we lose 100% of our deposit. However, if the exchange rate +is that of the green curve, we get 5000 shares, which limits our rounding losses to at most 0.02%. + +![Minting shares](/erc4626-mint.png) + +Symmetrically, if we focus on limiting our losses to a maximum of 0.5%, we need to get at least 200 shares. With the green exchange rate that requires +just 20 tokens, but with the orange rate that requires 200000 tokens. + +We can clearly see that the blue and green curves correspond to vaults that are safer than the yellow and orange curves. + +The idea of an inflation attack is that an attacker can donate assets to the vault to move the rate curve to the right, and make the vault unsafe. + +![Inflation attack without protection](/erc4626-attack.png) + +Figure 6 shows how an attacker can manipulate the rate of an empty vault. First the attacker must deposit a small amount of tokens (1 token) and follow up with +a donation of 1e5 tokens directly to the vault to move the exchange rate "right". This puts the vault in a state where any deposit smaller than 1e5 would be +completely lost to the vault. Given that the attacker is the only shareholder (from their donation), the attacker would steal all the tokens deposited. + +An attacker would typically wait for a user to do the first deposit into the vault, and would frontrun that operation with the attack described above. The risk is +low, and the size of the "donation" required to manipulate the vault is equivalent to the size of the deposit that is being attacked. + +In math that gives: + +* $a_0$ the attacker deposit +* $a_1$ the attacker donation +* $u$ the user deposit + +| | Assets | Shares | Rate | +| --- | --- | --- | --- | +| initial | $0$ | $0$ | - | +| after attacker's deposit | $a_0$ | $a_0$ | $1$ | +| after attacker's donation | $a_0 + a_1$ | $a_0$ | $\frac{a_0}{a_1 + a_0}$ | + +This means a deposit of $u$ will give this number of shares: + +```math +\frac{u \times a_0}{a_0 + a_1} +``` + +For the attacker to dilute that deposit to 0 shares, causing the user to lose all its deposit, it must ensure that + +```math +\frac{u \times a_0}{a_0+a_1} < 1 \iff u < 1 + \frac{a_1}{a_0} +``` + +Using $a_0 = 1$ and $a_1 = u$ is enough. So the attacker only needs $u+1$ assets to perform a successful attack. + +It is easy to generalize the above results to scenarios where the attacker is going after a smaller fraction of the user’s deposit. In order to target $\frac{u}{n}$, the user needs to suffer rounding of a similar fraction, which means the user must receive at most $n$ shares. This results in: + +```math +\frac{u \times a_0}{a_0+a_1} < n \iff \frac{u}{n} < 1 + \frac{a_1}{a_0} +``` + +In this scenario, the attack is $n$ times less powerful (in how much it is stealing) and costs $n$ times less to execute. In both cases, the amount of funds the attacker needs to commit is equivalent to its potential earnings. + +### Defending with a virtual offset + +The defense we propose is based on the approach used in [YieldBox](https://github.com/boringcrypto/YieldBox). It consists of two parts: + +* Use an offset between the "precision" of the representation of shares and assets. Said otherwise, we use more decimal places to represent the shares than the underlying token does to represent the assets. +* Include virtual shares and virtual assets in the exchange rate computation. These virtual assets enforce the conversion rate when the vault is empty. + +These two parts work together in enforcing the security of the vault. First, the increased precision corresponds to a high rate, which we saw is safer as it reduces the rounding error when computing the amount of shares. Second, the virtual assets and shares (in addition to simplifying a lot of the computations) capture part of the donation, making it unprofitable to perform an attack. + +Following the previous math definitions, we have: + +* $\delta$ the vault offset +* $a_0$ the attacker deposit +* $a_1$ the attacker donation +* $u$ the user deposit + +| | Assets | Shares | Rate | +| --- | --- | --- | --- | +| initial | $1$ | $10^\delta$ | $10^\delta$ | +| after attacker's deposit | $1+a_0$ | $10^\delta \times (1+a_0)$ | $10^\delta$ | +| after attacker's donation | $1+a_0+a_1$ | $10^\delta \times (1+a_0)$ | $10^\delta$ | + +One important thing to note is that the attacker only owns a fraction $\frac{a_0}{1 + a_0}$ of the shares, so when doing the donation, he will only be able +to recover that fraction $\frac{a_1 * a_0}{1 + a_0}$ of the donation. The remaining $\frac{a_1}{1+a_0}$ are captured by the vault. + +```math +\mathit{loss} = \frac{a_1}{1+a_0} +``` + +When the user deposits $u$, he receives + +```math +10^\delta \times u \times \frac{1+a_0}{1+a_0+a_1} +``` + +For the attacker to dilute that deposit to 0 shares, causing the user to lose all its deposit, it must ensure that + +```math +10^\delta \times u \times \frac{1+a_0}{1+a_0+a_1} < 1 +``` + +```math +\iff 10^\delta \times u < \frac{1+a_0+a_1}{1+a_0} +``` + +```math +\iff 10^\delta \times u < 1 + \frac{a_1}{1+a_0} +``` + +```math +\iff 10^\delta \times u \le \mathit{loss} +``` + +* If the offset is 0, the attacker loss is at least equal to the user’s deposit. +* If the offset is greater than 0, the attacker will have to suffer losses that are orders of magnitude bigger than the amount of value that can hypothetically be stolen from the user. + +This shows that even with an offset of 0, the virtual shares and assets make this attack non profitable for the attacker. Bigger offsets increase the security even further by making any attack on the user extremely wasteful. + +The following figure shows how the offset impacts the initial rate and limits the ability of an attacker with limited funds to inflate it effectively. + +![Inflation attack without offset=3](/erc4626-attack-3a.png) +$\delta = 3$, $a_0 = 1$, $a_1 = 10^5$ + +![Inflation attack without offset=3 and an attacker deposit that limits its losses](/erc4626-attack-3b.png) +$\delta = 3$, $a_0 = 100$, $a_1 = 10^5$ + +![Inflation attack without offset=6](/erc4626-attack-6.png) +$\delta = 6$, $a_0 = 1$, $a_1 = 10^5$ + +## Usage + +### Custom behavior: Adding fees to the vault + +In ERC4626 vaults, fees can be captured during deposit/mint and/or withdraw/redeem operations. It is essential to remain +compliant with the ERC4626 requirements regarding the preview functions. Fees are calculated through the [FeeConfigTrait](/contracts-cairo/2.x/api/erc20#ERC4626Component-FeeConfigTrait) +implementation. By default, the ERC4626 component charges no fees. If this is the desired behavior, you can use the default +[ERC4626DefaultNoFees](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/token/src/erc20/extensions/erc4626/erc4626.cairo#L899) implementation. + + +Starting from v3.0.0, fees can be charged in either assets or shares. Prior versions only supported fees taken in assets. +See the updated [FeeConfigTrait](/contracts-cairo/2.x/api/erc20#ERC4626Component-FeeConfigTrait) and implementation examples in [ERC4626 mocks](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo). + + +For example, if calling `deposit(100, receiver)`, the caller should deposit exactly 100 underlying tokens, including fees, and the receiver should receive a number of shares that matches the value returned by `preview_deposit(100)`. +Similarly, `preview_mint` should account for the fees that the user will have to pay on top of share’s cost. + +As for the `Deposit` event, while this is less clear in the EIP spec itself, +there seems to be consensus that it should include the number of assets paid for by the user, including the fees. + +On the other hand, when withdrawing assets, the number given by the user should correspond to what the user receives. +Any fees should be added to the quote (in shares) performed by `preview_withdraw`. + +The `Withdraw` event should include the number of shares the user burns (including fees) and the number of assets the user actually receives (after fees are deducted). + +The consequence of this design is that both the `Deposit` and `Withdraw` events will describe two exchange rates. +The spread between the "Buy-in" and the "Exit" prices correspond to the fees taken by the vault. + +The following example describes how fees taken in assets on deposits/withdrawals and in shares on mints/redemptions +proportional to the deposited/withdrawn amount can be implemented: + +```rust +#[starknet::contract] +#[with_components(./erc20, ERC4626)] +pub mod ERC4626Fees { + use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; + use openzeppelin_token::erc20::extensions::erc4626::ERC4626Component::{Fee, FeeConfigTrait}; + use openzeppelin_token::erc20::extensions::erc4626::{ + DefaultConfig, ERC4626DefaultNoLimits, ERC4626SelfAssetsManagement, + }; + use openzeppelin_token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20HooksEmptyImpl}; + use openzeppelin_utils::math; + use openzeppelin_utils::math::Rounding; + use starknet::ContractAddress; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + const _BASIS_POINT_SCALE: u256 = 10_000; + const FEE_TRANSFER_FAILED: felt252 = 0x1; + + // ERC4626 + #[abi(embed_v0)] + impl ERC4626ComponentImpl = ERC4626Component::ERC4626Impl; + // ERC4626MetadataImpl is a custom impl of IERC20Metadata + #[abi(embed_v0)] + impl ERC4626MetadataImpl = ERC4626Component::ERC4626MetadataImpl; + + // ERC20 + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; + + #[storage] + pub struct Storage { + pub entry_fee_basis_point_value: u256, + pub entry_fee_recipient: ContractAddress, + pub exit_fee_basis_point_value: u256, + pub exit_fee_recipient: ContractAddress, + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + underlying_asset: ContractAddress, + initial_supply: u256, + recipient: ContractAddress, + entry_fee: u256, + entry_treasury: ContractAddress, + exit_fee: u256, + exit_treasury: ContractAddress, + ) { + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, initial_supply); + self.erc4626.initializer(underlying_asset); + + self.entry_fee_basis_point_value.write(entry_fee); + self.entry_fee_recipient.write(entry_treasury); + self.exit_fee_basis_point_value.write(exit_fee); + self.exit_fee_recipient.write(exit_treasury); + } + + /// Hooks + impl ERC4626HooksImpl of ERC4626Component::ERC4626HooksTrait { + fn after_deposit( + ref self: ERC4626Component::ComponentState, + caller: ContractAddress, + receiver: ContractAddress, + assets: u256, + shares: u256, + fee: Option, + ) { + if let Option::Some(fee) = fee { + let mut contract_state = self.get_contract_mut(); + let fee_recipient = contract_state.entry_fee_recipient.read(); + match fee { + Fee::Assets(fee) => { + let asset_address = contract_state.asset(); + let asset_dispatcher = IERC20Dispatcher { contract_address: asset_address }; + assert( + asset_dispatcher.transfer(fee_recipient, fee), FEE_TRANSFER_FAILED, + ); + }, + Fee::Shares(fee) => contract_state.erc20.mint(fee_recipient, fee), + }; + } + } + + fn before_withdraw( + ref self: ERC4626Component::ComponentState, + caller: ContractAddress, + receiver: ContractAddress, + owner: ContractAddress, + assets: u256, + shares: u256, + fee: Option, + ) { + if let Option::Some(fee) = fee { + let mut contract_state = self.get_contract_mut(); + let fee_recipient = contract_state.exit_fee_recipient.read(); + match fee { + Fee::Assets(fee) => { + let asset_address = contract_state.asset(); + let asset_dispatcher = IERC20Dispatcher { contract_address: asset_address }; + assert( + asset_dispatcher.transfer(fee_recipient, fee), FEE_TRANSFER_FAILED, + ); + }, + Fee::Shares(fee) => { + if caller != owner { + contract_state.erc20._spend_allowance(owner, caller, fee); + } + contract_state.erc20._transfer(owner, fee_recipient, fee); + }, + }; + } + } + } + + /// Calculate fees + impl FeeConfigImpl of FeeConfigTrait { + fn calculate_deposit_fee( + self: @ERC4626Component::ComponentState, assets: u256, shares: u256, + ) -> Option { + let contract_state = self.get_contract(); + let fee = fee_on_total(assets, contract_state.entry_fee_basis_point_value.read()); + Option::Some(Fee::Assets(fee)) + } + + fn calculate_mint_fee( + self: @ERC4626Component::ComponentState, assets: u256, shares: u256, + ) -> Option { + let contract_state = self.get_contract(); + let fee = fee_on_raw(shares, contract_state.entry_fee_basis_point_value.read()); + Option::Some(Fee::Shares(fee)) + } + + fn calculate_withdraw_fee( + self: @ERC4626Component::ComponentState, assets: u256, shares: u256, + ) -> Option { + let contract_state = self.get_contract(); + let fee = fee_on_raw(assets, contract_state.exit_fee_basis_point_value.read()); + Option::Some(Fee::Assets(fee)) + } + + fn calculate_redeem_fee( + self: @ERC4626Component::ComponentState, assets: u256, shares: u256, + ) -> Option { + let contract_state = self.get_contract(); + let fee = fee_on_total(shares, contract_state.exit_fee_basis_point_value.read()); + Option::Some(Fee::Shares(fee)) + } + } + + /// Calculates the fees that should be added to an amount `assets` that does not already + /// include fees. + /// Used in IERC4626::mint and IERC4626::withdraw operations. + fn fee_on_raw( + assets: u256, + fee_basis_points: u256, + ) -> u256 { + math::u256_mul_div(assets, fee_basis_points, _BASIS_POINT_SCALE, Rounding::Ceil) + } + + /// Calculates the fee part of an amount `assets` that already includes fees. + /// Used in IERC4626::deposit and IERC4626::redeem operations. + fn fee_on_total( + assets: u256, + fee_basis_points: u256, + ) -> u256 { + math::u256_mul_div( + assets, fee_basis_points, fee_basis_points + _BASIS_POINT_SCALE, Rounding::Ceil, + ) + } +} +``` + +## Interface + +The following interface represents the full ABI of the Contracts for Cairo [ERC4626Component](/contracts-cairo/2.x/api/erc20#ERC4626Component). +The full interface includes the [IERC4626](/contracts-cairo/2.x/api/erc20#IERC4626), [IERC20](/contracts-cairo/2.x/api/erc20#IERC20), and [IERC20Metadata](/contracts-cairo/2.x/api/erc20#IERC20Metadata) interfaces. +Note that implementing the IERC20Metadata interface is a requirement of IERC4626. + +```rust +#[starknet::interface] +pub trait ERC4626ABI { + // IERC4626 + fn asset() -> ContractAddress; + fn total_assets() -> u256; + fn convert_to_shares(assets: u256) -> u256; + fn convert_to_assets(shares: u256) -> u256; + fn max_deposit(receiver: ContractAddress) -> u256; + fn preview_deposit(assets: u256) -> u256; + fn deposit(assets: u256, receiver: ContractAddress) -> u256; + fn max_mint(receiver: ContractAddress) -> u256; + fn preview_mint(shares: u256) -> u256; + fn mint(shares: u256, receiver: ContractAddress) -> u256; + fn max_withdraw(owner: ContractAddress) -> u256; + fn preview_withdraw(assets: u256) -> u256; + fn withdraw( + assets: u256, receiver: ContractAddress, owner: ContractAddress, + ) -> u256; + fn max_redeem(owner: ContractAddress) -> u256; + fn preview_redeem(shares: u256) -> u256; + fn redeem( + shares: u256, receiver: ContractAddress, owner: ContractAddress, + ) -> u256; + + // IERC20 + fn total_supply() -> u256; + fn balance_of(account: ContractAddress) -> u256; + fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; + fn transfer(recipient: ContractAddress, amount: u256) -> bool; + fn transfer_from( + sender: ContractAddress, recipient: ContractAddress, amount: u256, + ) -> bool; + fn approve(spender: ContractAddress, amount: u256) -> bool; + + // IERC20Metadata + fn name() -> ByteArray; + fn symbol() -> ByteArray; + fn decimals() -> u8; + + // IERC20CamelOnly + fn totalSupply() -> u256; + fn balanceOf(account: ContractAddress) -> u256; + fn transferFrom( + sender: ContractAddress, recipient: ContractAddress, amount: u256, + ) -> bool; +} +``` diff --git a/content/contracts-cairo/3.x/erc721.mdx b/content/contracts-cairo/3.x/erc721.mdx new file mode 100644 index 00000000..cee0e614 --- /dev/null +++ b/content/contracts-cairo/3.x/erc721.mdx @@ -0,0 +1,213 @@ +--- +title: ERC721 +--- + +The ERC721 token standard is a specification for [non-fungible tokens](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens), or more colloquially: NFTs. +`token::erc721::ERC721Component` provides an approximation of [EIP-721](https://eips.ethereum.org/EIPS/eip-721) in Cairo for Starknet. + +## Usage + +Using Contracts for Cairo, constructing an ERC721 contract requires integrating both `ERC721Component` and `SRC5Component`. +The contract should also set up the constructor to initialize the token’s name, symbol, and interface support. +Here’s an example of a basic contract: + +```rust +#[starknet::contract] +mod MyNFT { + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC721 Mixin + #[abi(embed_v0)] + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721: ERC721Component::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + recipient: ContractAddress + ) { + let name = "MyNFT"; + let symbol = "NFT"; + let base_uri = "https://api.example.com/v1/"; + let token_id = 1; + + self.erc721.initializer(name, symbol, base_uri); + self.erc721.mint(recipient, token_id); + } +} +``` + +## Interface + +The following interface represents the full ABI of the Contracts for Cairo [ERC721Component](/contracts-cairo/2.x/api/erc721#ERC721Component). +The interface includes the [IERC721](/contracts-cairo/2.x/api/erc721#IERC721) standard interface and the optional [IERC721Metadata](/contracts-cairo/2.x/api/erc721#IERC721Metadata) interface. + +To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. + +```rust +#[starknet::interface] +pub trait ERC721ABI { + // IERC721 + fn balance_of(account: ContractAddress) -> u256; + fn owner_of(token_id: u256) -> ContractAddress; + fn safe_transfer_from( + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ); + fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256); + fn approve(to: ContractAddress, token_id: u256); + fn set_approval_for_all(operator: ContractAddress, approved: bool); + fn get_approved(token_id: u256) -> ContractAddress; + fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; + + // IERC721Metadata + fn name() -> ByteArray; + fn symbol() -> ByteArray; + fn token_uri(token_id: u256) -> ByteArray; + + // IERC721CamelOnly + fn balanceOf(account: ContractAddress) -> u256; + fn ownerOf(tokenId: u256) -> ContractAddress; + fn safeTransferFrom( + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ); + fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256); + fn setApprovalForAll(operator: ContractAddress, approved: bool); + fn getApproved(tokenId: u256) -> ContractAddress; + fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; + + // IERC721MetadataCamelOnly + fn tokenURI(tokenId: u256) -> ByteArray; +} +``` + +## ERC721 compatibility + +Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard. +This implementation does, however, include a few notable differences such as: + +* ``interface_id``s are hardcoded and initialized by the constructor. +The hardcoded values derive from Starknet’s selector calculations. +See the [Introspection](./introspection) docs. +* `safe_transfer_from` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721, because function overloading is currently not possible in Cairo. +The difference between both functions consists of accepting `data` as an argument. +`safe_transfer_from` by default accepts the `data` argument which is interpreted as `Span`. +If `data` is not used, simply pass an empty array. +* ERC721 utilizes [SRC5](./introspection#src5) to declare and query interface support on Starknet as opposed to Ethereum’s [EIP165](https://eips.ethereum.org/EIPS/eip-165). +The design for `SRC5` is similar to OpenZeppelin’s [ERC165Storage](https://docs.openzeppelin.com/contracts/4.xapi/utils#ERC165Storage). +* `IERC721Receiver` compliant contracts return a hardcoded interface ID according to Starknet selectors (as opposed to selector calculation in Solidity). + +## Token transfers + +This library includes [transfer_from](/contracts-cairo/2.x/api/erc721#IERC721-transfer_from) and [safe_transfer_from](/contracts-cairo/2.x/api/erc721#IERC721-safe_transfer_from) to transfer NFTs. +If using `transfer_from`, **the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.** +The `safe_transfer_from` method mitigates this risk by querying the recipient contract’s interface support. + + +Usage of `safe_transfer_from` prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. + + +## Receiving tokens + +In order to be sure a non-account contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface. +The recipient contract must also implement the [SRC5](./introspection#src5) interface which, as described earlier, supports interface introspection. + +### IERC721Receiver + +```rust +#[starknet::interface] +pub trait IERC721Receiver { + fn on_erc721_received( + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252; +} +``` + +Implementing the `IERC721Receiver` interface exposes the [on_erc721_received](/contracts-cairo/2.x/api/erc721#IERC721Receiver-on_erc721_received) method. +When safe methods such as [safe_transfer_from](/contracts-cairo/2.x/api/erc721#IERC721-safe_transfer_from) and [safe_mint](/contracts-cairo/2.x/api/erc721#ERC721-safe_mint) are called, they invoke the recipient contract’s `on_erc721_received` method which **must** return the [IERC721Receiver interface ID](/contracts-cairo/2.x/api/erc721#IERC721Receiver). +Otherwise, the transaction will fail. + + +For information on how to calculate interface IDs, see [Computing the interface ID](./introspection#computing-the-interface-id). + + +### Creating a token receiver contract + +The Contracts for Cairo `IERC721ReceiverImpl` already returns the correct interface ID for safe token transfers. +To integrate the `IERC721Receiver` interface into a contract, simply include the ABI embed directive to the implementation and add the `initializer` in the contract’s constructor. +Here’s an example of a simple token receiver contract: + +```rust +#[starknet::contract] +mod MyTokenReceiver { + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc721::ERC721ReceiverComponent; + use starknet::ContractAddress; + + component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // ERC721Receiver Mixin + #[abi(embed_v0)] + impl ERC721ReceiverMixinImpl = ERC721ReceiverComponent::ERC721ReceiverMixinImpl; + impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc721_receiver: ERC721ReceiverComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721ReceiverEvent: ERC721ReceiverComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.erc721_receiver.initializer(); + } +} +``` + +## Storing ERC721 URIs + +Token URIs were previously stored as single field elements prior to Cairo v0.2.5. +ERC721Component now stores only the base URI as a `ByteArray` and the full token URI is returned as the `ByteArray` concatenation of the base URI and the token ID through the [token_uri](/contracts-cairo/2.x/api/erc721#IERC721Metadata-token_uri) method. +This design mirrors OpenZeppelin’s default [Solidity implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/932fddf69a699a9a80fd2396fd1a2ab91cdda123/contracts/token/ERC721/ERC721.sol#L85-L93) for ERC721. diff --git a/content/contracts-cairo/3.x/finance.mdx b/content/contracts-cairo/3.x/finance.mdx new file mode 100644 index 00000000..50e91a4e --- /dev/null +++ b/content/contracts-cairo/3.x/finance.mdx @@ -0,0 +1,216 @@ +--- +title: Finance +--- + +This module includes primitives for financial systems. + +## Vesting component + +The [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent) manages the gradual release of ERC-20 tokens to a designated beneficiary based on a predefined vesting schedule. +The implementing contract must implement the [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent), where the contract owner is regarded as the vesting beneficiary. +This structure allows ownership rights of both the contract and the vested tokens to be assigned and transferred. + + +Any assets transferred to this contract will follow the vesting schedule as if they were locked from the beginning of the vesting period. +As a result, if the vesting has already started, a portion of the newly transferred tokens may become immediately releasable. + + + +By setting the duration to 0, it’s possible to configure this contract to behave like an asset timelock that holds tokens +for a beneficiary until a specified date. + + +### Vesting schedule + +The [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait defines the logic for calculating the vested amount based on a given timestamp. This +logic is not part of the [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent), so any contract implementing the [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent) must provide its own +implementation of the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait. + + +There’s a ready-made implementation of the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait available named [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule). +It incorporates a cliff period by returning 0 vested amount until the cliff ends. After the cliff, the vested amount +is calculated as directly proportional to the time elapsed since the beginning of the vesting schedule. + + +### Usage + +The contract must integrate [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent) and [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent) as dependencies. The contract’s constructor +should initialize both components. Core vesting parameters, such as `beneficiary`, `start`, `duration` +and `cliff_duration`, are passed as arguments to the constructor and set at the time of deployment. + +The implementing contract must provide an implementation of the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait. This can be achieved either by importing +a ready-made [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule) implementation or by defining a custom one. + +Here’s an example of a simple vesting wallet contract with a [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule), where the vested amount +is calculated as being directly proportional to the time elapsed since the start of the vesting period. + +```rust +#[starknet::contract] +mod LinearVestingWallet { + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_finance::vesting::{VestingComponent, LinearVestingSchedule}; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: VestingComponent, storage: vesting, event: VestingEvent); + + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + #[abi(embed_v0)] + impl VestingImpl = VestingComponent::VestingImpl; + impl VestingInternalImpl = VestingComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + vesting: VestingComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + VestingEvent: VestingComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + beneficiary: ContractAddress, + start: u64, + duration: u64, + cliff_duration: u64 + ) { + self.ownable.initializer(beneficiary); + self.vesting.initializer(start, duration, cliff_duration); + } +} +``` + +A vesting schedule will often follow a custom formula. In such cases, the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait is useful. +To support a custom vesting schedule, the contract must provide an implementation of the +[calculate_vested_amount](/contracts-cairo/2.x/api/finance#VestingComponent-calculate_vested_amount) function based on the desired formula. + + +When using a custom [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) implementation, the [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule) must be excluded from the imports. + + + +If there are additional parameters required for calculations, which are stored in the contract’s storage, you can access them using `self.get_contract()`. + + +Here’s an example of a vesting wallet contract with a custom [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) implementation, where tokens +are vested in a number of steps. + +```rust +#[starknet::contract] +mod StepsVestingWallet { + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_finance::vesting::VestingComponent::VestingScheduleTrait; + use openzeppelin_finance::vesting::VestingComponent; + use starknet::ContractAddress; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: VestingComponent, storage: vesting, event: VestingEvent); + + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + #[abi(embed_v0)] + impl VestingImpl = VestingComponent::VestingImpl; + impl VestingInternalImpl = VestingComponent::InternalImpl; + + #[storage] + struct Storage { + total_steps: u64, + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + vesting: VestingComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + VestingEvent: VestingComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + total_steps: u64, + beneficiary: ContractAddress, + start: u64, + duration: u64, + cliff: u64, + ) { + self.total_steps.write(total_steps); + self.ownable.initializer(beneficiary); + self.vesting.initializer(start, duration, cliff); + } + + impl VestingSchedule of VestingScheduleTrait { + fn calculate_vested_amount( + self: @VestingComponent::ComponentState, + token: ContractAddress, + total_allocation: u256, + timestamp: u64, + start: u64, + duration: u64, + cliff: u64, + ) -> u256 { + if timestamp < cliff { + 0 + } else if timestamp >= start + duration { + total_allocation + } else { + let total_steps = self.get_contract().total_steps.read(); + let vested_per_step = total_allocation / total_steps.into(); + let step_duration = duration / total_steps; + let current_step = (timestamp - start) / step_duration; + let vested_amount = vested_per_step * current_step.into(); + vested_amount + } + } + } +} +``` + +### Interface + +Here is the full interface of a standard contract implementing the vesting functionality: + +```rust +#[starknet::interface] +pub trait VestingABI { + // IVesting + fn start(self: @TState) -> u64; + fn cliff(self: @TState) -> u64; + fn duration(self: @TState) -> u64; + fn end(self: @TState) -> u64; + fn released(self: @TState, token: ContractAddress) -> u256; + fn releasable(self: @TState, token: ContractAddress) -> u256; + fn vested_amount(self: @TState, token: ContractAddress, timestamp: u64) -> u256; + fn release(ref self: TState, token: ContractAddress) -> u256; + + // IOwnable + fn owner(self: @TState) -> ContractAddress; + fn transfer_ownership(ref self: TState, new_owner: ContractAddress); + fn renounce_ownership(ref self: TState); + + // IOwnableCamelOnly + fn transferOwnership(ref self: TState, newOwner: ContractAddress); + fn renounceOwnership(ref self: TState); +} +``` diff --git a/content/contracts-cairo/3.x/governance/governor.mdx b/content/contracts-cairo/3.x/governance/governor.mdx new file mode 100644 index 00000000..cfad3946 --- /dev/null +++ b/content/contracts-cairo/3.x/governance/governor.mdx @@ -0,0 +1,443 @@ +--- +title: Governor +--- + +Decentralized protocols are in constant evolution from the moment they are publicly released. Often, +the initial team retains control of this evolution in the first stages, but eventually delegates it +to a community of stakeholders. The process by which this community makes decisions is called +on-chain governance, and it has become a central component of decentralized protocols, fueling +varied decisions such as parameter tweaking, smart contract upgrades, integrations with other +protocols, treasury management, grants, etc. + +This governance protocol is generally implemented in a special-purpose contract called “Governor”. In +OpenZeppelin Contracts for Cairo, we set out to build a modular system of Governor components where different +requirements can be accommodated by implementing specific traits. You will find the most common requirements out of the box, +but writing additional ones is simple, and we will be adding new features as requested by the community in future releases. + +## Usage and setup + +### Token + +The voting power of each account in our governance setup will be determined by an ERC20 or an ERC721 token. The token has +to implement the [VotesComponent](../api/governance#VotesComponent) extension. This extension will keep track of historical balances so that voting power +is retrieved from past snapshots rather than current balance, which is an important protection that prevents double voting. + +If your project already has a live token that does not include Votes and is not upgradeable, you can wrap it in a +governance token by using a wrapper. This will allow token holders to participate in governance by wrapping their tokens 1-to-1. + + +The library currently does not include a wrapper for tokens, but it will be added in a future release. + + + +Currently, the clock mode is fixed to block timestamps, since the Votes component uses the block timestamp to track +checkpoints. We plan to add support for more flexible clock modes in Votes in a future release, allowing to use, for example, +block numbers instead. + + +### Governor + +We will initially build a Governor without a timelock. The core logic is given by the [GovernorComponent](../api/governance#GovernorComponent), but we +still need to choose: + +1) how voting power is determined, + +2) how many votes are needed for quorum, + +3) what options people have when casting a vote and how those votes are counted, and + +4) the execution mechanism that should be used. + +Each of these aspects is customizable by writing your own extensions, +or more easily choosing one from the library. + +***For 1)*** we will use the GovernorVotes extension, which hooks to an [IVotes](../api/governance#IVotes) instance to determine the voting power +of an account based on the token balance they hold when a proposal becomes active. +This module requires the address of the token to be passed as an argument to the initializer. + +***For 2)*** we will use GovernorVotesQuorumFraction. This works together with the [IVotes](../api/governance#IVotes) instance to define the quorum as a +percentage of the total supply at the block when a proposal’s voting power is retrieved. This requires an initializer +parameter to set the percentage besides the votes token address. Most Governors nowadays use 4%. Since the quorum denominator +is 1000 for precision, we initialize the module with a numerator of 40, resulting in a 4% quorum (40/1000 = 0.04 or 4%). + +***For 3)*** we will use GovernorCountingSimple, an extension that offers 3 options to voters: For, Against, and Abstain, +and where only For and Abstain votes are counted towards quorum. + +***For 4)*** we will use GovernorCoreExecution, an extension that allows proposal execution directly through the governor. + + +Another option is GovernorTimelockExecution. An example can be found in the next section. + + +Besides these, we also need an implementation for the GovernorSettingsTrait defining the voting delay, voting period, +and proposal threshold. While we can use the GovernorSettings extension which allows to set these parameters by the +governor itself, we will implement the trait locally in the contract and set the voting delay, voting period, +and proposal threshold as constant values. + +__voting_delay__: How long after a proposal is created should voting power be fixed. A large voting delay gives +users time to unstake tokens if necessary. + +__voting_period__: How long does a proposal remain open to votes. + + +These parameters are specified in the unit defined in the token’s clock, which is for now always timestamps. + + +__proposal_threshold__: This restricts proposal creation to accounts who have enough voting power. + +An implementation of `GovernorComponent::ImmutableConfig` is also required. For the example below, we have used +the `DefaultConfig`. Check the immutable-config guide for more details. + +The last missing step is to add an `SNIP12Metadata` implementation used to retrieve the name and version of the governor. + +```rust +#[starknet::contract] +mod MyGovernor { + use openzeppelin_governance::governor::GovernorComponent::InternalTrait as GovernorInternalTrait; + use openzeppelin_governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait; + use openzeppelin_governance::governor::extensions::{ + GovernorVotesQuorumFractionComponent, GovernorCountingSimpleComponent, + GovernorCoreExecutionComponent, + }; + use openzeppelin_governance::governor::{GovernorComponent, DefaultConfig}; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; + use starknet::ContractAddress; + + pub const VOTING_DELAY: u64 = 86400; // 1 day + pub const VOTING_PERIOD: u64 = 604800; // 1 week + pub const PROPOSAL_THRESHOLD: u256 = 10; + pub const QUORUM_NUMERATOR: u256 = 40; // 4% + + component!(path: GovernorComponent, storage: governor, event: GovernorEvent); + component!( + path: GovernorVotesQuorumFractionComponent, + storage: governor_votes, + event: GovernorVotesEvent + ); + component!( + path: GovernorCountingSimpleComponent, + storage: governor_counting_simple, + event: GovernorCountingSimpleEvent + ); + component!( + path: GovernorCoreExecutionComponent, + storage: governor_core_execution, + event: GovernorCoreExecutionEvent + ); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // Governor + #[abi(embed_v0)] + impl GovernorImpl = GovernorComponent::GovernorImpl; + + // Extensions external + #[abi(embed_v0)] + impl QuorumFractionImpl = + GovernorVotesQuorumFractionComponent::QuorumFractionImpl; + + // Extensions internal + impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum; + impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes; + impl GovernorCountingSimpleImpl = + GovernorCountingSimpleComponent::GovernorCounting; + impl GovernorCoreExecutionImpl = + GovernorCoreExecutionComponent::GovernorExecution; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + pub governor: GovernorComponent::Storage, + #[substorage(v0)] + pub governor_votes: GovernorVotesQuorumFractionComponent::Storage, + #[substorage(v0)] + pub governor_counting_simple: GovernorCountingSimpleComponent::Storage, + #[substorage(v0)] + pub governor_core_execution: GovernorCoreExecutionComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + GovernorEvent: GovernorComponent::Event, + #[flat] + GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event, + #[flat] + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event, + #[flat] + GovernorCoreExecutionEvent: GovernorCoreExecutionComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + #[constructor] + fn constructor(ref self: ContractState, votes_token: ContractAddress) { + self.governor.initializer(); + self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR); + } + + // + // SNIP12 Metadata + // + + pub impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + 'DAPP_NAME' + } + + fn version() -> felt252 { + 'DAPP_VERSION' + } + } + + // + // Locally implemented extensions + // + + pub impl GovernorSettings of GovernorComponent::GovernorSettingsTrait { + /// See `GovernorComponent::GovernorSettingsTrait::voting_delay`. + fn voting_delay(self: @GovernorComponent::ComponentState) -> u64 { + VOTING_DELAY + } + + /// See `GovernorComponent::GovernorSettingsTrait::voting_period`. + fn voting_period(self: @GovernorComponent::ComponentState) -> u64 { + VOTING_PERIOD + } + + /// See `GovernorComponent::GovernorSettingsTrait::proposal_threshold`. + fn proposal_threshold(self: @GovernorComponent::ComponentState) -> u256 { + PROPOSAL_THRESHOLD + } + } +} +``` + +### Timelock + +It is good practice to add a timelock to governance decisions. This allows users to exit the system if they disagree +with a decision before it is executed. We will use OpenZeppelin’s [TimelockController](#timelock) in combination with the +GovernorTimelockExecution extension. + + +When using a timelock, it is the timelock that will execute proposals and thus the timelock that should +hold any funds, ownership, and access control roles. + + +TimelockController uses an [AccessControl](../access#role-based-accesscontrol) setup that we need to understand in order to set up roles. + +The Proposer role is in charge of queueing operations: this is the role the Governor instance must be granted, +and it MUST be the only proposer (and canceller) in the system. + +The Executor role is in charge of executing already available operations: we can assign this role to the special +zero address to allow anyone to execute (if operations can be particularly time sensitive, the Governor should be made Executor instead). + +The Canceller role is in charge of canceling operations: the Governor instance must be granted this role, +and it MUST be the only canceller in the system. + +Lastly, there is the Admin role, which can grant and revoke the two previous roles: this is a very sensitive role that will be granted automatically to the timelock itself, and optionally to a second account, which can be used for ease of setup but should promptly renounce the role. + +The following example uses the GovernorTimelockExecution extension, together with GovernorSettings, and uses a +fixed quorum value instead of a percentage: + +```rust +#[starknet::contract] +pub mod MyTimelockedGovernor { + use openzeppelin_governance::governor::GovernorComponent::InternalTrait as GovernorInternalTrait; + use openzeppelin_governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait; + use openzeppelin_governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait; + use openzeppelin_governance::governor::extensions::GovernorVotesComponent::InternalTrait as GovernorVotesInternalTrait; + use openzeppelin_governance::governor::extensions::{ + GovernorVotesComponent, GovernorSettingsComponent, GovernorCountingSimpleComponent, + GovernorTimelockExecutionComponent + }; + use openzeppelin_governance::governor::{GovernorComponent, DefaultConfig}; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; + use starknet::ContractAddress; + + pub const VOTING_DELAY: u64 = 86400; // 1 day + pub const VOTING_PERIOD: u64 = 604800; // 1 week + pub const PROPOSAL_THRESHOLD: u256 = 10; + pub const QUORUM: u256 = 100_000_000; + + component!(path: GovernorComponent, storage: governor, event: GovernorEvent); + component!(path: GovernorVotesComponent, storage: governor_votes, event: GovernorVotesEvent); + component!( + path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent + ); + component!( + path: GovernorCountingSimpleComponent, + storage: governor_counting_simple, + event: GovernorCountingSimpleEvent + ); + component!( + path: GovernorTimelockExecutionComponent, + storage: governor_timelock_execution, + event: GovernorTimelockExecutionEvent + ); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // Governor + #[abi(embed_v0)] + impl GovernorImpl = GovernorComponent::GovernorImpl; + + // Extensions external + #[abi(embed_v0)] + impl VotesTokenImpl = GovernorVotesComponent::VotesTokenImpl; + #[abi(embed_v0)] + impl GovernorSettingsAdminImpl = + GovernorSettingsComponent::GovernorSettingsAdminImpl; + #[abi(embed_v0)] + impl TimelockedImpl = + GovernorTimelockExecutionComponent::TimelockedImpl; + + // Extensions internal + impl GovernorVotesImpl = GovernorVotesComponent::GovernorVotes; + impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings; + impl GovernorCountingSimpleImpl = + GovernorCountingSimpleComponent::GovernorCounting; + impl GovernorTimelockExecutionImpl = + GovernorTimelockExecutionComponent::GovernorExecution; + + // SRC5 + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + + #[storage] + struct Storage { + #[substorage(v0)] + pub governor: GovernorComponent::Storage, + #[substorage(v0)] + pub governor_votes: GovernorVotesComponent::Storage, + #[substorage(v0)] + pub governor_settings: GovernorSettingsComponent::Storage, + #[substorage(v0)] + pub governor_counting_simple: GovernorCountingSimpleComponent::Storage, + #[substorage(v0)] + pub governor_timelock_execution: GovernorTimelockExecutionComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + GovernorEvent: GovernorComponent::Event, + #[flat] + GovernorVotesEvent: GovernorVotesComponent::Event, + #[flat] + GovernorSettingsEvent: GovernorSettingsComponent::Event, + #[flat] + GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event, + #[flat] + GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + #[constructor] + fn constructor( + ref self: ContractState, votes_token: ContractAddress, timelock_controller: ContractAddress + ) { + self.governor.initializer(); + self.governor_votes.initializer(votes_token); + self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD); + self.governor_timelock_execution.initializer(timelock_controller); + } + + // + // SNIP12 Metadata + // + + pub impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + 'DAPP_NAME' + } + + fn version() -> felt252 { + 'DAPP_VERSION' + } + } + + // + // Locally implemented extensions + // + + impl GovernorQuorum of GovernorComponent::GovernorQuorumTrait { + /// See `GovernorComponent::GovernorQuorumTrait::quorum`. + fn quorum(self: @GovernorComponent::ComponentState, timepoint: u64) -> u256 { + QUORUM + } + } +} +``` + +## Interface + +This is the full interface of the `Governor` implementation: +```rust +#[starknet::interface] +pub trait IGovernor { + fn name(self: @TState) -> felt252; + fn version(self: @TState) -> felt252; + fn COUNTING_MODE(self: @TState) -> ByteArray; + fn hash_proposal(self: @TState, calls: Span, description_hash: felt252) -> felt252; + fn state(self: @TState, proposal_id: felt252) -> ProposalState; + fn proposal_threshold(self: @TState) -> u256; + fn proposal_snapshot(self: @TState, proposal_id: felt252) -> u64; + fn proposal_deadline(self: @TState, proposal_id: felt252) -> u64; + fn proposal_proposer(self: @TState, proposal_id: felt252) -> ContractAddress; + fn proposal_eta(self: @TState, proposal_id: felt252) -> u64; + fn proposal_needs_queuing(self: @TState, proposal_id: felt252) -> bool; + fn voting_delay(self: @TState) -> u64; + fn voting_period(self: @TState) -> u64; + fn quorum(self: @TState, timepoint: u64) -> u256; + fn get_votes(self: @TState, account: ContractAddress, timepoint: u64) -> u256; + fn get_votes_with_params( + self: @TState, account: ContractAddress, timepoint: u64, params: Span + ) -> u256; + fn has_voted(self: @TState, proposal_id: felt252, account: ContractAddress) -> bool; + fn propose(ref self: TState, calls: Span, description: ByteArray) -> felt252; + fn queue(ref self: TState, calls: Span, description_hash: felt252) -> felt252; + fn execute(ref self: TState, calls: Span, description_hash: felt252) -> felt252; + fn cancel(ref self: TState, calls: Span, description_hash: felt252) -> felt252; + fn cast_vote(ref self: TState, proposal_id: felt252, support: u8) -> u256; + fn cast_vote_with_reason( + ref self: TState, proposal_id: felt252, support: u8, reason: ByteArray + ) -> u256; + fn cast_vote_with_reason_and_params( + ref self: TState, + proposal_id: felt252, + support: u8, + reason: ByteArray, + params: Span + ) -> u256; + fn cast_vote_by_sig( + ref self: TState, + proposal_id: felt252, + support: u8, + voter: ContractAddress, + signature: Span + ) -> u256; + fn cast_vote_with_reason_and_params_by_sig( + ref self: TState, + proposal_id: felt252, + support: u8, + voter: ContractAddress, + reason: ByteArray, + params: Span, + signature: Span + ) -> u256; + fn nonces(self: @TState, voter: ContractAddress) -> felt252; + fn relay(ref self: TState, call: Call); +} +``` diff --git a/content/contracts-cairo/3.x/governance/multisig.mdx b/content/contracts-cairo/3.x/governance/multisig.mdx new file mode 100644 index 00000000..c98fdf9d --- /dev/null +++ b/content/contracts-cairo/3.x/governance/multisig.mdx @@ -0,0 +1,150 @@ +--- +title: Multisig +--- + +The Multisig component implements a multi-signature mechanism to enhance the security and +governance of smart contract transactions. It ensures that no single signer can unilaterally +execute critical actions, requiring multiple registered signers to approve and collectively +execute transactions. + +This component is designed to secure operations such as fund management or protocol governance, +where collective decision-making is essential. The Multisig Component is self-administered, +meaning that changes to signers or quorum must be approved through the multisig process itself. + +## Key features + +* **Multi-Signature Security**: transactions must be approved by multiple signers, ensuring +distributed governance. +* **Quorum Enforcement**: defines the minimum number of approvals required for transaction execution. +* **Self-Administration**: all modifications to the component (e.g., adding or removing signers) +must pass through the multisig process. +* **Event Logging**: provides comprehensive event logging for transparency and auditability. + +## Signer management + +The Multisig component introduces the concept of signers and quorum: + +* **Signers**: only registered signers can submit, confirm, revoke, or execute transactions. The Multisig +Component supports adding, removing, or replacing signers. +* **Quorum**: the quorum defines the minimum number of confirmations required to approve a transaction. + + +To prevent unauthorized modifications, only the contract itself can add, remove, or replace signers or change the quorum. +This ensures that all modifications pass through the multisig approval process. + + +## Transaction lifecycle + +The state of a transaction is represented by the `TransactionState` enum and can be retrieved +by calling the `get_transaction_state` function with the transaction’s identifier. + +The identifier of a multisig transaction is a `felt252` value, computed as the Pedersen hash +of the transaction’s calls and salt. It can be computed by invoking the implementing contract’s +`hash_transaction` method for single-call transactions or `hash_transaction_batch` for multi-call +transactions. Submitting a transaction with identical calls and the same salt value a second time +will fail, as transaction identifiers must be unique. To resolve this, use a different salt value +to generate a unique identifier. + +A transaction in the Multisig component follows a specific lifecycle: + +`NotFound` → `Pending` → `Confirmed` → `Executed` + +* **NotFound**: the transaction does not exist. +* **Pending**: the transaction exists but has not reached the required confirmations. +* **Confirmed**: the transaction has reached the quorum but has not yet been executed. +* **Executed**: the transaction has been successfully executed. + +## Usage + +Integrating the Multisig functionality into a contract requires implementing [MultisigComponent](../api/governance#MultisigComponent). +The contract’s constructor should initialize the component with a quorum value and a list of initial signers. + +Here’s an example of a simple wallet contract featuring the Multisig functionality: + +```rust +#[starknet::contract] +mod MultisigWallet { + use openzeppelin_governance::multisig::MultisigComponent; + use starknet::ContractAddress; + + component!(path: MultisigComponent, storage: multisig, event: MultisigEvent); + + #[abi(embed_v0)] + impl MultisigImpl = MultisigComponent::MultisigImpl; + impl MultisigInternalImpl = MultisigComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + multisig: MultisigComponent::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + MultisigEvent: MultisigComponent::Event, + } + + #[constructor] + fn constructor(ref self: ContractState, quorum: u32, signers: Span) { + self.multisig.initializer(quorum, signers); + } +} +``` + +## Interface + +This is the interface of a contract implementing the [MultisigComponent](../api/governance#MultisigComponent): + +```rust +#[starknet::interface] +pub trait MultisigABI { + // Read functions + fn get_quorum(self: @TState) -> u32; + fn is_signer(self: @TState, signer: ContractAddress) -> bool; + fn get_signers(self: @TState) -> Span; + fn is_confirmed(self: @TState, id: TransactionID) -> bool; + fn is_confirmed_by(self: @TState, id: TransactionID, signer: ContractAddress) -> bool; + fn is_executed(self: @TState, id: TransactionID) -> bool; + fn get_submitted_block(self: @TState, id: TransactionID) -> u64; + fn get_transaction_state(self: @TState, id: TransactionID) -> TransactionState; + fn get_transaction_confirmations(self: @TState, id: TransactionID) -> u32; + fn hash_transaction( + self: @TState, + to: ContractAddress, + selector: felt252, + calldata: Span, + salt: felt252, + ) -> TransactionID; + fn hash_transaction_batch(self: @TState, calls: Span, salt: felt252) -> TransactionID; + + // Write functions + fn add_signers(ref self: TState, new_quorum: u32, signers_to_add: Span); + fn remove_signers(ref self: TState, new_quorum: u32, signers_to_remove: Span); + fn replace_signer( + ref self: TState, signer_to_remove: ContractAddress, signer_to_add: ContractAddress, + ); + fn change_quorum(ref self: TState, new_quorum: u32); + fn submit_transaction( + ref self: TState, + to: ContractAddress, + selector: felt252, + calldata: Span, + salt: felt252, + ) -> TransactionID; + fn submit_transaction_batch( + ref self: TState, calls: Span, salt: felt252, + ) -> TransactionID; + fn confirm_transaction(ref self: TState, id: TransactionID); + fn revoke_confirmation(ref self: TState, id: TransactionID); + fn execute_transaction( + ref self: TState, + to: ContractAddress, + selector: felt252, + calldata: Span, + salt: felt252, + ); + fn execute_transaction_batch(ref self: TState, calls: Span, salt: felt252); +} +``` diff --git a/content/contracts-cairo/3.x/governance/timelock.mdx b/content/contracts-cairo/3.x/governance/timelock.mdx new file mode 100644 index 00000000..d3404511 --- /dev/null +++ b/content/contracts-cairo/3.x/governance/timelock.mdx @@ -0,0 +1,198 @@ +--- +title: Timelock Controller +--- + +The Timelock Controller provides a means of enforcing time delays on the execution of transactions. This is considered good practice regarding governance systems because it allows users the opportunity to exit the system if they disagree with a decision before it is executed. + + +The Timelock contract itself executes transactions, not the user. The Timelock should, therefore, hold associated funds, ownership, and access control roles. + + +## Operation lifecycle + +The state of an operation is represented by the `OperationState` enum and can be retrieved +by calling the `get_operation_state` function with the operation’s identifier. + +The identifier of an operation is a `felt252` value, computed as the Pedersen hash of the +operation’s call or calls, its predecessor, and salt. It can be computed by invoking the +implementing contract’s `hash_operation` function for single-call operations or +`hash_operation_batch` for multi-call operations. Submitting an operation with identical calls, +predecessor, and the same salt value a second time will fail, as operation identifiers must be +unique. To resolve this, use a different salt value to generate a unique identifier. + +Timelocked operations follow a specific lifecycle: + +`Unset` → `Waiting` → `Ready` → `Done` + +* `Unset`: the operation has not been scheduled or has been canceled. +* `Waiting`: the operation has been scheduled and is pending the scheduled delay. +* `Ready`: the timer has expired, and the operation is eligible for execution. +* `Done`: the operation has been executed. + +## Timelock flow + +### Schedule + +When a proposer calls [schedule](../api/governance#ITimelock-schedule), the `OperationState` moves from `Unset` to `Waiting`. +This starts a timer that must be greater than or equal to the minimum delay. +The timer expires at a timestamp accessible through [get_timestamp](../api/governance#ITimelock-get_timestamp). +Once the timer expires, the `OperationState` automatically moves to the `Ready` state. +At this point, it can be executed. + +### Execute + +By calling [execute](../api/governance#ITimelock-execute), an executor triggers the operation’s underlying transactions and moves it to the `Done` state. If the operation has a predecessor, the predecessor’s operation must be in the `Done` state for this transaction to succeed. + +### Cancel + +The [cancel](../api/governance#ITimelock-cancel) function allows cancellers to cancel any pending operations. +This resets the operation to the `Unset` state. +It is therefore possible for a proposer to re-schedule an operation that has been cancelled. +In this case, the timer restarts when the operation is re-scheduled. + +## Roles + +[TimelockControllerComponent](../api/governance#TimelockControllerComponent) leverages an [AccessControlComponent](../api/access#AccessControlComponent) setup that we need to understand in order to set up roles. + +* `PROPOSER_ROLE` - in charge of queueing operations. +* `CANCELLER_ROLE` - may cancel scheduled operations. +During initialization, accounts granted with `PROPOSER_ROLE` will also be granted `CANCELLER_ROLE`. +Therefore, the initial proposers may also cancel operations after they are scheduled. +* `EXECUTOR_ROLE` - in charge of executing already available operations. +* `DEFAULT_ADMIN_ROLE` - can grant and revoke the three previous roles. + + +The `DEFAULT_ADMIN_ROLE` is a sensitive role that will be granted automatically to the timelock itself and optionally to a second account. +The latter case may be required to ease a contract’s initial configuration; however, this role should promptly be renounced. + + +Furthermore, the timelock component supports the concept of open roles for the `EXECUTOR_ROLE`. +This allows anyone to execute an operation once it’s in the `Ready` OperationState. +To enable the `EXECUTOR_ROLE` to be open, grant the zero address with the `EXECUTOR_ROLE`. + + +Be very careful with enabling open roles as _anyone_ can call the function. + + +## Minimum delay + +The minimum delay of the timelock acts as a buffer from when a proposer schedules an operation to the earliest point at which an executor may execute that operation. +The idea is for users, should they disagree with a scheduled proposal, to have options such as exiting the system or making their case for cancellers to cancel the operation. + +After initialization, the only way to change the timelock’s minimum delay is to schedule it and execute it with the same flow as any other operation. + +The minimum delay of a contract is accessible through [get_min_delay](../api/governance#ITimelock-get_min_delay). + +## Usage + +Integrating the timelock into a contract requires integrating [TimelockControllerComponent](../api/governance#TimelockControllerComponent) as well as [SRC5Component](../api/introspection#SRC5Component) and [AccessControlComponent](../api/access#AccessControlComponent) as dependencies. +The contract’s constructor should initialize the timelock which consists of setting the: + +* Proposers and executors. +* Minimum delay between scheduling and executing an operation. +* Optional admin if additional configuration is required. + + +The optional admin should renounce their role once configuration is complete. + + +Here’s an example of a simple timelock contract: + +```rust +#[starknet::contract] +mod TimelockControllerContract { + use openzeppelin_access::accesscontrol::AccessControlComponent; + use openzeppelin_governance::timelock::TimelockControllerComponent; + use openzeppelin_introspection::src5::SRC5Component; + use starknet::ContractAddress; + + component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); + component!(path: TimelockControllerComponent, storage: timelock, event: TimelockEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // Timelock Mixin + #[abi(embed_v0)] + impl TimelockMixinImpl = + TimelockControllerComponent::TimelockMixinImpl; + impl TimelockInternalImpl = TimelockControllerComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + access_control: AccessControlComponent::Storage, + #[substorage(v0)] + timelock: TimelockControllerComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccessControlEvent: AccessControlComponent::Event, + #[flat] + TimelockEvent: TimelockControllerComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + min_delay: u64, + proposers: Span, + executors: Span, + admin: ContractAddress + ) { + self.timelock.initializer(min_delay, proposers, executors, admin); + } +} +``` + +## Interface + +This is the full interface of the TimelockMixinImpl implementation: + +```rust +#[starknet::interface] +pub trait TimelockABI { + // ITimelock + fn is_operation(self: @TState, id: felt252) -> bool; + fn is_operation_pending(self: @TState, id: felt252) -> bool; + fn is_operation_ready(self: @TState, id: felt252) -> bool; + fn is_operation_done(self: @TState, id: felt252) -> bool; + fn get_timestamp(self: @TState, id: felt252) -> u64; + fn get_operation_state(self: @TState, id: felt252) -> OperationState; + fn get_min_delay(self: @TState) -> u64; + fn hash_operation(self: @TState, call: Call, predecessor: felt252, salt: felt252) -> felt252; + fn hash_operation_batch( + self: @TState, calls: Span, predecessor: felt252, salt: felt252 + ) -> felt252; + fn schedule(ref self: TState, call: Call, predecessor: felt252, salt: felt252, delay: u64); + fn schedule_batch( + ref self: TState, calls: Span, predecessor: felt252, salt: felt252, delay: u64 + ); + fn cancel(ref self: TState, id: felt252); + fn execute(ref self: TState, call: Call, predecessor: felt252, salt: felt252); + fn execute_batch(ref self: TState, calls: Span, predecessor: felt252, salt: felt252); + fn update_delay(ref self: TState, new_delay: u64); + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; + + // IAccessControl + fn has_role(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn get_role_admin(self: @TState, role: felt252) -> felt252; + fn grant_role(ref self: TState, role: felt252, account: ContractAddress); + fn revoke_role(ref self: TState, role: felt252, account: ContractAddress); + fn renounce_role(ref self: TState, role: felt252, account: ContractAddress); + + // IAccessControlCamel + fn hasRole(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn getRoleAdmin(self: @TState, role: felt252) -> felt252; + fn grantRole(ref self: TState, role: felt252, account: ContractAddress); + fn revokeRole(ref self: TState, role: felt252, account: ContractAddress); + fn renounceRole(ref self: TState, role: felt252, account: ContractAddress); +} +``` diff --git a/content/contracts-cairo/3.x/governance/votes.mdx b/content/contracts-cairo/3.x/governance/votes.mdx new file mode 100644 index 00000000..44d9e731 --- /dev/null +++ b/content/contracts-cairo/3.x/governance/votes.mdx @@ -0,0 +1,222 @@ +--- +title: Votes +--- + +The [VotesComponent](../api/governance#VotesComponent) provides a flexible system for tracking and delegating voting power. This system allows users to delegate their voting power to other addresses, enabling more active participation in governance. + + +By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. + + + +The transferring of voting units must be handled by the implementing contract. In the case of `ERC20` and `ERC721` this is usually done via the hooks. You can check the [usage](#usage) section for examples of how to implement this. + + +## Key features + +1. **Delegation**: Users can delegate their voting power to any address, including themselves. Vote power can be delegated either by calling the [delegate](../api/governance#VotesComponent-delegate) function directly, or by providing a signature to be used with [delegate_by_sig](../api/governance#VotesComponent-delegate_by_sig). +2. **Historical lookups**: The system keeps track of historical snapshots for each account, which allows the voting power of an account to be queried at a specific timestamp.\ +This can be used for example to determine the voting power of an account when a proposal was created, rather than using the current balance. + +## Usage + +When integrating the `VotesComponent`, the [VotingUnitsTrait](../api/governance#VotingUnitsTrait) must be implemented to get the voting units for a given account as a function of the implementing contract.\ +For simplicity, this module already provides two implementations for `ERC20` and `ERC721` tokens, which will work out of the box if the respective components are integrated.\ +Additionally, you must implement the [NoncesComponent](../api/utilities#NoncesComponent) and the [SNIP12Metadata](../api/utilities#snip12) trait to enable delegation by signatures. + +Here’s an example of how to structure a simple ERC20Votes contract: + +```rust +#[starknet::contract] +mod ERC20VotesContract { + use openzeppelin_governance::votes::VotesComponent; + use openzeppelin_token::erc20::{ERC20Component, DefaultConfig}; + use openzeppelin_utils::cryptography::nonces::NoncesComponent; + use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; + use starknet::ContractAddress; + + component!(path: VotesComponent, storage: erc20_votes, event: ERC20VotesEvent); + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent); + + // Votes + #[abi(embed_v0)] + impl VotesImpl = VotesComponent::VotesImpl; + impl VotesInternalImpl = VotesComponent::InternalImpl; + + // ERC20 + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + // Nonces + #[abi(embed_v0)] + impl NoncesImpl = NoncesComponent::NoncesImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub erc20_votes: VotesComponent::Storage, + #[substorage(v0)] + pub erc20: ERC20Component::Storage, + #[substorage(v0)] + pub nonces: NoncesComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20VotesEvent: VotesComponent::Event, + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + NoncesEvent: NoncesComponent::Event + } + + // Required for hash computation. + pub impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + 'DAPP_NAME' + } + fn version() -> felt252 { + 'DAPP_VERSION' + } + } + + // We need to call the `transfer_voting_units` function after + // every mint, burn and transfer. + // For this, we use the `after_update` hook of the `ERC20Component::ERC20HooksTrait`. + impl ERC20VotesHooksImpl of ERC20Component::ERC20HooksTrait { + fn after_update( + ref self: ERC20Component::ComponentState, + from: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) { + let mut contract_state = self.get_contract_mut(); + contract_state.erc20_votes.transfer_voting_units(from, recipient, amount); + } + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.erc20.initializer("MyToken", "MTK"); + } +} +``` + +And here’s an example of how to structure a simple ERC721Votes contract: + +```rust +#[starknet::contract] +pub mod ERC721VotesContract { + use openzeppelin_governance::votes::VotesComponent; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc721::ERC721Component; + use openzeppelin_utils::cryptography::nonces::NoncesComponent; + use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; + use starknet::ContractAddress; + + component!(path: VotesComponent, storage: erc721_votes, event: ERC721VotesEvent); + component!(path: ERC721Component, storage: erc721, event: ERC721Event); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent); + + // Votes + #[abi(embed_v0)] + impl VotesImpl = VotesComponent::VotesImpl; + impl VotesInternalImpl = VotesComponent::InternalImpl; + + // ERC721 + #[abi(embed_v0)] + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; + impl ERC721InternalImpl = ERC721Component::InternalImpl; + + // Nonces + #[abi(embed_v0)] + impl NoncesImpl = NoncesComponent::NoncesImpl; + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub erc721_votes: VotesComponent::Storage, + #[substorage(v0)] + pub erc721: ERC721Component::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + #[substorage(v0)] + pub nonces: NoncesComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC721VotesEvent: VotesComponent::Event, + #[flat] + ERC721Event: ERC721Component::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + NoncesEvent: NoncesComponent::Event + } + + /// Required for hash computation. + pub impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + 'DAPP_NAME' + } + fn version() -> felt252 { + 'DAPP_VERSION' + } + } + + // We need to call the `transfer_voting_units` function after + // every mint, burn and transfer. + // For this, we use the `before_update` hook of the + //`ERC721Component::ERC721HooksTrait`. + // This hook is called before the transfer is executed. + // This gives us access to the previous owner. + impl ERC721VotesHooksImpl of ERC721Component::ERC721HooksTrait { + fn before_update( + ref self: ERC721Component::ComponentState, + to: ContractAddress, + token_id: u256, + auth: ContractAddress + ) { + let mut contract_state = self.get_contract_mut(); + + // We use the internal function here since it does not check if the token + // id exists which is necessary for mints + let previous_owner = self._owner_of(token_id); + contract_state.erc721_votes.transfer_voting_units(previous_owner, to, 1); + } + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.erc721.initializer("MyToken", "MTK", ""); + } +} +``` + +## Interface + +This is the full interface of the `VotesImpl` implementation: + +```rust +#[starknet::interface] +pub trait VotesABI { + // IVotes + fn get_votes(self: @TState, account: ContractAddress) -> u256; + fn get_past_votes(self: @TState, account: ContractAddress, timepoint: u64) -> u256; + fn get_past_total_supply(self: @TState, timepoint: u64) -> u256; + fn delegates(self: @TState, account: ContractAddress) -> ContractAddress; + fn delegate(ref self: TState, delegatee: ContractAddress); + fn delegate_by_sig(ref self: TState, delegator: ContractAddress, delegatee: ContractAddress, nonce: felt252, expiry: u64, signature: Span); + + // INonces + fn nonces(self: @TState, owner: ContractAddress) -> felt252; +} +``` diff --git a/content/contracts-cairo/3.x/guides/deploy-udc.mdx b/content/contracts-cairo/3.x/guides/deploy-udc.mdx new file mode 100644 index 00000000..5e20ac1e --- /dev/null +++ b/content/contracts-cairo/3.x/guides/deploy-udc.mdx @@ -0,0 +1,273 @@ +--- +title: UDC Appchain Deployment +--- + +While the Universal Deployer Contract (UDC) is deployed on Starknet public networks, appchains may need to deploy +their own instance of the UDC for their own use. This guide will walk you through this process while keeping the +same final address on all networks. + +## Prerequisites + +This guide assumes you have: + +* Familiarity with [Scarb](https://docs.swmansion.com/scarb/docs.html) and Starknet development environment. +* A functional account available on the network you’re deploying to. +* Familiarity with the process of declaring contracts through the [declare transaction](https://docs.starknet.io/resources/transactions-reference/#declare_transaction). + + +For declaring contracts on Starknet, you can use the [sncast](https://foundry-rs.github.io/starknet-foundry/starknet/declare.html) tool from the [starknet-foundry](https://foundry-rs.github.io/starknet-foundry/index.html) project. + + +## Note on the UDC final address + +It is important that the Universal Deployer Contract (UDC) in Starknet maintains the same address across all +networks because essential developer tools like **starkli** and **sncast** rely on this address by default when deploying contracts. +These tools are widely used in the Starknet ecosystem to streamline and standardize contract deployment workflows. + +If the UDC address is consistent, developers can write deployment scripts, CI/CD pipelines, and integrations that work seamlessly +across testnets, mainnet, and appchains without needing to update configuration files or handle special cases for each +environment. + +In the following sections, we’ll walk you through the process of deploying the UDC on appchains while keeping the same address, +under one important assumption: **the declared UDC class hash MUST be the same across all networks**. +Different compiler versions may produce different class hashes for the same contract, so you need to make +sure you are using the same compiler version to build the UDC class (and the release profile). + +The latest version of the UDC available in the `openzeppelin_presets` package was compiled with **Cairo v2.11.4** (release profile) and the resulting class hash is `0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8`. + + +If you are using a different compiler version, you need to make sure the class hash is the same as the one above in order to keep the same address across all networks. + + + +To avoid potential issues by using a different compiler version, you can directly import the contract class deployed on Starknet mainnet and declare it on your appchain. At +the time of writing, this is not easily achievable with the `sncast` tool, but you can leverage `[starkli](https://book.starkli.rs/declaring-classes)` to do it. + +Quick reference: + +```bash +starkli class-by-hash --parse \ + 0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8 \ + --network mainnet \ + > udc.json +``` + +This will output a `udc.json` file that you can use to declare the UDC on your appchain. + +```bash +starkli declare udc.json --rpc +``` + + + +## Madara Appchains + +[Madara](https://github.com/madara-alliance/madara/blob/main/README.md) is a popular Starknet node implementation that has a friendly and robust interface for building appchains. If +you are using it for this purpose, you are probably familiar with the [Madara Bootstrapper](https://github.com/madara-alliance/madara/tree/main/bootstrapper#readme), which already declares and +deploys a few contracts for you when you create a new appchain, including accounts and the UDC. + +However, since the UDC was migrated to a new version in June 2025, it’s possible that the appchain was created before +this change, meaning the UDC on the appchain is an older version. If that’s the case, you can follow the steps below to +deploy the new UDC. + +### 1. Declare and deploy the Bootstrapper + +In the Starknet ecosystem, contracts need to be declared before they can be deployed, and deployments can only happen +either via the `deploy_syscall`, or using a `deploy_account` transaction. The latter would require adding account +functionality to the UDC, which is not optimal, so we’ll use the `deploy_syscall`, which requires having an account +with this functionality enabled. + + +Madara declares an account with this functionality enabled as part of the bootstrapping process. You may be able to +use that implementation directly to skip this step. + + +#### Bootstrapper Contract + +The bootstrapper contract is a simple contract that declares the UDC and allows for its deployment via the `deploy_syscall`. +You can find a reference implementation below: + + +This reference implementation targets Cairo v2.11.4. If you are using a different version of Cairo, you may need to update the code to match your compiler version. + + +```rust +#[starknet::contract(account)] +mod UniversalDeployerBootstrapper { + use core::num::traits::Zero; + use openzeppelin_account::AccountComponent; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_utils::deployments::calculate_contract_address_from_deploy_syscall; + use starknet::{ClassHash, ContractAddress, SyscallResultTrait}; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // + // Account features (deployable, declarer, and invoker) + // + + #[abi(embed_v0)] + pub(crate) impl DeployableImpl = + AccountComponent::DeployableImpl; + #[abi(embed_v0)] + impl DeclarerImpl = AccountComponent::DeclarerImpl; + #[abi(embed_v0)] + impl SRC6Impl = AccountComponent::SRC6Impl; + impl AccountInternalImpl = AccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + pub account: AccountComponent::Storage, + #[substorage(v0)] + pub src5: SRC5Component::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + pub(crate) enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + } + + #[constructor] + pub fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + } + + #[abi(per_item)] + #[generate_trait] + impl ExternalImpl of ExternalTrait { + #[external(v0)] + fn deploy_udc(ref self: ContractState, udc_class_hash: ClassHash) { + self.account.assert_only_self(); + starknet::syscalls::deploy_syscall(udc_class_hash, 0, array![].span(), true) + .unwrap_syscall(); + } + + #[external(v0)] + fn get_udc_address(ref self: ContractState, udc_class_hash: ClassHash) -> ContractAddress { + calculate_contract_address_from_deploy_syscall( + 0, udc_class_hash, array![].span(), Zero::zero(), + ) + } + +} +``` + +#### Deploying the Bootstrapper + +This guide assumes you have a functional account available on the network you’re deploying to, and familiarity +with the process of declaring contracts through the `declare` transaction. To recap, the reason we are deploying +this bootstrapper account contract is to be able to deploy the UDC via the `deploy_syscall`. + + +sncast v0.45.0 was used in the examples below. + + +As a quick example, if your account is configured for **sncast**, you can declare the bootstrapper contract with the following command: + +```bash +sncast -p declare \ + --contract-name UniversalDeployerBootstrapper +``` + +The bootstrapper implements the `IDeployable` trait, meaning it can be counterfactually deployed. Check out the +[Counterfactual Deployments](./deployment) guide. Continuing with the **sncast** examples, you can create and deploy the bootstrapper with the following commands: + +##### Create the account + +```bash +sncast account create --name bootstrapper \ + --network \ + --class-hash \ + --type oz +``` + +##### Deploy it to the network + + +You need to prefund the account with enough funds before you can deploy it. + + +```bash +sncast account deploy \ + --network \ + --name bootstrapper +``` + +### 2. Declare and deploy the UDC + +Once the bootstrapper is deployed, you can declare and deploy the UDC through it. + +#### Declaring the UDC + +The UDC source code is available in the `openzeppelin_presets` package. You can copy it to your project and declare it with the following command: + +```bash +sncast -p declare \ + --contract-name UniversalDeployer +``` + + +If you followed the [Note on the UDC final address](#note-on-the-udc-final-address) section, your declared class hash should be +`0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8`. + + +#### Previewing the UDC address + +You can preview the UDC address with the following command: + +```bash +sncast call \ + --network \ + --contract-address \ + --function "get_udc_address" \ + --arguments '' +``` + +If the UDC class hash is the same as the one in the [Note on the UDC final address](#note-on-the-udc-final-address) section, +the output should be `0x2ceed65a4bd731034c01113685c831b01c15d7d432f71afb1cf1634b53a2125`. + +#### Deploying the UDC + +Now everything is set up to deploy the UDC. You can use the following command to deploy it: + + +Note that the bootstrapper contract MUST call itself to successfully deploy the UDC, since the `deploy_udc` function is protected. + + +```bash +sncast \ + --account bootstrapper \ + invoke \ + --network \ + --contract-address \ + --function "deploy_udc" \ + --arguments '' +``` + +## Other Appchain providers + +If you are using an appchain provider different from Madara, you can follow the same steps to deploy the UDC +as long as you have access to an account that can declare contracts. + +Summarizing, the steps to follow are: + +1. Declare the Bootstrapper +2. Counterfactually deploy the Bootstrapper +3. Declare the UDC +4. Preview the UDC address +5. Deploy the UDC from the Bootstrapper + +## Conclusion + +By following this guide, you have successfully deployed the Universal Deployer Contract on your appchain while ensuring consistency with +Starknet’s public networks. Maintaining the same UDC address and class hash across all environments is crucial for seamless contract deployment +and tooling compatibility, allowing developers to leverage tools like **sncast** and **starkli** without additional configuration. This process not only +improves the reliability of your deployment workflows but also ensures that your appchain remains compatible with the broader Starknet ecosystem. +With the UDC correctly deployed, you are now ready to take full advantage of streamlined contract +deployments and robust developer tooling on your appchain. diff --git a/content/contracts-cairo/3.x/guides/deployment.mdx b/content/contracts-cairo/3.x/guides/deployment.mdx new file mode 100644 index 00000000..42a924db --- /dev/null +++ b/content/contracts-cairo/3.x/guides/deployment.mdx @@ -0,0 +1,40 @@ +--- +title: Counterfactual deployments +--- + +A counterfactual contract is a contract we can interact with even before actually deploying it on-chain. +For example, we can send funds or assign privileges to a contract that doesn’t yet exist. +Why? Because deployments in Starknet are deterministic, allowing us to predict the address where our contract will be deployed. +We can leverage this property to make a contract pay for its own deployment by simply sending funds in advance. We call this a counterfactual deployment. + +This process can be described with the following steps: + + +For testing this flow you can check the [Starknet Foundry](https://foundry-rs.github.io/starknet-foundry/starknet/account.html) or the [Starkli](https://book.starkli.rs/accounts#account-deployment) guides for deploying accounts. + + +1. Deterministically precompute the `contract_address` given a `class_hash`, `salt`, and constructor `calldata`. +Note that the `class_hash` must be previously declared for the deployment to succeed. +2. Send funds to the `contract_address`. Usually you will estimate the fee of the transaction first. Existing +tools usually do this for you. +3. Send a `DeployAccount` type transaction to the network. +4. The protocol will then validate the transaction with the `__validate_deploy__` entrypoint of the contract to be deployed. +5. If the validation succeeds, the protocol will charge the fee and then register the contract as deployed. + + +Although this method is very popular to deploy accounts, this works for any kind of contract. + + +## Deployment validation + +To be counterfactually deployed, the deploying contract must implement the `__validate_deploy__` entrypoint, +called by the protocol when a `DeployAccount` transaction is sent to the network. + +```rust +trait IDeployable { + /// Must return 'VALID' when the validation is successful. + fn __validate_deploy__( + class_hash: felt252, contract_address_salt: felt252, public_key: felt252 + ) -> felt252; +} +``` diff --git a/content/contracts-cairo/3.x/guides/erc20-permit.mdx b/content/contracts-cairo/3.x/guides/erc20-permit.mdx new file mode 100644 index 00000000..28259458 --- /dev/null +++ b/content/contracts-cairo/3.x/guides/erc20-permit.mdx @@ -0,0 +1,63 @@ +--- +title: ERC20Permit +--- + +The [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) standard, commonly referred to as ERC20Permit, is designed to support gasless token approvals. This is achieved with an off-chain +signature following the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard, rather than with an on-chain transaction. The [permit](../api/erc20#ERC20Component-permit) function verifies the signature and sets +the spender’s allowance if the signature is valid. This approach improves user experience and reduces gas costs. + +## Differences from Solidity + +Although this extension is mostly similar to the [Solidity implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Permit.sol) of [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612), there are some notable differences in the parameters of the [permit](../api/erc20#ERC20Component-permit) function: + +* The `deadline` parameter is represented by `u64` rather than `u256`. +* The `signature` parameter is represented by a span of felts rather than `v`, `r`, and `s` values. + + +Unlike Solidity, there is no enforced format for signatures on Starknet. A signature is represented by an array or span of felts, +and there is no universal method for validating signatures of unknown formats. Consequently, a signature provided to the [permit](../api/erc20#ERC20Component-permit) function +is validated through an external `is_valid_signature` call to the contract at the `owner` address. + + + +## Usage + +The functionality is provided as an embeddable [ERC20Permit](../api/erc20#ERC20Component-Embeddable-Impls-ERC20PermitImpl) trait of the [ERC20Component](../api/erc20#ERC20Component). + +```rust +#[abi(embed_v0)] +impl ERC20PermitImpl = ERC20Component::ERC20PermitImpl; +``` + +A contract must meet the following requirements to be able to use the [ERC20Permit](../api/erc20#ERC20Component-Embeddable-Impls-ERC20PermitImpl) trait: + +* Implement [ERC20Component](../api/erc20#ERC20Component). +* Implement [NoncesComponent](../api/utilities#NoncesComponent). +* Implement [SNIP12Metadata](../api/utilities#snip12) trait (used in signature generation). + +## Typed message + +To safeguard against replay attacks and ensure the uniqueness of each approval via [permit](../api/erc20#ERC20Component-permit), the data signed includes: + +* The address of the `owner`. +* The parameters specified in the [approve](../api/erc20#ERC20Component-approve) function (`spender` and `amount`) +* The address of the `token` contract itself. +* A `nonce`, which must be unique for each operation. +* The `chain_id`, which protects against cross-chain replay attacks. + +The format of the `Permit` structure in a signed permit message is as follows: +```rust +struct Permit { + token: ContractAddress, + spender: ContractAddress, + amount: u256, + nonce: felt252, + deadline: u64, +} +``` + + +The owner of the tokens is also part of the signed message. It is used as the `signer` parameter in the `get_message_hash` call. + + +Further details on preparing and signing a typed message can be found in the [SNIP12 guide](./snip12). diff --git a/content/contracts-cairo/3.x/guides/erc20-supply.mdx b/content/contracts-cairo/3.x/guides/erc20-supply.mdx new file mode 100644 index 00000000..51465637 --- /dev/null +++ b/content/contracts-cairo/3.x/guides/erc20-supply.mdx @@ -0,0 +1,148 @@ +--- +title: Creating ERC20 Supply +--- + +The standard interface implemented by tokens built on Starknet comes from the popular token standard on Ethereum called ERC20. +[EIP20](https://eips.ethereum.org/EIPS/eip-20), from which ERC20 contracts are derived, does not specify how tokens are created. +This guide will go over strategies for creating both a fixed and dynamic token supply. + +## Fixed Supply + +Let’s say we want to create a token named `MyToken` with a fixed token supply. +We can achieve this by setting the token supply in the constructor which will execute upon deployment. + +```rust +#[starknet::contract] +mod MyToken { + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // ERC20 Mixin + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc20: ERC20Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + fixed_supply: u256, + recipient: ContractAddress + ) { + let name = "MyToken"; + let symbol = "MTK"; + + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, fixed_supply); + } +} +``` + +In the constructor, we’re first calling the ERC20 initializer to set the token name and symbol. +Next, we’re calling the internal `mint` function which creates `fixed_supply` of tokens and allocates them to `recipient`. +Since the internal `mint` is not exposed in our contract, it will not be possible to create any more tokens. +In other words, we’ve implemented a fixed token supply! + +## Dynamic Supply + +ERC20 contracts with a dynamic supply include a mechanism for creating or destroying tokens. +Let’s make a few changes to the almighty `MyToken` contract and create a minting mechanism. + +```rust +#[starknet::contract] +mod MyToken { + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // ERC20 Mixin + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc20: ERC20Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + let name = "MyToken"; + let symbol = "MTK"; + + self.erc20.initializer(name, symbol); + } + + #[external(v0)] + fn mint( + ref self: ContractState, + recipient: ContractAddress, + amount: u256 + ) { + // This function is NOT protected which means + // ANYONE can mint tokens + self.erc20.mint(recipient, amount); + } +} +``` + +The exposed `mint` above will create `amount` tokens and allocate them to `recipient`. +We now have our minting mechanism! + +There is, however, a big problem. +`mint` does not include any restrictions on who can call this function. +For the sake of good practices, let’s implement a simple permissioning mechanism with `Ownable`. + +```rust +#[starknet::contract] +mod MyToken { + + (...) + + // Integrate Ownable + + #[external(v0)] + fn mint( + ref self: ContractState, + recipient: ContractAddress, + amount: u256 + ) { + // Set permissions with Ownable + self.ownable.assert_only_owner(); + + // Mint tokens if called by the contract owner + self.erc20.mint(recipient, amount); + } +} +``` + +In the constructor, we pass the owner address to set the owner of the `MyToken` contract. +The `mint` function includes `assert_only_owner` which will ensure that only the contract owner can call this function. +Now, we have a protected ERC20 minting mechanism to create a dynamic token supply. + + +For a more thorough explanation of permission mechanisms, see [Access Control](../access). + diff --git a/content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx b/content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx new file mode 100644 index 00000000..022c2c47 --- /dev/null +++ b/content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx @@ -0,0 +1,163 @@ +--- +title: 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: + +```rust +use openzeppelin_token::erc20::interface::IERC20; +``` + +or + +```rust +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. + +```rust +#[starknet::interface] +pub trait 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; +} +``` + +### 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 standards 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. + + +```rust +#[starknet::interface] +pub trait ERC20ABI { + // 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: + +```rust +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](https://book.cairo-lang.org/ch15-02-interacting-with-another-contract.html) 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 + + +The `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](https://community.starknet.io/t/the-great-interface-migration/92107) 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: + +1. `camelCase` interfaces, which are the ones we’ve been using so far. +2. `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](https://github.com/OpenZeppelin/cairo-contracts/releases/tag/v0.6.1) and below). + + +### `IERC20` + +The default version of the ERC20 interface trait exposes `snake_case` functions: + +```rust +#[starknet::interface] +pub trait IERC20 { + 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: + +```rust +#[starknet::interface] +pub trait IERC20Camel { + 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; +} +``` diff --git a/content/contracts-cairo/3.x/guides/snip12.mdx b/content/contracts-cairo/3.x/guides/snip12.mdx new file mode 100644 index 00000000..98ac1f4b --- /dev/null +++ b/content/contracts-cairo/3.x/guides/snip12.mdx @@ -0,0 +1,344 @@ +--- +title: SNIP12 and Typed Messages +--- + +Similar to [EIP712](https://eips.ethereum.org/EIPS/eip-712), [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) is a standard for secure off-chain signature verification on Starknet. +It provides a way to hash and sign generic typed structs rather than just strings. When building decentralized +applications, usually you might need to sign a message with complex data. The purpose of signature verification +is then to ensure that the received message was indeed signed by the expected signer, and it hasn’t been tampered with. + +OpenZeppelin Contracts for Cairo provides a set of utilities to make the implementation of this standard +as easy as possible, and in this guide we will walk you through the process of generating the hashes of typed messages +using these utilities for on-chain signature verification. For that, let’s build an example with a custom [ERC20](/contracts-cairo/2.x/api/erc20#ERC20) contract +adding an extra `transfer_with_signature` method. + + +This is an educational example, and it is not intended to be used in production environments. + + +## CustomERC20 + +Let’s start with a basic ERC20 contract leveraging the [ERC20Component](/contracts-cairo/2.x/api/erc20#ERC20Component), and let’s add the new function. +Note that some declarations are omitted for brevity. The full example will be available at the end of the guide. + +```rust +#[starknet::contract] +mod CustomERC20 { + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + (...) + + #[constructor] + fn constructor( + ref self: ContractState, + initial_supply: u256, + recipient: ContractAddress + ) { + self.erc20.initializer("MyToken", "MTK"); + self.erc20.mint(recipient, initial_supply); + } + + #[external(v0)] + fn transfer_with_signature( + ref self: ContractState, + recipient: ContractAddress, + amount: u256, + nonce: felt252, + expiry: u64, + signature: Array + ) { + (...) + } +} +``` + +The `transfer_with_signature` function will allow a user to transfer tokens to another account by providing a signature. +The signature will be generated off-chain, and it will be used to verify the message on-chain. Note that the message +we need to verify is a struct with the following fields: + +* `recipient`: The address of the recipient. +* `amount`: The amount of tokens to transfer. +* `nonce`: A unique number to prevent replay attacks. +* `expiry`: The timestamp when the signature expires. + +Note that generating the hash of this message on-chain is a requirement to verify the signature, because if we accept +the message as a parameter, it could be easily tampered with. + +## Generating the Typed Message Hash + +To generate the hash of the message, we need to follow these steps: + +### 1. Define the message struct. + +In this particular example, the message struct looks like this: + +```rust +struct Message { + recipient: ContractAddress, + amount: u256, + nonce: felt252, + expiry: u64 +} +``` + +### 2. Get the message type hash. + +This is the `starknet_keccak(encode_type(message))` as defined in the [SNIP](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md#how-to-work-with-each-type). + +In this case it can be computed as follows: + +```rust +// Since there's no u64 type in SNIP-12, we use u128 for `expiry` in the type hash generation. +let message_type_hash = selector!( + "\"Message\"(\"recipient\":\"ContractAddress\",\"amount\":\"u256\",\"nonce\":\"felt\",\"expiry\":\"u128\")\"u256\"(\"low\":\"u128\",\"high\":\"u128\")" +); +``` + +which is the same as: + +```rust +let message_type_hash = 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; +``` + + +In practice it’s better to compute the type hash off-chain and hardcode it in the contract, since it is a constant value. + + +### 3. Implement the `StructHash` trait for the struct. + +You can import the trait from: `openzeppelin_utils::snip12::StructHash`. And this implementation +is nothing more than the encoding of the message as defined in the [SNIP](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md#how-to-work-with-each-type). + +```rust +use core::hash::{HashStateExTrait, HashStateTrait}; +use core::poseidon::PoseidonTrait; +use openzeppelin_utils::snip12::StructHash; +use starknet::ContractAddress; + +const MESSAGE_TYPE_HASH: felt252 = + 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; + +#[derive(Copy, Drop, Hash)] +struct Message { + recipient: ContractAddress, + amount: u256, + nonce: felt252, + expiry: u64 +} + +impl StructHashImpl of StructHash { + fn hash_struct(self: @Message) -> felt252 { + let hash_state = PoseidonTrait::new(); + hash_state.update_with(MESSAGE_TYPE_HASH).update_with(*self).finalize() + } +} +``` + +### 4. Implement the `SNIP12Metadata` trait. + +This implementation determines the values of the domain separator. Only the `name` and `version` fields are required +because the `chain_id` is obtained on-chain, and the `revision` is hardcoded to `1`. + +```rust +use openzeppelin_utils::snip12::SNIP12Metadata; + +impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { 'DAPP_NAME' } + fn version() -> felt252 { 'v1' } +} +``` + +In the above example, no storage reads are required which avoids unnecessary extra gas costs, but in +some cases we may need to read from storage to get the domain separator values. This can be accomplished even when +the trait is not bounded to the ContractState, like this: + +```rust +use openzeppelin_utils::snip12::SNIP12Metadata; + +impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + let state = unsafe_new_contract_state(); + + // Some logic to get the name from storage + state.erc20.name().at(0).unwrap().into() + } + + fn version() -> felt252 { 'v1' } +} +``` + +### 5. Generate the hash. + +The final step is to use the `OffchainMessageHashImpl` implementation to generate the hash of the message +using the `get_message_hash` function. The implementation is already available as a utility. + +```rust +use core::hash::{HashStateExTrait, HashStateTrait}; +use core::poseidon::PoseidonTrait; +use openzeppelin_utils::snip12::{SNIP12Metadata, StructHash, OffchainMessageHash}; +use starknet::ContractAddress; + +const MESSAGE_TYPE_HASH: felt252 = + 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; + +#[derive(Copy, Drop, Hash)] +struct Message { + recipient: ContractAddress, + amount: u256, + nonce: felt252, + expiry: u64 +} + +impl StructHashImpl of StructHash { + fn hash_struct(self: @Message) -> felt252 { + let hash_state = PoseidonTrait::new(); + hash_state.update_with(MESSAGE_TYPE_HASH).update_with(*self).finalize() + } +} + +impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + 'DAPP_NAME' + } + fn version() -> felt252 { + 'v1' + } +} + +fn get_hash( + account: ContractAddress, recipient: ContractAddress, amount: u256, nonce: felt252, expiry: u64 +) -> felt252 { + let message = Message { recipient, amount, nonce, expiry }; + message.get_message_hash(account) +} +``` + + +The expected parameter for the `get_message_hash` function is the address of account that signed the message. + + +## Full Implementation + +Finally, the full implementation of the `CustomERC20` contract looks like this: + + +We are using the [`ISRC6Dispatcher`](/contracts-cairo/2.x/api/account#ISRC6) to verify the signature, +and the [`NoncesComponent`](/contracts-cairo/2.x/api/utilities#NoncesComponent) to handle nonces to prevent replay attacks. + + +```rust +use core::hash::{HashStateExTrait, HashStateTrait}; +use core::poseidon::PoseidonTrait; +use openzeppelin_utils::snip12::{SNIP12Metadata, StructHash, OffchainMessageHash}; +use starknet::ContractAddress; + +const MESSAGE_TYPE_HASH: felt252 = + 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; + +#[derive(Copy, Drop, Hash)] +struct Message { + recipient: ContractAddress, + amount: u256, + nonce: felt252, + expiry: u64 +} + +impl StructHashImpl of StructHash { + fn hash_struct(self: @Message) -> felt252 { + let hash_state = PoseidonTrait::new(); + hash_state.update_with(MESSAGE_TYPE_HASH).update_with(*self).finalize() + } +} + +#[starknet::contract] +mod CustomERC20 { + use openzeppelin_account::interface::{ISRC6Dispatcher, ISRC6DispatcherTrait}; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use openzeppelin_utils::cryptography::nonces::NoncesComponent; + use starknet::ContractAddress; + + use super::{Message, OffchainMessageHash, SNIP12Metadata}; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: NoncesComponent, storage: nonces, event: NoncesEvent); + + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[abi(embed_v0)] + impl NoncesImpl = NoncesComponent::NoncesImpl; + impl NoncesInternalImpl = NoncesComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] + nonces: NoncesComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + NoncesEvent: NoncesComponent::Event + } + + #[constructor] + fn constructor(ref self: ContractState, initial_supply: u256, recipient: ContractAddress) { + self.erc20.initializer("MyToken", "MTK"); + self.erc20.mint(recipient, initial_supply); + } + + /// Required for hash computation. + impl SNIP12MetadataImpl of SNIP12Metadata { + fn name() -> felt252 { + 'CustomERC20' + } + fn version() -> felt252 { + 'v1' + } + } + + #[external(v0)] + fn transfer_with_signature( + ref self: ContractState, + recipient: ContractAddress, + amount: u256, + nonce: felt252, + expiry: u64, + signature: Array + ) { + assert(starknet::get_block_timestamp() <= expiry, 'Expired signature'); + let owner = starknet::get_caller_address(); + + // Check and increase nonce + self.nonces.use_checked_nonce(owner, nonce); + + // Build hash for calling `is_valid_signature` + let message = Message { recipient, amount, nonce, expiry }; + let hash = message.get_message_hash(owner); + + let is_valid_signature_felt = ISRC6Dispatcher { contract_address: owner } + .is_valid_signature(hash, signature); + + // Check either 'VALID' or true for backwards compatibility + let is_valid_signature = is_valid_signature_felt == starknet::VALIDATED + || is_valid_signature_felt == 1; + assert(is_valid_signature, 'Invalid signature'); + + // Transfer tokens + self.erc20._transfer(owner, recipient, amount); + } +} +``` diff --git a/content/contracts-cairo/3.x/index.mdx b/content/contracts-cairo/3.x/index.mdx new file mode 100644 index 00000000..d78d99d5 --- /dev/null +++ b/content/contracts-cairo/3.x/index.mdx @@ -0,0 +1,122 @@ +--- +title: Contracts for Cairo +--- + +[starknet]:https://starkware.co/product/starknet/ +[scarb]:https://docs.swmansion.com/scarb/ +[scarb-install]:https://docs.swmansion.com/scarb/download.html + + +**A library for secure smart contract development** written in Cairo for [Starknet][starknet]. This library consists of a set of +[reusable components](/contracts-cairo/2.x/components) to build custom smart contracts, as well as ready-to-deploy [presets](/contracts-cairo/2.x/presets). You can also +find other [utilities](/contracts-cairo/2.x/api/utilities) including [interfaces and dispatchers](/contracts-cairo/alpha/interfaces) and [test utilities](/contracts-cairo/2.x/api/testing) +that facilitate testing with Starknet Foundry. + + +You can track our roadmap and future milestones in our [Github Project](https://github.com/orgs/OpenZeppelin/projects/29/). + + +## Installation + +The library is available as a [Scarb][scarb] package. Follow [this guide][scarb-install] for installing Cairo and Scarb on your machine +before proceeding, and run the following command to check that the installation was successful: + +```bash +$ scarb --version + +scarb 2.12.0 (639d0a65e 2025-08-04) +cairo: 2.12.0 (https://crates.io/crates/cairo-lang-compiler/2.12.0) +sierra: 1.7.0 +``` + +### Set up your project + +Create an empty directory, and `cd` into it: + +```bash +mkdir my_project/ && cd my_project/ +``` + +Initialize a new Scarb project: + +```bash +scarb init +``` + +The contents of `my_project/` should now look like this: + +```bash +$ ls + +Scarb.toml src +``` + +### Install the library + +Install the library by declaring it as a dependency in the project’s `Scarb.toml` file: + +```javascript +[dependencies] +openzeppelin = "{{umbrella_version}}" +``` + +The previous example would import the entire library. We can also add each package as a separate dependency to +improve the building time by not including modules that won’t be used: + +```toml +[dependencies] +openzeppelin_access = "{{umbrella_version}}" +``` + +## Basic usage + +This is how it looks to build an ERC20 contract using the [ERC20 component](/contracts-cairo/2.x/erc20). +Copy the code into `src/lib.cairo`. + +```rust +#[starknet::contract] +mod MyERC20Token { + // NOTE: If you added the entire library as a dependency, + // use `openzeppelin::token` instead. + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + + // ERC20 Mixin + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + erc20: ERC20Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + fixed_supply: u256, + recipient: ContractAddress + ) { + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, fixed_supply); + } +} +``` + +You can now compile it: + +```bash +scarb build +``` diff --git a/content/contracts-cairo/3.x/introspection.mdx b/content/contracts-cairo/3.x/introspection.mdx new file mode 100644 index 00000000..da51ffd1 --- /dev/null +++ b/content/contracts-cairo/3.x/introspection.mdx @@ -0,0 +1,137 @@ +--- +title: Introspection +--- + +To smooth interoperability, often standards require smart contracts to implement [introspection mechanisms](https://en.wikipedia.org/wiki/Type_introspection). + +In Ethereum, the [EIP165](https://eips.ethereum.org/EIPS/eip-165) standard defines how contracts should declare +their support for a given interface, and how other contracts may query this support. + +Starknet offers a similar mechanism for interface introspection defined by the [SRC5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md) standard. + +## SRC5 + +Similar to its Ethereum counterpart, the [SRC5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md) standard requires contracts to implement the `supports_interface` function, +which can be used by others to query if a given interface is supported. + +### Usage + +To expose this functionality, the contract must implement the [SRC5Component](/contracts-cairo/2.x/api/introspection#SRC5Component), which defines the `supports_interface` function. +Here is an example contract: + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_introspection::src5::SRC5Component; + + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + impl SRC5InternalImpl = SRC5Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + self.src5.register_interface(selector!("some_interface")); + } +} +``` + +### Interface + +```rust +#[starknet::interface] +pub trait ISRC5 { + /// Query if a contract implements an interface. + /// Receives the interface identifier as specified in SRC-5. + /// Returns `true` if the contract implements `interface_id`, `false` otherwise. + fn supports_interface(interface_id: felt252) -> bool; +} +``` + +## Computing the interface ID + +The interface ID, as specified in the standard, is the [XOR](https://en.wikipedia.org/wiki/Exclusive_or) of all the +[Extended Function Selectors](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md#extended-function-selector) +of the interface. We strongly advise reading the SNIP to understand the specifics of computing these +extended function selectors. There are tools such as [src5-rs](https://github.com/ericnordelo/src5-rs) that can help with this process. + +## Registering interfaces + +For a contract to declare its support for a given interface, we recommend using the SRC5 component to register support upon contract deployment through a constructor either directly or indirectly (as an initializer) like this: + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_account::interface as interface; + use openzeppelin_introspection::src5::SRC5Component; + + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + #[abi(embed_v0)] + impl SRC5Impl = SRC5Component::SRC5Impl; + impl InternalImpl = SRC5Component::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState) { + // Register the contract's support for the ISRC6 interface + self.src5.register_interface(interface::ISRC6_ID); + } + + (...) +} +``` + +## Querying interfaces + +Use the `supports_interface` function to query a contract’s support for a given interface. + +```rust +#[starknet::contract] +mod MyContract { + use openzeppelin_account::interface as interface; + use openzeppelin_introspection::interface::ISRC5DispatcherTrait; + use openzeppelin_introspection::interface::ISRC5Dispatcher; + use starknet::ContractAddress; + + #[storage] + struct Storage {} + + #[external(v0)] + fn query_is_account(self: @ContractState, target: ContractAddress) -> bool { + let dispatcher = ISRC5Dispatcher { contract_address: target }; + dispatcher.supports_interface(interface::ISRC6_ID) + } +} +``` + + +If you are unsure whether a contract implements SRC5 or not, you can follow the process described in +[here](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md#how-to-detect-if-a-contract-implements-src-5). + diff --git a/content/contracts-cairo/3.x/macros.mdx b/content/contracts-cairo/3.x/macros.mdx new file mode 100644 index 00000000..13482da4 --- /dev/null +++ b/content/contracts-cairo/3.x/macros.mdx @@ -0,0 +1,15 @@ +--- +title: Macros +--- + +This crate provides a collection of macros that streamline and simplify development with the library. +To use them, you need to add the `openzeppelin_macros` crate as a dependency in your `Scarb.toml` file: + +```toml +openzeppelin_macros = "{{umbrella_version}}" +``` + +## Attribute macros + +* [with_components](./macros/with_components) +* [type_hash](./macros/type_hash) diff --git a/content/contracts-cairo/3.x/macros/type_hash.mdx b/content/contracts-cairo/3.x/macros/type_hash.mdx new file mode 100644 index 00000000..279090a2 --- /dev/null +++ b/content/contracts-cairo/3.x/macros/type_hash.mdx @@ -0,0 +1,193 @@ +--- +title: type_hash +--- + +This macro generates a SNIP-12-compatible type hash for a given struct or enum. + + +This macro is fully compatible with the SNIP-12 standard revision 1. + + +## Usage + +```rust +#[type_hash(name: "My Struct", debug: true)] +struct MyStruct { + #[snip12(name: "My Field")] + my_field: felt252, +} +``` + +This will generate a type hash for the struct. + +```rust +pub const MY_STRUCT_TYPE_HASH: felt252 = 0x1735aa9819941b96c651b740b792a96c854565eaff089b7e293d996828b88a8; +``` + +And because of the `debug` argument, it will generate the following code: + +```rust +pub fn __MY_STRUCT_encoded_type() { + println!("\"My Struct\"(\"My Field\":\"felt\")"); +} +``` + +## Basic types + +The list of supported basic types as defined in the SNIP-12 standard is: + +* felt252 +* shortstring +* ClassHash +* ContractAddress +* timestamp +* selector +* merkletree +* u128 +* i128 + +### Examples + +Struct with basic types and custom names and kinds: + +```rust +#[type_hash(name: "My Struct", debug: true)] +pub struct MyStruct { + #[snip12(name: "Simple Felt")] // Optional custom name + pub simple_felt: felt252, + #[snip12(name: "Class Hash")] + pub class_hash: ClassHash, + #[snip12(name: "Target Token")] + pub target: ContractAddress, + #[snip12(name: "Timestamp", kind: "timestamp")] + pub timestamp: u128, + #[snip12(name: "Selector", kind: "selector")] + pub selector: felt252, +} + +pub const MY_STRUCT_TYPE_HASH: felt252 + = 0x522e0c3dc5e13b0978f4645760a436b1e119fd335842523fee8fbae6057b8c; + +``` + +Enum with basic types and custom names and kinds: + +```rust +#[type_hash(name: "My Enum", debug: true)] +pub enum MyEnum { + #[snip12(name: "Simple Felt")] + SimpleFelt: felt252, + #[snip12(name: "Class Hash")] + ClassHash: ClassHash, + #[snip12(name: "Target Token")] + ContractAddress: ContractAddress, + #[snip12(name: "Timestamp", kind: "timestamp")] + Timestamp: u128, + #[snip12(name: "Selector", kind: "selector")] + Selector: felt252, +} + +pub const MY_ENUM_TYPE_HASH: felt252 + = 0x3f30aaa6cda9f699d4131940b10602b78b986feb88f28a19f3b48567cb4b566; +``` + +## Collection types + +The list of supported collection types as defined in the SNIP-12 standard is: + +* Array +* Tuple ***(Only supported for enums)*** +* Span ***(Treated as an array)*** + + +While Span is not directly supported by the SNIP-12 standard, it is treated as an array for the purposes of this macro, since +it is sometimes helpful to use `Span` instead of `Array` in order to save on gas. + + +### Examples + +Struct with collection types: + +```rust +#[type_hash(name: "My Struct", debug: true)] +pub struct MyStruct { + #[snip12(name: "Member 1")] + pub member1: Array, + #[snip12(name: "Member 2")] + pub member2: Span, + #[snip12(name: "Timestamps", kind: "Array")] + pub timestamps: Array, +} + +pub const MY_STRUCT_TYPE_HASH: felt252 + = 0x369cdec45d8c55e70986aed44da0e330375171ba6e25b58e741c0ce02fa8ac; +``` + +Enum with collection types: + +```rust +#[type_hash(name: "My Enum", debug: true)] +pub enum MyEnum { + #[snip12(name: "Member 1")] + Member1: Array, + #[snip12(name: "Member 2")] + Member2: Span, + #[snip12(name: "Timestamps", kind: "Array")] + Timestamps: Array, + #[snip12(name: "Name and Last Name", kind: "(shortstring, shortstring)")] + NameAndLastName: (felt252, felt252), +} + +pub const MY_ENUM_TYPE_HASH: felt252 + = 0x9e3e1ebad4448a8344b3318f9cfda5df237588fd8328e1c2968635f09c735d; +``` + +## Preset types + +The list of supported preset types as defined in the SNIP-12 standard is: + +* TokenAmount +* NftId +* u256 + +### Examples + +Struct with preset types: + +```rust +#[type_hash(name: "My Struct", debug: true)] +pub struct MyStruct { + #[snip12(name: "Token Amount")] + pub token_amount: TokenAmount, + #[snip12(name: "NFT ID")] + pub nft_id: NftId, + #[snip12(name: "Number")] + pub number: u256, +} + +pub const MY_STRUCT_TYPE_HASH: felt252 + = 0x19f63528d68c4f44b7d9003a5a6b7793f5bb6ffc8a22bdec82b413ddf4f9412; +``` + +Enum with preset types: + +```rust +#[type_hash(name: "My Enum", debug: true)] +pub enum MyEnum { + #[snip12(name: "Token Amount")] + TokenAmount: TokenAmount, + #[snip12(name: "NFT ID")] + NftId: NftId, + #[snip12(name: "Number")] + Number: u256, +} + +pub const MY_ENUM_TYPE_HASH: felt252 + = 0x39dd19c7e5c5f89e084b78a26200b712c6ae3265f2bae774471c588858421b7; +``` + +## User-defined types + +User-defined types are currently ***NOT SUPPORTED*** since the macro doesn’t have access to scope outside of the +target struct/enum. In the future it may be supported by extending the syntax to explicitly declare the custom type +definition. diff --git a/content/contracts-cairo/3.x/macros/with_components.mdx b/content/contracts-cairo/3.x/macros/with_components.mdx new file mode 100644 index 00000000..d8bbd7ba --- /dev/null +++ b/content/contracts-cairo/3.x/macros/with_components.mdx @@ -0,0 +1,133 @@ +--- +title: with_components +--- + +This macro simplifies the syntax for adding a set of components to a contract. It: + +* _Imports the corresponding components into the contract._ +* _Adds the corresponding `component!` macro entries._ +* _Adds the storage entries for each component to the Storage struct._ +* _Adds the event entries for each component to the Event struct, or creates the struct if it is missing._ +* _Brings the corresponding internal implementations into scope._ +* _Provides some diagnostics for each specific component to help the developer avoid common mistakes._ + + +Since the macro does not expose any external implementations, developers must make sure to specify explicitly +the ones required by the contract. + + +## Security considerations + +The macro was designed to be simple and effective while still being very hard to misuse. For this reason, the features +that it provides are limited, and things that might make the contract behave in unexpected ways must be +explicitly specified by the developer. It does not specify external implementations, so contracts won’t find +themselves in a situation where external functions are exposed without the developer’s knowledge. It brings +the internal implementations into scope so these functions are available by default, but if they are not used, +they won’t have any effect on the contract’s behavior. + +## Usage + +This is how a contract with multiple components looks when using the macro. + +```rust +#[with_components(Account, SRC5, SRC9, Upgradeable)] +#[starknet::contract(account)] +mod OutsideExecutionAccountUpgradeable { + use openzeppelin_upgrades::interface::IUpgradeable; + use starknet::{ClassHash, ContractAddress}; + + // External + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + #[abi(embed_v0)] + impl OutsideExecutionV2Impl = + SRC9Component::OutsideExecutionV2Impl; + + #[storage] + struct Storage {} + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + self.src9.initializer(); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.account.assert_only_self(); + self.upgradeable.upgrade(new_class_hash); + } + } +} +``` + +This is how the same contract looks using regular syntax. + +```rust +#[starknet::contract(account)] +mod OutsideExecutionAccountUpgradeable { + use openzeppelin::account::AccountComponent; + use openzeppelin::account::extensions::SRC9Component; + use openzeppelin::introspection::src5::SRC5Component; + use openzeppelin::upgrades::UpgradeableComponent; + use openzeppelin::upgrades::interface::IUpgradeable; + use starknet::ClassHash; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + component!(path: SRC9Component, storage: src9, event: SRC9Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // External + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + #[abi(embed_v0)] + impl OutsideExecutionV2Impl = + SRC9Component::OutsideExecutionV2Impl; + + // Internal + impl AccountInternalImpl = AccountComponent::InternalImpl; + impl OutsideExecutionInternalImpl = SRC9Component::InternalImpl; + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage, + #[substorage(v0)] + src9: SRC9Component::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage, + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event, + #[flat] + SRC9Event: SRC9Component::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event, + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + self.src9.initializer(); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.account.assert_only_self(); + self.upgradeable.upgrade(new_class_hash); + } + } +} +``` diff --git a/content/contracts-cairo/3.x/presets.mdx b/content/contracts-cairo/3.x/presets.mdx new file mode 100644 index 00000000..082c1c0d --- /dev/null +++ b/content/contracts-cairo/3.x/presets.mdx @@ -0,0 +1,141 @@ +--- +title: Presets +--- + +Presets are ready-to-deploy contracts provided by the library. Since presets are intended to be very simple +and as generic as possible, there’s no support for custom or complex contracts such as `ERC20Pausable` or `ERC721Mintable`. + + +For contract customization and combination of modules you can use [Wizard for Cairo](https://wizard.openzeppelin.com), our code-generation tool. + + +## Available presets + +List of available presets and their corresponding [Sierra class hashes](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/). Like Contracts for Cairo, +use of preset contracts are subject to the terms of the +[MIT License](https://github.com/OpenZeppelin/cairo-contracts?tab=MIT-1-ov-file#readme). + + +Class hashes were computed using scarb `v{{class_hash_scarb_version}}` and the `scarb --release` profile. + + + +Before version 2.x, class hashes were computed using the `scarb --dev` profile. + + +| Name | Sierra Class Hash | +| --- | --- | +| [`AccountUpgradeable`](/contracts-cairo/2.x/api/account#AccountUpgradeable) | `{{AccountUpgradeableClassHash}}` | +| [`ERC20Upgradeable`](/contracts-cairo/2.x/api/erc20#ERC20Upgradeable) | `{{ERC20UpgradeableClassHash}}` | +| [`ERC721Upgradeable`](/contracts-cairo/2.x/api/erc721#ERC721Upgradeable) | `{{ERC721UpgradeableClassHash}}` | +| [`ERC1155Upgradeable`](/contracts-cairo/2.x/api/erc1155#ERC1155Upgradeable) | `{{ERC1155UpgradeableClassHash}}` | +| [`EthAccountUpgradeable`](/contracts-cairo/2.x/api/account#EthAccountUpgradeable) | `{{EthAccountUpgradeableClassHash}}` | +| [`UniversalDeployer`](/contracts-cairo/2.x/api/udc#UniversalDeployer) | `{{UniversalDeployerClassHash}}` | +| [`VestingWallet`](/contracts-cairo/2.x/api/finance#VestingWallet) | `{{VestingWalletClassHash}}` | + + +[starkli](https://book.starkli.rs/introduction) class-hash command can be used to compute the class hash from a Sierra artifact. + + +## Usage + +These preset contracts are ready-to-deploy which means they should already be declared on the Sepolia network. +Simply deploy the preset class hash and add the appropriate constructor arguments. +Deploying the ERC20Upgradeable preset with [starkli](https://book.starkli.rs/introduction), for example, will look like this: + +```bash +starkli deploy {ERC20Upgradeable-class-hash} \ + \ + --network="sepolia" +``` + +If a class hash has yet to be declared, copy/paste the preset contract code and declare it locally. +Start by [setting up a project](/contracts-cairo#set-up-your-project) and [installing the Contracts for Cairo library](/contracts-cairo#install-the-library). +Copy the target preset contract from the [presets directory](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v{{umbrella_version}}/packages/presets/src) +and paste it in the new project’s `src/lib.cairo` like this: + +```rust +// src/lib.cairo + +#[starknet::contract] +mod ERC20Upgradeable { + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use openzeppelin_upgrades::UpgradeableComponent; + use openzeppelin_upgrades::interface::IUpgradeable; + use starknet::{ContractAddress, ClassHash}; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // Ownable Mixin + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + // ERC20 Mixin + #[abi(embed_v0)] + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; + impl ERC20InternalImpl = ERC20Component::InternalImpl; + + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + erc20: ERC20Component::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } + + #[constructor] + fn constructor( + ref self: ContractState, + name: ByteArray, + symbol: ByteArray, + fixed_supply: u256, + recipient: ContractAddress, + owner: ContractAddress + ) { + self.ownable.initializer(owner); + self.erc20.initializer(name, symbol); + self.erc20.mint(recipient, fixed_supply); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + self.ownable.assert_only_owner(); + self.upgradeable.upgrade(new_class_hash); + } + } +} +``` + +Next, compile the contract. + +```bash +scarb build +``` + +Finally, declare the preset. + +```bash +starkli declare target/dev/my_project_ERC20Upgradeable.contract_class.json \ + --network="sepolia" +``` diff --git a/content/contracts-cairo/3.x/security.mdx b/content/contracts-cairo/3.x/security.mdx new file mode 100644 index 00000000..b7853675 --- /dev/null +++ b/content/contracts-cairo/3.x/security.mdx @@ -0,0 +1,218 @@ +--- +title: Security +--- + +The following documentation provides context, reasoning, and examples of modules found under `openzeppelin_security`. + + +Expect these modules to evolve. + + +## Initializable + +The [Initializable](/contracts-cairo/2.x/api/security#InitializableComponent) component provides a simple mechanism that mimics +the functionality of a constructor. +More specifically, it enables logic to be performed once and only once which is useful to set up a contract’s initial state when a constructor cannot be used, for example when there are circular dependencies at construction time. + +### Usage + +You can use the component in your contracts like this: + +```rust +#[starknet::contract] +mod MyInitializableContract { + use openzeppelin_security::InitializableComponent; + + component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); + + impl InternalImpl = InitializableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + initializable: InitializableComponent::Storage, + param: felt252 + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + InitializableEvent: InitializableComponent::Event + } + + fn initializer(ref self: ContractState, some_param: felt252) { + // Makes the method callable only once + self.initializable.initialize(); + + // Initialization logic + self.param.write(some_param); + } +} +``` + + +This Initializable pattern should only be used in one function. + + +### Interface + +The component provides the following external functions as part of the `InitializableImpl` implementation: + +```rust +#[starknet::interface] +pub trait InitializableABI { + fn is_initialized() -> bool; +} +``` + +## Pausable + +The [Pausable](/contracts-cairo/2.x/api/security#PausableComponent) component allows contracts to implement an emergency stop mechanism. +This can be useful for scenarios such as preventing trades until the end of an evaluation period or having an emergency switch to freeze all transactions in the event of a large bug. + +To become pausable, the contract should include `pause` and `unpause` functions (which should be protected). +For methods that should be available only when paused or not, insert calls to `[assert_paused](/contracts-cairo/2.x/api/security#PausableComponent-assert_paused)` and `[assert_not_paused](/contracts-cairo/2.x/api/security#PausableComponent-assert_not_paused)` +respectively. + +### Usage + +For example (using the [Ownable](/contracts-cairo/2.x/api/access#OwnableComponent) component for access control): + +```rust +#[starknet::contract] +mod MyPausableContract { + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_security::PausableComponent; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: PausableComponent, storage: pausable, event: PausableEvent); + + // Ownable Mixin + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + // Pausable + #[abi(embed_v0)] + impl PausableImpl = PausableComponent::PausableImpl; + impl PausableInternalImpl = PausableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + pausable: PausableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + PausableEvent: PausableComponent::Event + } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + self.ownable.initializer(owner); + } + + #[external(v0)] + fn pause(ref self: ContractState) { + self.ownable.assert_only_owner(); + self.pausable.pause(); + } + + #[external(v0)] + fn unpause(ref self: ContractState) { + self.ownable.assert_only_owner(); + self.pausable.unpause(); + } + + #[external(v0)] + fn when_not_paused(ref self: ContractState) { + self.pausable.assert_not_paused(); + // Do something + } + + #[external(v0)] + fn when_paused(ref self: ContractState) { + self.pausable.assert_paused(); + // Do something + } +} +``` + +### Interface + +The component provides the following external functions as part of the `PausableImpl` implementation: + +```rust +#[starknet::interface] +pub trait PausableABI { + fn is_paused() -> bool; +} +``` + +## Reentrancy Guard + +A [reentrancy attack](https://gus-tavo-guim.medium.com/reentrancy-attack-on-smart-contracts-how-to-identify-the-exploitable-and-an-example-of-an-attack-4470a2d8dfe4) occurs when the caller is able to obtain more resources than allowed by recursively calling a target’s function. + +### Usage + +Since Cairo does not support modifiers like Solidity, the [ReentrancyGuard](/contracts-cairo/2.x/api/security#ReentrancyGuardComponent) +component exposes two methods `[start](/contracts-cairo/2.x/api/security#ReentrancyGuardComponent-start)` and `[end](/contracts-cairo/2.x/api/security#ReentrancyGuardComponent-end)` to protect functions against reentrancy attacks. +The protected function must call `start` before the first function statement, and `end` before the return statement, as shown below: + +```rust +#[starknet::contract] +mod MyReentrancyContract { + use openzeppelin_security::ReentrancyGuardComponent; + + component!( + path: ReentrancyGuardComponent, storage: reentrancy_guard, event: ReentrancyGuardEvent + ); + + impl InternalImpl = ReentrancyGuardComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + reentrancy_guard: ReentrancyGuardComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ReentrancyGuardEvent: ReentrancyGuardComponent::Event + } + + #[external(v0)] + fn protected_function(ref self: ContractState) { + self.reentrancy_guard.start(); + + // Do something + + self.reentrancy_guard.end(); + } + + #[external(v0)] + fn another_protected_function(ref self: ContractState) { + self.reentrancy_guard.start(); + + // Do something + + self.reentrancy_guard.end(); + } +} +``` + + +The guard prevents the execution flow occurring inside `protected_function` +to call itself or `another_protected_function`, and vice versa. + diff --git a/content/contracts-cairo/3.x/udc.mdx b/content/contracts-cairo/3.x/udc.mdx new file mode 100644 index 00000000..0b85d46e --- /dev/null +++ b/content/contracts-cairo/3.x/udc.mdx @@ -0,0 +1,114 @@ +--- +title: Universal Deployer Contract +--- + +The Universal Deployer Contract (UDC) is a singleton smart contract that wraps the [deploy syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) to expose it to any contract that doesn’t implement it, such as account contracts. You can think of it as a standardized generic factory for Starknet contracts. + +Since Starknet has no deployment transaction type, it offers a standardized way to deploy smart contracts by following the [Standard Deployer Interface](https://community.starknet.io/t/snip-deployer-contract-interface/2772) and emitting a [ContractDeployed](/contracts-cairo/2.x/api/udc#IUniversalDeployer-ContractDeployed) event. + +For details on the motivation and the decision making process, see the [Universal Deployer Contract proposal](https://community.starknet.io/t/universal-deployer-contract-proposal/1864). + +## UDC contract address + +The UDC is deployed at address `0x02ceed65a4bd731034c01113685c831b01c15d7d432f71afb1cf1634b53a2125` on Starknet sepolia and mainnet. + +## Interface + +```rust +#[starknet::interface] +pub trait IUniversalDeployer { + fn deploy_contract( + class_hash: ClassHash, + salt: felt252, + not_from_zero: bool, + calldata: Span + ) -> ContractAddress; +} +``` + +## Deploying a contract with the UDC + +First, [declare](https://docs.starknet.io/learn/cheatsheets/transactions-reference#declare-v3) the target contract (if it’s not already declared). +Next, call the UDC’s `deploy_contract` method. +Here’s an implementation example in Cairo: + +```rust +use openzeppelin_utils::interfaces::{IUniversalDeployerDispatcher, IUniversalDeployerDispatcherTrait}; + +const UDC_ADDRESS: felt252 = 0x04...; + +fn deploy() -> ContractAddress { + let dispatcher = IUniversalDeployerDispatcher { + contract_address: UDC_ADDRESS.try_into().unwrap() + }; + + // Deployment parameters + let class_hash = class_hash_const::< + 0x5c478ee27f2112411f86f207605b2e2c58cdb647bac0df27f660ef2252359c6 + >(); + let salt = 1234567879; + let not_from_zero = true; + let calldata = array![]; + + // The UDC returns the deployed contract address + dispatcher.deploy_contract(class_hash, salt, not_from_zero, calldata.span()) +} +``` + +## Deployment types + +The Universal Deployer Contract offers two types of addresses to deploy: origin-dependent and origin-independent. +As the names suggest, the origin-dependent type includes the deployer’s address in the address calculation, +whereas, the origin-independent type does not. +The `not_from_zero` boolean parameter ultimately determines the type of deployment. + + + + +When deploying a contract that uses `get_caller_address` in the constructor calldata, remember that the UDC, not the account, deploys that contract. +Therefore, querying `get_caller_address` in a contract’s constructor returns the UDC’s address, _not the account’s address_. + + + +### Origin-dependent + +By making deployments dependent upon the origin address, users can reserve a whole address space to prevent someone else from taking ownership of the address. + +Only the owner of the origin address can deploy to those addresses. + +Achieving this type of deployment necessitates that the origin sets `not_from_zero` to `true` in the [deploy_contract](/contracts-cairo/2.x/api/udc#UniversalDeployer-deploy_contract) call. +Under the hood, the function passes a modified salt to the `deploy_syscall`, which is the hash of the origin’s address with the given salt. + +To deploy a unique contract address pass: + +```js +let deployed_addr = udc.deploy_contract(class_hash, salt, true, calldata.span()); +``` + +### Origin-independent + +Origin-independent contract deployments create contract addresses independent of the deployer and the UDC instance. +Instead, only the class hash, salt, and constructor arguments determine the address. +This type of deployment enables redeployments of accounts and known systems across multiple networks. +To deploy a reproducible deployment, set `not_from_zero` to `false`. + +```rust +let deployed_addr = udc.deploy_contract(class_hash, salt, false, calldata.span()); +``` + +## Version changes + + +See the [previous Universal Deployer API](https://docs.starknet.io/learn/protocol/accounts#deploying-a-new-account) for the initial spec. + + +The latest iteration of the UDC includes some notable changes to the API which include: + +* `deployContract` method is replaced with the snake_case [deploy_contract](/contracts-cairo/2.x/api/udc#UniversalDeployer-deploy_contract). +* `unique` parameter is replaced with `not_from_zero` in both the `deploy_contract` method and [ContractDeployed](/contracts-cairo/2.x/api/udc#IUniversalDeployer-ContractDeployed) event. + +## Precomputing contract addresses + +This library offers utility functions written in Cairo to precompute contract addresses. +They include the generic [calculate_contract_address_from_deploy_syscall](/contracts-cairo/2.x/api/utilities#deployments-calculate_contract_address_from_deploy_syscall) as well as the UDC-specific [calculate_contract_address_from_udc](/contracts-cairo/2.x/api/utilities#deployments-calculate_contract_address_from_udc). +Check out the [deployments](/contracts-cairo/2.x/api/utilities#deployments) for more information. diff --git a/content/contracts-cairo/3.x/upgrades.mdx b/content/contracts-cairo/3.x/upgrades.mdx new file mode 100644 index 00000000..7765a01d --- /dev/null +++ b/content/contracts-cairo/3.x/upgrades.mdx @@ -0,0 +1,127 @@ +--- +title: Upgrades +--- + +In different blockchains, multiple patterns have been developed for making a contract upgradeable including the widely adopted proxy patterns. + +Starknet has native upgradeability through a syscall that updates the contract source code, removing [the need for proxies](#proxies-in-starknet). + + +Make sure you follow [our security recommendations](#security) before upgrading. + + +## Replacing contract classes + +To better comprehend how upgradeability works in Starknet, it’s important to understand the difference between a contract and its contract class. + +[Contract Classes](https://docs.starknet.io/build/starknet-by-example/applications/upgradeable-contract#upgradeable-contract) represent the source code of a program. All +contracts are associated to a class, and many contracts can be instances of the same one. Classes are usually represented by +a [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/), and before a contract of a class can be deployed, +the class hash needs to be declared. + +### `replace_class_syscall` + +The `[replace_class](https://docs.starknet.io/build/corelib/core-starknet-syscalls-replace_class_syscall#core-starknet-syscalls-replace-class-syscall)` syscall allows a contract to +update its source code by replacing its class hash once deployed. + +```rust +/// Upgrades the contract source code to the new contract class. +fn upgrade(new_class_hash: ClassHash) { + let CLASS_HASH_CANNOT_BE_ZERO: felt252 = 0x1; + assert(!new_class_hash.is_zero(), CLASS_HASH_CANNOT_BE_ZERO); + starknet::replace_class_syscall(new_class_hash).unwrap_syscall(); +} +``` + + +If a contract is deployed without this mechanism, its class hash can still be replaced through [library calls](https://docs.starknet.io/build/starknet-by-example/advanced/library-calls#library-calls). + + +## `Upgradeable` component + +OpenZeppelin Contracts for Cairo provides [Upgradeable](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v{{umbrella_version}}/packages/upgrades/src/upgradeable.cairo) to add upgradeability support to your contracts. + +### Usage + +Upgrades are often very sensitive operations, and some form of access control is usually required to +avoid unauthorized upgrades. The [Ownable](./access#ownership-and-ownable) module is used in this example. + + +We will be using the following module to implement the [IUpgradeable](/contracts-cairo/2.x/api/upgrades#IUpgradeable) interface described in the API Reference section. + + +```rust +#[starknet::contract] +mod UpgradeableContract { + use openzeppelin_access::ownable::OwnableComponent; + use openzeppelin_upgrades::UpgradeableComponent; + use openzeppelin_upgrades::interface::IUpgradeable; + use starknet::ClassHash; + use starknet::ContractAddress; + + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + + // Ownable Mixin + #[abi(embed_v0)] + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + // Upgradeable + impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + ownable: OwnableComponent::Storage, + #[substorage(v0)] + upgradeable: UpgradeableComponent::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + OwnableEvent: OwnableComponent::Event, + #[flat] + UpgradeableEvent: UpgradeableComponent::Event + } + + #[constructor] + fn constructor(ref self: ContractState, owner: ContractAddress) { + self.ownable.initializer(owner); + } + + #[abi(embed_v0)] + impl UpgradeableImpl of IUpgradeable { + fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { + // This function can only be called by the owner + self.ownable.assert_only_owner(); + + // Replace the class hash upgrading the contract + self.upgradeable.upgrade(new_class_hash); + } + } +} +``` + +## Security + +Upgrades can be very sensitive operations, and security should always be top of mind while performing one. Please make sure you thoroughly review the changes and their consequences before upgrading. Some aspects to consider are: + +* API changes that might affect integration. For example, changing an external function’s arguments might break existing contracts or offchain systems calling your contract. +* Storage changes that might result in lost data (e.g. changing a storage slot name, making existing storage inaccessible). +* Collisions (e.g. mistakenly reusing the same storage slot from another component) are also possible, although less likely if best practices are followed, for example prepending storage variables with the component’s name (e.g. `ERC20_balances`). +* Always check for [backwards compatibility](./backwards-compatibility) before upgrading between versions of OpenZeppelin Contracts. + +## Proxies in Starknet + +Proxies enable different patterns such as upgrades and clones. But since Starknet achieves the same in different ways is that there’s no support to implement them. + +In the case of contract upgrades, it is achieved by simply changing the contract’s class hash. As of clones, contracts already are like clones of the class they implement. + +Implementing a proxy pattern in Starknet has an important limitation: there is no fallback mechanism to be used +for redirecting every potential function call to the implementation. This means that a generic proxy contract +can’t be implemented. Instead, a limited proxy contract can implement specific functions that forward +their execution to another contract class. +This can still be useful for example to upgrade the logic of some functions. diff --git a/content/contracts-cairo/3.x/utils/constants.js b/content/contracts-cairo/3.x/utils/constants.js new file mode 100644 index 00000000..d7da4149 --- /dev/null +++ b/content/contracts-cairo/3.x/utils/constants.js @@ -0,0 +1,19 @@ +export const UMBRELLA_VERSION = "2.0.0"; +export const CLASS_HASH_SCARB_VERSION = "2.11.4"; + +export const CLASS_HASHES = { + AccountUpgradeableClassHash: + "0x079a9a12fdfa0481e8d8d46599b90226cd7247b2667358bb00636dd864002314", + ERC20UpgradeableClassHash: + "0x065daa9c6005dcbccb0571ffdf530e2e263d1ff00eac2cbd66b2d0fa0871dafa", + ERC721UpgradeableClassHash: + "0x06d1cd9d8c2008d36bd627e204c3e5f565d4e632de4e50b36d2388c7ba7a64ce", + ERC1155UpgradeableClassHash: + "0x036d453774916578336db8f5f18257f0211011270a5c31adf3a2bd86416943b7", + EthAccountUpgradeableClassHash: + "0x070177fca30a0a9025465f16f8174d4ea220f61bf44cb1beecb89459fe966285", + UniversalDeployerClassHash: + "0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8", + VestingWalletClassHash: + "0x010a786d4e5f74d68e0a500aeadbf7a81486f069c06afa242a050a1a09ac42f0", +}; diff --git a/content/contracts-cairo/3.x/utils/replacements.ts b/content/contracts-cairo/3.x/utils/replacements.ts new file mode 100644 index 00000000..c3a1695e --- /dev/null +++ b/content/contracts-cairo/3.x/utils/replacements.ts @@ -0,0 +1,10 @@ +import { CLASS_HASHES, CLASS_HASH_SCARB_VERSION, UMBRELLA_VERSION } from "./constants"; + +export const REPLACEMENTS = { + include: ['**/content/contracts-cairo/2.x/**/*.mdx'], + replacements: { + umbrella_version: UMBRELLA_VERSION, + class_hash_scarb_version: CLASS_HASH_SCARB_VERSION, + ...CLASS_HASHES, + } +} diff --git a/content/contracts-cairo/3.x/wizard.mdx b/content/contracts-cairo/3.x/wizard.mdx new file mode 100644 index 00000000..84f581f5 --- /dev/null +++ b/content/contracts-cairo/3.x/wizard.mdx @@ -0,0 +1,12 @@ +--- +title: Wizard for Cairo +--- + +Not sure where to start? Use the interactive generator below to bootstrap your +contract and learn about the components offered in OpenZeppelin Contracts for Cairo. + + +We strongly recommend checking the [Components](./components) section to understand how to extend from our library. + + + diff --git a/content/contracts-cairo/latest-versions.js b/content/contracts-cairo/latest-versions.js index ab44c70c..5b25e4f0 100644 --- a/content/contracts-cairo/latest-versions.js +++ b/content/contracts-cairo/latest-versions.js @@ -1,4 +1,4 @@ import { UMBRELLA_VERSION as latestAlphaVersion } from "./alpha/utils/constants.js"; -export const latestStable = "2.x"; +export const latestStable = "3.x"; export const latestAlpha = latestAlphaVersion; From 11ece23ceb81553e51ffd18d96b896b87a94b53f Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 23 Dec 2025 19:25:29 +0300 Subject: [PATCH 02/11] Use alpha docs as base for 3.x docs --- content/contracts-cairo/3.x/access.mdx | 26 +- content/contracts-cairo/3.x/accounts.mdx | 14 +- content/contracts-cairo/3.x/api/access.mdx | 892 +++- content/contracts-cairo/3.x/api/account.mdx | 13 +- content/contracts-cairo/3.x/api/erc1155.mdx | 19 +- content/contracts-cairo/3.x/api/erc20.mdx | 23 +- content/contracts-cairo/3.x/api/erc721.mdx | 21 +- content/contracts-cairo/3.x/api/finance.mdx | 11 +- .../contracts-cairo/3.x/api/governance.mdx | 23 +- .../contracts-cairo/3.x/api/introspection.mdx | 11 +- .../contracts-cairo/3.x/api/token_common.mdx | 84 +- content/contracts-cairo/3.x/api/udc.mdx | 11 +- content/contracts-cairo/3.x/api/upgrades.mdx | 15 +- content/contracts-cairo/3.x/api/utilities.mdx | 36 + content/contracts-cairo/3.x/components.mdx | 12 +- content/contracts-cairo/3.x/erc1155.mdx | 18 +- content/contracts-cairo/3.x/erc20.mdx | 6 +- content/contracts-cairo/3.x/erc4626.mdx | 10 +- content/contracts-cairo/3.x/erc721.mdx | 12 +- content/contracts-cairo/3.x/finance.mdx | 28 +- .../3.x/guides/interfaces-and-dispatchers.mdx | 13 +- content/contracts-cairo/3.x/guides/snip12.mdx | 10 +- content/contracts-cairo/3.x/index.mdx | 44 +- .../{alpha => 3.x}/interfaces.mdx | 0 content/contracts-cairo/3.x/introspection.mdx | 10 +- .../3.x/macros/with_components.mdx | 2 +- content/contracts-cairo/3.x/presets.mdx | 16 +- content/contracts-cairo/3.x/security.mdx | 12 +- content/contracts-cairo/3.x/udc.mdx | 12 +- content/contracts-cairo/3.x/upgrades.mdx | 4 +- .../contracts-cairo/3.x/utils/constants.js | 20 +- .../contracts-cairo/3.x/utils/replacements.ts | 6 +- content/contracts-cairo/3.x/wizard.mdx | 4 +- content/contracts-cairo/alpha/access.mdx | 515 --- content/contracts-cairo/alpha/accounts.mdx | 504 -- content/contracts-cairo/alpha/api/access.mdx | 1749 ------- content/contracts-cairo/alpha/api/account.mdx | 847 ---- content/contracts-cairo/alpha/api/erc1155.mdx | 788 ---- content/contracts-cairo/alpha/api/erc20.mdx | 1585 ------- content/contracts-cairo/alpha/api/erc721.mdx | 1159 ----- content/contracts-cairo/alpha/api/finance.mdx | 333 -- .../contracts-cairo/alpha/api/governance.mdx | 4064 ----------------- .../alpha/api/introspection.mdx | 105 - .../contracts-cairo/alpha/api/merkle-tree.mdx | 185 - .../contracts-cairo/alpha/api/security.mdx | 202 - content/contracts-cairo/alpha/api/testing.mdx | 10 - .../alpha/api/token_common.mdx | 539 --- content/contracts-cairo/alpha/api/udc.mdx | 90 - .../contracts-cairo/alpha/api/upgrades.mdx | 133 - .../contracts-cairo/alpha/api/utilities.mdx | 417 -- .../alpha/backwards-compatibility.mdx | 35 - content/contracts-cairo/alpha/components.mdx | 667 --- content/contracts-cairo/alpha/erc1155.mdx | 239 - content/contracts-cairo/alpha/erc20.mdx | 252 - content/contracts-cairo/alpha/erc4626.mdx | 433 -- content/contracts-cairo/alpha/erc721.mdx | 213 - content/contracts-cairo/alpha/finance.mdx | 216 - .../alpha/governance/governor.mdx | 443 -- .../alpha/governance/multisig.mdx | 150 - .../alpha/governance/timelock.mdx | 198 - .../alpha/governance/votes.mdx | 222 - .../alpha/guides/deploy-udc.mdx | 273 -- .../alpha/guides/deployment.mdx | 40 - .../alpha/guides/erc20-permit.mdx | 63 - .../alpha/guides/erc20-supply.mdx | 148 - .../guides/interfaces-and-dispatchers.mdx | 168 - .../contracts-cairo/alpha/guides/snip12.mdx | 344 -- content/contracts-cairo/alpha/index.mdx | 156 - .../contracts-cairo/alpha/introspection.mdx | 137 - content/contracts-cairo/alpha/macros.mdx | 15 - .../alpha/macros/type_hash.mdx | 193 - .../alpha/macros/with_components.mdx | 133 - content/contracts-cairo/alpha/presets.mdx | 141 - content/contracts-cairo/alpha/security.mdx | 218 - content/contracts-cairo/alpha/udc.mdx | 114 - content/contracts-cairo/alpha/upgrades.mdx | 127 - .../contracts-cairo/alpha/utils/constants.js | 21 - .../alpha/utils/replacements.ts | 12 - content/contracts-cairo/alpha/wizard.mdx | 14 - 79 files changed, 1259 insertions(+), 18789 deletions(-) rename content/contracts-cairo/{alpha => 3.x}/interfaces.mdx (100%) delete mode 100644 content/contracts-cairo/alpha/access.mdx delete mode 100644 content/contracts-cairo/alpha/accounts.mdx delete mode 100644 content/contracts-cairo/alpha/api/access.mdx delete mode 100644 content/contracts-cairo/alpha/api/account.mdx delete mode 100644 content/contracts-cairo/alpha/api/erc1155.mdx delete mode 100644 content/contracts-cairo/alpha/api/erc20.mdx delete mode 100644 content/contracts-cairo/alpha/api/erc721.mdx delete mode 100644 content/contracts-cairo/alpha/api/finance.mdx delete mode 100644 content/contracts-cairo/alpha/api/governance.mdx delete mode 100644 content/contracts-cairo/alpha/api/introspection.mdx delete mode 100644 content/contracts-cairo/alpha/api/merkle-tree.mdx delete mode 100644 content/contracts-cairo/alpha/api/security.mdx delete mode 100644 content/contracts-cairo/alpha/api/testing.mdx delete mode 100644 content/contracts-cairo/alpha/api/token_common.mdx delete mode 100644 content/contracts-cairo/alpha/api/udc.mdx delete mode 100644 content/contracts-cairo/alpha/api/upgrades.mdx delete mode 100644 content/contracts-cairo/alpha/api/utilities.mdx delete mode 100644 content/contracts-cairo/alpha/backwards-compatibility.mdx delete mode 100644 content/contracts-cairo/alpha/components.mdx delete mode 100644 content/contracts-cairo/alpha/erc1155.mdx delete mode 100644 content/contracts-cairo/alpha/erc20.mdx delete mode 100644 content/contracts-cairo/alpha/erc4626.mdx delete mode 100644 content/contracts-cairo/alpha/erc721.mdx delete mode 100644 content/contracts-cairo/alpha/finance.mdx delete mode 100644 content/contracts-cairo/alpha/governance/governor.mdx delete mode 100644 content/contracts-cairo/alpha/governance/multisig.mdx delete mode 100644 content/contracts-cairo/alpha/governance/timelock.mdx delete mode 100644 content/contracts-cairo/alpha/governance/votes.mdx delete mode 100644 content/contracts-cairo/alpha/guides/deploy-udc.mdx delete mode 100644 content/contracts-cairo/alpha/guides/deployment.mdx delete mode 100644 content/contracts-cairo/alpha/guides/erc20-permit.mdx delete mode 100644 content/contracts-cairo/alpha/guides/erc20-supply.mdx delete mode 100644 content/contracts-cairo/alpha/guides/interfaces-and-dispatchers.mdx delete mode 100644 content/contracts-cairo/alpha/guides/snip12.mdx delete mode 100644 content/contracts-cairo/alpha/index.mdx delete mode 100644 content/contracts-cairo/alpha/introspection.mdx delete mode 100644 content/contracts-cairo/alpha/macros.mdx delete mode 100644 content/contracts-cairo/alpha/macros/type_hash.mdx delete mode 100644 content/contracts-cairo/alpha/macros/with_components.mdx delete mode 100644 content/contracts-cairo/alpha/presets.mdx delete mode 100644 content/contracts-cairo/alpha/security.mdx delete mode 100644 content/contracts-cairo/alpha/udc.mdx delete mode 100644 content/contracts-cairo/alpha/upgrades.mdx delete mode 100644 content/contracts-cairo/alpha/utils/constants.js delete mode 100644 content/contracts-cairo/alpha/utils/replacements.ts delete mode 100644 content/contracts-cairo/alpha/wizard.mdx diff --git a/content/contracts-cairo/3.x/access.mdx b/content/contracts-cairo/3.x/access.mdx index 3573209e..cf78465c 100644 --- a/content/contracts-cairo/3.x/access.mdx +++ b/content/contracts-cairo/3.x/access.mdx @@ -13,13 +13,13 @@ The most common and basic form of access control is the concept of ownership: th of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. -OpenZeppelin Contracts for Cairo provides [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent) for implementing ownership in your contracts. +OpenZeppelin Contracts for Cairo provides [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent) for implementing ownership in your contracts. ### Usage Integrating this component into a contract first requires assigning an owner. The implementing contract’s constructor should set the initial owner by passing the owner’s address to Ownable’s -[`initializer`](/contracts-cairo/2.x/api/access#OwnableComponent-initializer) like this: +[`initializer`](/contracts-cairo/alpha/api/access#OwnableComponent-initializer) like this: ```rust #[starknet::contract] @@ -106,7 +106,7 @@ will no longer be callable! ### Two step transfer The component also offers a more robust way of transferring ownership via the -[OwnableTwoStepImpl](/contracts-cairo/2.x/api/access#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) implementation. A two step transfer mechanism helps +[OwnableTwoStepImpl](/contracts-cairo/alpha/api/access#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) implementation. A two step transfer mechanism helps to prevent unintended and irreversible owner transfers. Simply replace the `OwnableMixinImpl` with its respective two step variant: @@ -146,7 +146,7 @@ flexibility in this regard. In essence, we will be defining multiple roles, each allowed to perform different sets of actions. An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for -instead of simply using [`assert_only_owner`](/contracts-cairo/2.x/api/access#OwnableComponent-assert_only_owner). This check can be enforced through [`assert_only_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-assert_only_role). +instead of simply using [`assert_only_owner`](/contracts-cairo/alpha/api/access#OwnableComponent-assert_only_owner). This check can be enforced through [`assert_only_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-assert_only_role). Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. Most software uses access control systems that are role-based: some users are regular users, some may be supervisors @@ -158,7 +158,7 @@ For each role that you want to define, you will create a new _role identifier_ t check if an account has that role. See [Creating role identifiers](#creating-role-identifiers) for information on creating identifiers. -Here’s a simple example of implementing [AccessControl](/contracts-cairo/2.x/api/access#AccessControlComponent) on a portion of an ERC20 token contract which defines +Here’s a simple example of implementing [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) on a portion of an ERC20 token contract which defines and sets a 'minter' role: ```rust @@ -242,12 +242,12 @@ mod MyContract { ``` -Make sure you fully understand how [AccessControl](/contracts-cairo/2.x/api/access#AccessControlComponent) works before +Make sure you fully understand how [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) works before using it on your system, or copy-pasting the examples from this guide. While clear and explicit, this isn’t anything we wouldn’t have been able to achieve with -[Ownable](/contracts-cairo/2.x/api/access#OwnableComponent). Where [AccessControl](/contracts-cairo/2.x/api/access#AccessControlComponent) shines the most is in scenarios where granular +[Ownable](/contracts-cairo/alpha/api/access#OwnableComponent). Where [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) shines the most is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles. Let’s augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens: @@ -350,16 +350,16 @@ security practice. Note that each account may still have more than one role, if ### Granting and revoking roles -The ERC20 token example above uses [`_grant_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-_grant_role), +The ERC20 token example above uses [`_grant_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-_grant_role), an `internal` function that is useful when programmatically assigning roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts? By default, **accounts with a role cannot grant it or revoke it from other accounts**: all having a role does is making -the [`assert_only_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-assert_only_role) check pass. To grant and revoke roles dynamically, you will need help from the role’s _admin_. +the [`assert_only_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-assert_only_role) check pass. To grant and revoke roles dynamically, you will need help from the role’s _admin_. Every role has an associated admin role, which grants permission to call the -[`grant_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-grant_role) and -[`revoke_role`](/contracts-cairo/2.x/api/access#AccessControlComponent-revoke_role) functions. +[`grant_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-grant_role) and +[`revoke_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-revoke_role) functions. A role can be granted or revoked by using these if the calling account has the corresponding admin role. Multiple roles may have the same admin role to make management easier. A role’s admin can even be the same role itself, which would cause accounts with that role to be able @@ -369,7 +369,9 @@ This mechanism can be used to create complex permissioning structures resembling provides an easy way to manage simpler applications. `AccessControl` includes a special role with the role identifier of `0`, called `DEFAULT_ADMIN_ROLE`, which acts as the **default admin role for all roles**. An account with this role will be able to manage any other role, unless -[`set_role_admin`](/contracts-cairo/2.x/api/access#AccessControlComponent-set_role_admin) is used to select a new admin role. +[`set_role_admin`](/contracts-cairo/alpha/api/access#AccessControlComponent-set_role_admin) is used to select a new admin role. + +Since it is the admin for all roles by default, and in fact it is also its own admin, this role carries significant risk. To mitigate this risk we provide [AccessControlDefaultAdminRules](/contracts-cairo/alpha/api/access#AccessControlDefaultAdminRulesComponent), a recommended extension of AccessControl that adds a number of enforced security measures for this role: the admin is restricted to a single account, with a 2-step transfer procedure with a delay in between steps. Let’s take a look at the ERC20 token example, this time taking advantage of the default admin role: diff --git a/content/contracts-cairo/3.x/accounts.mdx b/content/contracts-cairo/3.x/accounts.mdx index 580037b8..b699c4b9 100644 --- a/content/contracts-cairo/3.x/accounts.mdx +++ b/content/contracts-cairo/3.x/accounts.mdx @@ -13,7 +13,7 @@ A more detailed discussion on the topic can be found in [Starknet Shaman’s forum](https://community.starknet.io/t/starknet-account-abstraction-model-part-1/781). -For detailed information on the usage and implementation check the [API Reference](/contracts-cairo/2.x/api/account) section. +For detailed information on the usage and implementation check the [API Reference](/contracts-cairo/alpha/api/account) section. ## What is an account? @@ -97,11 +97,11 @@ Starknet native account abstraction pattern allows for the creation of custom ac usually most account implementations validate transactions using the [Stark curve](https://docs.starknet.io/learn/protocol/cryptography#the-stark-curve) which is the most efficient way of validating signatures since it is a STARK-friendly curve. -OpenZeppelin Contracts for Cairo provides [AccountComponent](/contracts-cairo/2.x/api/account#AccountComponent) for implementing this validation scheme. +OpenZeppelin Contracts for Cairo provides [AccountComponent](/contracts-cairo/alpha/api/account#AccountComponent) for implementing this validation scheme. ### Usage -Constructing an account contract requires integrating both [AccountComponent](/contracts-cairo/2.x/api/account#AccountComponent) and [SRC5Component](/contracts-cairo/2.x/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: +Constructing an account contract requires integrating both [AccountComponent](/contracts-cairo/alpha/api/account#AccountComponent) and [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: ```rust #[starknet::contract(account)] @@ -180,11 +180,11 @@ pub trait AccountABI { ## Ethereum Account Besides the Stark-curve account, OpenZeppelin Contracts for Cairo also offers Ethereum-flavored accounts that use the [secp256k1](https://en.bitcoin.it/wiki/Secp256k1) curve for signature validation. -For this the [EthAccountComponent](/contracts-cairo/2.x/api/account#EthAccountComponent) must be used. +For this the [EthAccountComponent](/contracts-cairo/alpha/api/account#EthAccountComponent) must be used. ### Usage -Constructing a secp256k1 account contract also requires integrating both [EthAccountComponent](/contracts-cairo/2.x/api/account#EthAccountComponent) and [SRC5Component](/contracts-cairo/2.x/api/introspection#SRC5Component). +Constructing a secp256k1 account contract also requires integrating both [EthAccountComponent](/contracts-cairo/alpha/api/account#EthAccountComponent) and [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: @@ -192,7 +192,7 @@ Here’s an example of a basic contract: #[starknet::contract(account)] mod MyEthAccount { use openzeppelin_account::EthAccountComponent; - use openzeppelin_account::interface::EthPublicKey; + use openzeppelin_interfaces::accounts::EthPublicKey; use openzeppelin_introspection::src5::SRC5Component; use starknet::ClassHash; @@ -371,7 +371,7 @@ First, let’s take the example account we created before and deploy it: #[starknet::contract(account)] mod MyEthAccount { use openzeppelin_account::EthAccountComponent; - use openzeppelin_account::interface::EthPublicKey; + use openzeppelin_interfaces::accounts::EthPublicKey; use openzeppelin_introspection::src5::SRC5Component; component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); diff --git a/content/contracts-cairo/3.x/api/access.mdx b/content/contracts-cairo/3.x/api/access.mdx index 71fd194c..002a41fb 100644 --- a/content/contracts-cairo/3.x/api/access.mdx +++ b/content/contracts-cairo/3.x/api/access.mdx @@ -9,16 +9,21 @@ This crate provides ways to restrict who can access the functions of a contract ## Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + + import { UMBRELLA_VERSION } from "../utils/constants.js"; ### `IAccessControl` [toc] [#IAccessControl] ```rust -use openzeppelin_access::accesscontrol::interface::IAccessControl; +use openzeppelin_interfaces::accesscontrol::IAccessControl; ``` External interface of AccessControl. @@ -146,11 +151,11 @@ Emitted when `account` is revoked `role`. ### `IAccessControlWithDelay` [toc] [#IAccessControlWithDelay] ```rust -use openzeppelin_access::accesscontrol::interface::IAccessControlWithDelay; +use openzeppelin_interfaces::accesscontrol::IAccessControlWithDelay; ``` External interface for the extended `AccessControlWithDelay` functionality. @@ -206,6 +211,235 @@ Emitted when `account` is granted `role` with a delay. `sender` is the account that originated the contract call, an account with the admin role or the deployer address if [\_grant\_role\_with\_delay](#AccessControlComponent-_grant_role_with_delay) is called from the constructor. +### `IAccessControlDefaultAdminRules` [toc] [#IAccessControlDefaultAdminRules] + + +```rust +use openzeppelin_interfaces::accesscontrol_default_admin_rules::IAccessControlDefaultAdminRules; +``` + +External interface of AccessControlDefaultAdminRules declared to support [SRC5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md) detection. + +[SRC5 ID](./introspection#ISRC5) + +```text +0x3509b3083c9586afe5dae781146b0608c3846870510f8d4d21ae38676cc33eb +``` + +Functions + +- [`default_admin()`](#IAccessControlDefaultAdminRules-default_admin) +- [`pending_default_admin()`](#IAccessControlDefaultAdminRules-pending_default_admin) +- [`default_admin_delay()`](#IAccessControlDefaultAdminRules-default_admin_delay) +- [`pending_default_admin_delay()`](#IAccessControlDefaultAdminRules-pending_default_admin_delay) +- [`begin_default_admin_transfer(new_admin)`](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) +- [`cancel_default_admin_transfer()`](#IAccessControlDefaultAdminRules-cancel_default_admin_transfer) +- [`accept_default_admin_transfer()`](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) +- [`change_default_admin_delay(new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) +- [`rollback_default_admin_delay()`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) +- [`default_admin_delay_increase_wait()`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) + +Events + +- [`DefaultAdminTransferScheduled(new_admin, accept_schedule)`](#IAccessControlDefaultAdminRules-DefaultAdminTransferScheduled) +- [`DefaultAdminTransferCanceled()`](#IAccessControlDefaultAdminRules-DefaultAdminTransferCanceled) +- [`DefaultAdminDelayChangeScheduled(new_delay, effect_schedule)`](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeScheduled) +- [`DefaultAdminDelayChangeCanceled()`](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) + +#### Functions [!toc] [#IAccessControlDefaultAdminRules-Functions] + + +Returns the address of the current `DEFAULT_ADMIN_ROLE` holder. + + + +Returns a tuple of a `new_admin` and an `accept_schedule`. + +Returns a tuple of a `new_admin` and an `accept_schedule`. + +After the `accept_schedule` passes, the `new_admin` will be able to accept the `default_admin` role by calling [accept\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-accept_default_admin_transfer), completing the role transfer. + +A zero value only in `accept_schedule` indicates no pending admin transfer. + + +A zero address `new_admin` means that `default_admin` is being renounced. + + + + +Returns the delay required to schedule the acceptance of a `default_admin` transfer started. + +This delay will be added to the current timestamp when calling [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) to set the acceptance schedule. + + +If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function return the new delay. + + +See [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay). + + + +Returns a tuple of `new_delay` and an `effect_schedule`. + +After the `effect_schedule` passes, the `new_delay` will get into effect immediately for every new `default_admin` transfer started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). + +A zero value only in `effect_schedule` indicates no pending delay change. + +A zero value only for `new_delay` means that the next [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) will be zero after the effect schedule. + + + +Starts a `default_admin` transfer by setting a [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) scheduled for acceptance after the current timestamp plus a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay). + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminTransferScheduled](#IAccessControlDefaultAdminRules-DefaultAdminTransferScheduled) event. + + + +Cancels a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). + +A [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) not yet accepted can also be cancelled with this function. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminTransferCanceled](#IAccessControlDefaultAdminRules-DefaultAdminTransferCanceled) event. + + + +Completes a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). + +After calling the function: + +- `DEFAULT_ADMIN_ROLE` must be granted to the caller. +- `DEFAULT_ADMIN_ROLE` must be revoked from the previous holder. +- [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) must be reset to zero value. + +Requirements: + +- Only can be called by the [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin)'s `new_admin`. +- The [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin)'s `accept_schedule` should’ve passed. + + + +Initiates a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) update by setting a [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) scheduled to take effect after the current timestamp plus a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay). + +This function guarantees that any call to [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) done between the timestamp this method is called and the [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) effect schedule will use the current [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) set before calling. + +The [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay)'s effect schedule is defined in a way that waiting until the schedule and then calling [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) with the new delay will take at least the same as another `default_admin` complete transfer (including acceptance). + +The schedule is designed for two scenarios: + +- When the delay is changed for a larger one the schedule is `block.timestamp + new delay` capped by [default\_admin\_delay\_increase\_wait](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait). +- When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`. + +A [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) that never got into effect will be canceled in favor of a new scheduled change. + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminDelayChangeScheduled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeScheduled) event and may emit a [DefaultAdminDelayChangeCanceled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) event. + + + + +Cancels a scheduled [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) change. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminDelayChangeCanceled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) event. + + + +Maximum time in seconds for an increase to [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay)) to take effect. Defaults to 5 days. + +When the [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overridden for a custom [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) increase scheduling. + +Make sure to add a reasonable amount of time while overriding this value, otherwise, there’s a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (e.g. set milliseconds instead of seconds). + + +#### Events [!toc] [#IAccessControlDefaultAdminRules-Events] + + +Emitted when a `default_admin` transfer is started. + +Sets `new_admin` as the next address to become the `default_admin` by calling [accept\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) only after `accept_schedule` passes. + + + +Emitted when a [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) is reset if it was never accepted, regardless of its schedule. + + + +Emitted when a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) change is started. + +Sets `new_delay` as the next delay to be applied between default admins transfers after `effect_schedule` has passed. + +Emitted when a [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) is reset if its schedule didn’t pass. + + ## Core ### `OwnableComponent` [toc] [#OwnableComponent] @@ -506,7 +740,7 @@ Roles can be granted and revoked dynamically via the [grant\_role](#AccessContro By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means that only accounts with this role will be able to grant or revoke other roles. More complex role relationships can be created by using [set\_role\_admin](#AccessControlComponent-set_role_admin). -The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to grant and revoke this role. Extra precautions should be taken to secure accounts that have been granted it. +The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to grant and revoke this role. Extra precautions should be taken to secure accounts that have been granted it. See [AccessControlDefaultAdminRulesComponent](#AccessControlDefaultAdminRulesComponent). [Embeddable Mixin Implementations](../components#mixins) @@ -865,3 +1099,651 @@ See [IAccessControlWithDelay::RoleGrantedWithDelay](#IAccessControlWithDelay-Rol > See [IAccessControl::RoleRevoked](#IAccessControl-RoleRevoked). + +## Extensions + +### `AccessControlDefaultAdminRulesComponent` [toc] [#AccessControlDefaultAdminRulesComponent] + + +```rust +use openzeppelin_access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent; +``` + +Extension of [AccessControl](#AccessControlComponent) that allows specifying special rules to manage the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions over other roles that may potentially have privileged rights in the system. + +If a specific role doesn’t have an admin role assigned, the holder of the `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it. + +This contract implements the following risk mitigations on top of [AccessControl](#AccessControlComponent): + +- Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it’s potentially renounced. +- Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account. +- Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted. +- The delay can be changed by scheduling, see [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay). +- It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`. + +[Embeddable Mixin Implementations](../components#mixins) + +#### AccessControlMixinImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlMixinImpl] + +- [`AccessControlDefaultAdminRulesImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlDefaultAdminRulesImpl) +- [`AccessControlImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlImpl) +- [`AccessControlCamelImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlCamelImpl) +- [`AccessControlWithDelayImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlWithDelayImpl) +- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) + +Embeddable Implementations + +#### AccessControlDefaultAdminRulesImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlDefaultAdminRulesImpl] + +- [`default_admin(self)`](#IAccessControlDefaultAdminRules-default_admin) +- [`pending_default_admin(self)`](#IAccessControlDefaultAdminRules-pending_default_admin) +- [`default_admin_delay(self)`](#IAccessControlDefaultAdminRules-default_admin_delay) +- [`pending_default_admin_delay(self)`](#IAccessControlDefaultAdminRules-pending_default_admin_delay) +- [`begin_default_admin_transfer(self, new_admin)`](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) +- [`cancel_default_admin_transfer(self)`](#IAccessControlDefaultAdminRules-cancel_default_admin_transfer) +- [`accept_default_admin_transfer(self)`](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) +- [`change_default_admin_delay(self, new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) +- [`rollback_default_admin_delay(self)`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) +- [`default_admin_delay_increase_wait(self)`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) + +#### AccessControlImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlImpl] + +- [`has_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-has_role) +- [`get_role_admin(self, role)`](#AccessControlDefaultAdminRulesComponent-get_role_admin) +- [`grant_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-grant_role) +- [`revoke_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-revoke_role) +- [`renounce_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-renounce_role) + +#### AccessControlCamelImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlCamelImpl] + +- [`hasRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-hasRole) +- [`getRoleAdmin(self, role)`](#AccessControlDefaultAdminRulesComponent-getRoleAdmin) +- [`grantRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-grantRole) +- [`revokeRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-revokeRole) +- [`renounceRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-renounceRole) + +#### AccessControlWithDelayImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlWithDelayImpl] + +- [`get_role_status(self, role, account)`](#AccessControlDefaultAdminRulesComponent-get_role_status) +- [`grant_role_with_delay(self, role, account, delay)`](#AccessControlDefaultAdminRulesComponent-grant_role_with_delay) + +#### SRC5Impl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-SRC5Impl] + +- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) + +Internal Implementations + +#### InternalImpl [!toc] [#AccessControlDefaultAdminRulesComponent-InternalImpl] + +- [`initializer(self, initial_delay, initial_default_admin)`](#AccessControlDefaultAdminRulesComponent-initializer) +- [`assert_only_role(self, role)`](#AccessControlDefaultAdminRulesComponent-assert_only_role) +- [`is_role_effective(self, role, account)`](#AccessControlDefaultAdminRulesComponent-is_role_effective) +- [`resolve_role_status(self, role, account)`](#AccessControlDefaultAdminRulesComponent-resolve_role_status) +- [`is_role_granted(self, role, account)`](#AccessControlDefaultAdminRulesComponent-is_role_granted) +- [`set_role_admin(self, role, admin_role)`](#AccessControlDefaultAdminRulesComponent-set_role_admin) +- [`_grant_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-_grant_role) +- [`_grant_role_with_delay(self, role, account, delay)`](#AccessControlDefaultAdminRulesComponent-_grant_role_with_delay) +- [`_revoke_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-_revoke_role) +- [`set_pending_default_admin(self, new_admin, new_schedule)`](#AccessControlDefaultAdminRulesComponent-set_pending_default_admin) +- [`set_pending_delay(self, new_delay, new_schedule)`](#AccessControlDefaultAdminRulesComponent-set_pending_delay) +- [`delay_change_wait(self, new_delay)`](#AccessControlDefaultAdminRulesComponent-delay_change_wait) + +Events + +#### IAccessControl [!toc] [#AccessControlDefaultAdminRulesComponent-Events-IAccessControl] + +- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#AccessControlDefaultAdminRulesComponent-RoleAdminChanged) +- [`RoleGranted(role, account, sender)`](#AccessControlDefaultAdminRulesComponent-RoleGranted) +- [`RoleRevoked(role, account, sender)`](#AccessControlDefaultAdminRulesComponent-RoleRevoked) + +#### IAccessControlWithDelay [!toc] [#AccessControlDefaultAdminRulesComponent-Events-IAccessControlWithDelay] + +- [`RoleGrantedWithDelay(role, account, sender, delay)`](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) + +#### IAccessControlDefaultAdminRules [!toc] [#AccessControlDefaultAdminRulesComponent-Events-IAccessControlDefaultAdminRules] + +- [`DefaultAdminTransferScheduled(new_admin, accept_schedule)`](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferScheduled) +- [`DefaultAdminTransferCanceled()`](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferCanceled) +- [`DefaultAdminDelayChangeScheduled(new_delay, effect_schedule)`](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeScheduled) +- [`DefaultAdminDelayChangeCanceled()`](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) + +#### Embeddable functions [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Functions] + + +Returns the address of the current `DEFAULT_ADMIN_ROLE` holder. + + + +Returns a tuple of a `new_admin` and an `accept_schedule`. + +After the `accept_schedule` passes, the `new_admin` will be able to accept the `default_admin` role by calling [accept\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-accept_default_admin_transfer), completing the role transfer. + +A zero value only in `accept_schedule` indicates no pending admin transfer. + + +A zero address `new_admin` means that `default_admin` is being renounced. + + + + +Returns the delay required to schedule the acceptance of a `default_admin` transfer started. + +This delay will be added to the current timestamp when calling [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) to set the acceptance schedule. + + +If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function returns the new delay. + + +See [change\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-change_default_admin_delay). + + + +Returns a tuple of `new_delay` and an `effect_schedule`. + +After the `effect_schedule` passes, the `new_delay` will get into effect immediately for every new `default_admin` transfer started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). + +A zero value only in `effect_schedule` indicates no pending delay change. + +A zero value only for `new_delay` means that the next [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) will be zero after the effect schedule. + + + +Starts a `default_admin` transfer by setting a [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) scheduled for acceptance after the current timestamp plus a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay). + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminTransferScheduled](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferScheduled) event. + + + +Cancels a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). + +A [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) not yet accepted can also be cancelled with this function. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminTransferCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferCanceled) event. + + + +Completes a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). + +After calling the function: + +- `DEFAULT_ADMIN_ROLE` must be granted to the caller. +- `DEFAULT_ADMIN_ROLE` must be revoked from the previous holder. +- [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) must be reset to zero values. + +Requirements: + +- Only can be called by the [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin)'s `new_admin`. +- The [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin)'s `accept_schedule` should’ve passed. + + + +Initiates a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) update by setting a [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) scheduled for getting into effect after the current timestamp plus a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay). + +This function guarantees that any call to [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) done between the timestamp this method is called and the [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) effect schedule will use the current [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) set before calling. + +The [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay)'s effect schedule is defined in a way that waiting until the schedule and then calling [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) with the new delay will take at least the same as another `default_admin` complete transfer (including acceptance). + +The schedule is designed for two scenarios: + +- When the delay is changed for a larger one the schedule is `block.timestamp new delay` capped by [default\_admin\_delay\_increase\_wait](#AccessControlDefaultAdminRulesComponent-default_admin_delay_increase_wait). +- When the delay is changed for a shorter one, the schedule is `block.timestamp (current delay - new delay)`. + +A [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) that never got into effect will be canceled in favor of a new scheduled change. + +Requirements: + +- Only can be called by the current `default_admin`. + +Emits a [DefaultAdminDelayChangeScheduled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeScheduled) event and may emit a [DefaultAdminDelayChangeCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) event. + + + +Cancels a scheduled [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) change. + +Requirements: + +- Only can be called by the current `default_admin`. + +May emit a [DefaultAdminDelayChangeCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) event. + + + +Maximum time in seconds for an increase to [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-change_default_admin_delay)) to take effect. Defaults to 5 days. + +When the [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overridden for a custom [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) increase scheduling. + + +Make sure to add a reasonable amount of time while overriding this value, otherwise, there’s a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds). + + + + +Returns whether `account` can act as `role`. + + + +Returns the admin role that controls `role`. See [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). + +To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). + + + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + + + +Grants `role` to `account`. + +If `account` had not been already granted `role`, emits a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. + + + +Attempts to grant `role` to `account` with the specified activation delay. + +Requirements: + +- The caller must have \`role’s admin role. +- delay must be greater than 0. +- the `role` must not be already effective for `account`. + +May emit a [RoleGrantedWithDelay](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) event. + + + +Revokes `role` from `account`. + +If `account` had been granted `role`, emits a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + +Requirements: + +- the caller must have `role`'s admin role. + +May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + + + +Revokes `role` from the calling account. + +Roles are often managed via [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). + +If the calling account had been revoked `role`, emits a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + +Requirements: + +- the caller must be `account`. + +May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + + + +See [ISRC5::supports\_interface](./introspection#ISRC5-supports_interface). + + + +See [has\_role](#AccessControlDefaultAdminRulesComponent-has_role). + + + +See [get\_role\_admin](#AccessControlDefaultAdminRulesComponent-get_role_admin). + + + +See [grant\_role](#AccessControlDefaultAdminRulesComponent-grant_role). + + + +See [revoke\_role](#AccessControlDefaultAdminRulesComponent-revoke_role). + + + +See [renounce\_role](#AccessControlDefaultAdminRulesComponent-renounce_role). + + +#### Internal functions [!toc] [#AccessControlDefaultAdminRulesComponent-Internal-Functions] + + +Initializes the contract by registering the IAccessControl interface ID and setting the initial delay and default admin. + +Requirements: + +- `initial_default_admin` must not be the zero address. + + + +Validates that the caller can act as the given role. Otherwise it panics. + + + +Returns whether the account can act as the given role. + +The account can act as the role if it is active and the `effective_from` time is before or equal to the current time. + + +If the `effective_from` timepoint is 0, the role is effective immediately. This is backwards compatible with implementations that didn’t use delays but a single boolean flag. + + + + +Returns the account’s status for the given role. + +The possible statuses are: + +- `NotGranted`: the role has not been granted to the account. +- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. +- `Effective`: the role has been granted to the account and is currently active. + + + +Returns whether the account has the given role granted. + + +The account may not be able to act as the role yet, if a delay was set and has not passed yet. Use is\_role\_effective to check if the account can act as the role. + + + + +Sets `admin_role` as \`role’s admin role. + +Internal function without access restriction. + +Requirements: + +- `role` must not be `DEFAULT_ADMIN_ROLE`. + +Emits a [RoleAdminChanged](#AccessControlDefaultAdminRulesComponent-RoleAdminChanged) event. + + + +Attempts to grant `role` to `account`. The function does nothing if `role` is already effective for `account`. If `role` has been granted to `account`, but is not yet active due to a time delay, the delay is removed and `role` becomes effective immediately. + +Internal function without access restriction. + +For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn’t already a `default_admin` or if the role has been previously renounced. + +Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE` assignable again. Make sure to guarantee this is the expected behavior in your implementation. + +May emit a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. + + + +Attempts to grant `role` to `account` with the specified activation delay. + +The role will become effective after the given delay has passed. If the role is already active (`Effective`) for the account, the function will panic. If the role has been granted but is not yet active (being in the `Delayed` state), the existing delay will be overwritten with the new `delay`. + +Internal function without access restriction. + +Requirements: + +- `delay` must be greater than 0. +- the `role` must not be already effective for `account`. +- `role` must not be `DEFAULT_ADMIN_ROLE`. + +May emit a [RoleGrantedWithDelay](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) event. + + + +Attempts to revoke `role` from `account`. The function does nothing if `role` is not effective for `account`. If `role` has been revoked from `account`, but is still active due to a time delay, the delay is removed and `role` becomes inactive immediately. + +Internal function without access restriction. + +May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. + + + +Setter of the tuple for pending admin and its schedule. + +May emit a DefaultAdminTransferCanceled event. + + + +Setter of the tuple for pending delay and its schedule. + +May emit a DefaultAdminDelayChangeCanceled event. + + + +Returns the amount of seconds to wait after the `new_delay` will become the new `default_admin_delay`. + +The value returned guarantees that if the delay is reduced, it will go into effect after a wait that honors the previously set delay. + +See [default\_admin\_delay\_increase\_wait](#AccessControlDefaultAdminRulesComponent-default_admin_delay_increase_wait). + + +#### Events [!toc] [#AccessControlDefaultAdminRulesComponent-Events] + + +Emitted when `new_admin_role` is set as role’s admin role, replacing `previous_admin_role` + +`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite `RoleAdminChanged` not being emitted signaling this. + + + +Emitted when `account` is granted `role`. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role` is called from the constructor. + + + +Emitted when `role` is revoked for `account`. + +`sender` is the account that originated the contract call: + +- If using `revoke_role`, it is the admin role bearer. +- If using `renounce_role`, it is the role bearer (i.e. `account`). + + + +Emitted when `account` is granted `role` with a delay. + +`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role_with_delay` is called from the constructor. + + + +Emitted when a `default_admin` transfer is started. + +Sets `new_admin` as the next address to become the `default_admin` by calling [accept\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-accept_default_admin_transfer) only after `accept_schedule` passes. + + + +Emitted when a [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) is reset if it was never accepted, regardless of its schedule. + + + + +Emitted when a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) change is started. + +Sets `new_delay` as the next delay to be applied between default admins transfers after `effect_schedule` has passed. + +Emitted when a [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) is reset if its schedule didn’t pass. + diff --git a/content/contracts-cairo/3.x/api/account.mdx b/content/contracts-cairo/3.x/api/account.mdx index 159d9ddd..3519cece 100644 --- a/content/contracts-cairo/3.x/api/account.mdx +++ b/content/contracts-cairo/3.x/api/account.mdx @@ -6,16 +6,21 @@ This crate provides components to implement account contracts that can be used f ## Interfaces + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references documented here are +contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + + import { UMBRELLA_VERSION } from "../utils/constants.js"; ### `ISRC6` [toc] [#ISRC6] ```rust -use openzeppelin_account::interface::ISRC6; +use openzeppelin_interfaces::accounts::ISRC6; ``` Interface of the SRC6 Standard Account as defined in the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md). @@ -67,11 +72,11 @@ Returns the short string `'VALID'` if valid, otherwise it reverts. ### `ISRC9_V2` [toc] [#ISRC9_V2] ```rust -use openzeppelin_account::extensions::src9::ISRC9_V2; +use openzeppelin_interfaces::src9::ISRC9_V2; ``` Interface of the SRC9 Standard as defined in the [SNIP-9](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md). diff --git a/content/contracts-cairo/3.x/api/erc1155.mdx b/content/contracts-cairo/3.x/api/erc1155.mdx index 8526aaea..56cc5d5c 100644 --- a/content/contracts-cairo/3.x/api/erc1155.mdx +++ b/content/contracts-cairo/3.x/api/erc1155.mdx @@ -8,17 +8,22 @@ This module provides interfaces, presets, and utilities related to ERC1155 contr For an overview of ERC1155, read our [ERC1155 guide](../erc1155). -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + ### `IERC1155` [toc] [#IERC1155] ```rust -use openzeppelin_token::erc1155::interface::IERC1155; +use openzeppelin_interfaces::erc1155::IERC1155; ``` Interface of the IERC1155 standard as defined in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155). @@ -143,11 +148,11 @@ Emitted when the token URI is updated to `value` for the `id` token. ```rust -use openzeppelin_token::erc1155::interface::IERC1155MetadataURI; +use openzeppelin_interfaces::erc1155::IERC1155MetadataURI; ``` Interface for the optional metadata function in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155#metadata). @@ -176,11 +181,11 @@ Returns the Uniform Resource Identifier (URI) for the `token_id` token. ```rust -use openzeppelin_token::erc1155::interface::IERC1155Receiver; +use openzeppelin_interfaces::erc1155::IERC1155Receiver; ``` Interface for contracts that support receiving token transfers from `ERC1155` contracts. diff --git a/content/contracts-cairo/3.x/api/erc20.mdx b/content/contracts-cairo/3.x/api/erc20.mdx index 08678751..c7d28f93 100644 --- a/content/contracts-cairo/3.x/api/erc20.mdx +++ b/content/contracts-cairo/3.x/api/erc20.mdx @@ -6,18 +6,23 @@ This module provides interfaces, presets, and utilities related to ERC20 contrac For an overview of ERC20, read our [ERC20 guide](../erc20). -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + import { UMBRELLA_VERSION } from "../utils/constants.js"; ### `IERC20` [toc] [#IERC20] ```rust -use openzeppelin_token::erc20::interface::IERC20; +use openzeppelin_interfaces::erc20::IERC20; ``` Interface of the IERC20 standard as defined in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). @@ -117,11 +122,11 @@ Emitted when the allowance of a `spender` for an `owner` is set. `value` is the ### `IERC20Metadata` [toc] [#IERC20Metadata] ```rust -use openzeppelin_token::erc20::interface::IERC20Metadata; +use openzeppelin_interfaces::erc20::IERC20Metadata; ``` Interface for the optional metadata functions in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). @@ -167,11 +172,11 @@ This information is only used for *display* purposes: it in no way affects any o ### `IERC20Permit` [toc] [#IERC20Permit] ```rust -use openzeppelin_token::erc20::interface::IERC20Permit; +use openzeppelin_interfaces::erc20::IERC20Permit; ``` Interface of the ERC20Permit standard to support gasless token approvals as defined in [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612). @@ -211,11 +216,11 @@ Returns the domain separator used in generating a message hash for `permit` sign ### `IERC4626` [toc] [#IERC4626] ```rust -use openzeppelin_token::erc20::extensions::erc4626::interface::IERC4626; +use openzeppelin_interfaces::erc4626::IERC4626; ``` Interface of the IERC4626 standard as defined in [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626). diff --git a/content/contracts-cairo/3.x/api/erc721.mdx b/content/contracts-cairo/3.x/api/erc721.mdx index 9436efa2..c093c879 100644 --- a/content/contracts-cairo/3.x/api/erc721.mdx +++ b/content/contracts-cairo/3.x/api/erc721.mdx @@ -6,18 +6,23 @@ This module provides interfaces, presets, and utilities related to ERC721 contra For an overview of ERC721, read our [ERC721 guide](../erc721). -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + import { UMBRELLA_VERSION } from "../utils/constants.js"; ### `IERC721` [toc] [#IERC721] ```rust -use openzeppelin_token::erc721::interface::IERC721; +use openzeppelin_interfaces::erc721::IERC721; ``` Interface of the IERC721 standard as defined in [EIP721](https://eips.ethereum.org/EIPS/eip-721). @@ -150,11 +155,11 @@ Emitted when `token_id` token is transferred from `from` to `to`. ### `IERC721Metadata` [toc] [#IERC721Metadata] ```rust -use openzeppelin_token::erc721::interface::IERC721Metadata; +use openzeppelin_interfaces::erc721::IERC721Metadata; ``` Interface for the optional metadata functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). @@ -200,11 +205,11 @@ Returns the Uniform Resource Identifier (URI) for the `token_id` token. If the U ### `IERC721Receiver` [toc] [#IERC721Receiver] ```rust -use openzeppelin_token::erc721::interface::IERC721Receiver; +use openzeppelin_interfaces::erc721::IERC721Receiver; ``` Interface for contracts that support receiving `safe_transfer_from` transfers. @@ -232,7 +237,7 @@ Whenever an IERC721 `token_id` token is transferred to this non-account contract ### `IERC721Enumerable` [toc] [#IERC721Enumerable] Interface for the optional enumerable functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). diff --git a/content/contracts-cairo/3.x/api/finance.mdx b/content/contracts-cairo/3.x/api/finance.mdx index a41e0c64..f0981e42 100644 --- a/content/contracts-cairo/3.x/api/finance.mdx +++ b/content/contracts-cairo/3.x/api/finance.mdx @@ -6,17 +6,22 @@ import { UMBRELLA_VERSION } from "../utils/constants.js"; This crate includes primitives for financial systems. -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + ### `IVesting` [toc] [#IVesting] ```rust -use openzeppelin_finance::vesting::interface::IVesting; +use openzeppelin_interfaces::vesting::IVesting; ``` Common interface for contracts implementing the vesting functionality. diff --git a/content/contracts-cairo/3.x/api/governance.mdx b/content/contracts-cairo/3.x/api/governance.mdx index f0bd318f..e5d5bbe1 100644 --- a/content/contracts-cairo/3.x/api/governance.mdx +++ b/content/contracts-cairo/3.x/api/governance.mdx @@ -6,17 +6,22 @@ import { UMBRELLA_VERSION } from "../utils/constants.js"; This crate includes primitives for on-chain governance. -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + ### `IGovernor` [toc] [#IGovernor] ```rust -use openzeppelin_governance::governor::interface::IGovernor; +use openzeppelin_interfaces::governor::IGovernor; ``` Interface of a governor contract. @@ -400,11 +405,11 @@ Emitted when a vote is cast with params. ```rust -use openzeppelin_governance::multisig::interface::IMultisig; +use openzeppelin_interfaces::multisig::IMultisig; ``` Interface of a multisig contract. @@ -768,11 +773,11 @@ Emitted when a new transaction is submitted with non-zero salt. ```rust -use openzeppelin_governance::timelock::interface::ITimelock; +use openzeppelin_interfaces::timelock::ITimelock; ``` Interface of a timelock contract. @@ -1026,11 +1031,11 @@ Emitted when the minimum delay for future operations is modified. ```rust -use openzeppelin_governance::votes::interface::IVotes; +use openzeppelin_interfaces::votes::IVotes; ``` Common interface for Votes-enabled contracts. diff --git a/content/contracts-cairo/3.x/api/introspection.mdx b/content/contracts-cairo/3.x/api/introspection.mdx index a0bb6158..32a0450c 100644 --- a/content/contracts-cairo/3.x/api/introspection.mdx +++ b/content/contracts-cairo/3.x/api/introspection.mdx @@ -6,17 +6,22 @@ import { UMBRELLA_VERSION } from "../utils/constants.js"; This crate handles [type introspection](https://en.wikipedia.org/wiki/Type_introspection) of contracts. In other words, it examines which functions can be called on a given contract. This is referred to as the contract's interface. -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + ### `ISRC5` [toc] [#ISRC5] ```rust -use openzeppelin_introspection::interface::ISRC5; +use openzeppelin_interfaces::introspection::ISRC5; ``` Interface of the SRC5 Introspection Standard as defined in [SNIP-5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md). diff --git a/content/contracts-cairo/3.x/api/token_common.mdx b/content/contracts-cairo/3.x/api/token_common.mdx index 3be2e45f..de34561d 100644 --- a/content/contracts-cairo/3.x/api/token_common.mdx +++ b/content/contracts-cairo/3.x/api/token_common.mdx @@ -6,14 +6,19 @@ import { UMBRELLA_VERSION } from "../utils/constants.js"; This module provides extensions and utilities that are common to multiple token standards. -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + ### [](#IERC2981)`IERC2981` [toc] [#IERC2981] - + ```rust -use openzeppelin_token::common::erc2981::interface::IERC2981; +use openzeppelin_interfaces::erc2981::IERC2981; ``` [SRC5 ID](./introspection#ISRC5) @@ -40,10 +45,10 @@ Returns how much royalty is owed and to whom, based on a sale price that may be ### [](#IERC2981Info)`IERC2981Info` [toc] [#IERC2981Info] - + ```rust -use openzeppelin_token::common::erc2981::interface::IERC2981Info; +use openzeppelin_interfaces::erc2981::IERC2981Info; ``` Interface providing external read functions for discovering the state of ERC2981 component. @@ -85,10 +90,10 @@ The returned tuple contains: ### [](#IERC2981Admin)`IERC2981Admin` [toc] [#IERC2981Admin] - + ```rust -use openzeppelin_token::common::erc2981::interface::IERC2981Admin; +use openzeppelin_interfaces::erc2981::IERC2981Admin; ``` Interface providing external admin functions for managing the settings of ERC2981 component. @@ -181,6 +186,13 @@ ERC2981AdminAccessControlImpl - [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminAccessControlImpl-set_token_royalty) - [`reset_token_royalty(self, token_id)`](#ERC2981AdminAccessControlImpl-reset_token_royalty) +ERC2981AdminAccessControlDefaultAdminRulesImpl + +- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-set_default_royalty) +- [`delete_default_royalty(self)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-delete_default_royalty) +- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-set_token_royalty) +- [`reset_token_royalty(self, token_id)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-reset_token_royalty) + Internal implementations InternalImpl @@ -380,6 +392,64 @@ Requirements: - The caller must have `ROYALTY_ADMIN_ROLE` role. +#### [](#ERC2981Component-ERC2981AdminAccessControlDefaultAdminRulesImpl)ERC2981AdminAccessControlDefaultAdminRulesImpl [!toc] + +An alternative implementation of [IERC2981Admin](#IERC2981Admin). Provides admin functions for managing royalty settings +that require `ROYALTY_ADMIN_ROLE` to be granted to the caller. Requires the contract to implement [AccessControlDefaultAdminRulesComponent](./access#AccessControlDefaultAdminRulesComponent). + + + +Sets the royalty information that all ids in this contract will default to. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Sets the default royalty percentage and receiver to zero. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. + + + +Sets the royalty information for a specific token id that takes precedence over the global default. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. +- `receiver` cannot be the zero address. +- `fee_numerator` cannot be greater than the fee denominator. + + + +Resets royalty information for the token id back to unset. + +Requirements: + +- The caller must have `ROYALTY_ADMIN_ROLE` role. + + #### [](#ERC2981Component-Internal-functions)Internal functions [!toc] +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + ### [](#IUniversalDeployer)`IUniversalDeployer` [toc] [#IUniversalDeployer] - + ```rust -use openzeppelin_utils::interfaces::IUniversalDeployer; +use openzeppelin_interfaces::deployments::IUniversalDeployer; ``` Functions diff --git a/content/contracts-cairo/3.x/api/upgrades.mdx b/content/contracts-cairo/3.x/api/upgrades.mdx index 9780fde5..0fffeec6 100644 --- a/content/contracts-cairo/3.x/api/upgrades.mdx +++ b/content/contracts-cairo/3.x/api/upgrades.mdx @@ -6,14 +6,19 @@ import { UMBRELLA_VERSION } from "../utils/constants.js"; This crate provides interfaces and utilities related to upgradeability. -## Interfaces +## [](#interfaces)Interfaces + + +Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references +documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. + ### [](#IUpgradeable)`IUpgradeable` [toc] [#IUpgradeable] - + ```rust -use openzeppelin_upgrades::interface::IUpgradeable; +use openzeppelin_interfaces::upgrades::IUpgradeable; ``` Interface of an upgradeable contract. @@ -36,10 +41,10 @@ This function is usually protected by an [Access Control](../access) mechanism. ### [](#IUpgradeAndCall)`IUpgradeAndCall` [toc] [#IUpgradeAndCall] - + ```rust -use openzeppelin_upgrades::interface::IUpgradeAndCall; +use openzeppelin_interfaces::upgrades::IUpgradeAndCall; ``` Interface for an upgradeable contract that couples an upgrade with a function call in the upgraded context. diff --git a/content/contracts-cairo/3.x/api/utilities.mdx b/content/contracts-cairo/3.x/api/utilities.mdx index 5288699b..6a6380a5 100644 --- a/content/contracts-cairo/3.x/api/utilities.mdx +++ b/content/contracts-cairo/3.x/api/utilities.mdx @@ -167,6 +167,42 @@ Origin-independent deployments (deployed from zero) should pass `Option::None` a Origin-dependent deployments hash `salt` with `caller_address` (member of [DeployerInfo](#deployments-DeployerInfo)) and pass the hashed salt to the inner [deploy\_syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) as the `contract_address_salt` argument. +### `execution` [toc] [#execution] + + + +```rust +use openzeppelin_utils::execution; +``` + +Module containing utilities related to execution. + +#### Functions [!toc] [#execution-Functions] + + +Executes a list of calls and returns an array containing the return values from each call. + + + +Executes a single call and returns its return value. + + + +Validates a signature using SRC6 `is_valid_signature` and asserts it's valid. Checks both 'VALID' (`starknet::VALIDATED`) and true (1) for backwards compatibility. Reverts with `invalid_signature_error` if signature is invalid. + + ### `math` [toc] [#math] diff --git a/content/contracts-cairo/3.x/components.mdx b/content/contracts-cairo/3.x/components.mdx index 5266792a..d78deab7 100644 --- a/content/contracts-cairo/3.x/components.mdx +++ b/content/contracts-cairo/3.x/components.mdx @@ -132,7 +132,7 @@ By adding the embed attribute, `is_initialized` becomes a contract entrypoint fo Embeddable implementations, when available in this library’s components, are segregated from the internal component implementation which makes it easier to safely expose. Components also separate granular implementations from [mixin](#mixins) implementations. The API documentation design reflects these groupings. -See [ERC20Component](/contracts-cairo/2.x/api/erc20#erc20component) as an example which includes: +See [ERC20Component](/contracts-cairo/alpha/api/erc20#erc20component) as an example which includes: * **Embeddable Mixin Implementation** * **Embeddable Implementations** @@ -240,7 +240,7 @@ Always read the API Reference documentation for each integrated component. Some components require some sort of setup upon construction. Usually, this would be a job for a constructor; however, components themselves cannot implement constructors. Components instead offer ``initializer``s within their `InternalImpl` to call from the contract’s constructor. -Let’s look at how a contract would integrate [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent): +Let’s look at how a contract would integrate [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent): ```rust #[starknet::contract] @@ -285,7 +285,7 @@ constants declared in the component, customizing its functionality. The Immutable Component Config standard is defined in the SRC-107. -Here’s an example of how to use the Immutable Component Config pattern with the [ERC2981Component](/contracts-cairo/2.x/api/token_common#erc2981component): +Here’s an example of how to use the Immutable Component Config pattern with the [ERC2981Component](/contracts-cairo/alpha/api/token_common#erc2981component): ```rust #[starknet::contract] @@ -393,7 +393,7 @@ on how to use this function, refer to the [validate section of the SRC-107](http Some components include dependencies of other components. Contracts that integrate components with dependencies must also include the component dependency. -For instance, [AccessControlComponent](/contracts-cairo/2.x/api/access#accesscontrolcomponent) depends on [SRC5Component](/contracts-cairo/2.x/api/introspection#src5component). +For instance, [AccessControlComponent](/contracts-cairo/alpha/api/access#accesscontrolcomponent) depends on [SRC5Component](/contracts-cairo/alpha/api/introspection#src5component). Creating a contract with `AccessControlComponent` should look like this: ```rust @@ -539,7 +539,7 @@ mod ERC20Pausable { use openzeppelin_security::pausable::PausableComponent; use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; // Import the ERC20 interfaces to create custom implementations - use openzeppelin_token::erc20::interface::{IERC20, IERC20CamelOnly}; + use openzeppelin_interfaces::erc20::{IERC20, IERC20CamelOnly}; use starknet::ContractAddress; component!(path: PausableComponent, storage: pausable, event: PausableEvent); @@ -567,7 +567,7 @@ mod ERC20Pausable { The first thing to notice is that the contract imports the interfaces of the implementations that will be customized. These will be used in the next code example. -Next, the contract includes the [ERC20Component](/contracts-cairo/2.x/api/erc20#erc20component) implementations; however, `ERC20Impl` and `ERC20CamelOnlyImpl` are **not** embedded. +Next, the contract includes the [ERC20Component](/contracts-cairo/alpha/api/erc20#erc20component) implementations; however, `ERC20Impl` and `ERC20CamelOnlyImpl` are **not** embedded. Instead, we want to expose our custom implementation of an interface. The following example shows the pausable logic integrated into the ERC20 implementations: diff --git a/content/contracts-cairo/3.x/erc1155.mdx b/content/contracts-cairo/3.x/erc1155.mdx index d3e05494..45ed4c4a 100644 --- a/content/contracts-cairo/3.x/erc1155.mdx +++ b/content/contracts-cairo/3.x/erc1155.mdx @@ -8,11 +8,11 @@ The ERC1155 library implements an approximation of [EIP-1155](https://eips.ether ## Multi Token Standard The distinctive feature of ERC1155 is that it uses a single smart contract to represent multiple tokens at once. This -is why its [balance_of](/contracts-cairo/2.x/api/erc1155#IERC1155-balance_of) function differs from ERC20’s and ERC777’s: it has an additional ID argument for the +is why its [balance_of](/contracts-cairo/alpha/api/erc1155#IERC1155-balance_of) function differs from ERC20’s and ERC777’s: it has an additional ID argument for the identifier of the token that you want to query the balance of. This is similar to how ERC721 does things, but in that standard a token ID has no concept of balance: each token is -non-fungible and exists or doesn’t. The ERC721 [balance_of](/contracts-cairo/2.x/api/erc721#IERC721-balance_of) function refers to how many different tokens an account +non-fungible and exists or doesn’t. The ERC721 [balance_of](/contracts-cairo/alpha/api/erc721#IERC721-balance_of) function refers to how many different tokens an account has, not how many of each. On the other hand, in ERC1155 accounts have a distinct balance for each token ID, and non-fungible tokens are implemented by simply minting a single one of them. @@ -76,8 +76,8 @@ mod MyERC1155 { ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC1155Component](/contracts-cairo/2.x/api/erc1155#ERC1155Component). -The interface includes the [IERC1155](/contracts-cairo/2.x/api/erc1155#IERC1155) standard interface and the optional [IERC1155MetadataURI](/contracts-cairo/2.x/api/erc1155#IERC1155MetadataURI) interface together with [ISRC5](/contracts-cairo/2.x/api/introspection#ISRC5). +The following interface represents the full ABI of the Contracts for Cairo [ERC1155Component](/contracts-cairo/alpha/api/erc1155#ERC1155Component). +The interface includes the [IERC1155](/contracts-cairo/alpha/api/erc1155#IERC1155) standard interface and the optional [IERC1155MetadataURI](/contracts-cairo/alpha/api/erc1155#IERC1155MetadataURI) interface together with [ISRC5](/contracts-cairo/alpha/api/introspection#ISRC5). To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. @@ -148,13 +148,13 @@ Although Starknet is not EVM compatible, this implementation aims to be as close ## Batch operations -Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, [balance_of_batch](/contracts-cairo/2.x/api/erc1155#IERC1155-balance_of_batch) and [safe_batch_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_batch_transfer_from), that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive. We also have [safe_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_transfer_from) for non-batch operations. +Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, [balance_of_batch](/contracts-cairo/alpha/api/erc1155#IERC1155-balance_of_batch) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from), that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive. We also have [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) for non-batch operations. In the spirit of the standard, we’ve also included batch operations in the non-standard functions, such as -[batch_mint_with_acceptance_check](/contracts-cairo/2.x/api/erc1155#ERC1155Component-batch_mint_with_acceptance_check). +[batch_mint_with_acceptance_check](/contracts-cairo/alpha/api/erc1155#ERC1155Component-batch_mint_with_acceptance_check). -While [safe_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_batch_transfer_from) prevent loss by checking the receiver can handle the +While [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from) prevent loss by checking the receiver can handle the tokens, this yields execution to the receiver which can result in a [reentrant call](./security#reentrancy-guard). @@ -185,8 +185,8 @@ pub trait IERC1155Receiver { } ``` -Implementing the `IERC1155Receiver` interface exposes the [on_erc1155_received](/contracts-cairo/2.x/api/erc1155#IERC1155Receiver-on_erc1155_received) and [on_erc1155_batch_received](/contracts-cairo/2.x/api/erc1155#IERC1155Receiver-on_erc1155_batch_received) methods. -When [safe_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/2.x/api/erc1155#IERC1155-safe_batch_transfer_from) are called, they invoke the recipient contract’s `on_erc1155_received` or `on_erc1155_batch_received` methods respectively which **must** return the [IERC1155Receiver interface ID](/contracts-cairo/2.x/api/erc1155#IERC1155Receiver). +Implementing the `IERC1155Receiver` interface exposes the [on_erc1155_received](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver-on_erc1155_received) and [on_erc1155_batch_received](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver-on_erc1155_batch_received) methods. +When [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from) are called, they invoke the recipient contract’s `on_erc1155_received` or `on_erc1155_batch_received` methods respectively which **must** return the [IERC1155Receiver interface ID](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver). Otherwise, the transaction will fail. diff --git a/content/contracts-cairo/3.x/erc20.mdx b/content/contracts-cairo/3.x/erc20.mdx index 601d4902..4c0c5ba1 100644 --- a/content/contracts-cairo/3.x/erc20.mdx +++ b/content/contracts-cairo/3.x/erc20.mdx @@ -68,8 +68,8 @@ For a more complete guide on ERC20 token mechanisms, see [Creating ERC20 Supply] ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC20Component](/contracts-cairo/2.x/api/erc20#ERC20Component). -The interface includes the [IERC20](/contracts-cairo/2.x/api/erc20#IERC20) standard interface as well as the optional [IERC20Metadata](/contracts-cairo/2.x/api/erc20#IERC20Metadata). +The following interface represents the full ABI of the Contracts for Cairo [ERC20Component](/contracts-cairo/alpha/api/erc20#ERC20Component). +The interface includes the [IERC20](/contracts-cairo/alpha/api/erc20#IERC20) standard interface as well as the optional [IERC20Metadata](/contracts-cairo/alpha/api/erc20#IERC20Metadata). To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes an implementation of the interface written in camelCase. @@ -181,7 +181,7 @@ Note that we are not using the MixinImpl or the DefaultConfig in this case, sinc ```rust #[starknet::contract] mod MyToken { - use openzeppelin_token::erc20::interface as interface; + use openzeppelin_interfaces::erc20 as interface; use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; use starknet::ContractAddress; diff --git a/content/contracts-cairo/3.x/erc4626.mdx b/content/contracts-cairo/3.x/erc4626.mdx index e22f162e..bcacf612 100644 --- a/content/contracts-cairo/3.x/erc4626.mdx +++ b/content/contracts-cairo/3.x/erc4626.mdx @@ -174,13 +174,13 @@ $\delta = 6$, $a_0 = 1$, $a_1 = 10^5$ ### Custom behavior: Adding fees to the vault In ERC4626 vaults, fees can be captured during deposit/mint and/or withdraw/redeem operations. It is essential to remain -compliant with the ERC4626 requirements regarding the preview functions. Fees are calculated through the [FeeConfigTrait](/contracts-cairo/2.x/api/erc20#ERC4626Component-FeeConfigTrait) +compliant with the ERC4626 requirements regarding the preview functions. Fees are calculated through the [FeeConfigTrait](/contracts-cairo/alpha/api/erc20#ERC4626Component-FeeConfigTrait) implementation. By default, the ERC4626 component charges no fees. If this is the desired behavior, you can use the default [ERC4626DefaultNoFees](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/token/src/erc20/extensions/erc4626/erc4626.cairo#L899) implementation. Starting from v3.0.0, fees can be charged in either assets or shares. Prior versions only supported fees taken in assets. -See the updated [FeeConfigTrait](/contracts-cairo/2.x/api/erc20#ERC4626Component-FeeConfigTrait) and implementation examples in [ERC4626 mocks](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo). +See the updated [FeeConfigTrait](/contracts-cairo/alpha/api/erc20#ERC4626Component-FeeConfigTrait) and implementation examples in [ERC4626 mocks](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo). For example, if calling `deposit(100, receiver)`, the caller should deposit exactly 100 underlying tokens, including fees, and the receiver should receive a number of shares that matches the value returned by `preview_deposit(100)`. @@ -204,7 +204,7 @@ proportional to the deposited/withdrawn amount can be implemented: #[starknet::contract] #[with_components(./erc20, ERC4626)] pub mod ERC4626Fees { - use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; + use openzeppelin_interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; use openzeppelin_token::erc20::extensions::erc4626::ERC4626Component::{Fee, FeeConfigTrait}; use openzeppelin_token::erc20::extensions::erc4626::{ DefaultConfig, ERC4626DefaultNoLimits, ERC4626SelfAssetsManagement, @@ -379,8 +379,8 @@ pub mod ERC4626Fees { ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC4626Component](/contracts-cairo/2.x/api/erc20#ERC4626Component). -The full interface includes the [IERC4626](/contracts-cairo/2.x/api/erc20#IERC4626), [IERC20](/contracts-cairo/2.x/api/erc20#IERC20), and [IERC20Metadata](/contracts-cairo/2.x/api/erc20#IERC20Metadata) interfaces. +The following interface represents the full ABI of the Contracts for Cairo [ERC4626Component](/contracts-cairo/alpha/api/erc20#ERC4626Component). +The full interface includes the [IERC4626](/contracts-cairo/alpha/api/erc20#IERC4626), [IERC20](/contracts-cairo/alpha/api/erc20#IERC20), and [IERC20Metadata](/contracts-cairo/alpha/api/erc20#IERC20Metadata) interfaces. Note that implementing the IERC20Metadata interface is a requirement of IERC4626. ```rust diff --git a/content/contracts-cairo/3.x/erc721.mdx b/content/contracts-cairo/3.x/erc721.mdx index cee0e614..a2310b39 100644 --- a/content/contracts-cairo/3.x/erc721.mdx +++ b/content/contracts-cairo/3.x/erc721.mdx @@ -61,8 +61,8 @@ mod MyNFT { ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC721Component](/contracts-cairo/2.x/api/erc721#ERC721Component). -The interface includes the [IERC721](/contracts-cairo/2.x/api/erc721#IERC721) standard interface and the optional [IERC721Metadata](/contracts-cairo/2.x/api/erc721#IERC721Metadata) interface. +The following interface represents the full ABI of the Contracts for Cairo [ERC721Component](/contracts-cairo/alpha/api/erc721#ERC721Component). +The interface includes the [IERC721](/contracts-cairo/alpha/api/erc721#IERC721) standard interface and the optional [IERC721Metadata](/contracts-cairo/alpha/api/erc721#IERC721Metadata) interface. To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. @@ -126,7 +126,7 @@ The design for `SRC5` is similar to OpenZeppelin’s [ERC165Storage](https://doc ## Token transfers -This library includes [transfer_from](/contracts-cairo/2.x/api/erc721#IERC721-transfer_from) and [safe_transfer_from](/contracts-cairo/2.x/api/erc721#IERC721-safe_transfer_from) to transfer NFTs. +This library includes [transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-transfer_from) and [safe_transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-safe_transfer_from) to transfer NFTs. If using `transfer_from`, **the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.** The `safe_transfer_from` method mitigates this risk by querying the recipient contract’s interface support. @@ -153,8 +153,8 @@ pub trait IERC721Receiver { } ``` -Implementing the `IERC721Receiver` interface exposes the [on_erc721_received](/contracts-cairo/2.x/api/erc721#IERC721Receiver-on_erc721_received) method. -When safe methods such as [safe_transfer_from](/contracts-cairo/2.x/api/erc721#IERC721-safe_transfer_from) and [safe_mint](/contracts-cairo/2.x/api/erc721#ERC721-safe_mint) are called, they invoke the recipient contract’s `on_erc721_received` method which **must** return the [IERC721Receiver interface ID](/contracts-cairo/2.x/api/erc721#IERC721Receiver). +Implementing the `IERC721Receiver` interface exposes the [on_erc721_received](/contracts-cairo/alpha/api/erc721#IERC721Receiver-on_erc721_received) method. +When safe methods such as [safe_transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-safe_transfer_from) and [safe_mint](/contracts-cairo/alpha/api/erc721#ERC721-safe_mint) are called, they invoke the recipient contract’s `on_erc721_received` method which **must** return the [IERC721Receiver interface ID](/contracts-cairo/alpha/api/erc721#IERC721Receiver). Otherwise, the transaction will fail. @@ -209,5 +209,5 @@ mod MyTokenReceiver { ## Storing ERC721 URIs Token URIs were previously stored as single field elements prior to Cairo v0.2.5. -ERC721Component now stores only the base URI as a `ByteArray` and the full token URI is returned as the `ByteArray` concatenation of the base URI and the token ID through the [token_uri](/contracts-cairo/2.x/api/erc721#IERC721Metadata-token_uri) method. +ERC721Component now stores only the base URI as a `ByteArray` and the full token URI is returned as the `ByteArray` concatenation of the base URI and the token ID through the [token_uri](/contracts-cairo/alpha/api/erc721#IERC721Metadata-token_uri) method. This design mirrors OpenZeppelin’s default [Solidity implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/932fddf69a699a9a80fd2396fd1a2ab91cdda123/contracts/token/ERC721/ERC721.sol#L85-L93) for ERC721. diff --git a/content/contracts-cairo/3.x/finance.mdx b/content/contracts-cairo/3.x/finance.mdx index 50e91a4e..124efeb6 100644 --- a/content/contracts-cairo/3.x/finance.mdx +++ b/content/contracts-cairo/3.x/finance.mdx @@ -6,8 +6,8 @@ This module includes primitives for financial systems. ## Vesting component -The [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent) manages the gradual release of ERC-20 tokens to a designated beneficiary based on a predefined vesting schedule. -The implementing contract must implement the [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent), where the contract owner is regarded as the vesting beneficiary. +The [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) manages the gradual release of ERC-20 tokens to a designated beneficiary based on a predefined vesting schedule. +The implementing contract must implement the [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent), where the contract owner is regarded as the vesting beneficiary. This structure allows ownership rights of both the contract and the vested tokens to be assigned and transferred. @@ -22,26 +22,26 @@ for a beneficiary until a specified date. ### Vesting schedule -The [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait defines the logic for calculating the vested amount based on a given timestamp. This -logic is not part of the [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent), so any contract implementing the [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent) must provide its own -implementation of the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait. +The [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait defines the logic for calculating the vested amount based on a given timestamp. This +logic is not part of the [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent), so any contract implementing the [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) must provide its own +implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait. -There’s a ready-made implementation of the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait available named [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule). +There’s a ready-made implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait available named [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule). It incorporates a cliff period by returning 0 vested amount until the cliff ends. After the cliff, the vested amount is calculated as directly proportional to the time elapsed since the beginning of the vesting schedule. ### Usage -The contract must integrate [VestingComponent](/contracts-cairo/2.x/api/finance#VestingComponent) and [OwnableComponent](/contracts-cairo/2.x/api/access#OwnableComponent) as dependencies. The contract’s constructor +The contract must integrate [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) and [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent) as dependencies. The contract’s constructor should initialize both components. Core vesting parameters, such as `beneficiary`, `start`, `duration` and `cliff_duration`, are passed as arguments to the constructor and set at the time of deployment. -The implementing contract must provide an implementation of the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait. This can be achieved either by importing -a ready-made [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule) implementation or by defining a custom one. +The implementing contract must provide an implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait. This can be achieved either by importing +a ready-made [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule) implementation or by defining a custom one. -Here’s an example of a simple vesting wallet contract with a [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule), where the vested amount +Here’s an example of a simple vesting wallet contract with a [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule), where the vested amount is calculated as being directly proportional to the time elapsed since the start of the vesting period. ```rust @@ -93,19 +93,19 @@ mod LinearVestingWallet { } ``` -A vesting schedule will often follow a custom formula. In such cases, the [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) trait is useful. +A vesting schedule will often follow a custom formula. In such cases, the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait is useful. To support a custom vesting schedule, the contract must provide an implementation of the -[calculate_vested_amount](/contracts-cairo/2.x/api/finance#VestingComponent-calculate_vested_amount) function based on the desired formula. +[calculate_vested_amount](/contracts-cairo/alpha/api/finance#VestingComponent-calculate_vested_amount) function based on the desired formula. -When using a custom [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) implementation, the [LinearVestingSchedule](/contracts-cairo/2.x/api/finance#LinearVestingSchedule) must be excluded from the imports. +When using a custom [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) implementation, the [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule) must be excluded from the imports. If there are additional parameters required for calculations, which are stored in the contract’s storage, you can access them using `self.get_contract()`. -Here’s an example of a vesting wallet contract with a custom [VestingSchedule](/contracts-cairo/2.x/api/finance#VestingComponent-Vesting-Schedule) implementation, where tokens +Here’s an example of a vesting wallet contract with a custom [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) implementation, where tokens are vested in a number of steps. ```rust diff --git a/content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx b/content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx index 022c2c47..fbabbc32 100644 --- a/content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx +++ b/content/contracts-cairo/3.x/guides/interfaces-and-dispatchers.mdx @@ -4,18 +4,23 @@ title: 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`. +Interfaces can be found in the `openzeppelin_interfaces` package modules, such as `openzeppelin_interfaces::erc20`. + + +Starting from version `3.x.x`, OpenZeppelin Contracts for Cairo interfaces have been separated from their implementation modules into a dedicated package. +This architectural change brings several important benefits. Check the [Interfaces module](../interfaces) section for more information. + For example: ```rust -use openzeppelin_token::erc20::interface::IERC20; +use openzeppelin_interfaces::erc20::IERC20; ``` or ```rust -use openzeppelin_token::erc20::interface::ERC20ABI; +use openzeppelin_interfaces::erc20::ERC20ABI; ``` @@ -88,7 +93,7 @@ pub trait ERC20ABI { 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: ```rust -use openzeppelin_token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use openzeppelin_interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; ``` Other types of dispatchers are also auto-generated from the annotated trait. See the diff --git a/content/contracts-cairo/3.x/guides/snip12.mdx b/content/contracts-cairo/3.x/guides/snip12.mdx index 98ac1f4b..a86aae6b 100644 --- a/content/contracts-cairo/3.x/guides/snip12.mdx +++ b/content/contracts-cairo/3.x/guides/snip12.mdx @@ -9,7 +9,7 @@ is then to ensure that the received message was indeed signed by the expected si OpenZeppelin Contracts for Cairo provides a set of utilities to make the implementation of this standard as easy as possible, and in this guide we will walk you through the process of generating the hashes of typed messages -using these utilities for on-chain signature verification. For that, let’s build an example with a custom [ERC20](/contracts-cairo/2.x/api/erc20#ERC20) contract +using these utilities for on-chain signature verification. For that, let’s build an example with a custom [ERC20](/contracts-cairo/alpha/api/erc20#ERC20) contract adding an extra `transfer_with_signature` method. @@ -18,7 +18,7 @@ This is an educational example, and it is not intended to be used in production ## CustomERC20 -Let’s start with a basic ERC20 contract leveraging the [ERC20Component](/contracts-cairo/2.x/api/erc20#ERC20Component), and let’s add the new function. +Let’s start with a basic ERC20 contract leveraging the [ERC20Component](/contracts-cairo/alpha/api/erc20#ERC20Component), and let’s add the new function. Note that some declarations are omitted for brevity. The full example will be available at the end of the guide. ```rust @@ -229,8 +229,8 @@ The expected parameter for the `get_message_hash` function is the address of acc Finally, the full implementation of the `CustomERC20` contract looks like this: -We are using the [`ISRC6Dispatcher`](/contracts-cairo/2.x/api/account#ISRC6) to verify the signature, -and the [`NoncesComponent`](/contracts-cairo/2.x/api/utilities#NoncesComponent) to handle nonces to prevent replay attacks. +We are using the [`ISRC6Dispatcher`](/contracts-cairo/alpha/api/account#ISRC6) to verify the signature, +and the [`NoncesComponent`](/contracts-cairo/alpha/api/utilities#NoncesComponent) to handle nonces to prevent replay attacks. ```rust @@ -259,7 +259,7 @@ impl StructHashImpl of StructHash { #[starknet::contract] mod CustomERC20 { - use openzeppelin_account::interface::{ISRC6Dispatcher, ISRC6DispatcherTrait}; + use openzeppelin_interfaces::accounts::{ISRC6Dispatcher, ISRC6DispatcherTrait}; use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; use openzeppelin_utils::cryptography::nonces::NoncesComponent; use starknet::ContractAddress; diff --git a/content/contracts-cairo/3.x/index.mdx b/content/contracts-cairo/3.x/index.mdx index d78d99d5..9c768d64 100644 --- a/content/contracts-cairo/3.x/index.mdx +++ b/content/contracts-cairo/3.x/index.mdx @@ -8,8 +8,8 @@ title: Contracts for Cairo **A library for secure smart contract development** written in Cairo for [Starknet][starknet]. This library consists of a set of -[reusable components](/contracts-cairo/2.x/components) to build custom smart contracts, as well as ready-to-deploy [presets](/contracts-cairo/2.x/presets). You can also -find other [utilities](/contracts-cairo/2.x/api/utilities) including [interfaces and dispatchers](/contracts-cairo/alpha/interfaces) and [test utilities](/contracts-cairo/2.x/api/testing) +[reusable components](/contracts-cairo/alpha/components) to build custom smart contracts, as well as ready-to-deploy [presets](/contracts-cairo/alpha/presets). You can also +find other [utilities](/contracts-cairo/alpha/api/utilities) including [interfaces and dispatchers](/contracts-cairo/alpha/interfaces) and [test utilities](/contracts-cairo/alpha/api/testing) that facilitate testing with Starknet Foundry. @@ -24,8 +24,8 @@ before proceeding, and run the following command to check that the installation ```bash $ scarb --version -scarb 2.12.0 (639d0a65e 2025-08-04) -cairo: 2.12.0 (https://crates.io/crates/cairo-lang-compiler/2.12.0) +scarb 2.12.2 (dc0dbfd50 2025-09-15) +cairo: 2.12.2 (https://crates.io/crates/cairo-lang-compiler/2.12.2) sierra: 1.7.0 ``` @@ -53,6 +53,16 @@ Scarb.toml src ### Install the library + + +The `openzeppelin` package is an umbrella (meta) package that aggregates all library subpackages. Prior to v3.x, the umbrella and its subpackages were +versioned in lockstep—their versions always matched. Starting with v3.x, following the introduction of `openzeppelin_interfaces`, the umbrella is +versioned independently from some of the subpackages. + +See the [Versioning of the sub-packages](/contracts-cairo#versioning-of-the-sub-packages) section for more information. + + + Install the library by declaring it as a dependency in the project’s `Scarb.toml` file: ```javascript @@ -66,11 +76,35 @@ improve the building time by not including modules that won’t be used: ```toml [dependencies] openzeppelin_access = "{{umbrella_version}}" +openzeppelin_token = "{{umbrella_version}}" +openzeppelin_interfaces = "{{openzeppelin_interfaces_version}}" +``` + +## Versioning of the sub-packages + +Here you can find a reference of the versioning of the sub-packages for this umbrella version: + +```javascript +[dependencies] +openzeppelin_access = "{{umbrella_version}}" +openzeppelin_token = "{{umbrella_version}}" +openzeppelin_access = "{{umbrella_version}}" +openzeppelin_account = "{{umbrella_version}}" +openzeppelin_finance = "{{umbrella_version}}" +openzeppelin_interfaces = "{{openzeppelin_interfaces_version}}" +openzeppelin_governance = "{{umbrella_version}}" +openzeppelin_introspection = "{{umbrella_version}}" +openzeppelin_merkle_tree = "{{umbrella_version}}" +openzeppelin_presets = "{{umbrella_version}}" +openzeppelin_security = "{{umbrella_version}}" +openzeppelin_token = "{{umbrella_version}}" +openzeppelin_upgrades = "{{umbrella_version}}" +openzeppelin_utils = "{{openzeppelin_utils_version}}" ``` ## Basic usage -This is how it looks to build an ERC20 contract using the [ERC20 component](/contracts-cairo/2.x/erc20). +This is how it looks to build an ERC20 contract using the [ERC20 component](/contracts-cairo/alpha/erc20). Copy the code into `src/lib.cairo`. ```rust diff --git a/content/contracts-cairo/alpha/interfaces.mdx b/content/contracts-cairo/3.x/interfaces.mdx similarity index 100% rename from content/contracts-cairo/alpha/interfaces.mdx rename to content/contracts-cairo/3.x/interfaces.mdx diff --git a/content/contracts-cairo/3.x/introspection.mdx b/content/contracts-cairo/3.x/introspection.mdx index da51ffd1..09fd89b4 100644 --- a/content/contracts-cairo/3.x/introspection.mdx +++ b/content/contracts-cairo/3.x/introspection.mdx @@ -16,7 +16,7 @@ which can be used by others to query if a given interface is supported. ### Usage -To expose this functionality, the contract must implement the [SRC5Component](/contracts-cairo/2.x/api/introspection#SRC5Component), which defines the `supports_interface` function. +To expose this functionality, the contract must implement the [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component), which defines the `supports_interface` function. Here is an example contract: ```rust @@ -76,7 +76,7 @@ For a contract to declare its support for a given interface, we recommend using ```rust #[starknet::contract] mod MyContract { - use openzeppelin_account::interface as interface; + use openzeppelin_interfaces::accounts as interface; use openzeppelin_introspection::src5::SRC5Component; component!(path: SRC5Component, storage: src5, event: SRC5Event); @@ -115,9 +115,9 @@ Use the `supports_interface` function to query a contract’s support for a give ```rust #[starknet::contract] mod MyContract { - use openzeppelin_account::interface as interface; - use openzeppelin_introspection::interface::ISRC5DispatcherTrait; - use openzeppelin_introspection::interface::ISRC5Dispatcher; + use openzeppelin_interfaces::accounts as interface; + use openzeppelin_interfaces::introspection::ISRC5DispatcherTrait; + use openzeppelin_interfaces::introspection::ISRC5Dispatcher; use starknet::ContractAddress; #[storage] diff --git a/content/contracts-cairo/3.x/macros/with_components.mdx b/content/contracts-cairo/3.x/macros/with_components.mdx index d8bbd7ba..ba7a6dee 100644 --- a/content/contracts-cairo/3.x/macros/with_components.mdx +++ b/content/contracts-cairo/3.x/macros/with_components.mdx @@ -33,7 +33,7 @@ This is how a contract with multiple components looks when using the macro. #[with_components(Account, SRC5, SRC9, Upgradeable)] #[starknet::contract(account)] mod OutsideExecutionAccountUpgradeable { - use openzeppelin_upgrades::interface::IUpgradeable; + use openzeppelin_interfaces::upgrades::IUpgradeable; use starknet::{ClassHash, ContractAddress}; // External diff --git a/content/contracts-cairo/3.x/presets.mdx b/content/contracts-cairo/3.x/presets.mdx index 082c1c0d..5511457f 100644 --- a/content/contracts-cairo/3.x/presets.mdx +++ b/content/contracts-cairo/3.x/presets.mdx @@ -25,13 +25,13 @@ Before version 2.x, class hashes were computed using the `scarb --dev` profile. | Name | Sierra Class Hash | | --- | --- | -| [`AccountUpgradeable`](/contracts-cairo/2.x/api/account#AccountUpgradeable) | `{{AccountUpgradeableClassHash}}` | -| [`ERC20Upgradeable`](/contracts-cairo/2.x/api/erc20#ERC20Upgradeable) | `{{ERC20UpgradeableClassHash}}` | -| [`ERC721Upgradeable`](/contracts-cairo/2.x/api/erc721#ERC721Upgradeable) | `{{ERC721UpgradeableClassHash}}` | -| [`ERC1155Upgradeable`](/contracts-cairo/2.x/api/erc1155#ERC1155Upgradeable) | `{{ERC1155UpgradeableClassHash}}` | -| [`EthAccountUpgradeable`](/contracts-cairo/2.x/api/account#EthAccountUpgradeable) | `{{EthAccountUpgradeableClassHash}}` | -| [`UniversalDeployer`](/contracts-cairo/2.x/api/udc#UniversalDeployer) | `{{UniversalDeployerClassHash}}` | -| [`VestingWallet`](/contracts-cairo/2.x/api/finance#VestingWallet) | `{{VestingWalletClassHash}}` | +| [`AccountUpgradeable`](/contracts-cairo/alpha/api/account#AccountUpgradeable) | `{{AccountUpgradeableClassHash}}` | +| [`ERC20Upgradeable`](/contracts-cairo/alpha/api/erc20#ERC20Upgradeable) | `{{ERC20UpgradeableClassHash}}` | +| [`ERC721Upgradeable`](/contracts-cairo/alpha/api/erc721#ERC721Upgradeable) | `{{ERC721UpgradeableClassHash}}` | +| [`ERC1155Upgradeable`](/contracts-cairo/alpha/api/erc1155#ERC1155Upgradeable) | `{{ERC1155UpgradeableClassHash}}` | +| [`EthAccountUpgradeable`](/contracts-cairo/alpha/api/account#EthAccountUpgradeable) | `{{EthAccountUpgradeableClassHash}}` | +| [`UniversalDeployer`](/contracts-cairo/alpha/api/udc#UniversalDeployer) | `{{UniversalDeployerClassHash}}` | +| [`VestingWallet`](/contracts-cairo/alpha/api/finance#VestingWallet) | `{{VestingWalletClassHash}}` | [starkli](https://book.starkli.rs/introduction) class-hash command can be used to compute the class hash from a Sierra artifact. @@ -62,7 +62,7 @@ mod ERC20Upgradeable { use openzeppelin_access::ownable::OwnableComponent; use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; use openzeppelin_upgrades::UpgradeableComponent; - use openzeppelin_upgrades::interface::IUpgradeable; + use openzeppelin_interfaces::upgrades::IUpgradeable; use starknet::{ContractAddress, ClassHash}; component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); diff --git a/content/contracts-cairo/3.x/security.mdx b/content/contracts-cairo/3.x/security.mdx index b7853675..13c32d96 100644 --- a/content/contracts-cairo/3.x/security.mdx +++ b/content/contracts-cairo/3.x/security.mdx @@ -10,7 +10,7 @@ Expect these modules to evolve. ## Initializable -The [Initializable](/contracts-cairo/2.x/api/security#InitializableComponent) component provides a simple mechanism that mimics +The [Initializable](/contracts-cairo/alpha/api/security#InitializableComponent) component provides a simple mechanism that mimics the functionality of a constructor. More specifically, it enables logic to be performed once and only once which is useful to set up a contract’s initial state when a constructor cannot be used, for example when there are circular dependencies at construction time. @@ -68,16 +68,16 @@ pub trait InitializableABI { ## Pausable -The [Pausable](/contracts-cairo/2.x/api/security#PausableComponent) component allows contracts to implement an emergency stop mechanism. +The [Pausable](/contracts-cairo/alpha/api/security#PausableComponent) component allows contracts to implement an emergency stop mechanism. This can be useful for scenarios such as preventing trades until the end of an evaluation period or having an emergency switch to freeze all transactions in the event of a large bug. To become pausable, the contract should include `pause` and `unpause` functions (which should be protected). -For methods that should be available only when paused or not, insert calls to `[assert_paused](/contracts-cairo/2.x/api/security#PausableComponent-assert_paused)` and `[assert_not_paused](/contracts-cairo/2.x/api/security#PausableComponent-assert_not_paused)` +For methods that should be available only when paused or not, insert calls to `[assert_paused](/contracts-cairo/alpha/api/security#PausableComponent-assert_paused)` and `[assert_not_paused](/contracts-cairo/alpha/api/security#PausableComponent-assert_not_paused)` respectively. ### Usage -For example (using the [Ownable](/contracts-cairo/2.x/api/access#OwnableComponent) component for access control): +For example (using the [Ownable](/contracts-cairo/alpha/api/access#OwnableComponent) component for access control): ```rust #[starknet::contract] @@ -164,8 +164,8 @@ A [reentrancy attack](https://gus-tavo-guim.medium.com/reentrancy-attack-on-smar ### Usage -Since Cairo does not support modifiers like Solidity, the [ReentrancyGuard](/contracts-cairo/2.x/api/security#ReentrancyGuardComponent) -component exposes two methods `[start](/contracts-cairo/2.x/api/security#ReentrancyGuardComponent-start)` and `[end](/contracts-cairo/2.x/api/security#ReentrancyGuardComponent-end)` to protect functions against reentrancy attacks. +Since Cairo does not support modifiers like Solidity, the [ReentrancyGuard](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent) +component exposes two methods `[start](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent-start)` and `[end](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent-end)` to protect functions against reentrancy attacks. The protected function must call `start` before the first function statement, and `end` before the return statement, as shown below: ```rust diff --git a/content/contracts-cairo/3.x/udc.mdx b/content/contracts-cairo/3.x/udc.mdx index 0b85d46e..dd25f5e8 100644 --- a/content/contracts-cairo/3.x/udc.mdx +++ b/content/contracts-cairo/3.x/udc.mdx @@ -4,7 +4,7 @@ title: Universal Deployer Contract The Universal Deployer Contract (UDC) is a singleton smart contract that wraps the [deploy syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) to expose it to any contract that doesn’t implement it, such as account contracts. You can think of it as a standardized generic factory for Starknet contracts. -Since Starknet has no deployment transaction type, it offers a standardized way to deploy smart contracts by following the [Standard Deployer Interface](https://community.starknet.io/t/snip-deployer-contract-interface/2772) and emitting a [ContractDeployed](/contracts-cairo/2.x/api/udc#IUniversalDeployer-ContractDeployed) event. +Since Starknet has no deployment transaction type, it offers a standardized way to deploy smart contracts by following the [Standard Deployer Interface](https://community.starknet.io/t/snip-deployer-contract-interface/2772) and emitting a [ContractDeployed](/contracts-cairo/alpha/api/udc#IUniversalDeployer-ContractDeployed) event. For details on the motivation and the decision making process, see the [Universal Deployer Contract proposal](https://community.starknet.io/t/universal-deployer-contract-proposal/1864). @@ -76,7 +76,7 @@ By making deployments dependent upon the origin address, users can reserve a who Only the owner of the origin address can deploy to those addresses. -Achieving this type of deployment necessitates that the origin sets `not_from_zero` to `true` in the [deploy_contract](/contracts-cairo/2.x/api/udc#UniversalDeployer-deploy_contract) call. +Achieving this type of deployment necessitates that the origin sets `not_from_zero` to `true` in the [deploy_contract](/contracts-cairo/alpha/api/udc#UniversalDeployer-deploy_contract) call. Under the hood, the function passes a modified salt to the `deploy_syscall`, which is the hash of the origin’s address with the given salt. To deploy a unique contract address pass: @@ -104,11 +104,11 @@ See the [previous Universal Deployer API](https://docs.starknet.io/learn/protoco The latest iteration of the UDC includes some notable changes to the API which include: -* `deployContract` method is replaced with the snake_case [deploy_contract](/contracts-cairo/2.x/api/udc#UniversalDeployer-deploy_contract). -* `unique` parameter is replaced with `not_from_zero` in both the `deploy_contract` method and [ContractDeployed](/contracts-cairo/2.x/api/udc#IUniversalDeployer-ContractDeployed) event. +* `deployContract` method is replaced with the snake_case [deploy_contract](/contracts-cairo/alpha/api/udc#UniversalDeployer-deploy_contract). +* `unique` parameter is replaced with `not_from_zero` in both the `deploy_contract` method and [ContractDeployed](/contracts-cairo/alpha/api/udc#IUniversalDeployer-ContractDeployed) event. ## Precomputing contract addresses This library offers utility functions written in Cairo to precompute contract addresses. -They include the generic [calculate_contract_address_from_deploy_syscall](/contracts-cairo/2.x/api/utilities#deployments-calculate_contract_address_from_deploy_syscall) as well as the UDC-specific [calculate_contract_address_from_udc](/contracts-cairo/2.x/api/utilities#deployments-calculate_contract_address_from_udc). -Check out the [deployments](/contracts-cairo/2.x/api/utilities#deployments) for more information. +They include the generic [calculate_contract_address_from_deploy_syscall](/contracts-cairo/alpha/api/utilities#deployments-calculate_contract_address_from_deploy_syscall) as well as the UDC-specific [calculate_contract_address_from_udc](/contracts-cairo/alpha/api/utilities#deployments-calculate_contract_address_from_udc). +Check out the [deployments](/contracts-cairo/alpha/api/utilities#deployments) for more information. diff --git a/content/contracts-cairo/3.x/upgrades.mdx b/content/contracts-cairo/3.x/upgrades.mdx index 7765a01d..811738cd 100644 --- a/content/contracts-cairo/3.x/upgrades.mdx +++ b/content/contracts-cairo/3.x/upgrades.mdx @@ -47,7 +47,7 @@ Upgrades are often very sensitive operations, and some form of access control is avoid unauthorized upgrades. The [Ownable](./access#ownership-and-ownable) module is used in this example. -We will be using the following module to implement the [IUpgradeable](/contracts-cairo/2.x/api/upgrades#IUpgradeable) interface described in the API Reference section. +We will be using the following module to implement the [IUpgradeable](/contracts-cairo/alpha/api/upgrades#IUpgradeable) interface described in the API Reference section. ```rust @@ -55,7 +55,7 @@ We will be using the following module to implement the [IUpgradeable](/contracts mod UpgradeableContract { use openzeppelin_access::ownable::OwnableComponent; use openzeppelin_upgrades::UpgradeableComponent; - use openzeppelin_upgrades::interface::IUpgradeable; + use openzeppelin_interfaces::upgrades::IUpgradeable; use starknet::ClassHash; use starknet::ContractAddress; diff --git a/content/contracts-cairo/3.x/utils/constants.js b/content/contracts-cairo/3.x/utils/constants.js index d7da4149..d3ef174d 100644 --- a/content/contracts-cairo/3.x/utils/constants.js +++ b/content/contracts-cairo/3.x/utils/constants.js @@ -1,19 +1,21 @@ -export const UMBRELLA_VERSION = "2.0.0"; -export const CLASS_HASH_SCARB_VERSION = "2.11.4"; +export const OPENZEPPELIN_INTERFACES_VERSION = "2.1.0-alpha.0"; +export const OPENZEPPELIN_UTILS_VERSION = "2.1.0-alpha.0"; +export const UMBRELLA_VERSION = "3.0.0-alpha.3"; +export const CLASS_HASH_SCARB_VERSION = "2.12.2"; export const CLASS_HASHES = { AccountUpgradeableClassHash: - "0x079a9a12fdfa0481e8d8d46599b90226cd7247b2667358bb00636dd864002314", + "0x072b2479c3bf45bfc2391b0a04bb0fb4806b93a61a8fede391081535cad65038", ERC20UpgradeableClassHash: - "0x065daa9c6005dcbccb0571ffdf530e2e263d1ff00eac2cbd66b2d0fa0871dafa", + "0x0435835a8002b39bf6eb827678b32a75ed3e0bec580ef71a7c29a068d1a96d24", ERC721UpgradeableClassHash: - "0x06d1cd9d8c2008d36bd627e204c3e5f565d4e632de4e50b36d2388c7ba7a64ce", + "0x062212af6bc24e478b5f1c611d2f626270e3ef5825330173afa3e02a0848bcaa", ERC1155UpgradeableClassHash: - "0x036d453774916578336db8f5f18257f0211011270a5c31adf3a2bd86416943b7", + "0x01a312230aa2774b3271204bfd41e8633d3b2ab48f10f4b56be1ef17806599c4", EthAccountUpgradeableClassHash: - "0x070177fca30a0a9025465f16f8174d4ea220f61bf44cb1beecb89459fe966285", + "0x071fa21092599f6ffdaed5da83961d895668c668a488465251d88606c26a34b7", UniversalDeployerClassHash: - "0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8", + "0x038a75a9c5a0203e5fa94bb181850a4e6a349fc3fcda6a0ccbcaeb5c2f50c7c3", VestingWalletClassHash: - "0x010a786d4e5f74d68e0a500aeadbf7a81486f069c06afa242a050a1a09ac42f0", + "0x03e7d05eb2325c2e10d219f6b70468aef9e915352ac31521ec2950ebf0e3acb4", }; diff --git a/content/contracts-cairo/3.x/utils/replacements.ts b/content/contracts-cairo/3.x/utils/replacements.ts index c3a1695e..32791a2b 100644 --- a/content/contracts-cairo/3.x/utils/replacements.ts +++ b/content/contracts-cairo/3.x/utils/replacements.ts @@ -1,9 +1,11 @@ -import { CLASS_HASHES, CLASS_HASH_SCARB_VERSION, UMBRELLA_VERSION } from "./constants"; +import { CLASS_HASHES, CLASS_HASH_SCARB_VERSION, OPENZEPPELIN_INTERFACES_VERSION, OPENZEPPELIN_UTILS_VERSION, UMBRELLA_VERSION } from "./constants"; export const REPLACEMENTS = { - include: ['**/content/contracts-cairo/2.x/**/*.mdx'], + include: ['**/content/contracts-cairo/alpha/**/*.mdx'], replacements: { umbrella_version: UMBRELLA_VERSION, + openzeppelin_interfaces_version: OPENZEPPELIN_INTERFACES_VERSION, + openzeppelin_utils_version: OPENZEPPELIN_UTILS_VERSION, class_hash_scarb_version: CLASS_HASH_SCARB_VERSION, ...CLASS_HASHES, } diff --git a/content/contracts-cairo/3.x/wizard.mdx b/content/contracts-cairo/3.x/wizard.mdx index 84f581f5..7b07a3a3 100644 --- a/content/contracts-cairo/3.x/wizard.mdx +++ b/content/contracts-cairo/3.x/wizard.mdx @@ -9,4 +9,6 @@ contract and learn about the components offered in OpenZeppelin Contracts for Ca We strongly recommend checking the [Components](./components) section to understand how to extend from our library. - +import { UMBRELLA_VERSION } from "./utils/constants.js"; + + diff --git a/content/contracts-cairo/alpha/access.mdx b/content/contracts-cairo/alpha/access.mdx deleted file mode 100644 index cf78465c..00000000 --- a/content/contracts-cairo/alpha/access.mdx +++ /dev/null @@ -1,515 +0,0 @@ ---- -title: Access ---- - -Access control--that is, "who is allowed to do this thing"—is incredibly important in the world of smart contracts. -The access control of your contract may govern who can mint tokens, vote on proposals, freeze transfers, and many other things. -It is therefore critical to understand how you implement it, lest someone else -[steals your whole system](https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/). - -## Ownership and `Ownable` - -The most common and basic form of access control is the concept of ownership: there’s an account that is the `owner` -of a contract and can do administrative tasks on it. -This approach is perfectly reasonable for contracts that have a single administrative user. - -OpenZeppelin Contracts for Cairo provides [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent) for implementing ownership in your contracts. - -### Usage - -Integrating this component into a contract first requires assigning an owner. -The implementing contract’s constructor should set the initial owner by passing the owner’s address to Ownable’s -[`initializer`](/contracts-cairo/alpha/api/access#OwnableComponent-initializer) like this: - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_access::ownable::OwnableComponent; - use starknet::ContractAddress; - - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - - // Ownable Mixin - #[abi(embed_v0)] - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; - impl InternalImpl = OwnableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - ownable: OwnableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - OwnableEvent: OwnableComponent::Event - } - - #[constructor] - fn constructor(ref self: ContractState, owner: ContractAddress) { - // Set the initial owner of the contract - self.ownable.initializer(owner); - } - - (...) -} -``` - -To restrict a function’s access to the owner only, add in the `assert_only_owner` method: - -```rust -#[starknet::contract] -mod MyContract { - (...) - - #[external(v0)] - fn only_owner_allowed(ref self: ContractState) { - // This function can only be called by the owner - self.ownable.assert_only_owner(); - - (...) - } -} -``` - -### Interface - -This is the full interface of the `OwnableMixinImpl` implementation: - -```rust -#[starknet::interface] -pub trait OwnableABI { - // IOwnable - fn owner() -> ContractAddress; - fn transfer_ownership(new_owner: ContractAddress); - fn renounce_ownership(); - - // IOwnableCamelOnly - fn transferOwnership(newOwner: ContractAddress); - fn renounceOwnership(); -} -``` - -Ownable also lets you: - -* `transfer_ownership` from the owner account to a new one, and -* `renounce_ownership` for the owner to relinquish this administrative privilege, a common pattern -after an initial stage with centralized administration is over. - - -Removing the owner altogether will mean that administrative tasks that are protected by `assert_only_owner` -will no longer be callable! - - -### Two step transfer - -The component also offers a more robust way of transferring ownership via the -[OwnableTwoStepImpl](/contracts-cairo/alpha/api/access#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) implementation. A two step transfer mechanism helps -to prevent unintended and irreversible owner transfers. Simply replace the `OwnableMixinImpl` -with its respective two step variant: - -```rust -#[abi(embed_v0)] -impl OwnableTwoStepMixinImpl = OwnableComponent::OwnableTwoStepMixinImpl; -``` - -#### Interface - -This is the full interface of the two step `OwnableTwoStepMixinImpl` implementation: - -```rust -#[starknet::interface] -pub trait OwnableTwoStepABI { - // IOwnableTwoStep - fn owner() -> ContractAddress; - fn pending_owner() -> ContractAddress; - fn accept_ownership(); - fn transfer_ownership(new_owner: ContractAddress); - fn renounce_ownership(); - - // IOwnableTwoStepCamelOnly - fn pendingOwner() -> ContractAddress; - fn acceptOwnership(); - fn transferOwnership(newOwner: ContractAddress); - fn renounceOwnership(); -} -``` - -## Role-Based `AccessControl` - -While the simplicity of ownership can be useful for simple systems or quick prototyping, different levels of -authorization are often needed. You may want for an account to have permission to ban users from a system, but not -create new tokens. [Role-Based Access Control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) offers -flexibility in this regard. - -In essence, we will be defining multiple roles, each allowed to perform different sets of actions. -An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for -instead of simply using [`assert_only_owner`](/contracts-cairo/alpha/api/access#OwnableComponent-assert_only_owner). This check can be enforced through [`assert_only_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-assert_only_role). -Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. - -Most software uses access control systems that are role-based: some users are regular users, some may be supervisors -or managers, and a few will often have administrative privileges. - -### Usage - -For each role that you want to define, you will create a new _role identifier_ that is used to grant, revoke, and -check if an account has that role. See [Creating role identifiers](#creating-role-identifiers) for information -on creating identifiers. - -Here’s a simple example of implementing [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) on a portion of an ERC20 token contract which defines -and sets a 'minter' role: - -```rust -const MINTER_ROLE: felt252 = selector!("MINTER_ROLE"); - -#[starknet::contract] -mod MyContract { - use openzeppelin_access::accesscontrol::AccessControlComponent; - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; - use starknet::ContractAddress; - use super::MINTER_ROLE; - - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - // AccessControl - #[abi(embed_v0)] - impl AccessControlImpl = - AccessControlComponent::AccessControlImpl; - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // ERC20 - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - accesscontrol: AccessControlComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage, - #[substorage(v0)] - erc20: ERC20Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccessControlEvent: AccessControlComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - ERC20Event: ERC20Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - initial_supply: u256, - recipient: ContractAddress, - minter: ContractAddress - ) { - // ERC20-related initialization - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, initial_supply); - - // AccessControl-related initialization - self.accesscontrol.initializer(); - self.accesscontrol._grant_role(MINTER_ROLE, minter); - } - - /// This function can only be called by a minter. - #[external(v0)] - fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { - self.accesscontrol.assert_only_role(MINTER_ROLE); - self.erc20.mint(recipient, amount); - } -} -``` - - -Make sure you fully understand how [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) works before -using it on your system, or copy-pasting the examples from this guide. - - -While clear and explicit, this isn’t anything we wouldn’t have been able to achieve with -[Ownable](/contracts-cairo/alpha/api/access#OwnableComponent). Where [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) shines the most is in scenarios where granular -permissions are required, which can be implemented by defining _multiple_ roles. - -Let’s augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens: - -```rust -const MINTER_ROLE: felt252 = selector!("MINTER_ROLE"); -const BURNER_ROLE: felt252 = selector!("BURNER_ROLE"); - -#[starknet::contract] -mod MyContract { - use openzeppelin_access::accesscontrol::AccessControlComponent; - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; - use starknet::ContractAddress; - use super::{MINTER_ROLE, BURNER_ROLE}; - - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - // AccessControl - #[abi(embed_v0)] - impl AccessControlImpl = - AccessControlComponent::AccessControlImpl; - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // ERC20 - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - accesscontrol: AccessControlComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage, - #[substorage(v0)] - erc20: ERC20Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccessControlEvent: AccessControlComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - ERC20Event: ERC20Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - initial_supply: u256, - recipient: ContractAddress, - minter: ContractAddress, - burner: ContractAddress - ) { - // ERC20-related initialization - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, initial_supply); - - // AccessControl-related initialization - self.accesscontrol.initializer(); - self.accesscontrol._grant_role(MINTER_ROLE, minter); - self.accesscontrol._grant_role(BURNER_ROLE, burner); - } - - /// This function can only be called by a minter. - #[external(v0)] - fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { - self.accesscontrol.assert_only_role(MINTER_ROLE); - self.erc20.mint(recipient, amount); - } - - /// This function can only be called by a burner. - #[external(v0)] - fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { - self.accesscontrol.assert_only_role(BURNER_ROLE); - self.erc20.burn(account, amount); - } -} -``` - -So clean! -By splitting concerns this way, more granular levels of permission may be implemented than were possible with the -simpler ownership approach to access control. Limiting what each component of a system is able to do is known -as the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), and is a good -security practice. Note that each account may still have more than one role, if so desired. - -### Granting and revoking roles - -The ERC20 token example above uses [`_grant_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-_grant_role), -an `internal` function that is useful when programmatically assigning -roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts? - -By default, **accounts with a role cannot grant it or revoke it from other accounts**: all having a role does is making -the [`assert_only_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-assert_only_role) check pass. To grant and revoke roles dynamically, you will need help from the role’s _admin_. - -Every role has an associated admin role, which grants permission to call the -[`grant_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-grant_role) and -[`revoke_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-revoke_role) functions. -A role can be granted or revoked by using these if the calling account has the corresponding admin role. -Multiple roles may have the same admin role to make management easier. -A role’s admin can even be the same role itself, which would cause accounts with that role to be able -to also grant and revoke it. - -This mechanism can be used to create complex permissioning structures resembling organizational charts, but it also -provides an easy way to manage simpler applications. `AccessControl` includes a special role with the role identifier -of `0`, called `DEFAULT_ADMIN_ROLE`, which acts as the **default admin role for all roles**. -An account with this role will be able to manage any other role, unless -[`set_role_admin`](/contracts-cairo/alpha/api/access#AccessControlComponent-set_role_admin) is used to select a new admin role. - -Since it is the admin for all roles by default, and in fact it is also its own admin, this role carries significant risk. To mitigate this risk we provide [AccessControlDefaultAdminRules](/contracts-cairo/alpha/api/access#AccessControlDefaultAdminRulesComponent), a recommended extension of AccessControl that adds a number of enforced security measures for this role: the admin is restricted to a single account, with a 2-step transfer procedure with a delay in between steps. - -Let’s take a look at the ERC20 token example, this time taking advantage of the default admin role: - -```rust -const MINTER_ROLE: felt252 = selector!("MINTER_ROLE"); -const BURNER_ROLE: felt252 = selector!("BURNER_ROLE"); - -#[starknet::contract] -mod MyContract { - use openzeppelin_access::accesscontrol::AccessControlComponent; - use openzeppelin_access::accesscontrol::DEFAULT_ADMIN_ROLE; - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; - use starknet::ContractAddress; - use super::{MINTER_ROLE, BURNER_ROLE}; - - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - // AccessControl - #[abi(embed_v0)] - impl AccessControlImpl = - AccessControlComponent::AccessControlImpl; - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // ERC20 - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - (...) - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - initial_supply: u256, - recipient: ContractAddress, - admin: ContractAddress - ) { - // ERC20-related initialization - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, initial_supply); - - // AccessControl-related initialization - self.accesscontrol.initializer(); - self.accesscontrol._grant_role(DEFAULT_ADMIN_ROLE, admin); - } - - /// This function can only be called by a minter. - #[external(v0)] - fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { - self.accesscontrol.assert_only_role(MINTER_ROLE); - self.erc20.mint(recipient, amount); - } - - /// This function can only be called by a burner. - #[external(v0)] - fn burn(ref self: ContractState, account: ContractAddress, amount: u256) { - self.accesscontrol.assert_only_role(BURNER_ROLE); - self.erc20.burn(account, amount); - } -} -``` - - -The `grant_role` and `revoke_role` functions are automatically exposed as `external` functions -from the `AccessControlImpl` by leveraging the `#[abi(embed_v0)]` annotation. - - -Note that, unlike the previous examples, no accounts are granted the 'minter' or 'burner' roles. -However, because those roles' admin role is the default admin role, and that role was granted to the 'admin', that -same account can call `grant_role` to give minting or burning permission, and `revoke_role` to remove it. - -Dynamic role allocation is often a desirable property, for example in systems where trust in a participant may vary -over time. It can also be used to support use cases such as [KYC](https://en.wikipedia.org/wiki/Know_your_customer), -where the list of role-bearers may not be known up-front, or may be prohibitively expensive to include in a single transaction. - -### Creating role identifiers - -In the Solidity implementation of AccessControl, contracts generally refer to the -[keccak256 hash](https://docs.soliditylang.org/en/latest/units-and-global-variables.html?highlight=keccak256#mathematical-and-cryptographic-functions) -of a role as the role identifier. - -For example: - -```rust -bytes32 public constant SOME_ROLE = keccak256("SOME_ROLE") -``` - -These identifiers take up 32 bytes (256 bits). - -Cairo field elements (`felt252`) store a maximum of 252 bits. -With this discrepancy, this library maintains an agnostic stance on how contracts should create identifiers. -Some ideas to consider: - -* Use [sn_keccak](https://docs.starknet.io/learn/protocol/cryptography#starknet-keccak) instead. -* Use Cairo friendly hashing algorithms like Poseidon, which are implemented in the -[Cairo corelib](https://github.com/starkware-libs/cairo/blob/main/corelib/src/poseidon.cairo). - - -The `selector!` macro can be used to compute [sn_keccak](https://docs.starknet.io/learn/protocol/cryptography#starknet-keccak) in Cairo. - - -### Interface - -This is the full interface of the `AccessControlMixinImpl` implementation: - -```rust -#[starknet::interface] -pub trait AccessControlABI { - // IAccessControl - fn has_role(role: felt252, account: ContractAddress) -> bool; - fn get_role_admin(role: felt252) -> felt252; - fn grant_role(role: felt252, account: ContractAddress); - fn revoke_role(role: felt252, account: ContractAddress); - fn renounce_role(role: felt252, account: ContractAddress); - - // IAccessControlCamel - fn hasRole(role: felt252, account: ContractAddress) -> bool; - fn getRoleAdmin(role: felt252) -> felt252; - fn grantRole(role: felt252, account: ContractAddress); - fn revokeRole(role: felt252, account: ContractAddress); - fn renounceRole(role: felt252, account: ContractAddress); - - // ISRC5 - fn supports_interface(interface_id: felt252) -> bool; -} -``` - -`AccessControl` also lets you `renounce_role` from the calling account. -The method expects an account as input as an extra security measure, to ensure you are -not renouncing a role from an unintended account. diff --git a/content/contracts-cairo/alpha/accounts.mdx b/content/contracts-cairo/alpha/accounts.mdx deleted file mode 100644 index b699c4b9..00000000 --- a/content/contracts-cairo/alpha/accounts.mdx +++ /dev/null @@ -1,504 +0,0 @@ ---- -title: Accounts ---- - -Unlike Ethereum where accounts are derived from a private key, all Starknet accounts are contracts. This means there’s no Externally Owned Account (EOA) -concept on Starknet. - -Instead, the network features native account abstraction and signature validation happens at the contract level. - -For a general overview of account abstraction, see -[Starknet’s documentation](https://docs.starknet.io/learn/protocol/accounts). -A more detailed discussion on the topic can be found in -[Starknet Shaman’s forum](https://community.starknet.io/t/starknet-account-abstraction-model-part-1/781). - - -For detailed information on the usage and implementation check the [API Reference](/contracts-cairo/alpha/api/account) section. - - -## What is an account? - -Accounts in Starknet are smart contracts, and so they can be deployed and interacted -with like any other contract, and can be extended to implement any custom logic. However, an account is a special type -of contract that is used to validate and execute transactions. For this reason, it must implement a set of entrypoints -that the protocol uses for this execution flow. The [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) proposal defines a standard interface for accounts, -supporting this execution flow and interoperability with DApps in the ecosystem. - -### ISRC6 Interface - -```rust -/// Represents a call to a target contract function. -struct Call { - to: ContractAddress, - selector: felt252, - calldata: Span -} - -/// Standard Account Interface -#[starknet::interface] -pub trait ISRC6 { - /// Executes a transaction through the account. - fn __execute__(calls: Array); - - /// Asserts whether the transaction is valid to be executed. - fn __validate__(calls: Array) -> felt252; - - /// Asserts whether a given signature for a given hash is valid. - fn is_valid_signature(hash: felt252, signature: Array) -> felt252; -} -``` - - -The `calldata` member of the `Call` struct in the accounts has been updated to `Span` for optimization -purposes, but the interface ID remains the same for backwards compatibility. This inconsistency will be fixed in future releases. - - -[SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) adds the `is_valid_signature` method. This method is not used by the protocol, but it’s useful for -DApps to verify the validity of signatures, supporting features like Sign In with Starknet. - -SNIP-6 also defines that compliant accounts must implement the SRC5 interface following [SNIP-5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md), as -a mechanism for detecting whether a contract is an account or not through introspection. - -### ISRC5 Interface - -```rust -/// Standard Interface Detection -#[starknet::interface] -pub trait ISRC5 { - /// Queries if a contract implements a given interface. - fn supports_interface(interface_id: felt252) -> bool; -} -``` - -[SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) compliant accounts must return `true` when queried for the ISRC6 interface ID. - -Even though these interfaces are not enforced by the protocol, it’s recommended to implement them for enabling -interoperability with the ecosystem. - -### Protocol-level methods - -The Starknet protocol uses a few entrypoints for abstracting the accounts. We already mentioned the first two -as part of the ISRC6 interface, and both are required for enabling accounts to be used for executing transactions. The rest are optional: - -1. `__validate__` verifies the validity of the transaction to be executed. This is usually used to validate signatures, -but the entrypoint implementation can be customized to feature any validation mechanism [with some limitations](https://docs.starknet.io/learn/protocol/accounts#limitations). -2. `__execute__` executes the transaction if the validation is successful. -3. `__validate_declare__` optional entrypoint similar to `__validate__` but for transactions -meant to declare other contracts. -4. `__validate_deploy__` optional entrypoint similar to `__validate__` but meant for [counterfactual deployments](./guides/deployment). - - -Although these entrypoints are available to the protocol for its regular transaction flow, they can also be called like any other method. - - -## Starknet Account - -Starknet native account abstraction pattern allows for the creation of custom accounts with different validation schemes, but -usually most account implementations validate transactions using the [Stark curve](https://docs.starknet.io/learn/protocol/cryptography#the-stark-curve) which is the most efficient way -of validating signatures since it is a STARK-friendly curve. - -OpenZeppelin Contracts for Cairo provides [AccountComponent](/contracts-cairo/alpha/api/account#AccountComponent) for implementing this validation scheme. - -### Usage - -Constructing an account contract requires integrating both [AccountComponent](/contracts-cairo/alpha/api/account#AccountComponent) and [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: - -```rust -#[starknet::contract(account)] -mod MyAccount { - use openzeppelin_account::AccountComponent; - use openzeppelin_introspection::src5::SRC5Component; - - component!(path: AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // Account Mixin - #[abi(embed_v0)] - impl AccountMixinImpl = AccountComponent::AccountMixinImpl; - impl AccountInternalImpl = AccountComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - account: AccountComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccountEvent: AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState, public_key: felt252) { - self.account.initializer(public_key); - } -} -``` - -### Interface - -This is the full interface of the `AccountMixinImpl` implementation: - -```rust -#[starknet::interface] -pub trait AccountABI { - // ISRC6 - fn __execute__(calls: Array); - fn __validate__(calls: Array) -> felt252; - fn is_valid_signature(hash: felt252, signature: Array) -> felt252; - - // ISRC5 - fn supports_interface(interface_id: felt252) -> bool; - - // IDeclarer - fn __validate_declare__(class_hash: felt252) -> felt252; - - // IDeployable - fn __validate_deploy__( - class_hash: felt252, contract_address_salt: felt252, public_key: felt252 - ) -> felt252; - - // IPublicKey - fn get_public_key() -> felt252; - fn set_public_key(new_public_key: felt252, signature: Span); - - // ISRC6CamelOnly - fn isValidSignature(hash: felt252, signature: Array) -> felt252; - - // IPublicKeyCamel - fn getPublicKey() -> felt252; - fn setPublicKey(newPublicKey: felt252, signature: Span); -} -``` - -## Ethereum Account - -Besides the Stark-curve account, OpenZeppelin Contracts for Cairo also offers Ethereum-flavored accounts that use the [secp256k1](https://en.bitcoin.it/wiki/Secp256k1) curve for signature validation. -For this the [EthAccountComponent](/contracts-cairo/alpha/api/account#EthAccountComponent) must be used. - -### Usage - -Constructing a secp256k1 account contract also requires integrating both [EthAccountComponent](/contracts-cairo/alpha/api/account#EthAccountComponent) and [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component). -The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. -Here’s an example of a basic contract: - -```rust -#[starknet::contract(account)] -mod MyEthAccount { - use openzeppelin_account::EthAccountComponent; - use openzeppelin_interfaces::accounts::EthPublicKey; - use openzeppelin_introspection::src5::SRC5Component; - use starknet::ClassHash; - - component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // EthAccount Mixin - #[abi(embed_v0)] - impl EthAccountMixinImpl = - EthAccountComponent::EthAccountMixinImpl; - impl EthAccountInternalImpl = EthAccountComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - eth_account: EthAccountComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - EthAccountEvent: EthAccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState, public_key: EthPublicKey) { - self.eth_account.initializer(public_key); - } -} -``` - -### Interface - -This is the full interface of the `EthAccountMixinImpl` implementation: - -```rust -#[starknet::interface] -pub trait EthAccountABI { - // ISRC6 - fn __execute__(calls: Array); - fn __validate__(calls: Array) -> felt252; - fn is_valid_signature(hash: felt252, signature: Array) -> felt252; - - // ISRC5 - fn supports_interface(interface_id: felt252) -> bool; - - // IDeclarer - fn __validate_declare__(class_hash: felt252) -> felt252; - - // IEthDeployable - fn __validate_deploy__( - class_hash: felt252, contract_address_salt: felt252, public_key: EthPublicKey - ) -> felt252; - - // IEthPublicKey - fn get_public_key() -> EthPublicKey; - fn set_public_key(new_public_key: EthPublicKey, signature: Span); - - // ISRC6CamelOnly - fn isValidSignature(hash: felt252, signature: Array) -> felt252; - - // IEthPublicKeyCamel - fn getPublicKey() -> EthPublicKey; - fn setPublicKey(newPublicKey: EthPublicKey, signature: Span); -} - -``` - -## Deploying an account - -In Starknet there are two ways of deploying smart contracts: using the `deploy_syscall` and doing -counterfactual deployments. -The former can be easily done with the [Universal Deployer Contract (UDC)](./udc), a contract that -wraps and exposes the `deploy_syscall` to provide arbitrary deployments through regular contract calls. -But if you don’t have an account to invoke it, you will probably want to use the latter. - -To do counterfactual deployments, you need to implement another protocol-level entrypoint named -`__validate_deploy__`. Check the [counterfactual deployments](./guides/deployment) guide to learn how. - -## Sending transactions - -Let’s now explore how to send transactions through these accounts. - -### Starknet Account - -First, let’s take the example account we created before and deploy it: - -```rust -#[starknet::contract(account)] -mod MyAccount { - use openzeppelin_account::AccountComponent; - use openzeppelin_introspection::src5::SRC5Component; - - component!(path: AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // Account Mixin - #[abi(embed_v0)] - impl AccountMixinImpl = AccountComponent::AccountMixinImpl; - impl AccountInternalImpl = AccountComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - account: AccountComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccountEvent: AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState, public_key: felt252) { - self.account.initializer(public_key); - } -} -``` - -To deploy the account variant, compile the contract and declare the class hash because custom accounts are likely not declared. -This means that you’ll need an account already deployed. - -Next, create the account JSON with Starknet Foundry’s [custom account setup](https://foundry-rs.github.io/starknet-foundry/starknet/account.html#custom-account-contract) and include the `--class-hash` flag with the declared class hash. -The flag enables custom account variants. - - -The following examples use `sncast` [v0.23.0](https://github.com/foundry-rs/starknet-foundry/releases/tag/v0.23.0). - - -```bash -$ sncast \ - --url http://127.0.0.1:5050 \ - account create \ - --name my-custom-account \ - --class-hash 0x123456... -``` - -This command will output the precomputed contract address and the recommended `max-fee`. -To counterfactually deploy the account, send funds to the address and then deploy the custom account. - -```bash -$ sncast \ - --url http://127.0.0.1:5050 \ - account deploy \ - --name my-custom-account -``` - -Once the account is deployed, set the `--account` flag with the custom account name to send transactions from that account. - -```bash -$ sncast \ - --account my-custom-account \ - --url http://127.0.0.1:5050 \ - invoke \ - --contract-address 0x123... \ - --function "some_function" \ - --calldata 1 2 3 -``` - -### Ethereum Account - -First, let’s take the example account we created before and deploy it: - -```rust -#[starknet::contract(account)] -mod MyEthAccount { - use openzeppelin_account::EthAccountComponent; - use openzeppelin_interfaces::accounts::EthPublicKey; - use openzeppelin_introspection::src5::SRC5Component; - - component!(path: EthAccountComponent, storage: eth_account, event: EthAccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // EthAccount Mixin - #[abi(embed_v0)] - impl EthAccountMixinImpl = - EthAccountComponent::EthAccountMixinImpl; - impl EthAccountInternalImpl = EthAccountComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - eth_account: EthAccountComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - EthAccountEvent: EthAccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState, public_key: EthPublicKey) { - self.eth_account.initializer(public_key); - } -} -``` - -Special tooling is required in order to deploy and send transactions with an Ethereum-flavored account contract. -The following examples utilize the [StarknetJS](https://www.starknetjs.com/) library. - -Compile and declare the contract on the target network. -Next, precompute the EthAccount contract address using the declared class hash. - - -The following examples use unreleased features from StarknetJS (`starknetjs@next`) at commit [d002baea0abc1de3ac6e87a671f3dec3757437b3](https://github.com/starknet-io/starknet.js/commit/d002baea0abc1de3ac6e87a671f3dec3757437b3). - - -```javascript -import * as dotenv from 'dotenv'; -import { CallData, EthSigner, hash } from 'starknet'; -import { ABI as ETH_ABI } from '../abis/eth_account.js'; -dotenv.config(); - -const ethSigner = new EthSigner(process.env.ETH_PRIVATE_KEY); -const ethPubKey = await ethSigner.getPubKey(); -const ethAccountClassHash = ''; -const ethCallData = new CallData(ETH_ABI); -const ethAccountConstructorCalldata = ethCallData.compile('constructor', { - public_key: ethPubKey -}) -const salt = '0x12345'; -const deployerAddress = '0x0'; -const ethContractAddress = hash.calculateContractAddressFromHash( - salt, - ethAccountClassHash, - ethAccountConstructorCalldata, - deployerAddress -); -console.log('Pre-calculated EthAccount address: ', ethContractAddress); -``` - -Send funds to the pre-calculated EthAccount address and deploy the contract. - -```javascript -import * as dotenv from 'dotenv'; -import { Account, CallData, EthSigner, RpcProvider, stark } from 'starknet'; -import { ABI as ETH_ABI } from '../abis/eth_account.js'; -dotenv.config(); - -const provider = new RpcProvider({ nodeUrl: process.env.API_URL }); -const ethSigner = new EthSigner(process.env.ETH_PRIVATE_KEY); -const ethPubKey = await ethSigner.getPubKey(); -const ethAccountAddress = '' -const ethAccount = new Account(provider, ethAccountAddress, ethSigner); - -const ethAccountClassHash = '' -const ethCallData = new CallData(ETH_ABI); -const ethAccountConstructorCalldata = ethCallData.compile('constructor', { - public_key: ethPubKey -}) -const salt = '0x12345'; -const deployPayload = { - classHash: ethAccountClassHash, - constructorCalldata: ethAccountConstructorCalldata, - addressSalt: salt, -}; - -const { suggestedMaxFee: feeDeploy } = await ethAccount.estimateAccountDeployFee(deployPayload); -const { transaction_hash, contract_address } = await ethAccount.deployAccount( - deployPayload, - { maxFee: stark.estimatedFeeToMaxFee(feeDeploy, 100) } -); -await provider.waitForTransaction(transaction_hash); -console.log('EthAccount deployed at: ', contract_address); -``` - -Once deployed, connect the EthAccount instance to the target contract which enables calls to come from the EthAccount. -Here’s what an ERC20 transfer from an EthAccount looks like. - -```javascript -import * as dotenv from 'dotenv'; -import { Account, RpcProvider, Contract, EthSigner } from 'starknet'; -dotenv.config(); - -const provider = new RpcProvider({ nodeUrl: process.env.API_URL }); -const ethSigner = new EthSigner(process.env.ETH_PRIVATE_KEY); -const ethAccountAddress = '' -const ethAccount = new Account(provider, ethAccountAddress, ethSigner); - -const erc20 = new Contract(compiledErc20.abi, erc20Address, provider); - -erc20.connect(ethAccount); - -const transferCall = erc20.populate('transfer', { - recipient: recipient.address, - amount: 50n -}); -const tx = await erc20.transfer( - transferCall.calldata, { maxFee: 900_000_000_000_000 } -); -await provider.waitForTransaction(tx.transaction_hash); -``` diff --git a/content/contracts-cairo/alpha/api/access.mdx b/content/contracts-cairo/alpha/api/access.mdx deleted file mode 100644 index 002a41fb..00000000 --- a/content/contracts-cairo/alpha/api/access.mdx +++ /dev/null @@ -1,1749 +0,0 @@ ---- -title: Access Control ---- - -This crate provides ways to restrict who can access the functions of a contract or when they can do it. - -- [Ownable](#OwnableComponent) is a simple mechanism with a single "owner" role that can be assigned to a single account. This mechanism can be useful in simple scenarios, but fine grained access needs are likely to outgrow it. -- [AccessControl](#AccessControlComponent) provides a general role based access control mechanism. Multiple hierarchical roles can be created and assigned each to multiple accounts. - -## Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -### `IAccessControl` [toc] [#IAccessControl] - - -```rust -use openzeppelin_interfaces::accesscontrol::IAccessControl; -``` - -External interface of AccessControl. - -[SRC5 ID](./introspection#ISRC5) - -```text -0x23700be02858dbe2ac4dc9c9f66d0b6b0ed81ec7f970ca6844500a56ff61751 -``` - -Functions - -- [`has_role(role, account)`](#IAccessControl-has_role) -- [`get_role_admin(role)`](#IAccessControl-get_role_admin) -- [`grant_role(role, account)`](#IAccessControl-grant_role) -- [`revoke_role(role, account)`](#IAccessControl-revoke_role) -- [`renounce_role(role, account)`](#IAccessControl-renounce_role) - -Events - -- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#IAccessControl-RoleAdminChanged) -- [`RoleGranted(role, account, sender)`](#IAccessControl-RoleGranted) -- [`RoleRevoked(role, account, sender)`](#IAccessControl-RoleRevoked) - - -#### Functions [!toc] [#IAccessControl-Functions] - - -Returns whether `account` can act as `role`. - - - -Returns the admin role that controls `role`. See [grant\_role](#IAccessControl-grant_role) and [revoke\_role](#IAccessControl-revoke_role). - -To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). - - - -Grants `role` to `account`. - -If `account` had not been already granted `role`, emits a [RoleGranted](#IAccessControl-RoleGranted) event. - -Requirements: - -- the caller must have `role`'s admin role. - - - -Revokes `role` from `account`. - -If `account` had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. - -Requirements: - -- the caller must have `role`'s admin role. - - - -Revokes `role` from the calling account. - -Roles are often managed via [grant\_role](#IAccessControl-grant_role) and [revoke\_role](#IAccessControl-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). - -If the calling account had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. - -Requirements: - -- the caller must be `account`. - - -#### Events [!toc] [#IAccessControl-Events] - - -Emitted when `new_admin_role` is set as `role`'s admin role, replacing `previous_admin_role` - -`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite [RoleAdminChanged](#IAccessControl-RoleAdminChanged) not being emitted signaling this. - - - -Emitted when `account` is granted `role`. - -`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role` is called from the constructor. - - - -Emitted when `account` is revoked `role`. - -`sender` is the account that originated the contract call: - -- if using `revoke_role`, it is the admin role bearer. -- if using `renounce_role`, it is the role bearer (i.e. `account`). - - -### `IAccessControlWithDelay` [toc] [#IAccessControlWithDelay] - - -```rust -use openzeppelin_interfaces::accesscontrol::IAccessControlWithDelay; -``` - -External interface for the extended `AccessControlWithDelay` functionality. - -Functions - -- [`get_role_status(role, account)`](#IAccessControlWithDelay-get_role_status) -- [`grant_role_with_delay(role, account, delay)`](#IAccessControlWithDelay-grant_role_with_delay) - -Events - -- [`RoleGrantedWithDelay(role, account, sender, delay)`](#IAccessControlWithDelay-RoleGrantedWithDelay) - -#### Functions [!toc] [#IAccessControlWithDelay-Functions] - - -Returns the account’s status for the given role. The possible statuses are: - -- `NotGranted`: the role has not been granted to the account. -- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. -- `Effective`: the role has been granted to the account and is currently active. - - - -Attempts to grant `role` to `account` with the specified activation delay. - -Requirements: - -- The caller must have `role`'s admin role. -- delay must be greater than 0. -- the `role` must not be already effective for `account`. - -May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. - - -#### Events [!toc] [#IAccessControlWithDelay-Events] - - -Emitted when `account` is granted `role` with a delay. - -`sender` is the account that originated the contract call, an account with the admin role or the deployer address if [\_grant\_role\_with\_delay](#AccessControlComponent-_grant_role_with_delay) is called from the constructor. - - -### `IAccessControlDefaultAdminRules` [toc] [#IAccessControlDefaultAdminRules] - - -```rust -use openzeppelin_interfaces::accesscontrol_default_admin_rules::IAccessControlDefaultAdminRules; -``` - -External interface of AccessControlDefaultAdminRules declared to support [SRC5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md) detection. - -[SRC5 ID](./introspection#ISRC5) - -```text -0x3509b3083c9586afe5dae781146b0608c3846870510f8d4d21ae38676cc33eb -``` - -Functions - -- [`default_admin()`](#IAccessControlDefaultAdminRules-default_admin) -- [`pending_default_admin()`](#IAccessControlDefaultAdminRules-pending_default_admin) -- [`default_admin_delay()`](#IAccessControlDefaultAdminRules-default_admin_delay) -- [`pending_default_admin_delay()`](#IAccessControlDefaultAdminRules-pending_default_admin_delay) -- [`begin_default_admin_transfer(new_admin)`](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) -- [`cancel_default_admin_transfer()`](#IAccessControlDefaultAdminRules-cancel_default_admin_transfer) -- [`accept_default_admin_transfer()`](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) -- [`change_default_admin_delay(new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) -- [`rollback_default_admin_delay()`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) -- [`default_admin_delay_increase_wait()`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) - -Events - -- [`DefaultAdminTransferScheduled(new_admin, accept_schedule)`](#IAccessControlDefaultAdminRules-DefaultAdminTransferScheduled) -- [`DefaultAdminTransferCanceled()`](#IAccessControlDefaultAdminRules-DefaultAdminTransferCanceled) -- [`DefaultAdminDelayChangeScheduled(new_delay, effect_schedule)`](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeScheduled) -- [`DefaultAdminDelayChangeCanceled()`](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) - -#### Functions [!toc] [#IAccessControlDefaultAdminRules-Functions] - - -Returns the address of the current `DEFAULT_ADMIN_ROLE` holder. - - - -Returns a tuple of a `new_admin` and an `accept_schedule`. - -Returns a tuple of a `new_admin` and an `accept_schedule`. - -After the `accept_schedule` passes, the `new_admin` will be able to accept the `default_admin` role by calling [accept\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-accept_default_admin_transfer), completing the role transfer. - -A zero value only in `accept_schedule` indicates no pending admin transfer. - - -A zero address `new_admin` means that `default_admin` is being renounced. - - - - -Returns the delay required to schedule the acceptance of a `default_admin` transfer started. - -This delay will be added to the current timestamp when calling [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) to set the acceptance schedule. - - -If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function return the new delay. - - -See [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay). - - - -Returns a tuple of `new_delay` and an `effect_schedule`. - -After the `effect_schedule` passes, the `new_delay` will get into effect immediately for every new `default_admin` transfer started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). - -A zero value only in `effect_schedule` indicates no pending delay change. - -A zero value only for `new_delay` means that the next [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) will be zero after the effect schedule. - - - -Starts a `default_admin` transfer by setting a [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) scheduled for acceptance after the current timestamp plus a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay). - -Requirements: - -- Only can be called by the current `default_admin`. - -Emits a [DefaultAdminTransferScheduled](#IAccessControlDefaultAdminRules-DefaultAdminTransferScheduled) event. - - - -Cancels a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). - -A [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) not yet accepted can also be cancelled with this function. - -Requirements: - -- Only can be called by the current `default_admin`. - -May emit a [DefaultAdminTransferCanceled](#IAccessControlDefaultAdminRules-DefaultAdminTransferCanceled) event. - - - -Completes a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer). - -After calling the function: - -- `DEFAULT_ADMIN_ROLE` must be granted to the caller. -- `DEFAULT_ADMIN_ROLE` must be revoked from the previous holder. -- [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) must be reset to zero value. - -Requirements: - -- Only can be called by the [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin)'s `new_admin`. -- The [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin)'s `accept_schedule` should’ve passed. - - - -Initiates a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) update by setting a [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) scheduled to take effect after the current timestamp plus a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay). - -This function guarantees that any call to [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) done between the timestamp this method is called and the [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) effect schedule will use the current [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) set before calling. - -The [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay)'s effect schedule is defined in a way that waiting until the schedule and then calling [begin\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) with the new delay will take at least the same as another `default_admin` complete transfer (including acceptance). - -The schedule is designed for two scenarios: - -- When the delay is changed for a larger one the schedule is `block.timestamp + new delay` capped by [default\_admin\_delay\_increase\_wait](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait). -- When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`. - -A [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) that never got into effect will be canceled in favor of a new scheduled change. - -Requirements: - -- Only can be called by the current `default_admin`. - -Emits a [DefaultAdminDelayChangeScheduled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeScheduled) event and may emit a [DefaultAdminDelayChangeCanceled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) event. - - - - -Cancels a scheduled [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) change. - -Requirements: - -- Only can be called by the current `default_admin`. - -May emit a [DefaultAdminDelayChangeCanceled](#IAccessControlDefaultAdminRules-DefaultAdminDelayChangeCanceled) event. - - - -Maximum time in seconds for an increase to [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay)) to take effect. Defaults to 5 days. - -When the [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overridden for a custom [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) increase scheduling. - -Make sure to add a reasonable amount of time while overriding this value, otherwise, there’s a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (e.g. set milliseconds instead of seconds). - - -#### Events [!toc] [#IAccessControlDefaultAdminRules-Events] - - -Emitted when a `default_admin` transfer is started. - -Sets `new_admin` as the next address to become the `default_admin` by calling [accept\_default\_admin\_transfer](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) only after `accept_schedule` passes. - - - -Emitted when a [pending\_default\_admin](#IAccessControlDefaultAdminRules-pending_default_admin) is reset if it was never accepted, regardless of its schedule. - - - -Emitted when a [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) change is started. - -Sets `new_delay` as the next delay to be applied between default admins transfers after `effect_schedule` has passed. - -Emitted when a [pending\_default\_admin\_delay](#IAccessControlDefaultAdminRules-pending_default_admin_delay) is reset if its schedule didn’t pass. - - -## Core - -### `OwnableComponent` [toc] [#OwnableComponent] - - -```rust -use openzeppelin_access::ownable::OwnableComponent; -``` - -`Ownable` provides a basic access control mechanism where an account (an owner) can be granted exclusive access to specific functions. - -This module includes the internal `assert_only_owner` to restrict a function to be used only by the owner. - -[Embeddable Mixin Implementations](../components#mixins) - -#### OwnableMixinImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableMixinImpl] - -- [`OwnableImpl`](#OwnableComponent-Embeddable-Impls-OwnableImpl) -- [`OwnableCamelOnlyImpl`](#OwnableComponent-Embeddable-Impls-OwnableCamelOnlyImpl) - -#### OwnableTwoStepMixinImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableTwoStepMixinImpl] - -- [`OwnableTwoStepImpl`](#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) -- [`OwnableTwoStepCamelOnlyImpl`](#OwnableComponent-Embeddable-Impls-OwnableTwoStepCamelOnlyImpl) - -Embeddable Implementations - -#### OwnableImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableImpl] - -- [`owner(self)`](#OwnableComponent-owner) -- [`transfer_ownership(self, new_owner)`](#OwnableComponent-transfer_ownership) -- [`renounce_ownership(self)`](#OwnableComponent-renounce_ownership) - -#### OwnableTwoStepImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl] - -- [`owner(self)`](#OwnableComponent-two-step-owner) -- [`pending_owner(self)`](#OwnableComponent-two-step-pending_owner) -- [`accept_ownership(self)`](#OwnableComponent-two-step-accept_ownership) -- [`transfer_ownership(self, new_owner)`](#OwnableComponent-two-step-transfer_ownership) -- [`renounce_ownership(self)`](#OwnableComponent-two-step-renounce_ownership) - -#### OwnableCamelOnlyImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableCamelOnlyImpl] - -- [`transferOwnership(self, newOwner)`](#OwnableComponent-transferOwnership) -- [`renounceOwnership(self)`](#OwnableComponent-renounceOwnership) - -#### OwnableTwoStepCamelOnlyImpl [!toc] [#OwnableComponent-Embeddable-Impls-OwnableTwoStepCamelOnlyImpl] - -- [`pendingOwner(self)`](#OwnableComponent-two-step-pendingOwner) -- [`acceptOwnership(self)`](#OwnableComponent-two-step-acceptOwnership) -- [`transferOwnership(self, new_owner)`](#OwnableComponent-two-step-transferOwnership) -- [`renounceOwnership(self)`](#OwnableComponent-two-step-renounceOwnership) - -Internal Implementations - -#### InternalImpl [!toc] [#OwnableComponent-InternalImpl] - -- [`initializer(self, owner)`](#OwnableComponent-initializer) -- [`assert_only_owner(self)`](#OwnableComponent-assert_only_owner) -- [`_transfer_ownership(self, new_owner)`](#OwnableComponent-_transfer_ownership) -- [`_propose_owner(self, new_owner)`](#OwnableComponent-_propose_owner) - -Events - -- [`OwnershipTransferStarted(previous_owner, new_owner)`](#OwnableComponent-OwnershipTransferStarted) -- [`OwnershipTransferred(previous_owner, new_owner)`](#OwnableComponent-OwnershipTransferred) - -#### Embeddable functions [!toc] [#OwnableComponent-Embeddable-Functions] - - -Returns the address of the current owner. - - - -Transfers ownership of the contract to a new account (`new_owner`). Can only be called by the current owner. - -Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. - - - -Leaves the contract without owner. It will not be possible to call `assert_only_owner` functions anymore. Can only be called by the current owner. - - -Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. - - - -#### Embeddable functions (two step transfer) [!toc] [#OwnableComponent-Embeddable-Functions-Two-Step] - - -Returns the address of the current owner. - - - -Returns the address of the pending owner. - - - -Transfers ownership of the contract to the pending owner. Can only be called by the pending owner. Resets pending owner to zero address. - -Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. - - - -Starts the two step ownership transfer process, by setting the pending owner. Setting `new_owner` to the zero address is allowed, this can be used to cancel an initiated ownership transfer. - -Can only be called by the current owner. - -Emits an [OwnershipTransferStarted](#OwnableComponent-OwnershipTransferStarted) event. - - - -Leaves the contract without owner. It will not be possible to call `assert_only_owner` functions anymore. Can only be called by the current owner. - - -Renouncing ownership will leave the contract without an owner, thereby removing any functionality that is only available to the owner. - - - - -See [transfer\_ownership](#OwnableComponent-transfer_ownership). - - - -See [renounce\_ownership](#OwnableComponent-renounce_ownership). - - - -See [pending\_owner](#OwnableComponent-two-step-pending_owner). - - - -See [accept\_ownership](#OwnableComponent-two-step-accept_ownership). - - - -See [transfer\_ownership](#OwnableComponent-two-step-transfer_ownership). - - - -See [renounce\_ownership](#OwnableComponent-two-step-renounce_ownership). - - -#### Internal functions [!toc] [#OwnableComponent-Internal-Functions] - - -Initializes the contract and sets `owner` as the initial owner. - -Requirements: - -- `owner` cannot be the zero address. - -Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. - - - -Panics if called by any account other than the owner. - - - -Transfers ownership of the contract to a new account (`new_owner`). Internal function without access restriction. - -Emits an [OwnershipTransferred](#OwnableComponent-OwnershipTransferred) event. - - - -Sets a new pending owner in a two step transfer. - -Internal function without access restriction. - -Emits an [OwnershipTransferStarted](#OwnableComponent-OwnershipTransferStarted) event. - - -#### Events [!toc] [#OwnableComponent-Events] - - -Emitted when the pending owner is updated. - - - -Emitted when the ownership is transferred. - - -### `AccessControlComponent` [toc] [#AccessControlComponent] - - -```rust -use openzeppelin_access::accesscontrol::AccessControlComponent; -``` - -Component that allows contracts to implement role-based access control mechanisms. Roles are referred to by their `felt252` identifier: - -```rust -const MY_ROLE: felt252 = selector!("MY_ROLE"); -``` - -Roles can be used to represent a set of permissions. To restrict access to a function call, use [`assert_only_role`](#AccessControlComponent-assert_only_role): - -```rust -(...) - -#[external(v0)] -fn foo(ref self: ContractState) { - self.accesscontrol.assert_only_role(MY_ROLE); - - // Do something -} -``` - -Roles can be granted and revoked dynamically via the [grant\_role](#AccessControlComponent-grant_role), [grant\_role\_with\_delay](#IAccessControlWithDelay-grant_role_with_delay) and [revoke\_role](#AccessControlComponent-revoke_role) functions. Each role has an associated admin role, and only accounts that have a role’s admin role can call [grant\_role](#AccessControlComponent-grant_role), [grant\_role\_with\_delay](#IAccessControlWithDelay-grant_role_with_delay) and [revoke\_role](#AccessControlComponent-revoke_role). - -By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means that only accounts with this role will be able to grant or revoke other roles. More complex role relationships can be created by using [set\_role\_admin](#AccessControlComponent-set_role_admin). - - -The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to grant and revoke this role. Extra precautions should be taken to secure accounts that have been granted it. See [AccessControlDefaultAdminRulesComponent](#AccessControlDefaultAdminRulesComponent). - - -[Embeddable Mixin Implementations](../components#mixins) - -#### AccessControlMixinImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlMixinImpl] - -- [`AccessControlImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlImpl) -- [`AccessControlCamelImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlCamelImpl) -- [`AccessControlWithDelayImpl`](#AccessControlComponent-Embeddable-Impls-AccessControlWithDelayImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) - -Embeddable Implementations - -#### AccessControlImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlImpl] - -- [`has_role(self, role, account)`](#AccessControlComponent-has_role) -- [`get_role_admin(self, role)`](#AccessControlComponent-get_role_admin) -- [`grant_role(self, role, account)`](#AccessControlComponent-grant_role) -- [`revoke_role(self, role, account)`](#AccessControlComponent-revoke_role) -- [`renounce_role(self, role, account)`](#AccessControlComponent-renounce_role) - -#### AccessControlCamelImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlCamelImpl] - -- [`hasRole(self, role, account)`](#AccessControlComponent-hasRole) -- [`getRoleAdmin(self, role)`](#AccessControlComponent-getRoleAdmin) -- [`grantRole(self, role, account)`](#AccessControlComponent-grantRole) -- [`revokeRole(self, role, account)`](#AccessControlComponent-revokeRole) -- [`renounceRole(self, role, account)`](#AccessControlComponent-renounceRole) - -#### AccessControlWithDelayImpl [!toc] [#AccessControlComponent-Embeddable-Impls-AccessControlWithDelayImpl] - -- [`get_role_status(self, role, account)`](#AccessControlComponent-get_role_status) -- [`grant_role_with_delay(self, role, account, delay)`](#AccessControlComponent-grant_role_with_delay) - -#### SRC5Impl [!toc] [#AccessControlComponent-Embeddable-Impls-SRC5Impl] - -- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) - -Internal Implementations - -#### InternalImpl [!toc] [#AccessControlComponent-InternalImpl] - -- [`initializer(self)`](#AccessControlComponent-initializer) -- [`assert_only_role(self, role)`](#AccessControlComponent-assert_only_role) -- [`is_role_effective(self, role, account)`](#AccessControlComponent-is_role_effective) -- [`resolve_role_status(self, role, account)`](#AccessControlComponent-resolve_role_status) -- [`is_role_granted(self, role, account)`](#AccessControlComponent-is_role_granted) -- [`set_role_admin(self, role, admin_role)`](#AccessControlComponent-set_role_admin) -- [`_grant_role(self, role, account)`](#AccessControlComponent-_grant_role) -- [`_grant_role_with_delay(self, role, account, delay)`](#AccessControlComponent-_grant_role_with_delay) -- [`_revoke_role(self, role, account)`](#AccessControlComponent-_revoke_role) - -Events - -#### IAccessControl [!toc] [#AccessControlComponent-Events-IAccessControl] - -- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#AccessControlComponent-RoleAdminChanged) -- [`RoleGranted(role, account, sender)`](#AccessControlComponent-RoleGranted) -- [`RoleRevoked(role, account, sender)`](#AccessControlComponent-RoleRevoked) - -#### IAccessControlWithDelay [!toc] [#AccessControlComponent-Events-IAccessControlWithDelay] - -- [`RoleGrantedWithDelay(role, account, sender, delay)`](#AccessControlComponent-RoleGrantedWithDelay) - -#### Embeddable functions [!toc] [#AccessControlComponent-Embeddable-Functions] - - -Returns whether `account` can act as `role`. - - - -Returns the admin role that controls `role`. See [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). - -To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). - - - -Returns the account’s status for the given role. - -The possible statuses are: - -- `NotGranted`: the role has not been granted to the account. -- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. -- `Effective`: the role has been granted to the account and is currently active. - - - -Grants `role` to `account`. - -If `account` had not been already granted `role`, emits a [RoleGranted](#IAccessControl-RoleGranted) event. - -Requirements: - -- the caller must have `role`'s admin role. - -May emit a [RoleGranted](#IAccessControl-RoleGranted) event. - - - -Attempts to grant `role` to `account` with the specified activation delay. - -Requirements: - -- The caller must have \`role’s admin role. -- delay must be greater than 0. -- the `role` must not be already effective for `account`. - -May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. - - - -Revokes `role` from `account`. - -If `account` had been granted `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. - -Requirements: - -- the caller must have `role`'s admin role. - -May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. - - - -Revokes `role` from the calling account. - -Roles are often managed via [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). - -If the calling account had been revoked `role`, emits a [RoleRevoked](#IAccessControl-RoleRevoked) event. - -Requirements: - -- the caller must be `account`. - -May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. - - - -See [ISRC5::supports\_interface](./introspection#ISRC5-supports_interface). - - - -See [has\_role](#AccessControlComponent-has_role). - - - -See [get\_role\_admin](#AccessControlComponent-get_role_admin). - - - -See [grant\_role](#AccessControlComponent-grant_role). - - - -See [revoke\_role](#AccessControlComponent-revoke_role). - - - -See [renounce\_role](#AccessControlComponent-renounce_role). - - -#### Internal functions [!toc] [#AccessControlComponent-Internal-Functions] - - -Initializes the contract by registering the [IAccessControl](#IAccessControl) interface ID. - - - -Validates that the caller can act as the given role. Otherwise it panics. - - - -Returns whether the account can act as the given role. - -The account can act as the role if it is active and the `effective_from` time is before or equal to the current time. - - -If the `effective_from` timepoint is 0, the role is effective immediately. This is backwards compatible with implementations that didn’t use delays but a single boolean flag. - - - - -Returns the account’s status for the given role. - -The possible statuses are: - -- `NotGranted`: the role has not been granted to the account. -- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. -- `Effective`: the role has been granted to the account and is currently active. - - - -Returns whether the account has the given role granted. - - -The account may not be able to act as the role yet, if a delay was set and has not passed yet. Use `is_role_effective` to check if the account can act as the role. - - - - -Sets `admin_role` as `role`'s admin role. - -Internal function without access restriction. - -Emits a [RoleAdminChanged](#IAccessControl-RoleAdminChanged) event. - - - -Attempts to grant `role` to `account`. The function does nothing if `role` is already effective for `account`. If `role` has been granted to `account`, but is not yet active due to a time delay, the delay is removed and `role` becomes effective immediately. - -Internal function without access restriction. - -May emit a [RoleGranted](#IAccessControl-RoleGranted) event. - - - -Attempts to grant `role` to `account` with the specified activation delay. - -The role will become effective after the given delay has passed. If the role is already active (`Effective`) for the account, the function will panic. If the role has been granted but is not yet active (being in the `Delayed` state), the existing delay will be overwritten with the new `delay`. - -Internal function without access restriction. - -Requirements: - -- delay must be greater than 0. -- the `role` must not be already effective for `account`. - -May emit a [RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay) event. - - - -Revokes `role` from `account`. - -Internal function without access restriction. - -May emit a [RoleRevoked](#IAccessControl-RoleRevoked) event. - - -#### Events [!toc] [#AccessControlComponent-Events] - - -See [IAccessControl::RoleAdminChanged](#IAccessControl-RoleAdminChanged). - - - -See [IAccessControl::RoleGranted](#IAccessControl-RoleGranted). - - - -See [IAccessControlWithDelay::RoleGrantedWithDelay](#IAccessControlWithDelay-RoleGrantedWithDelay). - - - -See [IAccessControl::RoleRevoked](#IAccessControl-RoleRevoked). - - -## Extensions - -### `AccessControlDefaultAdminRulesComponent` [toc] [#AccessControlDefaultAdminRulesComponent] - - -```rust -use openzeppelin_access::accesscontrol::extensions::AccessControlDefaultAdminRulesComponent; -``` - -Extension of [AccessControl](#AccessControlComponent) that allows specifying special rules to manage the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions over other roles that may potentially have privileged rights in the system. - -If a specific role doesn’t have an admin role assigned, the holder of the `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it. - -This contract implements the following risk mitigations on top of [AccessControl](#AccessControlComponent): - -- Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it’s potentially renounced. -- Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account. -- Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted. -- The delay can be changed by scheduling, see [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay). -- It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`. - -[Embeddable Mixin Implementations](../components#mixins) - -#### AccessControlMixinImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlMixinImpl] - -- [`AccessControlDefaultAdminRulesImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlDefaultAdminRulesImpl) -- [`AccessControlImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlImpl) -- [`AccessControlCamelImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlCamelImpl) -- [`AccessControlWithDelayImpl`](#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlWithDelayImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) - -Embeddable Implementations - -#### AccessControlDefaultAdminRulesImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlDefaultAdminRulesImpl] - -- [`default_admin(self)`](#IAccessControlDefaultAdminRules-default_admin) -- [`pending_default_admin(self)`](#IAccessControlDefaultAdminRules-pending_default_admin) -- [`default_admin_delay(self)`](#IAccessControlDefaultAdminRules-default_admin_delay) -- [`pending_default_admin_delay(self)`](#IAccessControlDefaultAdminRules-pending_default_admin_delay) -- [`begin_default_admin_transfer(self, new_admin)`](#IAccessControlDefaultAdminRules-begin_default_admin_transfer) -- [`cancel_default_admin_transfer(self)`](#IAccessControlDefaultAdminRules-cancel_default_admin_transfer) -- [`accept_default_admin_transfer(self)`](#IAccessControlDefaultAdminRules-accept_default_admin_transfer) -- [`change_default_admin_delay(self, new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) -- [`rollback_default_admin_delay(self)`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) -- [`default_admin_delay_increase_wait(self)`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) - -#### AccessControlImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlImpl] - -- [`has_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-has_role) -- [`get_role_admin(self, role)`](#AccessControlDefaultAdminRulesComponent-get_role_admin) -- [`grant_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-grant_role) -- [`revoke_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-revoke_role) -- [`renounce_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-renounce_role) - -#### AccessControlCamelImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlCamelImpl] - -- [`hasRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-hasRole) -- [`getRoleAdmin(self, role)`](#AccessControlDefaultAdminRulesComponent-getRoleAdmin) -- [`grantRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-grantRole) -- [`revokeRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-revokeRole) -- [`renounceRole(self, role, account)`](#AccessControlDefaultAdminRulesComponent-renounceRole) - -#### AccessControlWithDelayImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlWithDelayImpl] - -- [`get_role_status(self, role, account)`](#AccessControlDefaultAdminRulesComponent-get_role_status) -- [`grant_role_with_delay(self, role, account, delay)`](#AccessControlDefaultAdminRulesComponent-grant_role_with_delay) - -#### SRC5Impl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-SRC5Impl] - -- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) - -Internal Implementations - -#### InternalImpl [!toc] [#AccessControlDefaultAdminRulesComponent-InternalImpl] - -- [`initializer(self, initial_delay, initial_default_admin)`](#AccessControlDefaultAdminRulesComponent-initializer) -- [`assert_only_role(self, role)`](#AccessControlDefaultAdminRulesComponent-assert_only_role) -- [`is_role_effective(self, role, account)`](#AccessControlDefaultAdminRulesComponent-is_role_effective) -- [`resolve_role_status(self, role, account)`](#AccessControlDefaultAdminRulesComponent-resolve_role_status) -- [`is_role_granted(self, role, account)`](#AccessControlDefaultAdminRulesComponent-is_role_granted) -- [`set_role_admin(self, role, admin_role)`](#AccessControlDefaultAdminRulesComponent-set_role_admin) -- [`_grant_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-_grant_role) -- [`_grant_role_with_delay(self, role, account, delay)`](#AccessControlDefaultAdminRulesComponent-_grant_role_with_delay) -- [`_revoke_role(self, role, account)`](#AccessControlDefaultAdminRulesComponent-_revoke_role) -- [`set_pending_default_admin(self, new_admin, new_schedule)`](#AccessControlDefaultAdminRulesComponent-set_pending_default_admin) -- [`set_pending_delay(self, new_delay, new_schedule)`](#AccessControlDefaultAdminRulesComponent-set_pending_delay) -- [`delay_change_wait(self, new_delay)`](#AccessControlDefaultAdminRulesComponent-delay_change_wait) - -Events - -#### IAccessControl [!toc] [#AccessControlDefaultAdminRulesComponent-Events-IAccessControl] - -- [`RoleAdminChanged(role, previous_admin_role, new_admin_role)`](#AccessControlDefaultAdminRulesComponent-RoleAdminChanged) -- [`RoleGranted(role, account, sender)`](#AccessControlDefaultAdminRulesComponent-RoleGranted) -- [`RoleRevoked(role, account, sender)`](#AccessControlDefaultAdminRulesComponent-RoleRevoked) - -#### IAccessControlWithDelay [!toc] [#AccessControlDefaultAdminRulesComponent-Events-IAccessControlWithDelay] - -- [`RoleGrantedWithDelay(role, account, sender, delay)`](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) - -#### IAccessControlDefaultAdminRules [!toc] [#AccessControlDefaultAdminRulesComponent-Events-IAccessControlDefaultAdminRules] - -- [`DefaultAdminTransferScheduled(new_admin, accept_schedule)`](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferScheduled) -- [`DefaultAdminTransferCanceled()`](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferCanceled) -- [`DefaultAdminDelayChangeScheduled(new_delay, effect_schedule)`](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeScheduled) -- [`DefaultAdminDelayChangeCanceled()`](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) - -#### Embeddable functions [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Functions] - - -Returns the address of the current `DEFAULT_ADMIN_ROLE` holder. - - - -Returns a tuple of a `new_admin` and an `accept_schedule`. - -After the `accept_schedule` passes, the `new_admin` will be able to accept the `default_admin` role by calling [accept\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-accept_default_admin_transfer), completing the role transfer. - -A zero value only in `accept_schedule` indicates no pending admin transfer. - - -A zero address `new_admin` means that `default_admin` is being renounced. - - - - -Returns the delay required to schedule the acceptance of a `default_admin` transfer started. - -This delay will be added to the current timestamp when calling [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) to set the acceptance schedule. - - -If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this function returns the new delay. - - -See [change\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-change_default_admin_delay). - - - -Returns a tuple of `new_delay` and an `effect_schedule`. - -After the `effect_schedule` passes, the `new_delay` will get into effect immediately for every new `default_admin` transfer started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). - -A zero value only in `effect_schedule` indicates no pending delay change. - -A zero value only for `new_delay` means that the next [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) will be zero after the effect schedule. - - - -Starts a `default_admin` transfer by setting a [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) scheduled for acceptance after the current timestamp plus a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay). - -Requirements: - -- Only can be called by the current `default_admin`. - -Emits a [DefaultAdminTransferScheduled](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferScheduled) event. - - - -Cancels a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). - -A [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) not yet accepted can also be cancelled with this function. - -Requirements: - -- Only can be called by the current `default_admin`. - -May emit a [DefaultAdminTransferCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminTransferCanceled) event. - - - -Completes a `default_admin` transfer previously started with [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer). - -After calling the function: - -- `DEFAULT_ADMIN_ROLE` must be granted to the caller. -- `DEFAULT_ADMIN_ROLE` must be revoked from the previous holder. -- [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) must be reset to zero values. - -Requirements: - -- Only can be called by the [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin)'s `new_admin`. -- The [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin)'s `accept_schedule` should’ve passed. - - - -Initiates a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) update by setting a [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) scheduled for getting into effect after the current timestamp plus a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay). - -This function guarantees that any call to [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) done between the timestamp this method is called and the [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) effect schedule will use the current [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) set before calling. - -The [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay)'s effect schedule is defined in a way that waiting until the schedule and then calling [begin\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-begin_default_admin_transfer) with the new delay will take at least the same as another `default_admin` complete transfer (including acceptance). - -The schedule is designed for two scenarios: - -- When the delay is changed for a larger one the schedule is `block.timestamp new delay` capped by [default\_admin\_delay\_increase\_wait](#AccessControlDefaultAdminRulesComponent-default_admin_delay_increase_wait). -- When the delay is changed for a shorter one, the schedule is `block.timestamp (current delay - new delay)`. - -A [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) that never got into effect will be canceled in favor of a new scheduled change. - -Requirements: - -- Only can be called by the current `default_admin`. - -Emits a [DefaultAdminDelayChangeScheduled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeScheduled) event and may emit a [DefaultAdminDelayChangeCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) event. - - - -Cancels a scheduled [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) change. - -Requirements: - -- Only can be called by the current `default_admin`. - -May emit a [DefaultAdminDelayChangeCanceled](#AccessControlDefaultAdminRulesComponent-DefaultAdminDelayChangeCanceled) event. - - - -Maximum time in seconds for an increase to [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-change_default_admin_delay)) to take effect. Defaults to 5 days. - -When the [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overridden for a custom [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) increase scheduling. - - -Make sure to add a reasonable amount of time while overriding this value, otherwise, there’s a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds). - - - - -Returns whether `account` can act as `role`. - - - -Returns the admin role that controls `role`. See [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). - -To change a role’s admin, use [set\_role\_admin](#AccessControlComponent-set_role_admin). - - - -Returns the account’s status for the given role. - -The possible statuses are: - -- `NotGranted`: the role has not been granted to the account. -- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. -- `Effective`: the role has been granted to the account and is currently active. - - - -Grants `role` to `account`. - -If `account` had not been already granted `role`, emits a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. - -Requirements: - -- the caller must have `role`'s admin role. - -May emit a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. - - - -Attempts to grant `role` to `account` with the specified activation delay. - -Requirements: - -- The caller must have \`role’s admin role. -- delay must be greater than 0. -- the `role` must not be already effective for `account`. - -May emit a [RoleGrantedWithDelay](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) event. - - - -Revokes `role` from `account`. - -If `account` had been granted `role`, emits a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. - -Requirements: - -- the caller must have `role`'s admin role. - -May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. - - - -Revokes `role` from the calling account. - -Roles are often managed via [grant\_role](#AccessControlComponent-grant_role) and [revoke\_role](#AccessControlComponent-revoke_role). This function’s purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). - -If the calling account had been revoked `role`, emits a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. - -Requirements: - -- the caller must be `account`. - -May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. - - - -See [ISRC5::supports\_interface](./introspection#ISRC5-supports_interface). - - - -See [has\_role](#AccessControlDefaultAdminRulesComponent-has_role). - - - -See [get\_role\_admin](#AccessControlDefaultAdminRulesComponent-get_role_admin). - - - -See [grant\_role](#AccessControlDefaultAdminRulesComponent-grant_role). - - - -See [revoke\_role](#AccessControlDefaultAdminRulesComponent-revoke_role). - - - -See [renounce\_role](#AccessControlDefaultAdminRulesComponent-renounce_role). - - -#### Internal functions [!toc] [#AccessControlDefaultAdminRulesComponent-Internal-Functions] - - -Initializes the contract by registering the IAccessControl interface ID and setting the initial delay and default admin. - -Requirements: - -- `initial_default_admin` must not be the zero address. - - - -Validates that the caller can act as the given role. Otherwise it panics. - - - -Returns whether the account can act as the given role. - -The account can act as the role if it is active and the `effective_from` time is before or equal to the current time. - - -If the `effective_from` timepoint is 0, the role is effective immediately. This is backwards compatible with implementations that didn’t use delays but a single boolean flag. - - - - -Returns the account’s status for the given role. - -The possible statuses are: - -- `NotGranted`: the role has not been granted to the account. -- `Delayed`: The role has been granted to the account but is not yet active due to a time delay. -- `Effective`: the role has been granted to the account and is currently active. - - - -Returns whether the account has the given role granted. - - -The account may not be able to act as the role yet, if a delay was set and has not passed yet. Use is\_role\_effective to check if the account can act as the role. - - - - -Sets `admin_role` as \`role’s admin role. - -Internal function without access restriction. - -Requirements: - -- `role` must not be `DEFAULT_ADMIN_ROLE`. - -Emits a [RoleAdminChanged](#AccessControlDefaultAdminRulesComponent-RoleAdminChanged) event. - - - -Attempts to grant `role` to `account`. The function does nothing if `role` is already effective for `account`. If `role` has been granted to `account`, but is not yet active due to a time delay, the delay is removed and `role` becomes effective immediately. - -Internal function without access restriction. - -For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn’t already a `default_admin` or if the role has been previously renounced. - -Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE` assignable again. Make sure to guarantee this is the expected behavior in your implementation. - -May emit a [RoleGranted](#AccessControlDefaultAdminRulesComponent-RoleGranted) event. - - - -Attempts to grant `role` to `account` with the specified activation delay. - -The role will become effective after the given delay has passed. If the role is already active (`Effective`) for the account, the function will panic. If the role has been granted but is not yet active (being in the `Delayed` state), the existing delay will be overwritten with the new `delay`. - -Internal function without access restriction. - -Requirements: - -- `delay` must be greater than 0. -- the `role` must not be already effective for `account`. -- `role` must not be `DEFAULT_ADMIN_ROLE`. - -May emit a [RoleGrantedWithDelay](#AccessControlDefaultAdminRulesComponent-RoleGrantedWithDelay) event. - - - -Attempts to revoke `role` from `account`. The function does nothing if `role` is not effective for `account`. If `role` has been revoked from `account`, but is still active due to a time delay, the delay is removed and `role` becomes inactive immediately. - -Internal function without access restriction. - -May emit a [RoleRevoked](#AccessControlDefaultAdminRulesComponent-RoleRevoked) event. - - - -Setter of the tuple for pending admin and its schedule. - -May emit a DefaultAdminTransferCanceled event. - - - -Setter of the tuple for pending delay and its schedule. - -May emit a DefaultAdminDelayChangeCanceled event. - - - -Returns the amount of seconds to wait after the `new_delay` will become the new `default_admin_delay`. - -The value returned guarantees that if the delay is reduced, it will go into effect after a wait that honors the previously set delay. - -See [default\_admin\_delay\_increase\_wait](#AccessControlDefaultAdminRulesComponent-default_admin_delay_increase_wait). - - -#### Events [!toc] [#AccessControlDefaultAdminRulesComponent-Events] - - -Emitted when `new_admin_role` is set as role’s admin role, replacing `previous_admin_role` - -`DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite `RoleAdminChanged` not being emitted signaling this. - - - -Emitted when `account` is granted `role`. - -`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role` is called from the constructor. - - - -Emitted when `role` is revoked for `account`. - -`sender` is the account that originated the contract call: - -- If using `revoke_role`, it is the admin role bearer. -- If using `renounce_role`, it is the role bearer (i.e. `account`). - - - -Emitted when `account` is granted `role` with a delay. - -`sender` is the account that originated the contract call, an account with the admin role or the deployer address if `_grant_role_with_delay` is called from the constructor. - - - -Emitted when a `default_admin` transfer is started. - -Sets `new_admin` as the next address to become the `default_admin` by calling [accept\_default\_admin\_transfer](#AccessControlDefaultAdminRulesComponent-accept_default_admin_transfer) only after `accept_schedule` passes. - - - -Emitted when a [pending\_default\_admin](#AccessControlDefaultAdminRulesComponent-pending_default_admin) is reset if it was never accepted, regardless of its schedule. - - - - -Emitted when a [default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-default_admin_delay) change is started. - -Sets `new_delay` as the next delay to be applied between default admins transfers after `effect_schedule` has passed. - -Emitted when a [pending\_default\_admin\_delay](#AccessControlDefaultAdminRulesComponent-pending_default_admin_delay) is reset if its schedule didn’t pass. - diff --git a/content/contracts-cairo/alpha/api/account.mdx b/content/contracts-cairo/alpha/api/account.mdx deleted file mode 100644 index 3519cece..00000000 --- a/content/contracts-cairo/alpha/api/account.mdx +++ /dev/null @@ -1,847 +0,0 @@ ---- -title: Account ---- - -This crate provides components to implement account contracts that can be used for interacting with the network. - -## Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references documented here are -contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -### `ISRC6` [toc] [#ISRC6] - - -```rust -use openzeppelin_interfaces::accounts::ISRC6; -``` - -Interface of the SRC6 Standard Account as defined in the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md). - -[SRC5 ID](./introspection#ISRC5) - -```text -0x2ceccef7f994940b3962a6c67e0ba4fcd37df7d131417c604f91e03caecc1cd -``` - -Functions - -- [`__execute__(calls)`](#ISRC6-__execute__) -- [`__validate__(calls)`](#ISRC6-__validate__) -- [`is_valid_signature(hash, signature)`](#ISRC6-is_valid_signature) - -#### Functions [!toc] [#ISRC6-Functions] - - -Executes the list of calls as a transaction after validation. - -The `Call` struct is defined in [corelib](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/account.cairo#L3). - - - -Validates a transaction before execution. - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - - -Validates whether a signature is valid or not for the given message hash. - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - -### `ISRC9_V2` [toc] [#ISRC9_V2] - - -```rust -use openzeppelin_interfaces::src9::ISRC9_V2; -``` - -Interface of the SRC9 Standard as defined in the [SNIP-9](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-9.md). - -[SRC5 ID](./introspection#ISRC5) - -```text -0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872 -``` - -Functions - -- [`execute_from_outside_v2(outside_execution, signature)`](#ISRC9_V2-execute_from_outside_v2) -- [`is_valid_outside_execution_nonce(nonce)`](#ISRC9_V2-is_valid_outside_execution_nonce) - -#### Functions [!toc] [#ISRC9_V2-Functions] - - -Allows anyone to submit a transaction on behalf of the account as long as they have the relevant signatures. - -This method allows reentrancy. A call to `__execute__` or `execute_from_outside_v2` can trigger another nested transaction to `execute_from_outside_v2` thus the implementation MUST verify that the provided `signature` matches the hash of `outside_execution` and that `nonce` was not already used. - -The implementation should expect version to be set to 2 in the domain separator. - -Arguments: - -- `outside_execution` - The parameters of the transaction to execute. -- `signature` - A valid signature on the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message encoding of `outside_execution`. - - - -Get the status of a given nonce. `true` if the nonce is available to use. - - -## Core - -### `AccountComponent` [toc] [#AccountComponent] - - -```rust -use openzeppelin_account::AccountComponent; -``` - -Account component implementing [`ISRC6`](#ISRC6) for signatures over the [Starknet curve](https://docs.starknet.io/learn/protocol/cryptography#the-stark-curve). - -Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. - -[Embeddable Mixin Implementations](../components#mixins) - -#### AccountMixinImpl [!toc] [#AccountComponent-Embeddable-Impls-AccountMixinImpl] - -- [`SRC6Impl`](#AccountComponent-Embeddable-Impls-SRC6Impl) -- [`DeclarerImpl`](#AccountComponent-Embeddable-Impls-DeclarerImpl) -- [`DeployableImpl`](#AccountComponent-Embeddable-Impls-DeployableImpl) -- [`PublicKeyImpl`](#AccountComponent-Embeddable-Impls-PublicKeyImpl) -- [`SRC6CamelOnlyImpl`](#AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl) -- [`PublicKeyCamelImpl`](#AccountComponent-Embeddable-Impls-PublicKeyCamelImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) - -Embeddable Implementations - -#### SRC6Impl [!toc] [#AccountComponent-Embeddable-Impls-SRC6Impl] - -- [`__execute__(self, calls)`](#AccountComponent-__execute__) -- [`__validate__(self, calls)`](#AccountComponent-__validate__) -- [`is_valid_signature(self, hash, signature)`](#AccountComponent-is_valid_signature) - -#### DeclarerImpl [!toc] [#AccountComponent-Embeddable-Impls-DeclarerImpl] - -- [`__validate_declare__(self, class_hash)`](#AccountComponent-__validate_declare__) - -#### DeployableImpl [!toc] [#AccountComponent-Embeddable-Impls-DeployableImpl] - -- [`__validate_deploy__(self, hash, signature)`](#AccountComponent-__validate_deploy__) - -#### PublicKeyImpl [!toc] [#AccountComponent-Embeddable-Impls-PublicKeyImpl] - -- [`get_public_key(self)`](#AccountComponent-get_public_key) -- [`set_public_key(self, new_public_key, signature)`](#AccountComponent-set_public_key) - -#### SRC6CamelOnlyImpl [!toc] [#AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] - -- [`isValidSignature(self, hash, signature)`](#AccountComponent-isValidSignature) - -#### PublicKeyCamelImpl [!toc] [#AccountComponent-Embeddable-Impls-PublicKeyCamelImpl] - -- [`getPublicKey(self)`](#AccountComponent-getPublicKey) -- [`setPublicKey(self, newPublicKey, signature)`](#AccountComponent-setPublicKey) - -#### SRC5Impl [!toc] [#AccountComponent-Embeddable-Impls-SRC5Impl] - -- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) - -Internal Implementations - -#### InternalImpl [!toc] [#AccountComponent-InternalImpl] - -- [`initializer(self, public_key)`](#AccountComponent-initializer) -- [`assert_only_self(self)`](#AccountComponent-assert_only_self) -- [`assert_valid_new_owner(self, current_owner, new_owner, signature)`](#AccountComponent-assert_valid_new_owner) -- [`validate_transaction(self)`](#AccountComponent-validate_transaction) -- [`_set_public_key(self, new_public_key)`](#AccountComponent-_set_public_key) -- [`_is_valid_signature(self, hash, signature)`](#AccountComponent-_is_valid_signature) - -Events - -- [`OwnerAdded(new_owner_guid)`](#AccountComponent-OwnerAdded) -- [`OwnerRemoved(removed_owner_guid)`](#AccountComponent-OwnerRemoved) - -#### Embeddable functions [!toc] [#AccountComponent-Embeddable-Functions] - - -See [ISRC6::\_\_execute\_\_](#ISRC6-__execute__). - - - -See [ISRC6::\_\_validate\_\_](#ISRC6-__validate__). - - - -See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). - - - -Validates a [`Declare` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#declare-v3). - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - - -Validates a [`DeployAccount` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#deploy-account-v3). See [Counterfactual Deployments](../guides/deployment). - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - - -Returns the current public key of the account. - - - -Sets a new public key for the account. Only accessible by the account calling itself through `__execute__`. - -Requirements: - -- The caller must be the contract itself. -- The signature must be valid for the new owner. - -Emits both an [OwnerRemoved](#AccountComponent-OwnerRemoved) and an [OwnerAdded](#AccountComponent-OwnerAdded) event. - -The message to be signed is computed in Cairo as follows: - -```javascript -let message_hash = PoseidonTrait::new() - .update_with('StarkNet Message') - .update_with('accept_ownership') - .update_with(get_contract_address()) - .update_with(current_owner) - .finalize(); -``` - - - -See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). - - - -See [get\_public\_key](#AccountComponent-get_public_key). - - - -See [set\_public\_key](#AccountComponent-set_public_key). - - -#### Internal functions [!toc] [#AccountComponent-Internal-Functions] - - -Initializes the account with the given public key, and registers the `ISRC6` interface ID. - -Emits an [OwnerAdded](#AccountComponent-OwnerAdded) event. - - - -Validates that the caller is the account itself. Otherwise it reverts. - - - -Validates that `new_owner` accepted the ownership of the contract through a signature. - -Requirements: - -- `signature` must be valid for the new owner. - -This function assumes that `current_owner` is the current owner of the contract, and does not validate this assumption. - - - -Validates a transaction signature from the [global context](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/info.cairo#L61). - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - - -Set the public key without validating the caller. - -Emits an [OwnerAdded](#AccountComponent-OwnerAdded) event. - -The usage of this method outside the `set_public_key` function is discouraged. - - - -Validates the provided `signature` for the `hash`, using the account's current public key. - - -#### Events [!toc] [#AccountComponent-Events] - - -Emitted when a `public_key` is added. - - - -Emitted when a `public_key` is removed. - - -### `EthAccountComponent` [toc] [#EthAccountComponent] - - -```rust -use openzeppelin_account::eth_account::EthAccountComponent; -``` - -Account component implementing [`ISRC6`](#ISRC6) for signatures over the [Secp256k1 curve](https://en.bitcoin.it/wiki/Secp256k1). - -Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. - -The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Point`. - -[Embeddable Mixin Implementations](../components#mixins) - -#### EthAccountMixinImpl [!toc] [#EthAccountComponent-Embeddable-Impls-EthAccountMixinImpl] - -- [`SRC6Impl`](#EthAccountComponent-Embeddable-Impls-SRC6Impl) -- [`DeclarerImpl`](#EthAccountComponent-Embeddable-Impls-DeclarerImpl) -- [`DeployableImpl`](#EthAccountComponent-Embeddable-Impls-DeployableImpl) -- [`PublicKeyImpl`](#EthAccountComponent-Embeddable-Impls-PublicKeyImpl) -- [`SRC6CamelOnlyImpl`](#EthAccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl) -- [`PublicKeyCamelImpl`](#EthAccountComponent-Embeddable-Impls-PublicKeyCamelImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) - -Embeddable Implementations - -#### SRC6Impl [!toc] [#EthAccountComponent-Embeddable-Impls-SRC6Impl] - -- [`__execute__(self, calls)`](#EthAccountComponent-__execute__) -- [`__validate__(self, calls)`](#EthAccountComponent-__validate__) -- [`is_valid_signature(self, hash, signature)`](#EthAccountComponent-is_valid_signature) - -#### DeclarerImpl [!toc] [#EthAccountComponent-Embeddable-Impls-DeclarerImpl] - -- [`__validate_declare__(self, class_hash)`](#EthAccountComponent-__validate_declare__) - -#### DeployableImpl [!toc] [#EthAccountComponent-Embeddable-Impls-DeployableImpl] - -- [`__validate_deploy__(self, hash, signature)`](#EthAccountComponent-__validate_deploy__) - -#### PublicKeyImpl [!toc] [#EthAccountComponent-Embeddable-Impls-PublicKeyImpl] - -- [`get_public_key(self)`](#EthAccountComponent-get_public_key) -- [`set_public_key(self, new_public_key, signature)`](#EthAccountComponent-set_public_key) - -#### SRC6CamelOnlyImpl [!toc] [#EthAccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] - -- [`isValidSignature(self, hash, signature)`](#EthAccountComponent-isValidSignature) - -#### PublicKeyCamelImpl [!toc] [#EthAccountComponent-Embeddable-Impls-PublicKeyCamelImpl] - -- [`getPublicKey(self)`](#EthAccountComponent-getPublicKey) -- [`setPublicKey(self, newPublicKey, signature)`](#EthAccountComponent-setPublicKey) - -#### SRC5Impl [!toc] [#EthAccountComponent-Embeddable-Impls-SRC5Impl] - -- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) - -Internal Implementations - -#### InternalImpl [!toc] [#EthAccountComponent-InternalImpl] - -- [`initializer(self, public_key)`](#EthAccountComponent-initializer) -- [`assert_only_self(self)`](#EthAccountComponent-assert_only_self) -- [`assert_valid_new_owner(self, current_owner, new_owner, signature)`](#EthAccountComponent-assert_valid_new_owner) -- [`validate_transaction(self)`](#EthAccountComponent-validate_transaction) -- [`_set_public_key(self, new_public_key)`](#EthAccountComponent-_set_public_key) -- [`_is_valid_signature(self, hash, signature)`](#EthAccountComponent-_is_valid_signature) - -Events - -- [`OwnerAdded(new_owner_guid)`](#EthAccountComponent-OwnerAdded) -- [`OwnerRemoved(removed_owner_guid)`](#EthAccountComponent-OwnerRemoved) - -#### Embeddable functions [!toc] [#EthAccountComponent-Embeddable-Functions] - - -See [ISRC6::\_\_execute\_\_](#ISRC6-__execute__). - - - -See [ISRC6::\_\_validate\_\_](#ISRC6-__validate__). - - - -See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). - - - -Validates a [`Declare` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#declare-v3). - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - - -Validates a [`DeployAccount` transaction](https://docs.starknet.io/learn/cheatsheets/transactions-reference#deploy-account-v3). See [Counterfactual Deployments](../guides/deployment). - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - - -Returns the current public key of the account. - - - -Sets a new public key for the account. Only accessible by the account calling itself through `__execute__`. - -Requirements: - -- The caller must be the contract itself. -- The signature must be valid for the new owner. - -Emits both an [OwnerRemoved](#EthAccountComponent-OwnerRemoved) and an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. - -The message to be signed is computed in Cairo as follows: - -```javascript -let message_hash = PoseidonTrait::new() - .update_with('StarkNet Message') - .update_with('accept_ownership') - .update_with(get_contract_address()) - .update_with(current_owner.get_coordinates().unwrap_syscall()) - .finalize(); -``` - - - -See [ISRC6::is\_valid\_signature](#ISRC6-is_valid_signature). - - - -See [get\_public\_key](#EthAccountComponent-get_public_key). - - - -See [set\_public\_key](#EthAccountComponent-set_public_key). - - -#### Internal functions [!toc] [#EthAccountComponent-Internal-Functions] - - -Initializes the account with the given public key, and registers the `ISRC6` interface ID. - -Emits an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. - - - -Validates that the caller is the account itself. Otherwise it reverts. - - - -Validates that `new_owner` accepted the ownership of the contract through a signature. - -Requirements: - -- The signature must be valid for the `new_owner`. - -This function assumes that `current_owner` is the current owner of the contract, and does not validate this assumption. - - - -Validates a transaction signature from the [global context](https://github.com/starkware-libs/cairo/blob/main/corelib/src/starknet/info.cairo#L61). - -Returns the short string `'VALID'` if valid, otherwise it reverts. - - - -Set the public key without validating the caller. - -Emits an [OwnerAdded](#EthAccountComponent-OwnerAdded) event. - -The usage of this method outside the `set_public_key` function is discouraged. - - - -Validates the provided `signature` for the `hash`, using the account's current public key. - - -#### Events [!toc] [#EthAccountComponent-Events] - -The `guid` is computed as the hash of the public key, using the poseidon hash function. - - -Emitted when a `public_key` is added. - - - -Emitted when a `public_key` is removed. - - -## Extensions - -### `SRC9Component` [toc] [#SRC9Component] - - -```rust -use openzeppelin_account::extensions::SRC9Component; -``` - -OutsideExecution component implementing [`ISRC9_V2`](#ISRC9_V2). - -This component is signature-agnostic, meaning it can be integrated into any account contract, as long as the account implements the ISRC6 interface. - -Embeddable Implementations - -#### OutsideExecutionV2Impl [!toc] [#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl] - -- [`execute_from_outside_v2(self, outside_execution, signature)`](#SRC9Component-execute_from_outside_v2) -- [`is_valid_outside_execution_nonce(self, nonce)`](#SRC9Component-is_valid_outside_execution_nonce) - -Internal Implementations - -#### InternalImpl [!toc] [#SRC9Component-InternalImpl] - -- [`initializer(self)`](#SRC9Component-initializer) - -#### Embeddable functions [!toc] [#SRC9Component-Embeddable-Functions] - - -Allows anyone to submit a transaction on behalf of the account as long as they have the relevant signatures. - -This method allows reentrancy. A call to `__execute__` or `execute_from_outside_v2` can trigger another nested transaction to `execute_from_outside_v2`. This implementation verifies that the provided `signature` matches the hash of `outside_execution` and that `nonce` was not already used. - -Arguments: - -- `outside_execution` - The parameters of the transaction to execute. -- `signature` - A valid signature on the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message encoding of `outside_execution`. - -Requirements: - -- The caller must be the `outside_execution.caller` unless 'ANY\_CALLER' is used. -- The current time must be within the `outside_execution.execute_after` and `outside_execution.execute_before` span. -- The `outside_execution.nonce` must not be used before. -- The `signature` must be valid. - - - -Returns the status of a given nonce. `true` if the nonce is available to use. - - -#### Internal functions [!toc] [#SRC9Component-Internal-Functions] - - -Initializes the account by registering the `ISRC9_V2` interface ID. - - -## Presets - -### `AccountUpgradeable` [toc] [#AccountUpgradeable] - - -```rust -use openzeppelin_presets::AccountUpgradeable; -``` - -Upgradeable account which can change its public key and declare, deploy, or call contracts. Supports outside execution by implementing [SRC9](#SRC9Component). - -[Sierra class hash](../presets) - -```text -{{AccountUpgradeableClassHash}} -``` - -Constructor - -- [`constructor(self, public_key)`](#AccountUpgradeable-constructor) - -Embedded Implementations - -AccountComponent - -- [`AccountMixinImpl`](#AccountComponent-Embeddable-Mixin-Impl) - -SRC9Component - -- [`OutsideExecutionV2Impl`](#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl) - -External Functions - -- [`upgrade(self, new_class_hash)`](#AccountUpgradeable-upgrade) - -#### Constructor [!toc] [#AccountUpgradeable-Constructor] - - -Sets the account `public_key` and registers the interfaces the contract supports. - - -#### External functions [!toc] [#AccountUpgradeable-External-Functions] - - -Upgrades the contract to a new implementation given by `new_class_hash`. - -Requirements: - -- The caller is the account contract itself. -- `new_class_hash` cannot be zero. - - -### `EthAccountUpgradeable` [toc] [#EthAccountUpgradeable] - - -```rust -use openzeppelin_presets::EthAccountUpgradeable; -``` - -Upgradeable account which can change its public key and declare, deploy, or call contracts, using Ethereum signing keys. Supports outside execution by implementing [SRC9](#SRC9Component). - -The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Point`. - -[Sierra class hash](../presets) - -```text -{{EthAccountUpgradeableClassHash}} -``` - -Constructor - -- [`constructor(self, public_key)`](#EthAccountUpgradeable-constructor) - -Embedded Implementations - -EthAccountComponent - -- [`EthAccountMixinImpl`](#EthAccountComponent-Embeddable-Mixin-Impl) - -SRC9Component - -- [`OutsideExecutionV2Impl`](#SRC9Component-Embeddable-Impls-OutsideExecutionV2Impl) - -External Functions - -- [`upgrade(self, new_class_hash)`](#EthAccountUpgradeable-upgrade) - -#### Constructor [!toc] [#EthAccountUpgradeable-Constructor] - - -Sets the account `public_key` and registers the interfaces the contract supports. - - -#### External functions [!toc] [#EthAccountUpgradeable-External-Functions] - - -Upgrades the contract to a new implementation given by `new_class_hash`. - -Requirements: - -- The caller is the account contract itself. -- `new_class_hash` cannot be zero. - diff --git a/content/contracts-cairo/alpha/api/erc1155.mdx b/content/contracts-cairo/alpha/api/erc1155.mdx deleted file mode 100644 index 56cc5d5c..00000000 --- a/content/contracts-cairo/alpha/api/erc1155.mdx +++ /dev/null @@ -1,788 +0,0 @@ ---- -title: ERC1155 ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This module provides interfaces, presets, and utilities related to ERC1155 contracts. - -For an overview of ERC1155, read our [ERC1155 guide](../erc1155). - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -### `IERC1155` [toc] [#IERC1155] - - - -```rust -use openzeppelin_interfaces::erc1155::IERC1155; -``` - -Interface of the IERC1155 standard as defined in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155). - -[SRC5 ID](./introspection#ISRC5) - -```text -0x6114a8f75559e1b39fcba08ce02961a1aa082d9256a158dd3e64964e4b1b52 -``` - -Functions - -- [`balance_of(account, token_id)`](#IERC1155-balance_of) -- [`balance_of_batch(accounts, token_ids)`](#IERC1155-balance_of_batch) -- [`safe_transfer_from(from, to, token_id, value, data)`](#IERC1155-safe_transfer_from) -- [`safe_batch_transfer_from(from, to, token_ids, values, data)`](#IERC1155-safe_batch_transfer_from) -- [`set_approval_for_all(operator, approved)`](#IERC1155-set_approval_for_all) -- [`is_approved_for_all(owner, operator)`](#IERC1155-is_approved_for_all) - -Events - -- [`TransferSingle(operator, from, to, id, value)`](#IERC1155-TransferSingle) -- [`TransferBatch(operator, from, to, ids, values)`](#IERC1155-TransferBatch) -- [`ApprovalForAll(owner, operator, approved)`](#IERC1155-ApprovalForAll) -- [`URI(value, id)`](#IERC1155-URI) - -#### Functions [!toc] [#functions] - - -Returns the amount of `token_id` tokens owned by `account`. - - - -Returns a list of balances derived from the `accounts` and `token_ids` pairs. - - - -Transfers ownership of `value` amount of `token_id` from `from` if `to` is either `IERC1155Receiver` or an account. - -`data` is additional data, it has no specified format and it is passed to `to`. - -Emits a [TransferSingle](#IERC1155-TransferSingle) event. - - - -Transfers ownership of `token_ids` and `values` pairs from `from` if `to` is either `IERC1155Receiver` or an account. - -`data` is additional data, it has no specified format and it is passed to `to`. - -Emits a [TransferBatch](#IERC1155-TransferBatch) event. - - - -Enables or disables approval for `operator` to manage all of the caller's assets. - -Emits an [ApprovalForAll](#IERC1155-ApprovalForAll) event. - - - -Queries if `operator` is an authorized operator for `owner`. - - -#### Events [!toc] [#events] - - -Emitted when `value` amount of `id` token is transferred from `from` to `to` through `operator`. - - - -Emitted when a batch of `values` amount of `ids` tokens are transferred from `from` to `to` through `operator`. - - - -Emitted when `owner` enables or disables `operator` to manage all of the owner's assets. - - - -Emitted when the token URI is updated to `value` for the `id` token. - - -### `IERC1155MetadataURI` [toc] [#IERC1155MetadataURI] - - - -```rust -use openzeppelin_interfaces::erc1155::IERC1155MetadataURI; -``` - -Interface for the optional metadata function in [EIP1155](https://eips.ethereum.org/EIPS/eip-1155#metadata). - -[SRC5 ID](./introspection#ISRC5) - -```text -0xcabe2400d5fe509e1735ba9bad205ba5f3ca6e062da406f72f113feb889ef7 -``` - -Functions - -- [`uri(token_id)`](#IERC1155MetadataURI-uri) - -#### Functions [!toc] [#functions_2] - - -Returns the Uniform Resource Identifier (URI) for the `token_id` token. - - -### `IERC1155Receiver` [toc] [#IERC1155Receiver] - - - -```rust -use openzeppelin_interfaces::erc1155::IERC1155Receiver; -``` - -Interface for contracts that support receiving token transfers from `ERC1155` contracts. - -[SRC5 ID](./introspection#ISRC5) - -```text -0x15e8665b5af20040c3af1670509df02eb916375cdf7d8cbaf7bd553a257515e -``` - -Functions - -- [`on_erc1155_received(operator, from, token_id, value, data)`](#IERC1155Receiver-on_erc1155_received) -- [`on_erc1155_batch_received(operator, from, token_ids, values, data)`](#IERC1155Receiver-on_erc1155_batch_received) - -#### Functions [!toc] [#functions_3] - - -This function is called whenever an ERC1155 `token_id` token is transferred to this `IERC1155Receiver` implementer via [IERC1155::safe\_transfer\_from](#IERC1155-safe_transfer_from) by `operator` from `from`. - - - -This function is called whenever multiple ERC1155 `token_ids` tokens are transferred to this `IERC1155Receiver` implementer via [IERC1155::safe\_batch\_transfer\_from](#IERC1155-safe_batch_transfer_from) by `operator` from `from`. - - -## [](#core)Core - -### `ERC1155Component` [toc] [#ERC1155Component] - - - -```rust -use openzeppelin_token::erc1155::ERC1155Component; -``` - -ERC1155 component implementing [IERC1155](#IERC1155) and [IERC1155MetadataURI](#IERC1155MetadataURI). - -Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. - -See [Hooks](#ERC1155Component-Hooks) to understand how are hooks used. - -#### Hooks [!toc] [#ERC1155Component-Hooks] - -#### ERC1155HooksTrait [!toc] [#ERC1155Component-ERC1155HooksTrait] - -- [`before_update(self, from, to, token_ids, values)`](#ERC1155Component-before_update) -- [`after_update(self, from, to, token_ids, values)`](#ERC1155Component-after_update) - -[Embeddable Mixin Implementations](../components#mixins) - -#### ERC1155MixinImpl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155MixinImpl] - -- [`ERC1155Impl`](#ERC1155Component-Embeddable-Impls-ERC1155Impl) -- [`ERC1155MetadataURIImpl`](#ERC1155Component-Embeddable-Impls-ERC1155MetadataURIImpl) -- [`ERC1155CamelImpl`](#ERC1155Component-Embeddable-Impls-ERC1155CamelImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls-SRC5Impl) - -Embeddable Implementations - -#### ERC1155Impl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155Impl] - -- [`balance_of(self, account, token_id)`](#ERC1155Component-balance_of) -- [`balance_of_batch(self, accounts, token_ids)`](#ERC1155Component-balance_of_batch) -- [`safe_transfer_from(self, from, to, token_id, value, data)`](#ERC1155Component-safe_transfer_from) -- [`safe_batch_transfer_from(self, from, to, token_ids, values, data)`](#ERC1155Component-safe_batch_transfer_from) -- [`set_approval_for_all(self, operator, approved)`](#ERC1155Component-set_approval_for_all) -- [`is_approved_for_all(self, owner, operator)`](#ERC1155Component-is_approved_for_all) - -#### ERC1155MetadataURIImpl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155MetadataURIImpl] - -- [`uri(self, token_id)`](#ERC1155Component-uri) - -#### ERC1155CamelImpl [!toc] [#ERC1155Component-Embeddable-Impls-ERC1155CamelImpl] - -- [`balanceOf(self, account, tokenId)`](#ERC1155Component-balanceOf) -- [`balanceOfBatch(self, accounts, tokenIds)`](#ERC1155Component-balanceOfBatch) -- [`safeTransferFrom(self, from, to, tokenId, value, data)`](#ERC1155Component-safeTransferFrom) -- [`safeBatchTransferFrom(self, from, to, tokenIds, values, data)`](#ERC1155Component-safeBatchTransferFrom) -- [`setApprovalForAll(self, operator, approved)`](#ERC1155Component-setApprovalForAll) -- [`isApprovedForAll(self, owner, operator)`](#ERC1155Component-isApprovedForAll) - -Internal Functions - -#### InternalImpl [!toc] [#ERC1155Component-InternalImpl] - -- [`initializer(self, base_uri)`](#ERC1155Component-initializer) -- [`initializer_no_metadata(self)`](#ERC1155Component-initializer_no_metadata) -- [`mint_with_acceptance_check(self, to, token_id, value, data)`](#ERC1155Component-mint_with_acceptance_check) -- [`batch_mint_with_acceptance_check(self, to, token_ids, values, data)`](#ERC1155Component-batch_mint_with_acceptance_check) -- [`burn(self, from, token_id, value)`](#ERC1155Component-burn) -- [`batch_burn(self, from, token_ids, values)`](#ERC1155Component-batch_burn) -- [`update_with_acceptance_check(self, from, to, token_ids, values, data)`](#ERC1155Component-update_with_acceptance_check) -- [`update(self, from, to, token_ids, values)`](#ERC1155Component-update) -- [`_set_base_uri(self, base_uri)`](#ERC1155Component-_set_base_uri) - -Events - -IERC1155 - -- [`TransferSingle(operator, from, to, id, value)`](#ERC1155Component-TransferSingle) -- [`TransferBatch(operator, from, to, ids, values)`](#ERC1155Component-TransferBatch) -- [`ApprovalForAll(owner, operator, approved)`](#ERC1155Component-ApprovalForAll) -- [`URI(value, id)`](#ERC1155Component-URI) - -Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC1155Component is expected to provide an implementation of the ERC1155HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. - -You can use `openzeppelin_token::erc1155::ERC1155HooksEmptyImpl` which is already available as part of the library for this purpose. - - -Function executed at the beginning of the [update](#ERC1155Component-update) function prior to any other logic. - - - -Function executed at the end of the [update](#ERC1155Component-update) function. - - -#### Embeddable functions [!toc] [#embeddable_functions] - - -Returns the amount of `token_id` tokens owned by `account`. - - - -Returns a list of balances derived from the `accounts` and `token_ids` pairs. - -Requirements: - -- `token_ids` and `accounts` must have the same length. - - - -Transfers ownership of `value` amount of `token_id` from `from` if `to` is either an account or `IERC1155Receiver`. - -`data` is additional data, it has no specified format and it is passed to `to`. - -This function can potentially allow a reentrancy attack when transferring tokens to an untrusted contract, when invoking `on_ERC1155_received` on the receiver. Ensure to follow the checks-effects-interactions pattern and consider employing reentrancy guards when interacting with untrusted contracts. - -Requirements: - -- Caller is either approved or the `token_id` owner. -- `from` is not the zero address. -- `to` is not the zero address. -- If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_received` and return the required magic value. - -Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. - - - -Transfers ownership of `values` and `token_ids` pairs from `from` if `to` is either an account or `IERC1155Receiver`. - -`data` is additional data, it has no specified format and it is passed to `to`. - -This function can potentially allow a reentrancy attack when transferring tokens to an untrusted contract, when invoking `on_ERC1155_batch_received` on the receiver. Ensure to follow the checks-effects-interactions pattern and consider employing reentrancy guards when interacting with untrusted contracts. - -Requirements: - -- Caller is either approved or the `token_id` owner. -- `from` is not the zero address. -- `to` is not the zero address. -- `token_ids` and `values` must have the same length. -- If `to` refers to a non-account contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` and return the acceptance magic value. - -Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. - - - -Enables or disables approval for `operator` to manage all of the callers assets. - -Requirements: - -- `operator` cannot be the caller. - -Emits an [ApprovalForAll](#ERC1155Component-ApprovalForAll) event. - - - -Queries if `operator` is an authorized operator for `owner`. - - - - -This implementation returns the same URI for **all** token types. It relies on the token type ID substitution mechanism [specified in the EIP](https://eips.ethereum.org/EIPS/eip-1155#metadata). - -Clients calling this function must replace the `id` substring with the actual token type ID. - - - -See [ERC1155Component::balance\_of](#ERC1155Component-balance_of). - - - -See [ERC1155Component::balance\_of\_batch](#ERC1155Component-balance_of_batch). - - - -See [ERC1155Component::safe\_transfer\_from](#ERC1155Component-safe_transfer_from). - - - -See [ERC1155Component::safe\_batch\_transfer\_from](#ERC1155Component-safe_batch_transfer_from). - - - -See [ERC1155Component::set\_approval\_for\_all](#ERC1155Component-set_approval_for_all). - - - -See [ERC1155Component::is\_approved\_for\_all](#ERC1155Component-is_approved_for_all). - - -#### Internal functions [!toc] [#internal_functions] - - -Initializes the contract by setting the token's base URI as `base_uri`, and registering the supported interfaces. This should only be used inside the contract's constructor. - -Most ERC1155 contracts expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface which is what this initializer is meant to support. If the contract DOES NOT expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface, meaning tokens do not have a URI, the contract must instead use [initializer\_no\_metadata](#ERC1155Component-initializer_no_metadata) in the constructor. Failure to abide by these instructions can lead to unexpected issues especially with UIs. - - - -Initializes the contract with no metadata by registering only the IERC1155 interface. - -This initializer should ONLY be used during construction in the very specific instance when the contract does NOT expose the [IERC1155MetadataURI](#IERC1155MetadataURI) interface. Initializing a contract with this initializer means that tokens will not have a URI. - - - -Creates a `value` amount of tokens of type `token_id`, and assigns them to `to`. - -Requirements: - -- `to` cannot be the zero address. -- If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_received` and return the acceptance magic value. - -Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. - - - -Batched version of [mint\_with\_acceptance\_check](#ERC1155Component-mint_with_acceptance_check). - -Requirements: - -- `to` cannot be the zero address. -- `token_ids` and `values` must have the same length. -- If `to` refers to a smart contract, it must implement `IERC1155Receiver::on_ERC1155_batch_received` and return the acceptance magic value. - -Emits a [TransferBatch](#ERC1155Component-TransferBatch) event. - - - -Destroys a `value` amount of tokens of type `token_id` from `from`. - -Requirements: - -- `from` cannot be the zero address. -- `from` must have at least `value` amount of tokens of type `token_id`. - -Emits a [TransferSingle](#ERC1155Component-TransferSingle) event. - - - -Batched version of [burn](#ERC1155Component-burn). - -Requirements: - -- `from` cannot be the zero address. -- `from` must have at least `value` amount of tokens of type `token_id`. -- `token_ids` and `values` must have the same length. - -Emits a [TransferBatch](#ERC1155Component-TransferBatch) event. - - - -Version of `update` that performs the token acceptance check by calling `onERC1155Received` or `onERC1155BatchReceived` in the receiver if it implements `IERC1155Receiver`, otherwise by checking if it is an account. - -Requirements: - -- `to` is either an account contract or supports the `IERC1155Receiver` interface. -- `token_ids` and `values` must have the same length. - -Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. - - - -Transfers a `value` amount of tokens of type `id` from `from` to `to`. Will mint (or burn) if `from` (or `to`) is the zero address. - -Requirements: - -- `token_ids` and `values` must have the same length. - -Emits a [TransferSingle](#ERC1155Component-TransferSingle) event if the arrays contain one element, and [TransferBatch](#ERC1155Component-TransferBatch) otherwise. - -This function can be extended using the [ERC1155HooksTrait](#ERC1155Component-ERC1155HooksTrait), to add functionality before and/or after the transfer, mint, or burn. - -The ERC1155 acceptance check is not performed in this function. See [update\_with\_acceptance\_check](#ERC1155Component-update_with_acceptance_check) instead. - - - -Sets a new URI for all token types, by relying on the token type ID substitution mechanism [specified in the EIP](https://eips.ethereum.org/EIPS/eip-1155#metadata). - -By this mechanism, any occurrence of the `id` substring in either the URI or any of the values in the JSON file at said URI will be replaced by clients with the token type ID. - -For example, the `https://token-cdn-domain/\id\.json` URI would be interpreted by clients as `https://token-cdn-domain/000000000000...000000000000004cce0.json` for token type ID `0x4cce0`. - -Because these URIs cannot be meaningfully represented by the `URI` event, this function emits no events. - - -#### Events [!toc] [#events_2] - - -See [IERC1155::TransferSingle](#IERC1155-TransferSingle). - - - -See [IERC1155::TransferBatch](#IERC1155-TransferBatch). - - - -See [IERC1155::ApprovalForAll](#IERC1155-ApprovalForAll). - - - - -See [IERC1155::URI](#IERC1155-URI). - - -### `ERC1155ReceiverComponent` [toc] [#ERC1155ReceiverComponent] - - - -```rust -use openzeppelin_token::erc1155::ERC1155ReceiverComponent; -``` - -ERC1155Receiver component implementing [IERC1155Receiver](#IERC1155Receiver). - -Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. - -[Embeddable Mixin Implementations](../components#mixins) - -#### ERC1155MixinImpl [!toc] [#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155MixinImpl] - -- [`ERC1155ReceiverImpl`](#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverImpl) -- [`ERC1155ReceiverCamelImpl`](#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverCamelImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls-SRC5Impl) - -Embeddable Implementations - -#### ERC1155ReceiverImpl [!toc] [#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverImpl] - -- [`on_erc1155_received(self, operator, from, token_id, value, data)`](#ERC1155ReceiverComponent-on_erc1155_received) -- [`on_erc1155_batch_received(self, operator, from, token_ids, values, data)`](#ERC1155ReceiverComponent-on_erc1155_batch_received) - -#### ERC1155ReceiverCamelImpl [!toc] [#ERC1155ReceiverComponent-Embeddable-Impls-ERC1155ReceiverCamelImpl] - -- [`onERC1155Received(self, operator, from, tokenId, value, data)`](#ERC1155ReceiverComponent-onERC1155Received) -- [`onERC1155BatchReceived(self, operator, from, tokenIds, values, data)`](#ERC1155ReceiverComponent-onERC1155BatchReceived) - -Internal Functions - -#### InternalImpl [!toc] [#ERC1155ReceiverComponent-InternalImpl] - -- [`initializer(self)`](#ERC1155ReceiverComponent-initializer) - -#### Embeddable functions [!toc] [#embeddable_functions_2] - - -Returns the `IERC1155Receiver` interface ID. - - - -Returns the `IERC1155Receiver` interface ID. - - - -See [ERC1155ReceiverComponent::on\_erc1155\_received](#ERC1155ReceiverComponent-on_erc1155_received). - - - -See [ERC1155ReceiverComponent::on\_erc1155\_batch\_received](#ERC1155ReceiverComponent-on_erc1155_batch_received). - - -#### Internal functions [!toc] [#internal_functions_2] - - -Registers the `IERC1155Receiver` interface ID as supported through introspection. - - -## [](#presets)Presets - -### `ERC1155Upgradeable` [toc] [#ERC1155Upgradeable] - - - -```rust -use openzeppelin_presets::ERC1155; -``` - -Upgradeable ERC1155 contract leveraging [ERC1155Component](#ERC1155Component). - -[Sierra class hash](../presets) - -```text -{{ERC1155UpgradeableClassHash}} -``` - -Constructor - -- [`constructor(self, base_uri, recipient, token_ids, values, owner)`](#ERC1155Upgradeable-constructor) - -Embedded Implementations - -ERC1155Component - -- [`ERC1155MixinImpl`](#ERC1155Component-Embeddable-Mixin-Impl) - -OwnableMixinImpl - -- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) - -External Functions - -- [`upgrade(self, new_class_hash)`](#ERC1155Upgradeable-upgrade) - -#### Constructor [!toc] [#ERC1155Upgradeable-constructor-section] - - -Sets the `base_uri` for all tokens and registers the supported interfaces. Mints the `values` for `token_ids` tokens to `recipient`. Assigns `owner` as the contract owner with permissions to upgrade. - -Requirements: - -- `to` is either an account contract (supporting ISRC6) or supports the `IERC1155Receiver` interface. -- `token_ids` and `values` must have the same length. - - -#### External Functions [!toc] [#ERC1155Upgradeable-external-functions] - - -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. - diff --git a/content/contracts-cairo/alpha/api/erc20.mdx b/content/contracts-cairo/alpha/api/erc20.mdx deleted file mode 100644 index c7d28f93..00000000 --- a/content/contracts-cairo/alpha/api/erc20.mdx +++ /dev/null @@ -1,1585 +0,0 @@ ---- -title: ERC20 ---- - -This module provides interfaces, presets, and utilities related to ERC20 contracts. - -For an overview of ERC20, read our [ERC20 guide](../erc20). - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -### `IERC20` [toc] [#IERC20] - - -```rust -use openzeppelin_interfaces::erc20::IERC20; -``` - -Interface of the IERC20 standard as defined in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). - -Functions - -- [`total_supply()`](#IERC20-total_supply) -- [`balance_of(account)`](#IERC20-balance_of) -- [`allowance(owner, spender)`](#IERC20-allowance) -- [`transfer(recipient, amount)`](#IERC20-transfer) -- [`transfer_from(sender, recipient, amount)`](#IERC20-transfer_from) -- [`approve(spender, amount)`](#IERC20-approve) - -Events - -- [`Transfer(from, to, value)`](#IERC20-Transfer) -- [`Approval(owner, spender, value)`](#IERC20-Approval) - -#### Functions [!toc] [#IERC20-Functions] - - -Returns the amount of tokens in existence. - - - -Returns the amount of tokens owned by `account`. - - - -Returns the remaining number of tokens that `spender` is allowed to spend on behalf of `owner` through [transfer\_from](#transfer_from). This is zero by default. - -This value changes when [approve](#IERC20-approve) or [transfer\_from](#IERC20-transfer_from) are called. - - - -Moves `amount` tokens from the caller's token balance to `to`. Returns `true` on success, reverts otherwise. - -Emits a [Transfer](#IERC20-Transfer) event. - - - -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](#IERC20-Transfer) event. - - - -Sets `amount` as the allowance of `spender` over the caller's tokens. Returns `true` on success, reverts otherwise. - -Emits an [Approval](#ERC20-Approval) event. - - -#### Events [!toc] [#IERC20-Events] - - -Emitted when `value` tokens are moved from one address (`from`) to another (`to`). - -Note that `value` may be zero. - - - -Emitted when the allowance of a `spender` for an `owner` is set. `value` is the new allowance. - - -### `IERC20Metadata` [toc] [#IERC20Metadata] - - -```rust -use openzeppelin_interfaces::erc20::IERC20Metadata; -``` - -Interface for the optional metadata functions in [EIP-20](https://eips.ethereum.org/EIPS/eip-20). - -Functions - -- [`name()`](#IERC20Metadata-name) -- [`symbol()`](#IERC20Metadata-symbol) -- [`decimals()`](#IERC20Metadata-decimals) - -#### Functions [!toc] [#IERC20Metadata-Functions] - - -Returns the name of the token. - - - -Returns the ticker symbol of the token. - - - -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](../erc20#customizing_decimals). - -This information is only used for *display* purposes: it in no way affects any of the arithmetic of the contract. - - -### `IERC20Permit` [toc] [#IERC20Permit] - - -```rust -use openzeppelin_interfaces::erc20::IERC20Permit; -``` - -Interface of the ERC20Permit standard to support gasless token approvals as defined in [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612). - -Functions - -- [`permit(owner, spender, amount, deadline, signature)`](#IERC20Permit-permit) -- [`nonces(owner)`](#IERC20Permit-nonces) -- [`DOMAIN_SEPARATOR()`](#IERC20Permit-DOMAIN_SEPARATOR) - -#### Functions [!toc] [#IERC20Permit-Functions] - - -Sets `amount` as the allowance of `spender` over `owner`'s tokens after validating the signature. - - - -Returns the current nonce of `owner`. A nonce value must be included whenever a signature for `permit` call is generated. - - - -Returns the domain separator used in generating a message hash for `permit` signature. The domain hashing logic follows the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard. - - -### `IERC4626` [toc] [#IERC4626] - - -```rust -use openzeppelin_interfaces::erc4626::IERC4626; -``` - -Interface of the IERC4626 standard as defined in [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626). - -Functions - -- [`asset()`](#IERC4626-asset) -- [`total_assets()`](#IERC4626-total_assets) -- [`convert_to_shares(assets)`](#IERC4626-convert_to_shares) -- [`convert_to_assets(shares)`](#IERC4626-convert_to_assets) -- [`max_deposit(receiver)`](#IERC4626-max_deposit) -- [`preview_deposit(assets)`](#IERC4626-preview_deposit) -- [`deposit(assets, receiver)`](#IERC4626-deposit) -- [`max_mint(receiver)`](#IERC4626-max_mint) -- [`preview_mint(shares)`](#IERC4626-preview_mint) -- [`mint(shares, receiver)`](#IERC4626-mint) -- [`max_withdraw(owner)`](#IERC4626-max_withdraw) -- [`preview_withdraw(assets)`](#IERC4626-preview_withdraw) -- [`withdraw(assets, receiver, owner)`](#IERC4626-withdraw) -- [`max_redeem(owner)`](#IERC4626-max_redeem) -- [`preview_redeem(shares)`](#IERC4626-preview_redeem) -- [`redeem(shares, receiver, owner)`](#IERC4626-redeem) - -Events - -- [`Deposit(sender, owner, assets, shares)`](#IERC4626-Deposit) -- [`Withdraw(sender, receiver, owner, assets, shares)`](#IERC4626-Withdraw) - -#### Functions [!toc] [#IERC4626-Functions] - - -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. - - - -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. - - - -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. - - - -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. - - - -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. - - - -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](#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](#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](#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. - - - -Mints Vault shares to `receiver` by depositing exactly amount of `assets`. - -Requirements: - -- MUST emit the [IERC4626::Deposit](#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. - - - -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. - - - -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](#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](#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](#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. - - - -Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. - -Requirements: - -- MUST emit the [IERC4626::Deposit](#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. - - - -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. - - - -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](#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](#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](#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. - - - -Burns shares from owner and sends exactly assets of underlying tokens to receiver. - -Requirements: - -- MUST emit the [IERC4626::Withdraw](#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. - - - -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. - - - -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](#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](#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](#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. - - - -Burns exactly shares from owner and sends assets of underlying tokens to receiver. - -Requirements: - -- MUST emit the [IERC4626::Withdraw](#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 [!toc] [#IERC4626-Events] - - -Emitted when `sender` exchanges `assets` for `shares` and transfers those `shares` to `owner`. - - - -Emitted when `sender` exchanges `shares`, owned by `owner`, for `assets` and transfers those `assets` to `receiver`. - - -## Core - -### `ERC20Component` [toc] [#ERC20Component] - - -```rust -use openzeppelin_token::erc20::ERC20Component; -``` - -ERC20 component extending [IERC20](#IERC20) and [IERC20Metadata](#IERC20Metadata). - -See [Hooks](#ERC20Component-Hooks) to understand how are hooks used. - -Hooks - -#### ERC20HooksTrait [!toc] [#ERC20Component-ERC20HooksTrait] - -- [`before_update(self, from, recipient, amount)`](#ERC20Component-before_update) -- [`after_update(self, from, recipient, amount)`](#ERC20Component-after_update) - -[Embeddable Mixin Implementations](../components#mixins) - -#### ERC20MixinImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20MixinImpl] - -- [`ERC20Impl`](#ERC20Component-Embeddable-Impls-ERC20Impl) -- [`ERC20MetadataImpl`](#ERC20Component-Embeddable-Impls-ERC20MetadataImpl) -- [`ERC20CamelOnlyImpl`](#ERC20Component-Embeddable-Impls-ERC20CamelOnlyImpl) - -Embeddable Implementations - -#### ERC20Impl [!toc] [#ERC20Component-Embeddable-Impls-ERC20Impl] - -- [`total_supply(self)`](#ERC20Component-total_supply) -- [`balance_of(self, account)`](#ERC20Component-balance_of) -- [`allowance(self, owner, spender)`](#ERC20Component-allowance) -- [`transfer(self, recipient, amount)`](#ERC20Component-transfer) -- [`transfer_from(self, sender, recipient, amount)`](#ERC20Component-transfer_from) -- [`approve(self, spender, amount)`](#ERC20Component-approve) - -#### ERC20MetadataImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20MetadataImpl] - -- [`name(self)`](#ERC20Component-name) -- [`symbol(self)`](#ERC20Component-symbol) -- [`decimals(self)`](#ERC20Component-decimals) - -#### ERC20CamelOnlyImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20CamelOnlyImpl] - -- [`totalSupply(self)`](#ERC20Component-totalSupply) -- [`balanceOf(self, account)`](#ERC20Component-balanceOf) -- [`transferFrom(self, sender, recipient, amount)`](#ERC20Component-transferFrom) - -#### ERC20PermitImpl [!toc] [#ERC20Component-Embeddable-Impls-ERC20PermitImpl] - -- [`permit(self, owner, spender, amount, deadline, signature)`](#ERC20Component-permit) -- [`nonces(self, owner)`](#ERC20Component-nonces) -- [`DOMAIN_SEPARATOR(self)`](#ERC20Component-DOMAIN_SEPARATOR) - -#### SNIP12MetadataExternalImpl [!toc] [#ERC20Component-Embeddable-Impls-SNIP12MetadataExternalImpl] - -- [`snip12_metadata(self)`](#ERC20Component-snip12_metadata) - -Internal implementations - -#### InternalImpl [!toc] [#ERC20Component-InternalImpl] - -- [`initializer(self, name, symbol)`](#ERC20Component-initializer) -- [`mint(self, recipient, amount)`](#ERC20Component-mint) -- [`burn(self, account, amount)`](#ERC20Component-burn) -- [`update(self, from, to, amount)`](#ERC20Component-update) -- [`_transfer(self, sender, recipient, amount)`](#ERC20Component-_transfer) -- [`_approve(self, owner, spender, amount)`](#ERC20Component-_approve) -- [`_spend_allowance(self, owner, spender, amount)`](#ERC20Component-_spend_allowance) - -Events - -- [`Transfer(from, to, value)`](#ERC20Component-Transfer) -- [`Approval(owner, spender, value)`](#ERC20Component-Approval) - -#### Hooks [!toc] [#ERC20Component-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. - - -Function executed at the beginning of the [update](#ERC20Component-update) function prior to any other logic. - - - -Function executed at the end of the [update](#ERC20Component-update) function. - - -#### Embeddable functions [!toc] [#ERC20Component-Embeddable-Functions] - - -See [IERC20::total\_supply](#IERC20-total_supply). - - - -See [IERC20::balance\_of](#IERC20-balance_of). - - - -See [IERC20::allowance](#IERC20-allowance). - - - -See [IERC20::transfer](#IERC20-transfer). - -Requirements: - -- `recipient` cannot be the zero address. -- The caller must have a balance of at least `amount`. - - - -See [IERC20::transfer\_from](#IERC20-transfer_from). - -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`. - - - -See [IERC20::approve](#IERC20-approve). - -Requirements: - -- `spender` cannot be the zero address. - - - -See [IERC20Metadata::name](#IERC20Metadata-name). - - - -See [IERC20Metadata::symbol](#IERC20Metadata-symbol). - - - -See [IERC20Metadata::decimals](#IERC20Metadata-decimals). - - - -See [IERC20::total\_supply](#IERC20-total_supply). - -Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). - - - -See [IERC20::balance\_of](#IERC20-balance_of). - -Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). - - - -See [IERC20::transfer\_from](#IERC20-transfer_from). - -Supports the Cairo v0 convention of writing external methods in camelCase as discussed [here](https://github.com/OpenZeppelin/cairo-contracts/discussions/34). - - - -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](#ERC20-Approval) event. Every successful call increases \`owner's nonce by one. - - - -Returns the current nonce of `owner`. A nonce value must be included whenever a signature for `permit` call is generated. - - - -Returns the domain separator used in generating a message hash for `permit` signature. The domain hashing logic follows the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard. - - - -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](./utilities#snip12) implementation. -- `t.1`: The version used in the [SNIP12Metadata](./utilities#snip12) implementation. - - -#### Internal functions [!toc] [#ERC20Component-Internal-Functions] - - -Initializes the contract by setting the token name and symbol. This should be used inside of the contract's constructor. - - - -Creates an `amount` number of tokens and assigns them to `recipient`. - -Emits a [Transfer](#ERC20Component-Transfer) event with `from` being the zero address. - -Requirements: - -- `recipient` cannot be the zero address. - - - -Destroys `amount` number of tokens from `account`. - -Emits a [Transfer](#ERC20Component-Transfer) event with `to` set to the zero address. - -Requirements: - -- `account` cannot be the zero address. - - - -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](#ERC20Component-ERC20HooksTrait), to add functionality before and/or after the transfer, mint, or burn. - -Emits a [Transfer](#ERC20Component-Transfer) event. - - - -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](#ERC20Component-Transfer) event. - -Requirements: - -- `from` cannot be the zero address. -- `to` cannot be the zero address. -- `from` must have a balance of at least `amount`. - - - -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](#ERC20Component-Approval) event. - -Requirements: - -- `owner` cannot be the zero address. -- `spender` cannot be the zero address. - - - -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](#ERC20Component-Approval) event. - - -#### Events [!toc] [#ERC20Component-Events] - - -See [IERC20::Transfer](#IERC20-Transfer). - - - -See [IERC20::Approval](#IERC20-Approval). - - -## Extensions - -### `ERC4626Component` [toc] [#ERC4626Component] - - -```rust -use openzeppelin_token::erc20::extensions::erc4626::ERC4626Component; -``` - -Extension of ERC20 that implements the [IERC4626](#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 Component Config](../components#immutable-config) - -#### constants [!toc] [#ERC4626Component-constants] - -- [`UNDERLYING_DECIMALS`](#ERC4626Component-IC-UNDERLYING_DECIMALS) -- [`DECIMALS_OFFSET`](#ERC4626Component-IC-DECIMALS_OFFSET) - -#### functions [!toc] [#ERC4626Component-functions] - -- [`validate()`](#ERC4626Component-IC-validate) - -Hooks - -#### FeeConfigTrait [!toc] [#ERC4626Component-FeeConfigTrait] - -- [`calculate_deposit_fee(self, assets, shares)`](#ERC4626Component-calculate_deposit_fee) -- [`calculate_mint_fee(self, assets, shares)`](#ERC4626Component-calculate_mint_fee) -- [`calculate_withdraw_fee(self, assets, shares)`](#ERC4626Component-calculate_withdraw_fee) -- [`calculate_redeem_fee(self, assets, shares)`](#ERC4626Component-calculate_redeem_fee) - -#### LimitConfigTrait [!toc] [#ERC4626Component-LimitConfigTrait] - -- [`deposit_limit(self, receiver)`](#ERC4626Component-deposit_limit) -- [`mint_limit(self, receiver)`](#ERC4626Component-mint_limit) -- [`withdraw_limit(self, owner)`](#ERC4626Component-withdraw_limit) -- [`redeem_limit(self, owner)`](#ERC4626Component-redeem_limit) - -#### ERC4626HooksTrait [!toc] [#ERC4626Component-ERC4626HooksTrait] - -- [`before_deposit(self, caller, receiver, assets, shares, fee)`](#ERC4626Component-before_deposit) -- [`after_deposit(self, caller, receiver, assets, shares, fee)`](#ERC4626Component-after_deposit) -- [`before_withdraw(self, caller, receiver, owner, assets, shares, fee)`](#ERC4626Component-before_withdraw) -- [`after_withdraw(self, caller, receiver, owner, assets, shares, fee)`](#ERC4626Component-after_withdraw) - -#### AssetsManagementTrait [!toc] [#ERC4626Component-AssetsManagementTrait] - -- [`get_total_assets(self)`](#ERC4626Component-get_total_assets) -- [`transfer_assets_in(self, from, assets)`](#ERC4626Component-transfer_assets_in) -- [`transfer_assets_out(self, to, assets)`](#ERC4626Component-transfer_assets_out) - -Embeddable Implementations - -#### ERC4626Impl [!toc] [#ERC4626Component-Embeddable-Impls-ERC4626Impl] - -- [`asset(self)`](#ERC4626Component-asset) -- [`total_assets(self)`](#ERC4626Component-total_assets) -- [`convert_to_shares(self, assets)`](#ERC4626Component-convert_to_shares) -- [`convert_to_assets(self, shares)`](#ERC4626Component-convert_to_assets) -- [`max_deposit(self, receiver)`](#ERC4626Component-max_deposit) -- [`preview_deposit(self, assets)`](#ERC4626Component-preview_deposit) -- [`deposit(self, assets, receiver)`](#ERC4626Component-deposit) -- [`max_mint(self, receiver)`](#ERC4626Component-max_mint) -- [`preview_mint(self, shares)`](#ERC4626Component-preview_mint) -- [`mint(self, shares, receiver)`](#ERC4626Component-mint) -- [`max_withdraw(self, owner)`](#ERC4626Component-max_withdraw) -- [`preview_withdraw(self, assets)`](#ERC4626Component-preview_withdraw) -- [`withdraw(self, assets, receiver, owner)`](#ERC4626Component-withdraw) -- [`max_redeem(self, owner)`](#ERC4626Component-max_redeem) -- [`preview_redeem(self, shares)`](#ERC4626Component-preview_redeem) -- [`redeem(self, shares, receiver, owner)`](#ERC4626Component-redeem) - -#### ERC20Impl [!toc] [#ERC4626Component-Embeddable-Impls-ERC20Impl] - -- [`total_supply(self)`](#ERC20Component-total_supply) -- [`balance_of(self, account)`](#ERC20Component-balance_of) -- [`allowance(self, owner, spender)`](#ERC20Component-allowance) -- [`transfer(self, recipient, amount)`](#ERC20Component-transfer) -- [`transfer_from(self, sender, recipient, amount)`](#ERC20Component-transfer_from) -- [`approve(self, spender, amount)`](#ERC20Component-approve) - -#### ERC4626MetadataImpl [!toc] [#ERC4626Component-Embeddable-Impls-ERC4626MetadataImpl] - -- [`name(self)`](#ERC4626Component-name) -- [`symbol(self)`](#ERC4626Component-symbol) -- [`decimals(self)`](#ERC4626Component-decimals) - -Internal functions - -#### InternalImpl [!toc] [#ERC4626Component-InternalImpl] - -- [`initializer(self, asset_address)`](#ERC4626Component-initializer) -- [`_deposit(self, caller, receiver, assets, shares)`](#ERC4626Component-_deposit) -- [`_withdraw(self, caller, receiver, owner, assets, shares)`](#ERC4626Component-_withdraw) -- [`_convert_to_shares(self, assets, rounding)`](#ERC4626Component-_convert_to_shares) -- [`_convert_to_assets(self, shares, rounding)`](#ERC4626Component-_convert_to_assets) - -#### Immutable Config [!toc] [#ERC4626Component-Immutable-Config] - - -Should match the underlying asset's decimals. The default value is `18`. - - - -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. - - - -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 [!toc] [#ERC4626Component-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 [!toc] [#ERC4626Component-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](https://eips.ethereum.org/EIPS/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](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L253) -- Contract charging fees in shares: [ERC4626SharesFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L426) - - -Calculates the entry fee for a deposit during [preview\_deposit](#ERC4626Component-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](#ERC4626Component-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. - - - -Calculates the entry fee for a mint during [preview\_mint](#ERC4626Component-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](#ERC4626Component-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. - - - -Calculates the exit fee for a withdraw during [preview\_withdraw](#ERC4626Component-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](#ERC4626Component-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. - - - -Calculates the exit fee for a redeem during [preview\_redeem](#ERC4626Component-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](#ERC4626Component-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 [!toc] [#ERC4626Component-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_` i.e. `deposit_limit` → `max_deposit`. - -The [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec states that the `max_` 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. - - -The max deposit allowed. - -Defaults (`Option::None`) to 2 \** 256 - 1. - - - -The max mint allowed. - -Defaults (`Option::None`) to 2 \** 256 - 1. - - - -The max withdraw allowed. - -Defaults (`Option::None`) to the full asset balance of `owner` converted from shares. - - - -The max redeem allowed. - -Defaults (`Option::None`) to the full asset balance of `owner`. - - -#### ERC4626HooksTrait [!toc] [#ERC4626Component-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](#ERC4626Component-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](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L253) and [ERC4626SharesFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L426) examples. - - -Hooks into [\_deposit](#ERC4626Component-_deposit). - -Executes logic before transferring assets and minting shares. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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. - - - -Hooks into [\_deposit](#ERC4626Component-_deposit). - -Executes logic after transferring assets and minting shares. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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. - - - -Hooks into [\_withdraw](#ERC4626Component-_withdraw). - -Executes logic before burning shares and transferring assets. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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. - - - -Hooks into [\_withdraw](#ERC4626Component-_withdraw). - -Executes logic after burning shares and transferring assets. The fee is calculated via [FeeConfigTrait](#ERC4626Component-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 [!toc] [#ERC4626Component-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: - -- Self-managed vault: [ERC4626SelfAssetsManagement](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/token/src/erc20/extensions/erc4626/erc4626.cairo#L760). -- External vault: [ERC4626ExternalAssetsManagement](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L92). - - -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. - - - -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. - - - -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 [!toc] [#ERC4626Component-Embeddable-Functions] - - -Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. - - - -Returns the total amount of the underlying asset that is "managed" by Vault. - - - -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](https://eips.ethereum.org/EIPS/eip-4626) spec, this may panic *only* if there's an overflow from an unreasonably large input. - - - -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](https://eips.ethereum.org/EIPS/eip-4626) spec, this may panic *only* if there's an overflow from an unreasonably large input. - - - -Returns the maximum amount of the underlying asset that can be deposited into the Vault for the `receiver`, through a [deposit](#ERC4626Component-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](#ERC4626Component-deposit_limit). - - - -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](#ERC4626Component-calculate_deposit_fee). - -This method must be inclusive of entry fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. - - - -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](#IERC4626-Deposit) event. - - - -Returns the maximum amount of the Vault shares that can be minted for `receiver` through a [mint](#ERC4626Component-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](#ERC4626Component-mint_limit). - - - -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](#ERC4626Component-calculate_mint_fee). - -This method must be inclusive of entry fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. - - - -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](#IERC4626-Deposit) event. - - - -Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the Vault, through a [withdraw](#ERC4626Component-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](#ERC4626Component-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. - - - -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](#ERC4626Component-calculate_withdraw_fee). - -This method must be inclusive of exit fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. - - - -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](#IERC4626-Withdraw) event. - - - -Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, through a [redeem](#ERC4626Component-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](#ERC4626Component-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. - - - -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](#ERC4626Component-calculate_redeem_fee). - -This method must be inclusive of exit fees to be compliant with the [EIP-4626](https://eips.ethereum.org/EIPS/eip-4626) spec. - - - -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](#IERC4626-Withdraw) event. - - - -Returns the name of the token. - - - -Returns the ticker symbol of the token, usually a shorter version of the name. - - - -Returns the cumulative number of decimals which includes both `UNDERLYING_DECIMALS` and `OFFSET_DECIMALS`. Both of which must be defined in the [ImmutableConfig](#ERC4626Component-Immutable-Config) inside the implementing contract. - - -#### Internal functions [!toc] [#ERC4626Component-Internal-Functions] - - -Validates the [ImmutableConfig](#ERC4626Component-Immutable-Config) 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. - - - -Internal logic for [deposit](#ERC4626Component-deposit) and [mint](#ERC4626Component-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](#ERC20Component-transfer_from) must return true. - -Emits two [ERC20::Transfer](#ERC20Component-Transfer) events (`ERC20::mint` and `ERC20::transfer_from`). - -Emits a [Deposit](#IERC4626-Deposit) event. - - - -Internal logic for [withdraw](#ERC4626Component-withdraw) and [redeem](#ERC4626Component-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](#ERC20Component-transfer) must return true. - -Emits two [ERC20::Transfer](#ERC20Component-Transfer) events (`ERC20::burn` and `ERC20::transfer`). - -Emits a [Withdraw](#IERC4626-Withdraw) event. - - - -Internal conversion function (from assets to shares) with support for `rounding` direction. - - - -Internal conversion function (from shares to assets) with support for `rounding` direction. - - -## Presets - -### `ERC20Upgradeable` [toc] [#ERC20Upgradeable] - - -```rust -use openzeppelin_presets::ERC20Upgradeable; -``` - -Upgradeable ERC20 contract leveraging [ERC20Component](#ERC20Component) with a fixed-supply mechanism for token distribution. - -[Sierra class hash](../presets) - -```text -{{ERC20UpgradeableClassHash}} -``` - -Constructor - -- [`constructor(self, name, symbol, fixed_supply, recipient, owner)`](#ERC20Upgradeable-constructor) - -Embedded Implementations - -#### ERC20MixinImpl [!toc] [#ERC20Upgradeable-Embedded-Impls-ERC20MixinImpl] - -- [`ERC20MixinImpl`](#ERC20Component-Embeddable-Mixin-Impl) - -#### OwnableMixinImpl [!toc] [#ERC20Upgradeable-Embedded-Impls-OwnableMixinImpl] - -- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) - -External Functions - -- [`upgrade(self, new_class_hash)`](#ERC20Upgradeable-upgrade) - -#### Constructor [!toc] [#ERC20Upgradeable-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 [!toc] [#ERC20Upgradeable-External-Functions] - - -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. - diff --git a/content/contracts-cairo/alpha/api/erc721.mdx b/content/contracts-cairo/alpha/api/erc721.mdx deleted file mode 100644 index c093c879..00000000 --- a/content/contracts-cairo/alpha/api/erc721.mdx +++ /dev/null @@ -1,1159 +0,0 @@ ---- -title: ERC721 ---- - -This module provides interfaces, presets, and utilities related to ERC721 contracts. - -For an overview of ERC721, read our [ERC721 guide](../erc721). - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -### `IERC721` [toc] [#IERC721] - - -```rust -use openzeppelin_interfaces::erc721::IERC721; -``` - -Interface of the IERC721 standard as defined in [EIP721](https://eips.ethereum.org/EIPS/eip-721). - -[SRC5 ID](./introspection#ISRC5) - -```text -0x33eb2f84c309543403fd69f0d0f363781ef06ef6faeb0131ff16ea3175bd943 -``` - -Functions - -- [`balance_of(account)`](#IERC721-balance_of) -- [`owner_of(token_id)`](#IERC721-owner_of) -- [`safe_transfer_from(from, to, token_id, data)`](#IERC721-safe_transfer_from) -- [`transfer_from(from, to, token_id)`](#IERC721-transfer_from) -- [`approve(to, token_id)`](#IERC721-approve) -- [`set_approval_for_all(operator, approved)`](#IERC721-set_approval_for_all) -- [`get_approved(token_id)`](#IERC721-get_approved) -- [`is_approved_for_all(owner, operator)`](#IERC721-is_approved_for_all) - -Events - -- [`Approval(owner, approved, token_id)`](#IERC721-Approval) -- [`ApprovalForAll(owner, operator, approved)`](#IERC721-ApprovalForAll) -- [`Transfer(from, to, token_id)`](#IERC721-Transfer) - -#### Functions [!toc] [#IERC721-Functions] - - -Returns the number of NFTs owned by `account`. - - - -Returns the owner address of `token_id`. - - - -Transfer ownership of `token_id` from `from` to `to`, checking first that `to` is aware of the ERC721 protocol to prevent tokens being locked forever. For information regarding how contracts communicate their awareness of the ERC721 protocol, see [Receiving Tokens](../erc721#receiving_tokens). - -Emits a [Transfer](#IERC721-Transfer) event. - - - -Transfer ownership of `token_id` from `from` to `to`. - -Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 transfers or else they may be permanently lost. Usage of [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from) prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. - -Emits a [Transfer](#IERC721-Transfer) event. - - - -Change or reaffirm the approved address for an NFT. - -Emits an [Approval](#IERC721-Approval) event. - - - -Enable or disable approval for `operator` to manage all of the caller's assets. - -Emits an [ApprovalForAll](#IERC721-ApprovalForAll) event. - - - -Returns the address approved for `token_id`. - - - -Query if `operator` is an authorized operator for `owner`. - - -#### Events [!toc] [#IERC721-Events] - - -Emitted when `owner` enables `approved` to manage the `token_id` token. - - - -Emitted when `owner` enables or disables `operator` to manage the `token_id` token. - - - -Emitted when `token_id` token is transferred from `from` to `to`. - - -### `IERC721Metadata` [toc] [#IERC721Metadata] - - -```rust -use openzeppelin_interfaces::erc721::IERC721Metadata; -``` - -Interface for the optional metadata functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). - -[SRC5 ID](./introspection#ISRC5) - -```text -0xabbcd595a567dce909050a1038e055daccb3c42af06f0add544fa90ee91f25 -``` - -Functions - -- [`name()`](#IERC721Metadata-name) -- [`symbol()`](#IERC721Metadata-symbol) -- [`token_uri(token_id)`](#IERC721Metadata-token_uri) - -#### Functions [!toc] [#IERC721Metadata-Functions] - - -Returns the NFT name. - - - -Returns the NFT ticker symbol. - - - -Returns the Uniform Resource Identifier (URI) for the `token_id` token. If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. - - -### `IERC721Receiver` [toc] [#IERC721Receiver] - - -```rust -use openzeppelin_interfaces::erc721::IERC721Receiver; -``` - -Interface for contracts that support receiving `safe_transfer_from` transfers. - -[SRC5 ID](./introspection#ISRC5) - -```text -0x3a0dff5f70d80458ad14ae37bb182a728e3c8cdda0402a5daa86620bdf910bc -``` - -Functions - -- [`on_erc721_received(operator, from, token_id, data)`](#IERC721Receiver-on_erc721_received) - -#### Functions [!toc] [#IERC721Receiver-Functions] - - -Whenever an IERC721 `token_id` token is transferred to this non-account contract via [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from) by `operator` from `from`, this function is called. - - -### `IERC721Enumerable` [toc] [#IERC721Enumerable] - - -Interface for the optional enumerable functions in [EIP721](https://eips.ethereum.org/EIPS/eip-721). - -[SRC5 ID](./introspection#ISRC5) - -```text -0x16bc0f502eeaf65ce0b3acb5eea656e2f26979ce6750e8502a82f377e538c87 -``` - -Functions - -- [`total_supply()`](#IERC721Enumerable-total_supply) -- [`token_by_index(index)`](#IERC721Enumerable-token_by_index) -- [`token_of_owner_by_index(owner, index)`](#IERC721Enumerable-token_of_owner_by_index) - -#### Functions [!toc] [#IERC721Enumerable-Functions] - - -Returns the total amount of tokens stored by the contract. - - - -Returns a token id at a given `index` of all the tokens stored by the contract. Use along with [IERC721Enumerable::total\_supply](#IERC721Enumerable-total_supply) to enumerate all tokens. - - - -Returns the token id owned by `owner` at a given `index` of its token list. Use along with [IERC721::balance\_of](#IERC721-balance_of) to enumerate all of `owner`'s tokens. - - -## Core - -### `ERC721Component` [toc] [#ERC721Component] - - -```rust -use openzeppelin_token::erc721::ERC721Component; -``` - -ERC721 component implementing [IERC721](#IERC721) and [IERC721Metadata](#IERC721Metadata). - -Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. - -See [Hooks](#ERC721Component-Hooks) to understand how are hooks used. - -Hooks - -#### ERC721HooksTrait [!toc] [#ERC721Component-ERC721HooksTrait] - -- [`before_update(self, to, token_id, auth)`](#ERC721Component-before_update) -- [`after_update(self, to, token_id, auth)`](#ERC721Component-after_update) - -[Embeddable Mixin Implementations](../components#mixins) - -#### ERC721MixinImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721MixinImpl] - -- [`ERC721Impl`](#ERC721Component-Embeddable-Impls-ERC721Impl) -- [`ERC721MetadataImpl`](#ERC721Component-Embeddable-Impls-ERC721MetadataImpl) -- [`ERC721CamelOnlyImpl`](#ERC721Component-Embeddable-Impls-ERC721CamelOnlyImpl) -- [`ERC721MetadataCamelOnlyImpl`](#ERC721Component-Embeddable-Impls-ERC721MetadataCamelOnlyImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) - -Embeddable Implementations - -#### ERC721Impl [!toc] [#ERC721Component-Embeddable-Impls-ERC721Impl] - -- [`balance_of(self, account)`](#ERC721Component-balance_of) -- [`owner_of(self, token_id)`](#ERC721Component-owner_of) -- [`safe_transfer_from(self, from, to, token_id, data)`](#ERC721Component-safe_transfer_from) -- [`transfer_from(self, from, to, token_id)`](#ERC721Component-transfer_from) -- [`approve(self, to, token_id)`](#ERC721Component-approve) -- [`set_approval_for_all(self, operator, approved)`](#ERC721Component-set_approval_for_all) -- [`get_approved(self, token_id)`](#ERC721Component-get_approved) -- [`is_approved_for_all(self, owner, operator)`](#ERC721Component-is_approved_for_all) - -#### ERC721MetadataImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721MetadataImpl] - -- [`name(self)`](#ERC721Component-name) -- [`symbol(self)`](#ERC721Component-symbol) -- [`token_uri(self, token_id)`](#ERC721Component-token_uri) - -#### ERC721CamelOnlyImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721CamelOnlyImpl] - -- [`balanceOf(self, account)`](#ERC721Component-balanceOf) -- [`ownerOf(self, tokenId)`](#ERC721Component-ownerOf) -- [`safeTransferFrom(self, from, to, tokenId, data)`](#ERC721Component-safeTransferFrom) -- [`transferFrom(self, from, to, tokenId)`](#ERC721Component-transferFrom) -- [`setApprovalForAll(self, operator, approved)`](#ERC721Component-setApprovalForAll) -- [`getApproved(self, tokenId)`](#ERC721Component-getApproved) -- [`isApprovedForAll(self, owner, operator)`](#ERC721Component-isApprovedForAll) - -#### ERC721MetadataCamelOnlyImpl [!toc] [#ERC721Component-Embeddable-Impls-ERC721MetadataCamelOnlyImpl] - -- [`tokenURI(self, tokenId)`](#ERC721Component-tokenURI) - -#### SRC5Impl [!toc] [#ERC721Component-Embeddable-Impls-SRC5Impl] - -- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) - -Internal functions - -#### InternalImpl [!toc] [#ERC721Component-InternalImpl] - -- [`initializer(self, name, symbol, base_uri)`](#ERC721Component-initializer) -- [`initializer_no_metadata(self)`](#ERC721Component-initializer_no_metadata) -- [`exists(self, token_id)`](#ERC721Component-exists) -- [`transfer(self, from, to, token_id)`](#ERC721Component-transfer) -- [`mint(self, to, token_id)`](#ERC721Component-mint) -- [`safe_transfer(self, from, to, token_id, data)`](#ERC721Component-safe_transfer) -- [`safe_mint(self, to, token_id, data)`](#ERC721Component-safe_mint) -- [`burn(self, token_id)`](#ERC721Component-burn) -- [`update(self, to, token_id, auth)`](#ERC721Component-update) -- [`_owner_of(self, token_id)`](#ERC721Component-_owner_of) -- [`_require_owned(self, token_id)`](#ERC721Component-_require_owned) -- [`_approve(self, to, token_id, auth)`](#ERC721Component-_approve) -- [`_approve_with_optional_event(self, to, token_id, auth, emit_event)`](#ERC721Component-_approve_with_optional_event) -- [`_set_approval_for_all(self, owner, operator, approved)`](#ERC721Component-_set_approval_for_all) -- [`_set_base_uri(self, base_uri)`](#ERC721Component-_set_base_uri) -- [`_base_uri(self)`](#ERC721Component-_base_uri) -- [`_is_authorized(self, owner, spender, token_id)`](#ERC721Component-_is_authorized) -- [`_check_authorized(self, owner, spender, token_id)`](#ERC721Component-_check_authorized) - -Events - -IERC721 - -- [`Approval(owner, approved, token_id)`](#ERC721Component-Approval) -- [`ApprovalForAll(owner, operator, approved)`](#ERC721Component-ApprovalForAll) -- [`Transfer(from, to, token_id)`](#ERC721Component-Transfer) - -#### Hooks [!toc] [#ERC721Component-Hooks] - -Hooks are functions which implementations can extend the functionality of the component source code. Every contract using ERC721Component is expected to provide an implementation of the ERC721HooksTrait. For basic token contracts, an empty implementation with no logic must be provided. - -You can use `openzeppelin_token::erc721::ERC721HooksEmptyImpl` which is already available as part of the library for this purpose. - - -Function executed at the beginning of the [update](#ERC721Component-update) function prior to any other logic. - - - -Function executed at the end of the [update](#ERC721Component-update) function. - - -#### [](#embeddable_functions)Embeddable functions [!toc] [#embeddable_functions] - - -See [IERC721::balance\_of](#IERC721-balance_of). - - - -See [IERC721::owner\_of](#IERC721-owner_of). - -Requirements: - -- `token_id` exists. - - - -See [IERC721::safe\_transfer\_from](#IERC721-safe_transfer_from). - -Requirements: - -- Caller is either approved or the `token_id` owner. -- `to` is not the zero address. -- `from` is not the zero address. -- `token_id` exists. -- `to` is either an account contract or supports the [IERC721Receiver](#IERC721Receiver) interface. - - - -See [IERC721::transfer\_from](#IERC721-transfer_from). - -Requirements: - -- Caller either approved or the `token_id` owner. -- `to` is not the zero address. -- `from` is not the zero address. -- `token_id` exists. - - - -See [IERC721::approve](#IERC721-approve). - -Requirements: - -- The caller is either an approved operator or the `token_id` owner. -- `to` cannot be the token owner or the zero address. -- `token_id` exists. - - - -See [IERC721::set\_approval\_for\_all](#IERC721-set_approval_for_all). - -Requirements: - -- `operator` is not the zero address. - - - -See [IERC721::get\_approved](#IERC721-get_approved). - -Requirements: - -- `token_id` exists. - - - -See [IERC721::is\_approved\_for\_all](#IERC721-is_approved_for_all). - - - -See [IERC721Metadata::name](#IERC721Metadata-name). - - - -See [IERC721Metadata::symbol](#IERC721Metadata-symbol). - - - -Returns the Uniform Resource Identifier (URI) for the `token_id` token. If a base URI is set, the resulting URI for each token will be the concatenation of the base URI and the token ID. For example, the base URI `https://token-cdn-domain/` would be returned as `https://token-cdn-domain/123` for token ID `123`. - -If the URI is not set for `token_id`, the return value will be an empty `ByteArray`. - - - -See [ERC721Component::balance\_of](#ERC721Component-balance_of). - - - -See [ERC721Component::owner\_of](#ERC721Component-owner_of). - - - -See [ERC721Component::safe\_transfer\_from](#ERC721Component-safe_transfer_from). - - - -See [ERC721Component::transfer\_from](#ERC721Component-transfer_from). - - - -See [ERC721Component::set\_approval\_for\_all](#ERC721Component-set_approval_for_all). - - - -See [ERC721Component::get\_approved](#ERC721Component-get_approved). - - - -See [ERC721Component::is\_approved\_for\_all](#ERC721Component-is_approved_for_all). - - - -See [ERC721Component::token\_uri](#ERC721Component-token_uri). - - -#### [](#internal_functions)Internal functions [!toc] [#internal_functions] - - -Initializes the contract by setting the token name and symbol. This should be used inside the contract's constructor. - -Most ERC721 contracts expose the [IERC721Metadata](#IERC721Metadata) interface which is what this initializer is meant to support. If the contract DOES NOT expose the [IERC721Metadata](#IERC721Metadata) interface, meaning the token does not have a name, symbol, or URI, the contract must instead use [initializer\_no\_metadata](#ERC721Component-initializer_no_metadata) in the constructor. Failure to abide by these instructions can lead to unexpected issues especially with UIs. - - - -Initializes the contract with no metadata by registering only the IERC721 interface. - -This initializer should ONLY be used during construction in the very specific instance when the contract does NOT expose the [IERC721Metadata](#IERC721Metadata) interface. Initializing a contract with this initializer means that tokens will not have a name, symbol, or URI. - - - -Internal function that returns whether `token_id` exists. - -Tokens start existing when they are minted ([mint](#ERC721-mint)), and stop existing when they are burned ([burn](#ERC721-burn)). - - - -Transfers `token_id` from `from` to `to`. - -Internal function without access restriction. - -This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. - -Requirements: - -- `to` is not the zero address. -- `from` is the token owner. -- `token_id` exists. - -Emits a [Transfer](#IERC721-Transfer) event. - - - -Mints `token_id` and transfers it to `to`. Internal function without access restriction. - -This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. - -Requirements: - -- `to` is not the zero address. -- `token_id` does not exist. - -Emits a [Transfer](#IERC721-Transfer) event. - - - -Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. - -`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. - -This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. - -Requirements: - -- `to` cannot be the zero address. -- `from` must be the token owner. -- `token_id` exists. -- `to` is either an account contract or supports the `IERC721Receiver` interface. - -Emits a [Transfer](#IERC721-Transfer) event. - - - -Mints `token_id` if `to` is either an account or `IERC721Receiver`. - -`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. - -This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. - -Requirements: - -- `token_id` does not exist. -- `to` is either an account contract or supports the `IERC721Receiver` interface. - -Emits a [Transfer](#IERC721-Transfer) event. - - - -Destroys `token_id`. The approval is cleared when the token is burned. - -This internal function does not check if the caller is authorized to operate on the token. - -Requirements: - -- `token_id` exists. - -Emits a [Transfer](#IERC721-Transfer) event. - - - -Transfers `token_id` from its current owner to `to`, or alternatively mints (or burns) if the current owner (or `to`) is the zero address. Returns the owner of the `token_id` before the update. - -The `auth` argument is optional. If the value passed is non-zero, then this function will check that `auth` is either the owner of the token, or approved to operate on the token (by the owner). - -Emits a [Transfer](#IERC721-Transfer) event. - -This function can be extended using the `ERC721HooksTrait`, to add functionality before and/or after the transfer, mint, or burn. - - - -Internal function that returns the owner address of `token_id`. - - - -Version of [\_owner\_of](#ERC721Component-_owner_of) that panics if owner is the zero address. - - - -Approve `to` to operate on `token_id` - -The `auth` argument is optional. If the value passed is non-zero, then this function will check that `auth` is either the owner of the token, or approved to operate on all tokens held by this owner. - -Emits an [Approval](#IERC721-Approval) event. - - - -Variant of [\_approve](#ERC721Component-_approve) with an optional flag to enable or disable the `Approval` event. The event is not emitted in the context of transfers. - -If `auth` is zero and `emit_event` is false, this function will not check that the token exists. - -Requirements: - -- if `auth` is non-zero, it must be either the owner of the token or approved to operate on all of its tokens. - -May emit an [Approval](#IERC721-Approval) event. - - - -Enables or disables approval for `operator` to manage all of the `owner` assets. - -Requirements: - -- `operator` is not the zero address. - -Emits an [Approval](#IERC721-Approval) event. - - - -Internal function that sets the `base_uri`. - - - -Base URI for computing [token\_uri](#IERC721Metadata-token_uri). - -If set, the resulting URI for each token will be the concatenation of the base URI and the token ID. Returns an empty `ByteArray` if not set. - - - -Returns whether `spender` is allowed to manage `owner`'s tokens, or `token_id` in particular (ignoring whether it is owned by `owner`). - -This function assumes that `owner` is the actual owner of `token_id` and does not verify this assumption. - - - -Checks if `spender` can operate on `token_id`, assuming the provided `owner` is the actual owner. - -Requirements: - -- `owner` cannot be the zero address. -- `spender` cannot be the zero address. -- `spender` must be the owner of `token_id` or be approved to operate on it. - -This function assumes that `owner` is the actual owner of `token_id` and does not verify this assumption. - - -#### [](#events_2)Events [!toc] [#events_2] - - -See [IERC721::Approval](#IERC721-Approval). - - - -See [IERC721::ApprovalForAll](#IERC721-ApprovalForAll). - - - -See [IERC721::Transfer](#IERC721-Transfer). - - -### `ERC721ReceiverComponent` [toc] [#ERC721ReceiverComponent] - - -```rust -use openzeppelin_token::erc721::ERC721ReceiverComponent; -``` - -ERC721Receiver component implementing [IERC721Receiver](#IERC721Receiver). - -Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. - -[Embeddable Mixin Implementations](../components#mixins) - -#### ERCReceiverMixinImpl [!toc] [#ERC721ReceiverComponent-Embeddable-Impls-ERCReceiverMixinImpl] - -- [`ERC721ReceiverImpl`](#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverImpl) -- [`ERC721ReceiverCamelImpl`](#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverCamelImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) - -Embeddable Implementations - -#### ERC721ReceiverImpl [!toc] [#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverImpl] - -- [`on_erc721_received(self, operator, from, token_id, data)`](#ERC721ReceiverComponent-on_erc721_received) - -#### ERC721ReceiverCamelImpl [!toc] [#ERC721ReceiverComponent-Embeddable-Impls-ERC721ReceiverCamelImpl] - -- [`onERC721Received(self, operator, from, tokenId, data)`](#ERC721ReceiverComponent-onERC721Received) - -Internal Functions - -#### InternalImpl [!toc] [#ERC721ReceiverComponent-InternalImpl] - -- [`initializer(self)`](#ERC721ReceiverComponent-initializer) - -#### [](#embeddable_functions_2)Embeddable functions [!toc] [#embeddable_functions_2] - - -Returns the `IERC721Receiver` interface ID. - - - -See [ERC721ReceiverComponent::on\_erc721\_received](#ERC721ReceiverComponent-on_erc721_received). - - -#### [](#internal_functions_2)Internal functions [!toc] [#internal_functions_2] - - -Registers the `IERC721Receiver` interface ID as supported through introspection. - - -## Extensions - -### `ERC721EnumerableComponent` [toc] [#ERC721EnumerableComponent] - - -```rust -use openzeppelin_token::erc721::extensions::ERC721EnumerableComponent; -``` - -Extension of ERC721 as defined in the EIP that adds enumerability of all the token ids in the contract as well as all token ids owned by each account. This extension allows contracts to publish their entire list of NFTs and make them discoverable. - -Implementing [ERC721Component](#ERC721Component) is a requirement for this component to be implemented. - -To properly track token ids, this extension requires that the [ERC721EnumerableComponent::before\_update](#ERC721EnumerableComponent-before_update) function is called before every transfer, mint, or burn operation. For this, the [ERC721HooksTrait::before\_update](#ERC721Component-before_update) hook must be used. Here's how the hook should be implemented in a contract: - -```[ -#[starknet::contract] -mod ERC721EnumerableContract { - (...) - - component!(path: ERC721Component, storage: erc721, event: ERC721Event); - component!(path: ERC721EnumerableComponent, storage: erc721_enumerable, event: ERC721EnumerableEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - impl ERC721HooksImpl of ERC721Component::ERC721HooksTrait { - fn before_update( - ref self: ERC721Component::ComponentState, - to: ContractAddress, - token_id: u256, - auth: ContractAddress - ) { - let mut contract_state = self.get_contract_mut(); - contract_state.erc721_enumerable.before_update(to, token_id); - } - } -} -``` - -Embeddable Implementations - -#### ERC721EnumerableImpl [!toc] [#ERC721EnumerableComponent-Embeddable-Impls-ERC721EnumerableImpl] - -- [`total_supply(self)`](#ERC721EnumerableComponent-total_supply) -- [`token_by_index(self, index)`](#ERC721EnumerableComponent-token_by_index) -- [`token_of_owner_by_index(self, address, index)`](#ERC721EnumerableComponent-token_of_owner_by_index) - -Internal functions - -#### InternalImpl [!toc] [#ERC721EnumerableComponent-InternalImpl] - -- [`initializer(self)`](#ERC721EnumerableComponent-initializer) -- [`before_update(self, to, token_id)`](#ERC721EnumerableComponent-before_update) -- [`all_tokens_of_owner(self, owner)`](#ERC721EnumerableComponent-all_tokens_of_owner) -- [`_add_token_to_owner_enumeration(self, to, token_id)`](#ERC721EnumerableComponent-_add_token_to_owner_enumeration) -- [`_add_token_to_all_tokens_enumeration(self, token_id)`](#ERC721EnumerableComponent-_add_token_to_all_tokens_enumeration) -- [`_remove_token_from_owner_enumeration(self, from, token_id)`](#ERC721EnumerableComponent-_remove_token_from_owner_enumeration) -- [`_remove_token_from_all_tokens_enumeration(self, token_id)`](#ERC721EnumerableComponent-_remove_token_from_all_tokens_enumeration) - -#### [](#ERC721EnumerableComponent-Embeddable-functions)Embeddable functions [!toc] [#ERC721EnumerableComponent-Embeddable-functions] - - -Returns the current amount of votes that `account` has. - - - -See [IERC721Enumerable::token\_by\_index](#IERC721Enumerable-token_by_index). - -Requirements: - -- `index` is less than the total token supply. - - - -See [IERC721Enumerable::token\_of\_owner\_by\_index](#IERC721Enumerable-token_of_owner_by_index). - -Requirements: - -- `index` is less than `owner`'s token balance. -- `owner` is not the zero address. - - -#### [](#ERC721EnumerableComponent-Internal-functions)Internal functions [!toc] [#ERC721EnumerableComponent-Internal-functions] - - -Registers the `IERC721Enumerable` interface ID as supported through introspection. - - - -Updates the ownership and token-tracking data structures. - -When a token is minted (or burned), `token_id` is added to (or removed from) the token-tracking structures. - -When a token is transferred, minted, or burned, the ownership-tracking data structures reflect the change in ownership of `token_id`. - -This must be added to the implementing contract's [ERC721HooksTrait::before\_update](#ERC721Component-before_update) hook. - - - -Returns a list of all token ids owned by the specified `owner`. This function provides a more efficient alternative to calling `ERC721::balance_of` and iterating through tokens with `ERC721Enumerable::token_of_owner_by_index`. - -Requirements: - -- `owner` is not the zero address. - - - -Adds token to this extension's ownership-tracking data structures. - - - -Adds token to this extension's token-tracking data structures. - - - -Removes a token from this extension's ownership-tracking data structures. - -This has 0(1) time complexity but alters the indexed order of owned tokens by swapping `token_id` and the index thereof with the last token id and the index thereof e.g. removing `1` from `[1, 2, 3, 4]` results in `[4, 2, 3]`. - - - -Removes `token_id` from this extension's token-tracking data structures. - -This has 0(1) time complexity but alters the indexed order by swapping `token_id` and the index thereof with the last token id and the index thereof e.g. removing `1` from `[1, 2, 3, 4]` results in `[4, 2, 3]`. - - -## Presets - -### `ERC721Upgradeable` [toc] [#ERC721Upgradeable] - - -```rust -use openzeppelin_presets::ERC721Upgradeable; -``` - -Upgradeable ERC721 contract leveraging [ERC721Component](#ERC721Component). - -[Sierra class hash](../presets) - -```text -{{ERC721UpgradeableClassHash}} -``` - -Constructor - -- [`constructor(self, name, symbol, recipient, token_ids, base_uri, owner)`](#ERC721Upgradeable-constructor) - -Embedded Implementations - -ERC721MixinImpl - -- [`ERC721MixinImpl`](#ERC721Component-Embeddable-Mixin-Impl) - -OwnableMixinImpl - -- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) - -External Functions - -- [`upgrade(self, new_class_hash)`](#ERC721Upgradeable-upgrade) - -#### [](#ERC721Upgradeable-constructor-section)Constructor [!toc] [#ERC721Upgradeable-constructor-section] - - -Sets the `name` and `symbol`. Mints `token_ids` tokens to `recipient` and sets the `base_uri`. Assigns `owner` as the contract owner with permissions to upgrade. - - -#### [](#ERC721Upgradeable-external-functions)External functions [!toc] [#ERC721Upgradeable-external-functions] - - -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. - diff --git a/content/contracts-cairo/alpha/api/finance.mdx b/content/contracts-cairo/alpha/api/finance.mdx deleted file mode 100644 index f0981e42..00000000 --- a/content/contracts-cairo/alpha/api/finance.mdx +++ /dev/null @@ -1,333 +0,0 @@ ---- -title: Finance ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This crate includes primitives for financial systems. - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -### `IVesting` [toc] [#IVesting] - - - -```rust -use openzeppelin_interfaces::vesting::IVesting; -``` - -Common interface for contracts implementing the vesting functionality. - -Functions - -- [`start()`](#IVesting-start) -- [`cliff()`](#IVesting-cliff) -- [`duration()`](#IVesting-duration) -- [`end()`](#IVesting-end) -- [`released(token)`](#IVesting-released) -- [`releasable(token)`](#IVesting-releasable) -- [`vested_amount(token, timestamp)`](#IVesting-vested_amount) -- [`release(token)`](#IVesting-release) - -Events - -- [`AmountReleased(token, amount)`](#IVesting-AmountReleased) - -#### Functions [!toc] [#IVesting-Functions] - - -Returns the timestamp marking the beginning of the vesting period. - - - -Returns the timestamp marking the end of the cliff period. - - - -Returns the total duration of the vesting period. - - - -Returns the timestamp marking the end of the vesting period. - - - -Returns the already released amount for a given `token`. - - - -Returns the amount of a given `token` that can be released at the time of the call. - - - -Returns the total vested amount of a specified `token` at a given `timestamp`. - - - -Releases the amount of a given `token` that has already vested and returns that amount. - -May emit an [AmountReleased](#IVesting-AmountReleased) event. - - -#### Events [!toc] [#IVesting-Events] - - -Emitted when vested tokens are released to the beneficiary. - - -## [](#vesting)Vesting - -### `VestingComponent` [toc] [#VestingComponent] - - - -```rust -use openzeppelin_finance::vesting::VestingComponent; -``` - -Vesting component implementing the [`IVesting`](#IVesting) interface. - -Vesting Schedule Trait Implementations - -#### functions [!toc] [#VestingComponent-Vesting-Schedule-functions] - -- [`calculate_vested_amount(self, token, total_allocation, timestamp, start, duration, cliff)`](#VestingComponent-calculate_vested_amount) - -Embeddable Implementations - -#### VestingImpl [!toc] [#VestingComponent-Embeddable-Impls-VestingImpl] - -- [`start(self)`](#VestingComponent-start) -- [`cliff(self)`](#VestingComponent-cliff) -- [`duration(self)`](#VestingComponent-duration) -- [`end(self)`](#VestingComponent-end) -- [`released(self, token)`](#VestingComponent-released) -- [`releasable(self, token)`](#VestingComponent-releasable) -- [`vested_amount(self, token, timestamp)`](#VestingComponent-vested_amount) -- [`release(self, token)`](#VestingComponent-release) - -Internal implementations - -#### InternalImpl [!toc] [#VestingComponent-InternalImpl] - -- [`initializer(self, start, duration, cliff_duration)`](#VestingComponent-initializer) -- [`resolve_vested_amount(self, token, timestamp)`](#VestingComponent-resolve_vested_amount) - -A trait that defines the logic for calculating the vested amount based on a given timestamp. - -You can read more about the trait's purpose and how to use it [here](../finance#vesting-schedule). - - -Calculates and returns the vested amount at a given `timestamp` based on the core vesting parameters. - - -#### Functions [!toc] [#VestingComponent-Functions] - - -Returns the timestamp marking the beginning of the vesting period. - - - -Returns the timestamp marking the end of the cliff period. - - - -Returns the total duration of the vesting period. - - - -Returns the timestamp marking the end of the vesting period. - - - -Returns the already released amount for a given `token`. - - - -Returns the amount of a given `token` that can be released at the time of the call. - - - -Returns the total vested amount of a specified `token` at a given `timestamp`. - - - -Releases the amount of a given `token` that has already vested and returns that amount. - -If the releasable amount is zero, this function won't emit the event or attempt to transfer the tokens. - -Requirements: - -- `transfer` call to the `token` must return `true` indicating a successful transfer. - -May emit an [AmountReleased](#IVesting-AmountReleased) event. - - -#### Internal functions [!toc] [#VestingComponent-Internal-Functions] - - -Initializes the component by setting the vesting `start`, `duration` and `cliff_duration`. To prevent reinitialization, this should only be used inside of a contract's constructor. - -Requirements: - -- `cliff_duration` must be less than or equal to `duration`. - - - -Returns the vested amount that's calculated using the [VestingSchedule](#VestingComponent-Vesting-Schedule) trait implementation. - - -### `LinearVestingSchedule` [toc] [#LinearVestingSchedule] - - - -```rust -use openzeppelin_finance::vesting::LinearVestingSchedule; -``` - -Defines the logic for calculating the vested amount, incorporating a cliff period. It returns 0 before the cliff ends. After the cliff period, the vested amount returned is directly proportional to the time passed since the start of the vesting schedule. - -## [](#presets)Presets - -### `VestingWallet` [toc] [#VestingWallet] - - - -```rust -use openzeppelin::presets::VestingWallet; -``` - -A non-upgradable contract leveraging [VestingComponent](#VestingComponent) and [OwnableComponent](./access#OwnableComponent). - -The contract is intentionally designed to be non-upgradable to ensure that neither the vesting initiator nor the vesting beneficiary can modify the vesting schedule without the consent of the other party. - -[Sierra class hash](../presets) - -```text -{{VestingWalletClassHash}} -``` - -Constructor - -- [`constructor(self, beneficiary, start, duration, cliff_duration)`](#VestingWallet-constructor) - -Embedded Implementations - -VestingComponent - -- [`VestingImpl`](#VestingComponent-Embeddable-Impls-VestingImpl) - -OwnableComponent - -- [`OwnableMixinImpl`](./access#OwnableComponent-Mixin-Impl) - -#### Constructor [!toc] [#VestingWallet-constructor-section] - - -Initializes the vesting component by setting the vesting `start`, `duration` and `cliff_duration`. Assigns `beneficiary` as the contract owner and the vesting beneficiary. - -Requirements: - -- `cliff_duration` must be less than or equal to `duration`. - diff --git a/content/contracts-cairo/alpha/api/governance.mdx b/content/contracts-cairo/alpha/api/governance.mdx deleted file mode 100644 index e5d5bbe1..00000000 --- a/content/contracts-cairo/alpha/api/governance.mdx +++ /dev/null @@ -1,4064 +0,0 @@ ---- -title: Governance ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This crate includes primitives for on-chain governance. - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -### `IGovernor` [toc] [#IGovernor] - - - -```rust -use openzeppelin_interfaces::governor::IGovernor; -``` - -Interface of a governor contract. - -[SRC5 ID](./introspection#ISRC5) - -```text -0x1100a1f8546595b5bd75a6cd8fcc5b015370655e66f275963321c5cd0357ac9 -``` - -Functions - -- [`name()`](#IGovernor-name) -- [`version()`](#IGovernor-version) -- [`COUNTING_MODE()`](#IGovernor-COUNTING_MODE) -- [`hash_proposal(calls, description_hash)`](#IGovernor-hash_proposal) -- [`state(proposal_id)`](#IGovernor-state) -- [`proposal_threshold()`](#IGovernor-proposal_threshold) -- [`proposal_snapshot(proposal_id)`](#IGovernor-proposal_snapshot) -- [`proposal_deadline(proposal_id)`](#IGovernor-proposal_deadline) -- [`proposal_proposer(proposal_id)`](#IGovernor-proposal_proposer) -- [`proposal_eta(proposal_id)`](#IGovernor-proposal_eta) -- [`proposal_needs_queuing(proposal_id)`](#IGovernor-proposal_needs_queuing) -- [`voting_delay()`](#IGovernor-voting_delay) -- [`voting_period()`](#IGovernor-voting_period) -- [`quorum(timepoint)`](#IGovernor-quorum) -- [`get_votes(account, timepoint)`](#IGovernor-get_votes) -- [`get_votes_with_params(account, timepoint, params)`](#IGovernor-get_votes_with_params) -- [`has_voted(proposal_id, account)`](#IGovernor-has_voted) -- [`propose(calls, description)`](#IGovernor-propose) -- [`queue(calls, description_hash)`](#IGovernor-queue) -- [`execute(calls, description_hash)`](#IGovernor-execute) -- [`cancel(proposal_id, description_hash)`](#IGovernor-cancel) -- [`cast_vote(proposal_id, support)`](#IGovernor-cast_vote) -- [`cast_vote_with_reason(proposal_id, support, reason)`](#IGovernor-cast_vote_with_reason) -- [`cast_vote_with_reason_and_params(proposal_id, support, reason, params)`](#IGovernor-cast_vote_with_reason_and_params) -- [`cast_vote_by_sig(proposal_id, support, reason, signature)`](#IGovernor-cast_vote_by_sig) -- [`cast_vote_with_reason_and_params_by_sig(proposal_id, support, reason, params, signature)`](#IGovernor-cast_vote_with_reason_and_params_by_sig) -- [`nonces(voter)`](#IGovernor-nonces) -- [`relay(call)`](#IGovernor-relay) - -Events - -- [`ProposalCreated(proposal_id, proposer, calls, signatures, vote_start, vote_end, description)`](#IGovernor-ProposalCreated) -- [`ProposalQueued(proposal_id, eta_seconds)`](#IGovernor-ProposalQueued) -- [`ProposalExecuted(proposal_id)`](#IGovernor-ProposalExecuted) -- [`ProposalCanceled(proposal_id)`](#IGovernor-ProposalCanceled) -- [`VoteCast(voter, proposal_id, support, weight, reason)`](#IGovernor-VoteCast) -- [`VoteCastWithParams(voter, proposal_id, support, weight, reason, params)`](#IGovernor-VoteCastWithParams) - -#### Functions [!toc] [#IGovernor-Functions] - - -Name of the governor instance (used in building the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). - - - -Version of the governor instance (used in building [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). - - - -A description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. - -There are 2 standard keys: `support` and `quorum`. - -- `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. -- `quorum=bravo` means that only For votes are counted towards quorum. -- `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. - -If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique name that describes the behavior. For example: - -- `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. -- `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. - - -The string can be decoded by the standard [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) JavaScript class. - - - - -Hashing function used to (re)build the proposal id from the proposal details. - - - -Returns the state of a proposal, given its id. - - - -The number of votes required in order for a voter to become a proposer. - - - -Timepoint used to retrieve user's votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. - - - -Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. - - - -The account that created a proposal. - - - -The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn't use the governor clock, and instead relies on the executor's clock which may be different. In most cases this will be a timestamp. - - - -Whether a proposal needs to be queued before execution. This indicates if the proposal needs to go through a timelock. - - - -Delay between when a proposal is created and when the vote starts. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. - -This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. - - - -Delay between when a vote starts and when it ends. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. - -The `voting_delay` can delay the start of the vote. This must be considered when setting the voting duration compared to the voting delay. - -This value is stored when the proposal is submitted so that possible changes to the value do not affect proposals that have already been submitted. - - - -Minimum number of votes required for a proposal to be successful. - -The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows the quorum to scale depending on values such as the total supply of a token at this timepoint. - - - -Returns the voting power of an `account` at a specific `timepoint`. - -This can be implemented in a number of ways, for example by reading the delegated balance from one (or multiple) `ERC20Votes` tokens. - - - -Returns the voting power of an `account` at a specific `timepoint`, given additional encoded parameters. - - - -Returns whether an `account` has cast a vote on a proposal. - - - -Creates a new proposal. Vote starts after a delay specified by `voting_delay` and lasts for a duration specified by `voting_period`. - -The state of the Governor and targets may change between the proposal creation and its execution. This may be the result of third party actions on the targeted contracts, or other governor proposals. For example, the balance of this contract could be updated or its access control permissions may be modified, possibly compromising the proposal's ability to execute successfully (e.g. the governor doesn't have enough value to cover a proposal with multiple transfers). - -Returns the id of the proposal. - - - -Queue a proposal. Some governors require this step to be performed before execution can happen. If queuing is not necessary, this function may revert. - -Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached. - -Returns the id of the proposal. - - - -Execute a successful proposal. This requires the quorum to be reached, the vote to be successful, and the deadline to be reached. Depending on the governor it might also be required that the proposal was queued and that some delay passed. - -Some modules can modify the requirements for execution, for example by adding an additional timelock (See `timelock_controller`). - -Returns the id of the proposal. - - - -Cancel a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e. before the vote starts. - -Returns the id of the proposal. - - - -Cast a vote on a proposal. - -Returns the weight of the vote. - - - -Cast a vote on a proposal with a `reason`. - -Returns the weight of the vote. - - - -Cast a vote on a proposal with a reason and additional encoded parameters. - -Returns the weight of the vote. - - - -Cast a vote on a proposal using the voter's signature. - -Returns the weight of the vote. - - - -Cast a vote on a proposal with a reason and additional encoded parameters using the `voter`'s signature. - -Returns the weight of the vote. - - - -Returns the next unused nonce for an address. - - - -Relays a transaction or function call to an arbitrary target. - -In cases where the governance executor is some contract other than the governor itself, like when using a timelock, this function can be invoked in a governance proposal to recover tokens that were sent to the governor contract by mistake. - -If the executor is simply the governor itself, use of `relay` is redundant. - - -#### Events [!toc] [#IGovernor-Events] - - -Emitted when a proposal is created. - - - -Emitted when a proposal is queued. - - - -Emitted when a proposal is executed. - - - -Emitted when a proposal is canceled. - - - -Emitted when a vote is cast. - - - -Emitted when a vote is cast with params. - - -### `IMultisig` [toc] [#IMultisig] - - - -```rust -use openzeppelin_interfaces::multisig::IMultisig; -``` - -Interface of a multisig contract. - -Functions - -- [`get_quorum()`](#IMultisig-get_quorum) -- [`is_signer(signer)`](#IMultisig-is_signer) -- [`get_signers()`](#IMultisig-get_signers) -- [`is_confirmed(id)`](#IMultisig-is_confirmed) -- [`is_confirmed_by(id, signer)`](#IMultisig-is_confirmed_by) -- [`is_executed(id)`](#IMultisig-is_executed) -- [`get_submitted_block(id)`](#IMultisig-get_submitted_block) -- [`get_transaction_state(id)`](#IMultisig-get_transaction_state) -- [`get_transaction_confirmations(id)`](#IMultisig-get_transaction_confirmations) -- [`hash_transaction(to, selector, calldata, salt)`](#IMultisig-hash_transaction) -- [`hash_transaction_batch(calls, salt)`](#IMultisig-hash_transaction_batch) -- [`add_signers(new_quorum, signers_to_add)`](#IMultisig-add_signers) -- [`remove_signers(new_quorum, signers_to_remove)`](#IMultisig-remove_signers) -- [`replace_signer(signer_to_remove, signer_to_add)`](#IMultisig-replace_signer) -- [`change_quorum(new_quorum)`](#IMultisig-change_quorum) -- [`submit_transaction(to, selector, calldata, salt)`](#IMultisig-submit_transaction) -- [`submit_transaction_batch(calls, salt)`](#IMultisig-submit_transaction_batch) -- [`confirm_transaction(id)`](#IMultisig-confirm_transaction) -- [`revoke_confirmation(id)`](#IMultisig-revoke_confirmation) -- [`execute_transaction(to, selector, calldata, salt)`](#IMultisig-execute_transaction) -- [`execute_transaction_batch(calls, salt)`](#IMultisig-execute_transaction_batch) - -Events - -- [`SignerAdded(signer)`](#IMultisig-SignerAdded) -- [`SignerRemoved(signer)`](#IMultisig-SignerRemoved) -- [`QuorumUpdated(old_quorum, new_quorum)`](#IMultisig-QuorumUpdated) -- [`TransactionSubmitted(id, signer)`](#IMultisig-TransactionSubmitted) -- [`TransactionConfirmed(id, signer)`](#IMultisig-TransactionConfirmed) -- [`ConfirmationRevoked(id, signer)`](#IMultisig-ConfirmationRevoked) -- [`TransactionExecuted(id)`](#IMultisig-TransactionExecuted) -- [`CallSalt(id, salt)`](#IMultisig-CallSalt) - -#### Functions [!toc] [#IMultisig-Functions] - - -Returns the current quorum value. The quorum is the minimum number of confirmations required to approve a transaction. - - - -Returns whether the given `signer` is registered. Only registered signers can submit, confirm, or execute transactions. - - - -Returns the list of all current signers. - - - -Returns whether the transaction with the given `id` has been confirmed. - - - -Returns whether the transaction with the given `id` has been confirmed by the specified `signer`. - - - -Returns whether the transaction with the given `id` has been executed. - - - -Returns the block number when the transaction with the given `id` was submitted. - - - -Returns the current state of the transaction with the given `id`. - - - -Returns the number of confirmations from registered signers for the transaction with the specified `id`. - - - -Returns the computed identifier of a transaction containing a single call. - - - -Returns the computed identifier of a transaction containing a batch of calls. - - - -Adds new signers and updates the quorum. - -Requirements: - -- The caller must be the contract itself. -- `new_quorum` must be less than or equal to the total number of signers after addition. - -Emits a [SignerAdded](#IMultisig-SignerAdded) event for each signer added. - -Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. - - - -Removes signers and updates the quorum. - -Requirements: - -- The caller must be the contract itself. -- `new_quorum` must be less than or equal to the total number of signers after removal. - -Emits a [SignerRemoved](#IMultisig-SignerRemoved) event for each signer removed. - -Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. - - - -Replaces an existing signer with a new signer. - -Requirements: - -- The caller must be the contract itself. -- `signer_to_remove` must be an existing signer. -- `signer_to_add` must not be an existing signer. - -Emits a [SignerRemoved](#IMultisig-SignerRemoved) event for the removed signer. - -Emits a [SignerAdded](#IMultisig-SignerAdded) event for the new signer. - - - -Updates the quorum value to `new_quorum` if it differs from the current quorum. - -Requirements: - -- The caller must be the contract itself. -- `new_quorum` must be non-zero. -- `new_quorum` must be less than or equal to the total number of signers. - -Emits a [QuorumUpdated](#IMultisig-QuorumUpdated) event if the quorum changes. - - - -Submits a new transaction for confirmation. - -Requirements: - -- The caller must be a registered signer. -- The transaction must not have been submitted before. - -Emits a [TransactionSubmitted](#IMultisig-TransactionSubmitted) event. - -Emits a [CallSalt](#IMultisig-CallSalt) event if `salt` is not zero. - - - - -Submits a new batch transaction for confirmation. - -Requirements: - -- The caller must be a registered signer. -- The transaction must not have been submitted before. - -Emits a [TransactionSubmitted](#IMultisig-TransactionSubmitted) event. - -Emits a [CallSalt](#IMultisig-CallSalt) event if `salt` is not zero. - - - -Confirms a transaction with the given `id`. - -Requirements: - -- The caller must be a registered signer. -- The transaction must exist and not be executed. -- The caller must not have already confirmed the transaction. - -Emits a [TransactionConfirmed](#IMultisig-TransactionConfirmed) event. - - - -Revokes a previous confirmation for a transaction with the given `id`. - -Requirements: - -- The transaction must exist and not be executed. -- The caller must have previously confirmed the transaction. - -Emits a [ConfirmationRevoked](#IMultisig-ConfirmationRevoked) event. - - - -Executes a confirmed transaction. - -Requirements: - -- The caller must be a registered signer. -- The transaction must be confirmed and not yet executed. - -Emits a [TransactionExecuted](#IMultisig-TransactionExecuted) event. - - - -Executes a confirmed batch transaction. - -Requirements: - -- The caller must be a registered signer. -- The transaction must be confirmed and not yet executed. - -Emits a [TransactionExecuted](#IMultisig-TransactionExecuted) event. - - -#### Events [!toc] [#IMultisig-Events] - - -Emitted when a new `signer` is added. - - - -Emitted when a `signer` is removed. - - - -Emitted when the `quorum` value is updated. - - - -Emitted when a new transaction is submitted by a `signer`. - - - -Emitted when a transaction is confirmed by a `signer`. - - - -Emitted when a `signer` revokes his confirmation. - - - -Emitted when a transaction is executed. - - - -Emitted when a new transaction is submitted with non-zero salt. - - -### `ITimelock` [toc] [#ITimelock] - - - -```rust -use openzeppelin_interfaces::timelock::ITimelock; -``` - -Interface of a timelock contract. - -Functions - -- [`is_operation(id)`](#ITimelock-is_operation) -- [`is_operation_pending(id)`](#ITimelock-is_operation_pending) -- [`is_operation_ready(id)`](#ITimelock-is_operation_ready) -- [`is_operation_done(id)`](#ITimelock-is_operation_done) -- [`get_timestamp(id)`](#ITimelock-get_timestamp) -- [`get_operation_state(id)`](#ITimelock-get_operation_state) -- [`get_min_delay()`](#ITimelock-get_min_delay) -- [`hash_operation(call, predecessor, salt)`](#ITimelock-hash_operation) -- [`hash_operation_batch(calls, predecessor, salt)`](#ITimelock-hash_operation_batch) -- [`schedule(call, predecessor, salt, delay)`](#ITimelock-schedule) -- [`schedule_batch(calls, predecessor, salt, delay)`](#ITimelock-schedule_batch) -- [`cancel(id)`](#ITimelock-cancel) -- [`execute(call, predecessor, salt)`](#ITimelock-execute) -- [`execute_batch(calls, predecessor, salt)`](#ITimelock-execute_batch) -- [`update_delay(new_delay)`](#ITimelock-update_delay) - -Events - -- [`CallScheduled(id, index, call, predecessor, delay)`](#ITimelock-CallScheduled) -- [`CallExecuted(id, index, call)`](#ITimelock-CallExecuted) -- [`CallSalt(id, salt)`](#ITimelock-CallSalt) -- [`CallCancelled(id)`](#ITimelock-CallCancelled) -- [`MinDelayChanged(old_duration, new_duration)`](#ITimelock-MinDelayChanged) - -#### Functions [!toc] [#ITimelock-Functions] - - -Returns whether `id` corresponds to a registered operation. This includes the OperationStates: `Waiting`, `Ready`, and `Done`. - - - -Returns whether the `id` OperationState is pending or not. Note that a pending operation may be either `Waiting` or `Ready`. - - - -Returns whether the `id` OperationState is `Ready` or not. - - - -Returns whether the `id` OperationState is `Done` or not. - - - -Returns the timestamp at which `id` becomes `Ready`. - -`0` means the OperationState is `Unset` and `1` means the OperationState is `Done`. - - - - -Returns the current state of the operation with the given `id`. - -The possible states are: - -- `Unset`: the operation has not been scheduled or has been canceled. -- `Waiting`: the operation has been scheduled and is pending the scheduled delay. -- `Ready`: the timer has expired, and the operation is eligible for execution. -- `Done`: the operation has been executed. - - - -Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls `update_delay`. - - - -Returns the identifier of an operation containing a single transaction. - - - -Returns the identifier of an operation containing a batch of transactions. - - - -Schedule an operation containing a single transaction. - -Requirements: - -- The caller must have the `PROPOSER_ROLE` role. - -Emits [CallScheduled](#ITimelock-CallScheduled) event. Emits [CallSalt](#ITimelock-CallSalt) event if `salt` is not zero. - - - -Schedule an operation containing a batch of transactions. - -Requirements: - -- The caller must have the `PROPOSER_ROLE` role. - -Emits one [CallScheduled](#ITimelock-CallScheduled) event for each transaction in the batch. Emits [CallSalt](#ITimelock-CallSalt) event if `salt` is not zero. - - - -Cancels an operation. A canceled operation returns to `Unset` OperationState. - -Requirements: - -- The caller must have the `CANCELLER_ROLE` role. -- `id` must be a pending operation. - -Emits a [CallCancelled](#ITimelock-CallCancelled) event. - - - -Execute a (Ready) operation containing a single Call. - -Requirements: - -- Caller must have `EXECUTOR_ROLE`. -- `id` must be in Ready OperationState. -- `predecessor` must either be `0` or in Done OperationState. - -Emits a [CallExecuted](#ITimelock-CallExecuted) event. - -This function can reenter, but it doesn't pose a risk because [`_after_call(self: @ContractState, id: felt252)` internal](#TimelockControllerComponent-_after_call) checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. - - - -Execute a (Ready) operation containing a batch of Calls. - -Requirements: - -- Caller must have `EXECUTOR_ROLE`. -- `id` must be in Ready OperationState. -- `predecessor` must either be `0` or in Done OperationState. - -Emits a [CallExecuted](#ITimelock-CallExecuted) event for each Call. - -This function can reenter, but it doesn't pose a risk because `_after_call` checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. - - - -Changes the minimum timelock duration for future operations. - -Requirements: - -- The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function. - -Emits a [MinDelayChanged](#ITimelock-MinDelayChanged) event. - - -#### Events [!toc] [#ITimelock-Events] - - -Emitted when `call` is scheduled as part of operation `id`. - - - -Emitted when `call` is performed as part of operation `id`. - - - -Emitted when a new proposal is scheduled with non-zero salt. - - - -Emitted when operation `id` is cancelled. - - - -Emitted when the minimum delay for future operations is modified. - - -### `IVotes` [toc] [#IVotes] - - - -```rust -use openzeppelin_interfaces::votes::IVotes; -``` - -Common interface for Votes-enabled contracts. - -Functions - -- [`get_votes(account)`](#IVotes-get_votes) -- [`get_past_votes(account, timepoint)`](#IVotes-get_past_votes) -- [`get_past_total_supply(timepoint)`](#IVotes-get_past_total_supply) -- [`delegates(account)`](#IVotes-delegates) -- [`delegate(delegatee)`](#IVotes-delegate) -- [`delegate_by_sig(delegator, delegatee, nonce, expiry, signature)`](#IVotes-delegate_by_sig) -- [`clock()`](#IVotes-clock) -- [`CLOCK_MODE()`](#IVotes-CLOCK_MODE) - -#### Functions [!toc] [#IVotes-Functions] - - -Returns the current amount of votes that `account` has. - - - -Returns the amount of votes that `account` had at a specific moment in the past. - - - -Returns the total supply of votes available at a specific moment in the past. - -This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote. - - - -Returns the delegate that `account` has chosen. - - - -Delegates votes from the sender to `delegatee`. - - - -Delegates votes from `delegator` to `delegatee` through a [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message signature validation. - - - -Returns the current timepoint determined by the contract's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). - -Requirements: - -- This function MUST always be non-decreasing. - - - -Returns a description of the clock the contract is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). - -Requirements: - -- The output MUST be formatted like a URL query string, decodable in standard JavaScript. - - -## [](#governor)Governor - -This modular system of Governor components allows the deployment of easily customizable on-chain voting protocols. - -For a walkthrough of how to implement a Governor, check the [Governor](../governance/governor) page. - -### `GovernorComponent` [toc] [#GovernorComponent] - - - -```rust -use openzeppelin_governance::governor::GovernorComponent; -``` - -Core of the governance system. - -The extension traits presented below are what make the GovernorComponent a modular and configurable system. The embeddable and internal implementations depends on these trait. They can be implemented locally in the contract, or through the provided library [component extensions](#governor_extensions). - -Implementing [SRC5Component](./introspection#SRC5Component) is a requirement for this component to be implemented. - -Extensions traits - -#### GovernorSettingsTrait [!toc] [#GovernorComponent-GovernorSettingsTrait] - -- [`voting_delay(self)`](#GovernorComponent-GovernorSettingsTrait-voting_delay) -- [`voting_period(self)`](#GovernorComponent-GovernorSettingsTrait-voting_period) -- [`proposal_threshold(self)`](#GovernorComponent-GovernorSettingsTrait-proposal_threshold) - -#### GovernorQuorumTrait [!toc] [#GovernorComponent-GovernorQuorumTrait] - -- [`quorum(self, timepoint)`](#GovernorComponent-GovernorQuorumTrait-quorum) - -#### GovernorCountingTrait [!toc] [#GovernorComponent-GovernorCountingTrait] - -- [`counting_mode(self)`](#GovernorComponent-GovernorCountingTrait-counting_mode) -- [`count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorComponent-GovernorCountingTrait-count_vote) -- [`has_voted(self, proposal_id, account)`](#GovernorComponent-GovernorCountingTrait-has_voted) -- [`quorum_reached(self, proposal_id)`](#GovernorComponent-GovernorCountingTrait-quorum_reached) -- [`vote_succeeded(self, proposal_id)`](#GovernorComponent-GovernorCountingTrait-vote_succeeded) - -#### GovernorVotesTrait [!toc] [#GovernorComponent-GovernorVotesTrait] - -- [`clock(self)`](#GovernorComponent-GovernorVotesTrait-clock) -- [`CLOCK_MODE(self)`](#GovernorComponent-GovernorVotesTrait-CLOCK_MODE) -- [`get_votes(self, account, timepoint, params)`](#GovernorComponent-GovernorVotesTrait-get_votes) - -#### GovernorExecutionTrait [!toc] [#GovernorComponent-GovernorExecutionTrait] - -- [`state(self, proposal_id)`](#GovernorComponent-GovernorExecutionTrait-state) -- [`executor(self)`](#GovernorComponent-GovernorExecutionTrait-executor) -- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorComponent-GovernorExecutionTrait-execute_operations) -- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorComponent-GovernorExecutionTrait-queue_operations) -- [`proposal_needs_queuing(self, proposal_id)`](#GovernorComponent-GovernorExecutionTrait-proposal_needs_queuing) -- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorComponent-GovernorExecutionTrait-cancel_operations) - -Embeddable Implementations - -#### GovernorImpl [!toc] [#GovernorComponent-GovernorImpl] - -- [`name(self)`](#GovernorComponent-name) -- [`version(self)`](#GovernorComponent-version) -- [`COUNTING_MODE(self)`](#GovernorComponent-COUNTING_MODE) -- [`hash_proposal(self, calls, description_hash)`](#GovernorComponent-hash_proposal) -- [`state(self, proposal_id)`](#GovernorComponent-state) -- [`proposal_threshold(self)`](#GovernorComponent-proposal_threshold) -- [`proposal_snapshot(self, proposal_id)`](#GovernorComponent-proposal_snapshot) -- [`proposal_deadline(self, proposal_id)`](#GovernorComponent-proposal_deadline) -- [`proposal_proposer(self, proposal_id)`](#GovernorComponent-proposal_proposer) -- [`proposal_eta(self, proposal_id)`](#GovernorComponent-proposal_eta) -- [`proposal_needs_queuing(self, proposal_id)`](#GovernorComponent-proposal_needs_queuing) -- [`voting_delay(self)`](#GovernorComponent-voting_delay) -- [`voting_period(self)`](#GovernorComponent-voting_period) -- [`quorum(self, timepoint)`](#GovernorComponent-quorum) -- [`get_votes(self, account, timepoint)`](#GovernorComponent-get_votes) -- [`get_votes_with_params(self, account, timepoint, params)`](#GovernorComponent-get_votes_with_params) -- [`has_voted(self, proposal_id, account)`](#GovernorComponent-has_voted) -- [`propose(self, calls, description)`](#GovernorComponent-propose) -- [`queue(self, calls, description_hash)`](#GovernorComponent-queue) -- [`execute(self, calls, description_hash)`](#GovernorComponent-execute) -- [`cancel(self, proposal_id, description_hash)`](#GovernorComponent-cancel) -- [`cast_vote(self, proposal_id, support)`](#GovernorComponent-cast_vote) -- [`cast_vote_with_reason(self, proposal_id, support, reason)`](#GovernorComponent-cast_vote_with_reason) -- [`cast_vote_with_reason_and_params(self, proposal_id, support, reason, params)`](#GovernorComponent-cast_vote_with_reason_and_params) -- [`cast_vote_by_sig(self, proposal_id, support, reason, signature)`](#GovernorComponent-cast_vote_by_sig) -- [`cast_vote_with_reason_and_params_by_sig(self, proposal_id, support, reason, params, signature)`](#GovernorComponent-cast_vote_with_reason_and_params_by_sig) -- [`nonces(self, voter)`](#GovernorComponent-nonces) -- [`relay(self, call)`](#GovernorComponent-relay) - -Internal Implementations - -#### InternalImpl [!toc] [#GovernorComponent-InternalImpl] - -- [`initializer(self)`](#GovernorComponent-initializer) -- [`get_proposal(self, proposal_id)`](#GovernorComponent-get_proposal) -- [`is_valid_description_for_proposer(self, proposer, description)`](#GovernorComponent-is_valid_description_for_proposer) -- [`_hash_proposal(self, calls, description_hash)`](#GovernorComponent-_hash_proposal) -- [`_proposal_snapshot(self, proposal_id)`](#GovernorComponent-_proposal_snapshot) -- [`_proposal_deadline(self, proposal_id)`](#GovernorComponent-_proposal_deadline) -- [`_proposal_proposer(self, proposal_id)`](#GovernorComponent-_proposal_proposer) -- [`_proposal_eta(self, proposal_id)`](#GovernorComponent-_proposal_eta) - -#### InternalExtendedImpl [!toc] [#GovernorComponent-InternalExtendedImpl] - -- [`assert_only_governance(self)`](#GovernorComponent-assert_only_governance) -- [`validate_state(self, proposal_id, allowed_states)`](#GovernorComponent-validate_state) -- [`use_nonce(self, voter)`](#GovernorComponent-use_nonce) -- [`_get_votes(self, account, timepoint, params)`](#GovernorComponent-_get_votes) -- [`_proposal_threshold(self)`](#GovernorComponent-_proposal_threshold) -- [`_state(self, proposal_id)`](#GovernorComponent-_state) -- [`_propose(self, calls, description, proposer)`](#GovernorComponent-_propose) -- [`_cancel(self, proposal_id, description_hash)`](#GovernorComponent-_cancel) -- [`_count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorComponent-_count_vote) -- [`_cast_vote(self, proposal_id, voter, support, reason, params)`](#GovernorComponent-_cast_vote) - -Events - -- [`ProposalCreated(proposal_id, proposer, calls, signatures, vote_start, vote_end, description)`](#GovernorComponent-ProposalCreated) -- [`ProposalQueued(proposal_id)`](#GovernorComponent-ProposalQueued) -- [`ProposalExecuted(proposal_id)`](#GovernorComponent-ProposalExecuted) -- [`ProposalCanceled(proposal_id)`](#GovernorComponent-ProposalCanceled) -- [`VoteCast(voter, proposal_id, support, weight, reason)`](#GovernorComponent-VoteCast) -- [`VoteCastWithParams(voter, proposal_id, support, weight, reason, params)`](#GovernorComponent-VoteCastWithParams) - -#### Extensions traits functions [!toc] [#GovernorComponent-Extensions-Traits] - - -Must return the delay, in number of timepoints, between when the proposal is created and when the vote starts. This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. - - - -Must return the delay, in number of timepoints, between the vote start and vote end. - - - -Must return the minimum number of votes that an account must have to create a proposal. - - - -Must return the minimum number of votes required for a proposal to succeed. - - - -Must return a description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. See [COUNTING\_MODE](#GovernorComponent-COUNTING_MODE) for more details. - - - -Must register a vote for `proposal_id` by `account` with a given `support`, voting `weight` and voting `params`. - -Support is generic and can represent various things depending on the voting system used. - - - -Must return whether an account has cast a vote on a proposal. - - - -Must return whether the minimum quorum has been reached for a proposal. - - - -Must return whether a proposal has succeeded or not. - - - -Returns the current timepoint determined by the governor's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). - -Requirements: - -- This function MUST always be non-decreasing. - - - -Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). - -Requirements: - -- The output MUST be formatted like a URL query string, decodable in standard JavaScript. - - - -Must return the voting power of an account at a specific timepoint with the given parameters. - - - -Must return the state of a proposal at the current time. - -The state can be either: - -- `Pending`: The proposal does not exist yet. -- `Active`: The proposal is active. -- `Canceled`: The proposal has been canceled. -- `Defeated`: The proposal has been defeated. -- `Succeeded`: The proposal has succeeded. -- `Queued`: The proposal has been queued. -- `Executed`: The proposal has been executed. - - - -Must return the address through which the governor executes action. Should be used to specify whether the module execute actions through another contract such as a timelock. - -MUST be the governor itself, or an instance of TimelockController with the governor as the only proposer, canceller, and executor. - -When the executor is not the governor itself (i.e. a timelock), it can call functions that are restricted with the `assert_only_governance` guard, and also potentially execute transactions on behalf of the governor. Because of this, this module is designed to work with the TimelockController as the unique potential external executor. - - - - -Execution mechanism. Can be used to modify the way operations are executed (for example adding a vault/timelock). - - - -Queuing mechanism. Can be used to modify the way queuing is performed (for example adding a vault/timelock). - -Requirements: - -- Must return a timestamp that describes the expected ETA for execution. If the returned value is 0, the core will consider queueing did not succeed, and the public `queue` function will revert. - - - -Must return whether proposals need to be queued before execution. This usually indicates if the proposal needs to go through a timelock. - - - -Cancel mechanism. Can be used to modify the way canceling is performed (for example adding a vault/timelock). - - -#### Embeddable functions [!toc] [#GovernorComponent-Embeddable-Functions] - - -Name of the governor instance (used in building the [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). - - - -Version of the governor instance (used in building [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) domain separator). - - - -A description of the possible `support` values for `cast_vote` and the way these votes are counted, meant to be consumed by UIs to show correct vote options and interpret the results. The string is a URL-encoded sequence of key-value pairs that each describe one aspect, for example `support=bravo&quorum=for,abstain`. - -There are 2 standard keys: `support` and `quorum`. - -- `support=bravo` refers to the vote options 0 = Against, 1 = For, 2 = Abstain, as in `GovernorBravo`. -- `quorum=bravo` means that only For votes are counted towards quorum. -- `quorum=for,abstain` means that both For and Abstain votes are counted towards quorum. - -If a counting module makes use of encoded `params`, it should include this under a `params` key with a unique name that describes the behavior. For example: - -- `params=fractional` might refer to a scheme where votes are divided fractionally between for/against/abstain. -- `params=erc721` might refer to a scheme where specific NFTs are delegated to vote. - -The string can be decoded by the standard [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) JavaScript class. - - - - -Hashing function used to (re)build the proposal id from the proposal details. - - - -Returns the state of a proposal, given its id. - - - -The number of votes required in order for a voter to become a proposer. - - - -Timepoint used to retrieve user's votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. - - - -Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. - - - -The account that created a proposal. - - - -The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn't use the governor clock, and instead relies on the executor's clock which may be different. In most cases this will be a timestamp. - - - -Whether a proposal needs to be queued before execution. This indicates if the proposal needs to go through a timelock. - - - -Delay between when a proposal is created and when the vote starts. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. - -This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. - - - -Delay between the vote start and vote end. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. - -The `voting_delay` can delay the start of the vote. This must be considered when setting the voting duration compared to the voting delay. - -This value is stored when the proposal is submitted so that possible changes to the value do not affect proposals that have already been submitted. - - - - -Minimum number of votes required for a proposal to be successful. - -The `timepoint` parameter corresponds to the snapshot used for counting vote. This allows the quorum to scale depending on values such as the total supply of a token at this timepoint. - - - -Returns the voting power of an `account` at a specific `timepoint`. - -This can be implemented in a number of ways, for example by reading the delegated balance from one (or multiple) `ERC20Votes` tokens. - - - -Returns the voting power of an account at a specific timepoint, given additional encoded parameters. - - - -Returns whether an account has cast a vote on a proposal. - - - -Creates a new proposal. Voting starts after the delay specified by `voting_delay` and lasts for a duration specified by `voting_period`. Returns the id of the proposal. - -This function has opt-in frontrunning protection, described in `is_valid_description_for_proposer`. - -The state of the Governor and targets may change between the proposal creation and its execution. This may be the result of third party actions on the targeted contracts, or other governor proposals. For example, the balance of this contract could be updated or its access control permissions may be modified, possibly compromising the proposal's ability to execute successfully (e.g. the governor doesn't have enough value to cover a proposal with multiple transfers). - -Requirements: - -- The proposer must be authorized to submit the proposal. -- The proposer must have enough votes to submit the proposal if `proposal_threshold` is greater than zero. -- The proposal must not already exist. - -Emits a [ProposalCreated](#GovernorComponent-ProposalCreated) event. - - - -Queues a proposal. Some governors require this step to be performed before execution can happen. If queuing is not necessary, this function may revert. Queuing a proposal requires the quorum to be reached, the vote to be successful, and the deadline to be reached. - -Returns the id of the proposal. - -Requirements: - -- The proposal must be in the `Succeeded` state. -- The queue operation must return a non-zero ETA. - -Emits a [ProposalQueued](#GovernorComponent-ProposalQueued) event. - - - - -Executes a successful proposal. This requires the quorum to be reached, the vote to be successful, and the deadline to be reached. Depending on the governor it might also be required that the proposal was queued and that some delay passed. - -Some modules can modify the requirements for execution, for example by adding an additional timelock (See `timelock_controller`). - -Returns the id of the proposal. - -Requirements: - -- The proposal must be in the `Succeeded` or `Queued` state. - -Emits a [ProposalExecuted](#GovernorComponent-ProposalExecuted) event. - - - -Cancels a proposal. A proposal is cancellable by the proposer, but only while it is Pending state, i.e. before the vote starts. - -Returns the id of the proposal. - -Requirements: - -- The proposal must be in the `Pending` state. -- The caller must be the proposer of the proposal. - -Emits a [ProposalCanceled](#GovernorComponent-ProposalCanceled) event. - - - -Cast a vote. - -Requirements: - -- The proposal must be active. - -Emits a [VoteCast](#GovernorComponent-VoteCast) event. - - - -Cast a vote with a `reason`. - -Requirements: - -- The proposal must be active. - -Emits a [VoteCast](#GovernorComponent-VoteCast) event. - - - -Cast a vote with a `reason` and additional serialized `params`. - -Requirements: - -- The proposal must be active. - -Emits either: - -- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. -- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. - - - -Cast a vote using the `voter`'s signature. - -Requirements: - -- The proposal must be active. -- The nonce in the signed message must match the account's current nonce. -- `voter` must implement `SRC6::is_valid_signature`. -- `signature` must be valid for the message hash. - -Emits a [VoteCast](#GovernorComponent-VoteCast) event. - - - -Cast a vote with a `reason` and additional serialized `params` using the `voter`'s signature. - -Requirements: - -- The proposal must be active. -- The nonce in the signed message must match the account's current nonce. -- `voter` must implement `SRC6::is_valid_signature`. -- `signature` must be valid for the message hash. - -Emits either: - -- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. -- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. - - - -Returns the next unused nonce for an address. - - - -Relays a transaction or function call to an arbitrary target. - -In cases where the governance executor is some contract other than the governor itself, like when using a timelock, this function can be invoked in a governance proposal to recover tokens that were sent to the governor contract by mistake. - -If the executor is simply the governor itself, use of `relay` is redundant. - - -#### Internal functions [!toc] [#GovernorComponent-Internal-Functions] - - -Initializes the contract by registering the supported interface id. - - - -Returns the proposal object given its id. - - - -Checks if the proposer is authorized to submit a proposal with the given description. - -If the proposal description ends with `#proposer=0x???`, where `0x???` is an address written as a hex string (case insensitive), then the submission of this proposal will only be authorized to said address. - -This is used for frontrunning protection. By adding this pattern at the end of their proposal, one can ensure that no other address can submit the same proposal. An attacker would have to either remove or change that part, which would result in a different proposal id. - -In Starknet, the Sequencer ensures the order of transactions, but frontrunning can still be achieved by nodes, and potentially other actors in the future with sequencer decentralization. - -If the description does not match this pattern, it is unrestricted and anyone can submit it. This includes: - -- If the `0x???` part is not a valid hex string. -- If the `0x???` part is a valid hex string, but does not contain exactly 64 hex digits. -- If it ends with the expected suffix followed by newlines or other whitespace. -- If it ends with some other similar suffix, e.g. `#other=abc`. -- If it does not end with any such suffix. - - - -Returns the proposal id computed from the given parameters. - -The proposal id is computed as a Pedersen hash of: - -- The array of calls being proposed -- The description hash - - - -Timepoint used to retrieve user's votes and quorum. If using block number, the snapshot is performed at the end of this block. Hence, voting for this proposal starts at the beginning of the following block. - - - -Timepoint at which votes close. If using block number, votes close at the end of this block, so it is possible to cast a vote during this block. - - - -The account that created a proposal. - - - -The time when a queued proposal becomes executable ("ETA"). Unlike `proposal_snapshot` and `proposal_deadline`, this doesn't use the governor clock, and instead relies on the executor's clock which may be different. In most cases this will be a timestamp. - - - -Asserts that the caller is the governance executor. - -When the executor is not the governor itself (i.e. a timelock), it can call functions that are restricted with this modifier, and also potentially execute transactions on behalf of the governor. Because of this, this module is designed to work with the TimelockController as the unique potential external executor. The timelock MUST have the governor as the only proposer, canceller, and executor. - - - -Validates that a proposal is in the expected state. Otherwise it panics. - - - -Consumes a nonce, returns the current value, and increments nonce. - - - -Internal wrapper for `GovernorVotesTrait::get_votes`. - - - -Internal wrapper for `GovernorProposeTrait::proposal_threshold`. - - - -Returns the state of a proposal, given its id. - -Requirements: - -- The proposal must exist. - - - -Internal propose mechanism. Returns the proposal id. - -Requirements: - -- The proposal must not already exist. - -Emits a [ProposalCreated](#GovernorComponent-ProposalCreated) event. - - - -Internal cancel mechanism with minimal restrictions. - -A proposal can be cancelled in any state other than Canceled or Executed. - -Once cancelled, a proposal can't be re-submitted. - - - -Internal wrapper for `GovernorCountingTrait::count_vote`. - - - -Internal vote-casting mechanism. - -Checks that the vote is pending and that it has not been cast yet. This function retrieves the voting weight using `get_votes` and then calls the `_count_vote` internal function. - -Emits either: - -- [VoteCast](#GovernorComponent-VoteCast) event if no params are provided. -- [VoteCastWithParams](#GovernorComponent-VoteCastWithParams) event otherwise. - - -#### Events [!toc] [#GovernorComponent-Events] - - -Emitted when a proposal is created. - - - -Emitted when a proposal is queued. - - - -Emitted when a proposal is executed. - - - -Emitted when a proposal is canceled. - - - -Emitted when a vote is cast. - - - -Emitted when a vote is cast with params. - - -## [](#governor_extensions)Governor extensions - -The Governor component can (and must) be extended by implementing the [extensions traits](#GovernorComponent-Extensions-Traits-Traits) to add the desired functionality. This can be achieved by directly implementing the traits on your contract, or by using a set of ready-to-use extensions provided by the library, which are presented below. - -### `GovernorCoreExecutionComponent` [toc] [#GovernorCoreExecutionComponent] - - - -```rust -use openzeppelin_governance::governor::extensions::GovernorCoreExecutionComponent; -``` - -Extension of [GovernorComponent](#GovernorComponent) providing an execution mechanism directly through the Governor itself. For a timelocked execution mechanism, see [GovernorTimelockExecutionComponent](#GovernorTimelockExecutionComponent). - -Extension traits implementations - -#### GovernorExecution [!toc] [#GovernorCoreExecutionComponent-GovernorExecution] - -- [`state(self, proposal_id)`](#GovernorCoreExecutionComponent-state) -- [`executor(self)`](#GovernorCoreExecutionComponent-executor) -- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorCoreExecutionComponent-execute_operations) -- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorCoreExecutionComponent-queue_operations) -- [`proposal_needs_queuing(self, proposal_id)`](#GovernorCoreExecutionComponent-proposal_needs_queuing) -- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorCoreExecutionComponent-cancel_operations) - -#### Extension traits functions [!toc] [#GovernorCoreExecutionComponent-Extension-Traits-Functions] - - -Returns the state of a proposal given its id. - -Requirements: - -- The proposal must exist. - - - -Returns the executor address. - -In this case, it returns the governor contract address since execution is performed directly through it. - - - -Executes the proposal's operations directly through the governor contract. - - - -In this implementation, queuing is not required so it returns 0. - - - -In this implementation, it always returns false. - - - -Cancels a proposal's operations. - - -### `GovernorCountingSimpleComponent` [toc] [#GovernorCountingSimpleComponent] - - - -```rust -use openzeppelin_governance::governor::extensions::GovernorCountingSimpleComponent; -``` - -Extension of [GovernorComponent](#GovernorComponent) for simple vote counting with three options. - -Extension traits implementations - -#### GovernorCounting [!toc] [#GovernorCountingSimpleComponent-GovernorCounting] - -- [`counting_mode(self)`](#GovernorCountingSimpleComponent-counting_mode) -- [`count_vote(self, proposal_id, account, support, total_weight, params)`](#GovernorCountingSimpleComponent-count_vote) -- [`has_voted(self, proposal_id, account)`](#GovernorCountingSimpleComponent-has_voted) -- [`quorum_reached(self, proposal_id)`](#GovernorCountingSimpleComponent-quorum_reached) -- [`vote_succeeded(self, proposal_id)`](#GovernorCountingSimpleComponent-vote_succeeded) - -#### Extension traits functions [!toc] [#GovernorCountingSimpleComponent-Extension-Traits-Functions] - - -Returns `"support=bravo&quorum=for,abstain"`. - -- `support=bravo` indicates that the support follows the Governor Bravo format where voters can vote For, Against, or Abstain -- `quorum=for,abstain` indicates that both For and Abstain votes count toward quorum - - - -Records a vote for a proposal. - -The support value follows the `VoteType` enum (0=Against, 1=For, 2=Abstain). - -Returns the weight that was counted. - - - -Returns whether an account has cast a vote on a proposal. - - - - -Returns whether a proposal has reached quorum. - -In this implementation, both For and Abstain votes count toward quorum. - - - -Returns whether a proposal has succeeded. - -In this implementation, the For votes must be strictly greater than Against votes. - - -### `GovernorSettingsComponent` [toc] [#GovernorSettingsComponent] - - - -```rust -use openzeppelin_governance::governor::extensions::GovernorSettingsComponent; -``` - -Extension of [GovernorComponent](#GovernorComponent) for settings that are updatable through governance. - -Extension traits implementations - -#### GovernorSettings [!toc] [#GovernorSettingsComponent-GovernorSettings] - -- [`voting_delay(self)`](#GovernorSettingsComponent-voting_delay) -- [`voting_period(self)`](#GovernorSettingsComponent-voting_period) -- [`proposal_threshold(self)`](#GovernorSettingsComponent-proposal_threshold) - -Embeddable implementations - -#### GovernorSettingsAdminImpl [!toc] [#GovernorSettingsComponent-GovernorSettingsAdminImpl] - -- [`set_voting_delay(self, new_voting_delay)`](#GovernorSettingsComponent-set_voting_delay) -- [`set_voting_period(self, new_voting_period)`](#GovernorSettingsComponent-set_voting_period) -- [`set_proposal_threshold(self, new_proposal_threshold)`](#GovernorSettingsComponent-set_proposal_threshold) - -Internal implementations - -#### InternalImpl [!toc] [#GovernorSettingsComponent-InternalImpl] - -- [`initializer(self, new_voting_delay, new_voting_period, new_proposal_threshold)`](#GovernorSettingsComponent-initializer) -- [`assert_only_governance(self)`](#GovernorSettingsComponent-assert_only_governance) -- [`_set_voting_delay(self, new_voting_delay)`](#GovernorSettingsComponent-_set_voting_delay) -- [`_set_voting_period(self, new_voting_period)`](#GovernorSettingsComponent-_set_voting_period) -- [`_set_proposal_threshold(self, new_proposal_threshold)`](#GovernorSettingsComponent-_set_proposal_threshold) - -Events - -- [`VotingDelayUpdated(old_voting_delay, new_voting_delay)`](#GovernorSettingsComponent-VotingDelayUpdated) -- [`VotingPeriodUpdated(old_voting_period, new_voting_period)`](#GovernorSettingsComponent-VotingPeriodUpdated) -- [`ProposalThresholdUpdated(old_proposal_threshold, new_proposal_threshold)`](#GovernorSettingsComponent-ProposalThresholdUpdated) - -#### Extension traits functions [!toc] [#GovernorSettingsComponent-ExtensionTraitsFunctions] - - -Returns the delay, between when a proposal is created and when voting starts. - - - -Returns the time period, during which votes can be cast. - - - -Returns the minimum number of votes required for an account to create a proposal. - - -#### Embeddable functions [!toc] [#GovernorSettingsComponent-EmbeddableFunctions] - - -Sets the voting delay. - -Requirements: - -- Caller must be the governance executor. - -This function does not emit an event if the new voting delay is the same as the old one. - -May emit a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated) event. - - - -Sets the voting period. - -This function does not emit an event if the new voting period is the same as the old one. - -Requirements: - -- Caller must be the governance executor. -- `new_voting_period` must be greater than 0. - -May emit a [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated) event. - - - -Sets the proposal threshold. - -This function does not emit an event if the new proposal threshold is the same as the old one. - -Requirements: - -- Caller must be the governance executor. - -May emit a [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. - - -#### Internal functions [!toc] [#GovernorSettingsComponent-InternalFunctions] - - -Initializes the component by setting the default values. - -Requirements: - -- `new_voting_period` must be greater than 0. - -Emits a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated), [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated), and [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. - - - -Asserts that the caller is the governance executor. - - - -Internal function to update the voting delay. - -This function does not emit an event if the new voting delay is the same as the old one. - -May emit a [VotingDelayUpdated](#GovernorSettingsComponent-VotingDelayUpdated) event. - - - -Internal function to update the voting period. - -Requirements: - -- `new_voting_period` must be greater than 0. - -This function does not emit an event if the new voting period is the same as the old one. - -May emit a [VotingPeriodUpdated](#GovernorSettingsComponent-VotingPeriodUpdated) event. - - - -Internal function to update the proposal threshold. - -This function does not emit an event if the new proposal threshold is the same as the old one. - -May emit a [ProposalThresholdUpdated](#GovernorSettingsComponent-ProposalThresholdUpdated) event. - - -#### Events [!toc] [#GovernorSettingsComponent-Events] - - -Emitted when the voting delay is updated. - - - -Emitted when the voting period is updated. - - - -Emitted when the proposal threshold is updated. - - -### `GovernorVotesComponent` [toc] [#GovernorVotesComponent] - - - -```rust -use openzeppelin_governance::governor::extensions::GovernorVotesComponent; -``` - -Extension of [GovernorComponent](#GovernorComponent) for voting weight extraction from a token with the [IVotes](#IVotes) extension. - -Extension traits implementations - -#### GovernorVotes [!toc] [#GovernorVotesComponent-GovernorVotes] - -- [`clock(self)`](#GovernorVotesComponent-clock) -- [`CLOCK_MODE(self)`](#GovernorVotesComponent-CLOCK_MODE) -- [`get_votes(self, account, timepoint, params)`](#GovernorVotesComponent-get_votes) - -Embeddable implementations - -#### VotesTokenImpl [!toc] [#GovernorVotesComponent-VotesTokenImpl] - -- [`token(self)`](#GovernorVotesComponent-token) - -Internal implementations - -#### InternalImpl [!toc] [#GovernorVotesComponent-InternalImpl] - -- [`initializer(self, votes_token)`](#GovernorVotesComponent-initializer) - -#### Extension traits functions [!toc] [#GovernorVotesComponent-ExtensionTraitsFunctions] - - -Returns the current timepoint determined by the governor's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). - -Requirements: - -- This function MUST always be non-decreasing. - - - -Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). - -Requirements: - -- The output MUST be formatted like a URL query string, decodable in standard JavaScript. - - - -Returns the voting power of `account` at a specific `timepoint` using the votes token. - - -#### Embeddable functions [!toc] [#GovernorVotesComponent-EmbeddableFunctions] - - -Returns the votes token that voting power is sourced from. - - -#### Internal functions [!toc] [#GovernorVotesComponent-InternalFunctions] - - -Initializes the component by setting the votes token. - -Requirements: - -- `votes_token` must not be zero. - - -### `GovernorVotesQuorumFractionComponent` [toc] [#GovernorVotesQuorumFractionComponent] - - - -```rust -use openzeppelin_governance::governor::extensions::GovernorVotesQuorumFractionComponent; -``` - -Extension of [GovernorComponent](#GovernorComponent) for voting weight extraction from a token with the [IVotes](#IVotes) extension and a quorum expressed as a fraction of the total supply. - -Extension traits implementations - -#### GovernorQuorum [!toc] [#GovernorVotesQuorumFractionComponent-GovernorQuorum] - -- [`quorum(self, timepoint)`](#GovernorVotesQuorumFractionComponent-quorum) - -#### GovernorVotes [!toc] [#GovernorVotesQuorumFractionComponent-GovernorVotes] - -- [`clock(self)`](#GovernorVotesQuorumFractionComponent-clock) -- [`CLOCK_MODE(self)`](#GovernorVotesQuorumFractionComponent-CLOCK_MODE) -- [`get_votes(self, account, timepoint, params)`](#GovernorVotesQuorumFractionComponent-get_votes) - -Embeddable implementations - -#### QuorumFractionImpl [!toc] [#GovernorVotesQuorumFractionComponent-QuorumFractionImpl] - -- [`token(self)`](#GovernorVotesQuorumFractionComponent-token) -- [`current_quorum_numerator(self)`](#GovernorVotesQuorumFractionComponent-current_quorum_numerator) -- [`quorum_numerator(self, timepoint)`](#GovernorVotesQuorumFractionComponent-quorum_numerator) -- [`quorum_denominator(self)`](#GovernorVotesQuorumFractionComponent-quorum_denominator) - -Internal implementations - -#### InternalImpl [!toc] [#GovernorVotesQuorumFractionComponent-InternalImpl] - -- [`initializer(self, votes_token, quorum_numerator)`](#GovernorVotesQuorumFractionComponent-initializer) -- [`update_quorum_numerator(self, new_quorum_numerator)`](#GovernorVotesQuorumFractionComponent-update_quorum_numerator) - -Events - -- [`QuorumNumeratorUpdated(old_quorum_numerator, new_quorum_numerator)`](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) - -#### Extension traits functions [!toc] [#GovernorVotesQuorumFractionComponent-ExtensionTraitsFunctions] - - -It is computed as a percentage of the votes token total supply at a given `timepoint` in the past. - - - -Returns the current timepoint determined by the governor's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). - -Requirements: - -- This function MUST always be non-decreasing. - - - -Returns a description of the clock the governor is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). - -Requirements: - -- The output MUST be formatted like a URL query string, decodable in standard JavaScript. - - - -Returns the voting power of `account` at a specific `timepoint` using the votes token. - - -#### Embeddable functions [!toc] [#GovernorVotesQuorumFractionComponent-EmbeddableFunctions] - - -Returns the address of the votes token used for voting power extraction. - - - -Returns the current quorum numerator value. - - - -Returns the quorum numerator value at a specific `timepoint` in the past. - - - -Returns the quorum denominator value. - - -#### Internal functions [!toc] [#GovernorVotesQuorumFractionComponent-InternalFunctions] - - -Initializes the component by setting the votes token and the initial quorum numerator value. - -Requirements: - -- `votes_token` must not be zero. -- `quorum_numerator` must be less than `quorum_denominator`. - -Emits a [QuorumNumeratorUpdated](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) event. - - - -Updates the quorum numerator. - -This function does not emit an event if the new quorum numerator is the same as the old one. - -Requirements: - -- `new_quorum_numerator` must be less than `quorum_denominator`. - -May emit a [QuorumNumeratorUpdated](#GovernorVotesQuorumFractionComponent-QuorumNumeratorUpdated) event. - - -#### Events [!toc] [#GovernorVotesQuorumFractionComponent-Events] - - -Emitted when the quorum numerator is updated. - - -### `GovernorTimelockExecutionComponent` [toc] [#GovernorTimelockExecutionComponent] - - - -```rust -use openzeppelin_governance::governor::extensions::GovernorTimelockExecutionComponent; -``` - -Extension of [GovernorComponent](#GovernorComponent) that binds the execution process to an instance of a contract implementing [TimelockControllerComponent](#TimelockControllerComponent). This adds a delay, enforced by the timelock to all successful proposals (in addition to the voting duration). - -The Governor needs the [PROPOSER, EXECUTOR, and CANCELLER roles](../governance/timelock#roles) to work properly. - -Using this model means the proposal will be operated by the timelock and not by the governor. Thus, the assets and permissions must be attached to the timelock. Any asset sent to the governor will be inaccessible from a proposal, unless executed via `Governor::relay`. - -Setting up the timelock to have additional proposers or cancellers besides the governor is very risky, as it grants them the ability to: 1) execute operations as the timelock, and thus possibly performing operations or accessing funds that are expected to only be accessible through a vote, and 2) block governance proposals that have been approved by the voters, effectively executing a Denial of Service attack. - -Extension traits implementations - -#### GovernorExecution [!toc] [#GovernorTimelockExecutionComponent-GovernorExecution] - -- [`state(self, proposal_id)`](#GovernorTimelockExecutionComponent-state) -- [`executor(self)`](#GovernorTimelockExecutionComponent-executor) -- [`execute_operations(self, proposal_id, calls, description_hash)`](#GovernorTimelockExecutionComponent-execute_operations) -- [`queue_operations(self, proposal_id, calls, description_hash)`](#GovernorTimelockExecutionComponent-queue_operations) -- [`proposal_needs_queuing(self, proposal_id)`](#GovernorTimelockExecutionComponent-proposal_needs_queuing) -- [`cancel_operations(self, proposal_id, description_hash)`](#GovernorTimelockExecutionComponent-cancel_operations) - -Embeddable implementations - -#### TimelockedImpl [!toc] [#GovernorTimelockExecutionComponent-TimelockedImpl] - -- [`timelock(self)`](#GovernorTimelockExecutionComponent-timelock) -- [`get_timelock_id(self, proposal_id)`](#GovernorTimelockExecutionComponent-get_timelock_id) -- [`update_timelock(self, new_timelock)`](#GovernorTimelockExecutionComponent-update_timelock) - -Internal implementations - -#### InternalImpl [!toc] [#GovernorTimelockExecutionComponent-InternalImpl] - -- [`initializer(self, timelock_controller)`](#GovernorTimelockExecutionComponent-initializer) -- [`assert_only_governance(self)`](#GovernorTimelockExecutionComponent-assert_only_governance) -- [`timelock_salt(self, description_hash)`](#GovernorTimelockExecutionComponent-timelock_salt) -- [`get_timelock_dispatcher(self)`](#GovernorTimelockExecutionComponent-get_timelock_dispatcher) -- [`_update_timelock(self, new_timelock)`](#GovernorTimelockExecutionComponent-_update_timelock) - -Events - -- [`TimelockUpdated(old_timelock, new_timelock)`](#GovernorTimelockExecutionComponent-TimelockUpdated) - -#### Extension traits functions [!toc] [#GovernorTimelockExecutionComponent-ExtensionTraitsFunctions] - - -Returns the state of a proposal given its id. - -Requirements: - -- The proposal must exist. - - - -Returns the executor address. - -In this module, the executor is the timelock controller. - - - -Runs the already queued proposal through the timelock. - - - -Queue a proposal to the timelock. - -Returns the eta for the execution of the queued proposal. - - - -In this implementation, it always returns true. - - - -Cancels the timelocked proposal if it has already been queued. - - -#### Embeddable functions [!toc] [#GovernorTimelockExecutionComponent-EmbeddableFunctions] - - -Returns the timelock controller address. - - - -Returns the timelock proposal id for a given proposal id. - - - -Updates the associated timelock. - -Requirements: - -- The caller must be the governance. - -Emits a [TimelockUpdated](#GovernorTimelockExecutionComponent-TimelockUpdated) event. - - -#### Internal functions [!toc] [#GovernorTimelockExecutionComponent-InternalFunctions] - - -Initializes the timelock controller. - -Requirements: - -- The timelock must not be the zero address. - - - -Ensures the caller is the executor (the timelock controller in this case). - - - -Computes the `TimelockController` operation salt as the XOR of the governor address and `description_hash`. - -It is computed with the governor address itself to avoid collisions across governor instances using the same timelock. - - - -Returns a dispatcher for interacting with the timelock controller. - - - -Internal function to update the timelock controller address. - -Emits a [TimelockUpdated](#GovernorTimelockExecutionComponent-TimelockUpdated) event. - - -#### Events [!toc] [#GovernorTimelockExecutionComponent-Events] - - -Emitted when the timelock controller is updated. - - -## [](#multisig)Multisig - -A Multisig module enhances security and decentralization by requiring multiple signers to approve and execute transactions. Features include configurable quorum, signer management, and self-administration, ensuring collective decision-making and transparency for critical operations. - -### `MultisigComponent` [toc] [#MultisigComponent] - - - -```rust -use openzeppelin_governance::multisig::MultisigComponent; -``` - -Component that implements [IMultisig](#IMultisig) and provides functionality for multisignature wallets, including transaction management, quorum handling, and signer operations. - -Embeddable Implementations - -#### MultisigImpl [!toc] [#MultisigComponent-MultisigImpl] - -- [`get_quorum(self)`](#MultisigComponent-get_quorum) -- [`is_signer(self, signer)`](#MultisigComponent-is_signer) -- [`get_signers(self)`](#MultisigComponent-get_signers) -- [`is_confirmed(self, id)`](#MultisigComponent-is_confirmed) -- [`is_confirmed_by(self, id, signer)`](#MultisigComponent-is_confirmed_by) -- [`is_executed(self, id)`](#MultisigComponent-is_executed) -- [`get_submitted_block(self, id)`](#MultisigComponent-get_submitted_block) -- [`get_transaction_state(self, id)`](#MultisigComponent-get_transaction_state) -- [`get_transaction_confirmations(self, id)`](#MultisigComponent-get_transaction_confirmations) -- [`hash_transaction(self, to, selector, calldata, salt)`](#MultisigComponent-hash_transaction) -- [`hash_transaction_batch(self, calls, salt)`](#MultisigComponent-hash_transaction_batch) -- [`add_signers(ref self, new_quorum, signers_to_add)`](#MultisigComponent-add_signers) -- [`remove_signers(ref self, new_quorum, signers_to_remove)`](#MultisigComponent-remove_signers) -- [`replace_signer(ref self, signer_to_remove, signer_to_add)`](#MultisigComponent-replace_signer) -- [`change_quorum(ref self, new_quorum)`](#MultisigComponent-change_quorum) -- [`submit_transaction(ref self, to, selector, calldata, salt)`](#MultisigComponent-submit_transaction) -- [`submit_transaction_batch(ref self, calls, salt)`](#MultisigComponent-submit_transaction_batch) -- [`confirm_transaction(ref self, id)`](#MultisigComponent-confirm_transaction) -- [`revoke_confirmation(ref self, id)`](#MultisigComponent-revoke_confirmation) -- [`execute_transaction(ref self, to, selector, calldata, salt)`](#MultisigComponent-execute_transaction) -- [`execute_transaction_batch(ref self, calls, salt)`](#MultisigComponent-execute_transaction_batch) - -Internal Implementations - -#### InternalImpl [!toc] [#MultisigComponent-InternalImpl] - -- [`initializer(ref self, quorum, signers)`](#MultisigComponent-initializer) -- [`resolve_tx_state(self, id)`](#MultisigComponent-resolve_tx_state) -- [`assert_one_of_signers(self, caller)`](#MultisigComponent-assert_one_of_signers) -- [`assert_tx_exists(self, id)`](#MultisigComponent-assert_tx_exists) -- [`assert_only_self(self)`](#MultisigComponent-assert_only_self) -- [`_add_signers(ref self, new_quorum, signers_to_add)`](#MultisigComponent-_add_signers) -- [`_remove_signers(ref self, new_quorum, signers_to_remove)`](#MultisigComponent-_remove_signers) -- [`_replace_signer(ref self, signer_to_remove, signer_to_add)`](#MultisigComponent-_replace_signer) -- [`_change_quorum(ref self, new_quorum)`](#MultisigComponent-_change_quorum) - -Events - -- [`SignerAdded(signer)`](#MultisigComponent-SignerAdded) -- [`SignerRemoved(signer)`](#MultisigComponent-SignerRemoved) -- [`QuorumUpdated(old_quorum, new_quorum)`](#MultisigComponent-QuorumUpdated) -- [`TransactionSubmitted(id, signer)`](#MultisigComponent-TransactionSubmitted) -- [`TransactionConfirmed(id, signer)`](#MultisigComponent-TransactionConfirmed) -- [`ConfirmationRevoked(id, signer)`](#MultisigComponent-ConfirmationRevoked) -- [`TransactionExecuted(id)`](#MultisigComponent-TransactionExecuted) -- [`CallSalt(id, salt)`](#MultisigComponent-CallSalt) - -#### Embeddable functions [!toc] [#MultisigComponent-EmbeddableFunctions] - - -Returns the current quorum value. - - - -Checks if a given `signer` is registered. - - - -Returns a list of all current signers. - - - -Returns whether the transaction with the given `id` has been confirmed. A confirmed transaction has received the required number of confirmations (quorum). - - - -Returns whether the transaction with the given `id` has been confirmed by the specified `signer`. - - - -Returns whether the transaction with the given `id` has been executed. - - - -Returns the block number when the transaction with the given `id` was submitted. - - - -Returns the current state of the transaction with the given `id`. - -The possible states are: - -- `NotFound`: the transaction does not exist. -- `Pending`: the transaction exists but hasn't reached the required confirmations. -- `Confirmed`: the transaction has reached the required confirmations but hasn't been executed. -- `Executed`: the transaction has been executed. - - - -Returns the number of confirmations from registered signers for the transaction with the specified `id`. - - - -Returns the computed identifier of a transaction containing a single call. - - - -Returns the computed identifier of a transaction containing a batch of calls. - - - -Adds new signers and updates the quorum. - -Requirements: - -- The caller must be the contract itself. -- `new_quorum` must be less than or equal to the total number of signers after addition. - -Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each signer added. - -Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. - - - -Removes signers and updates the quorum. - -Requirements: - -- The caller must be the contract itself. -- `new_quorum` must be less than or equal to the total number of signers after removal. - -Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for each signer removed. - -Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. - - - -Replaces an existing signer with a new signer. - -Requirements: - -- The caller must be the contract itself. -- `signer_to_remove` must be an existing signer. -- `signer_to_add` must not be an existing signer. - -Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for the removed signer. - -Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for the new signer. - - - -Updates the quorum value to `new_quorum`. - -Requirements: - -- The caller must be the contract itself. -- `new_quorum` must be non-zero. -- `new_quorum` must be less than or equal to the total number of signers. - -Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. - - - -Submits a new transaction for confirmation. - -Requirements: - -- The caller must be a registered signer. -- The transaction must not have been submitted before. - -Emits a [TransactionSubmitted](#MultisigComponent-TransactionSubmitted) event. - -Emits a [CallSalt](#MultisigComponent-CallSalt) event if `salt` is not zero. - - - -Submits a new batch transaction for confirmation. - -Requirements: - -- The caller must be a registered signer. -- The transaction must not have been submitted before. - -Emits a [TransactionSubmitted](#MultisigComponent-TransactionSubmitted) event. - -Emits a [CallSalt](#MultisigComponent-CallSalt) event if `salt` is not zero. - - - -Confirms a transaction with the given `id`. - -Requirements: - -- The caller must be a registered signer. -- The transaction must exist and not be executed. -- The caller must not have already confirmed the transaction. - -Emits a [TransactionConfirmed](#MultisigComponent-TransactionConfirmed) event. - - - -Revokes a previous confirmation for a transaction with the given `id`. - -Requirements: - -- The transaction must exist and not be executed. -- The caller must have previously confirmed the transaction. - -Emits a [ConfirmationRevoked](#MultisigComponent-ConfirmationRevoked) event. - - - -Executes a confirmed transaction. - -Requirements: - -- The caller must be a registered signer. -- The transaction must be confirmed and not yet executed. - -Emits a [TransactionExecuted](#MultisigComponent-TransactionExecuted) event. - - - -Executes a confirmed batch transaction. - -Requirements: - -- The caller must be a registered signer. -- The transaction must be confirmed and not yet executed. - -Emits a [TransactionExecuted](#MultisigComponent-TransactionExecuted) event. - - -#### Internal functions [!toc] [#MultisigComponent-InternalFunctions] - - -Initializes the Multisig component with the initial `quorum` and `signers`. This function must be called during contract initialization to set up the initial state. - -Requirements: - -- `quorum` must be non-zero and less than or equal to the number of `signers`. - -Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each signer added. - -Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event. - - - -Resolves and returns the current state of the transaction with the given `id`. - -The possible states are: - -- `NotFound`: the transaction does not exist. -- `Pending`: the transaction exists but hasn't reached the required confirmations. -- `Confirmed`: the transaction has reached the required confirmations but hasn't been executed. -- `Executed`: the transaction has been executed. - - - -Asserts that the `caller` is one of the registered signers. - -Requirements: - -- The `caller` must be a registered signer. - - - -Asserts that a transaction with the given `id` exists. - -Requirements: - -- The transaction with the given `id` must have been submitted. - - - -Asserts that the caller is the contract itself. - -Requirements: - -- The caller must be the contract's own address. - - - -Adds new signers and updates the quorum. - -Requirements: - -- Each signer address must be non-zero. -- `new_quorum` must be non-zero and less than or equal to the total number of signers after addition. - -Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for each new signer added. - -Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. - - - -Removes existing signers and updates the quorum. - -Requirements: - -- `new_quorum` must be non-zero and less than or equal to the total number of signers after removal. - -Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for each signer removed. - -Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. - - - -Replaces an existing signer with a new signer. - -Requirements: - -- `signer_to_remove` must be an existing signer. -- `signer_to_add` must not be an existing signer. -- `signer_to_add` must be a non-zero address. - -Emits a [SignerRemoved](#MultisigComponent-SignerRemoved) event for the removed signer. - -Emits a [SignerAdded](#MultisigComponent-SignerAdded) event for the new signer. - - - -Updates the quorum value to `new_quorum` if it differs from the current quorum. - -Requirements: - -- `new_quorum` must be non-zero. -- `new_quorum` must be less than or equal to the total number of signers. - -Emits a [QuorumUpdated](#MultisigComponent-QuorumUpdated) event if the quorum changes. - - -#### Events [!toc] [#MultisigComponent-Events] - - -Emitted when a new `signer` is added. - - - -Emitted when a `signer` is removed. - - - -Emitted when the `quorum` value is updated. - - - -Emitted when a new transaction is submitted by a `signer`. - - - -Emitted when a transaction is confirmed by a `signer`. - - - -Emitted when a `signer` revokes his confirmation. - - - -Emitted when a transaction is executed. - - - -Emitted when a new transaction is submitted with non-zero salt. - - -## [](#timelock)Timelock - -In a governance system, `TimelockControllerComponent` is in charge of introducing a delay between a proposal and its execution. - -### `TimelockControllerComponent` [toc] [#TimelockControllerComponent] - - - -```rust -use openzeppelin_governance::timelock::TimelockControllerComponent; -``` - -Component that implements [ITimelock](#ITimelock) and enables the implementing contract to act as a timelock controller. - -[Embeddable Mixin Implementations](../components#mixins) - -#### TimelockMixinImpl [!toc] [#TimelockControllerComponent-TimelockMixinImpl] - -- [`TimelockImpl`](#TimelockControllerComponent-Embeddable-Impls-TimelockImpl) -- [`SRC5Impl`](./introspection#SRC5Component-Embeddable-Impls) -- [`AccessControlImpl`](./access#AccessControlComponent-Embeddable-Impls) -- [`AccessControlCamelImpl`](./access#AccessControlComponent-Embeddable-Impls) - -Embeddable Implementations - -#### TimelockImpl [!toc] [#TimelockControllerComponent-TimelockImpl] - -- [`is_operation(self, id)`](#TimelockControllerComponent-is_operation) -- [`is_operation_pending(self, id)`](#TimelockControllerComponent-is_operation_pending) -- [`is_operation_ready(self, id)`](#TimelockControllerComponent-is_operation_ready) -- [`is_operation_done(self, id)`](#TimelockControllerComponent-is_operation_done) -- [`get_timestamp(self, id)`](#TimelockControllerComponent-get_timestamp) -- [`get_operation_state(self, id)`](#TimelockControllerComponent-get_operation_state) -- [`get_min_delay(self)`](#TimelockControllerComponent-get_min_delay) -- [`hash_operation(self, call, predecessor, salt)`](#TimelockControllerComponent-hash_operation) -- [`hash_operation_batch(self, calls, predecessor, salt)`](#TimelockControllerComponent-hash_operation_batch) -- [`schedule(self, call, predecessor, salt, delay)`](#TimelockControllerComponent-schedule) -- [`schedule_batch(self, calls, predecessor, salt, delay)`](#TimelockControllerComponent-schedule_batch) -- [`cancel(self, id)`](#TimelockControllerComponent-cancel) -- [`execute(self, call, predecessor, salt)`](#TimelockControllerComponent-execute) -- [`execute_batch(self, calls, predecessor, salt)`](#TimelockControllerComponent-execute_batch) -- [`update_delay(self, new_delay)`](#TimelockControllerComponent-update_delay) - -#### SRC5Impl [!toc] [#TimelockControllerComponent-SRC5Impl] - -- [`supports_interface(self, interface_id: felt252)`](./introspection#ISRC5-supports_interface) - -#### AccessControlImpl [!toc] [#TimelockControllerComponent-AccessControlImpl] - -- [`has_role(self, role, account)`](./access#IAccessControl-has_role) -- [`get_role_admin(self, role)`](./access#IAccessControl-get_role_admin) -- [`grant_role(self, role, account)`](./access#IAccessControl-grant_role) -- [`revoke_role(self, role, account)`](./access#IAccessControl-revoke_role) -- [`renounce_role(self, role, account)`](./access#IAccessControl-renounce_role) - -#### AccessControlCamelImpl [!toc] [#TimelockControllerComponent-AccessControlCamelImpl] - -- [`hasRole(self, role, account)`](./access#IAccessControl-hasRole) -- [`getRoleAdmin(self, role)`](./access#IAccessControl-getRoleAdmin) -- [`grantRole(self, role, account)`](./access#IAccessControl-grantRole) -- [`revokeRole(self, role, account)`](./access#IAccessControl-revokeRole) -- [`renounceRole(self, role, account)`](./access#IAccessControl-renounceRole) - -Internal Implementations - -#### InternalImpl [!toc] [#TimelockControllerComponent-InternalImpl] - -- [`initializer(self, min_delay, proposers, executors, admin)`](#TimelockControllerComponent-initializer) -- [`assert_only_role(self, role)`](#TimelockControllerComponent-assert_only_role) -- [`assert_only_role_or_open_role(self, role)`](#TimelockControllerComponent-assert_only_role_or_open_role) -- [`assert_only_self(self)`](#TimelockControllerComponent-assert_only_self) -- [`_before_call(self, id, predecessor)`](#TimelockControllerComponent-_before_call) -- [`_after_call(self, id)`](#TimelockControllerComponent-_after_call) -- [`_schedule(self, id, delay)`](#TimelockControllerComponent-_schedule) -- [`_execute(self, call)`](#TimelockControllerComponent-_execute) - -Events - -- [`CallScheduled(id, index, call, predecessor, delay)`](#TimelockControllerComponent-CallScheduled) -- [`CallExecuted(id, index, call)`](#TimelockControllerComponent-CallExecuted) -- [`CallSalt(id, salt)`](#TimelockControllerComponent-CallSalt) -- [`CallCancelled(id)`](#TimelockControllerComponent-CallCancelled) -- [`MinDelayChanged(old_duration, new_duration)`](#TimelockControllerComponent-MinDelayChanged) - -#### Embeddable functions [!toc] [#TimelockControllerComponent-EmbeddableFunctions] - - -Returns whether `id` corresponds to a registered operation. This includes the OperationStates: `Waiting`, `Ready`, and `Done`. - - - -Returns whether the `id` OperationState is pending or not. Note that a pending operation may be either `Waiting` or `Ready`. - - - -Returns whether the `id` OperationState is `Ready` or not. - - - -Returns whether the `id` OperationState is `Done` or not. - - - -Returns the timestamp at which `id` becomes `Ready`. - -`0` means the OperationState is `Unset` and `1` means the OperationState is `Done`. - - - -Returns the current state of the operation with the given `id`. - -The possible states are: - -- `Unset`: the operation has not been scheduled or has been canceled. -- `Waiting`: the operation has been scheduled and is pending the scheduled delay. -- `Ready`: the timer has expired, and the operation is eligible for execution. -- `Done`: the operation has been executed. - - - -Returns the minimum delay in seconds for an operation to become valid. This value can be changed by executing an operation that calls `update_delay`. - - - -Returns the identifier of an operation containing a single transaction. - - - -Returns the identifier of an operation containing a batch of transactions. - - - -Schedule an operation containing a single transaction. - -Requirements: - -- The caller must have the `PROPOSER_ROLE` role. -- The proposal must not already exist. -- `delay` must be greater than or equal to the min delay. - -Emits [CallScheduled](#TimelockControllerComponent-CallScheduled) event. Emits [CallSalt](#TimelockControllerComponent-CallSalt) event if `salt` is not zero. - - - -Schedule an operation containing a batch of transactions. - -Requirements: - -- The caller must have the `PROPOSER_ROLE` role. -- The proposal must not already exist. -- `delay` must be greater than or equal to the min delay. - -Emits one [CallScheduled](#TimelockControllerComponent-CallScheduled) event for each transaction in the batch. Emits [CallSalt](#TimelockControllerComponent-CallSalt) event if `salt` is not zero. - - - -Cancels an operation. A canceled operation returns to `Unset` OperationState. - -Requirements: - -- The caller must have the `CANCELLER_ROLE` role. -- `id` must be a pending operation. - -Emits a [CallCancelled](#TimelockControllerComponent-CallCancelled) event. - - - -Execute a (Ready) operation containing a single Call. - -Requirements: - -- Caller must have `EXECUTOR_ROLE`. -- `id` must be in Ready OperationState. -- `predecessor` must either be `0` or in Done OperationState. - -Emits a [CallExecuted](#TimelockControllerComponent-CallExecuted) event. - -This function can reenter, but it doesn't pose a risk because [`_after_call(self: @ContractState, id: felt252)` internal](#TimelockControllerComponent-_after_call) checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. - - - -Execute a (Ready) operation containing a batch of Calls. - -Requirements: - -- Caller must have `EXECUTOR_ROLE`. -- `id` must be in Ready OperationState. -- `predecessor` must either be `0` or in Done OperationState. - -Emits a [CallExecuted](#TimelockControllerComponent-CallExecuted) event for each Call. - -This function can reenter, but it doesn't pose a risk because `_after_call` checks that the proposal is pending, thus any modifications to the operation during reentrancy should be caught. - - - -Changes the minimum timelock duration for future operations. - -Requirements: - -- The caller must be the timelock itself. This can only be achieved by scheduling and later executing an operation where the timelock is the target and the data is the serialized call to this function. - -Emits a [MinDelayChanged](#TimelockControllerComponent-MinDelayChanged) event. - - -#### Internal functions [!toc] [#TimelockControllerComponent-InternalFunctions] - - -Initializes the contract by registering support for SRC5 and AccessControl. - -This function also configures the contract with the following parameters: - -- `min_delay`: initial minimum delay in seconds for operations. -- `proposers`: accounts to be granted proposer and canceller roles. -- `executors`: accounts to be granted executor role. -- `admin`: optional account to be granted admin role; disable with zero address. - -The optional admin can aid with initial configuration of roles after deployment without being subject to delay, but this role should be subsequently renounced in favor of administration through timelocked proposals. - -Emits two [IAccessControl::RoleGranted](./access#IAccessControl-RoleGranted) events for each account in `proposers` with `PROPOSER_ROLE` and `CANCELLER_ROLE` roles. - -Emits a [IAccessControl::RoleGranted](./access#IAccessControl-RoleGranted) event for each account in `executors` with `EXECUTOR_ROLE` role. - -May emit a [IAccessControl::RoleGranted](./access#IAccessControl-RoleGranted) event for `admin` with `DEFAULT_ADMIN_ROLE` role (if `admin` is not zero). - -Emits [MinDelayChanged](#TimelockControllerComponent-MinDelayChanged) event. - - - -Validates that the caller has the given `role`. Otherwise it panics. - - - -Validates that the caller has the given `role`. If `role` is granted to the zero address, then this is considered an open role which allows anyone to be the caller. - - - -Validates that the caller is the timelock contract itself. Otherwise it panics. - - - -Private function that checks before execution of an operation's calls. - -Requirements: - -- `id` must be in the `Ready` OperationState. -- `predecessor` must either be zero or be in the `Done` OperationState. - - - -Private function that checks after execution of an operation's calls and sets the OperationState of `id` to `Done`. - -Requirements: - -- `id` must be in the Ready OperationState. - - - -Private function that schedules an operation that is to become valid after a given `delay`. - - - -Private function that executes an operation's calls. - - -#### Events [!toc] [#TimelockControllerComponent-Events] - - -Emitted when `call` is scheduled as part of operation `id`. - - - -Emitted when `call` is performed as part of operation `id`. - - - -Emitted when a new proposal is scheduled with non-zero salt. - - - -Emitted when operation `id` is cancelled. - - - -Emitted when the minimum delay for future operations is modified. - - -## [](#votes)Votes - -The `VotesComponent` provides a flexible system for tracking and delegating voting power. This system allows users to delegate their voting power to other addresses, enabling more active participation in governance. - -### `VotesComponent` [toc] [#VotesComponent] - - - -```rust -use openzeppelin_governance::votes::VotesComponent; -``` - -Component that implements the [IVotes](#IVotes) interface and provides a flexible system for tracking and delegating voting power. - -By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. - -When using this module, your contract must implement the [VotingUnitsTrait](#VotingUnitsTrait). For convenience, this is done automatically for `ERC20` and `ERC721` tokens. - -Voting Units Trait Implementations - -#### ERC20VotesImpl [!toc] [#VotesComponent-ERC20VotesImpl] - -- [`get_voting_units(self, account)`](#VotesComponent-ERC20VotesImpl-get_voting_units) - -#### ERC721VotesImpl [!toc] [#VotesComponent-ERC721VotesImpl] - -- [`get_voting_units(self, account)`](#VotesComponent-ERC721VotesImpl-get_voting_units) - -Embeddable Implementations - -#### VotesImpl [!toc] [#VotesComponent-VotesImpl] - -- [`get_votes(self, account)`](#VotesComponent-get_votes) -- [`get_past_votes(self, account, timepoint)`](#VotesComponent-get_past_votes) -- [`get_past_total_supply(self, timepoint)`](#VotesComponent-get_past_total_supply) -- [`delegates(self, account)`](#VotesComponent-delegates) -- [`delegate(self, delegatee)`](#VotesComponent-delegate) -- [`delegate_by_sig(self, delegator, delegatee, nonce, expiry, signature)`](#VotesComponent-delegate_by_sig) -- [`clock(self)`](#VotesComponent-clock) -- [`CLOCK_MODE(self)`](#VotesComponent-CLOCK_MODE) - -Internal implementations - -#### InternalImpl [!toc] [#VotesComponent-InternalImpl] - -- [`get_total_supply(self)`](#VotesComponent-get_total_supply) -- [`move_delegate_votes(self, from, to, amount)`](#VotesComponent-move_delegate_votes) -- [`transfer_voting_units(self, from, to, amount)`](#VotesComponent-transfer_voting_units) -- [`num_checkpoints(self, account)`](#VotesComponent-num_checkpoints) -- [`checkpoints(self, account, pos)`](#VotesComponent-checkpoints) -- [`_delegate(self, account, delegatee)`](#VotesComponent-_delegate) - -Events - -- [`DelegateChanged(delegator, from_delegate, to_delegate)`](#VotesComponent-DelegateChanged) -- [`DelegateVotesChanged(delegate, previous_votes, new_votes)`](#VotesComponent-DelegateVotesChanged) - - -Returns the number of voting units for a given account. - -This implementation is specific to ERC20 tokens, where the balance of tokens directly represents the number of voting units. - -This implementation will work out of the box if the ERC20 component is implemented in the final contract. - -This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting. - - - -Returns the number of voting units for a given account. - -This implementation is specific to ERC721 tokens, where each token represents one voting unit. The function returns the balance of ERC721 tokens for the specified account. - -This implementation will work out of the box if the ERC721 component is implemented in the final contract. - -This implementation assumes tokens map to voting units 1:1. Any deviation from this formula when transferring voting units (e.g. by using hooks) may compromise the internal vote accounting. - - -#### Embeddable functions [!toc] [#VotesComponent-EmbeddableFunctions] - - -Returns the current amount of votes that `account` has. - - - -Returns the amount of votes that `account` had at a specific moment in the past. - -Requirements: - -- `timepoint` must be in the past. - - - -Returns the total supply of votes available at a specific moment in the past. - -This value is the sum of all available votes, which is not necessarily the sum of all delegated votes. Votes that have not been delegated are still part of total supply, even though they would not participate in a vote. - -Requirements: - -- `timepoint` must be in the past. - - - -Returns the delegate that `account` has chosen. - - - -Delegates votes from the sender to `delegatee`. - -Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. - -May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. - - - -Delegates votes from `delegator` to `delegatee` through a [SNIP-12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) message signature validation. - -Requirements: - -- `expiry` must not be in the past. -- `nonce` must match the account's current nonce. -- `delegator` must implement `SRC6::is_valid_signature`. -- `signature` should be valid for the message hash. - -Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. - -May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. - - - -Returns the current timepoint determined by the contract's operational mode, intended for use in time-sensitive logic. See [ERC-6372#clock](https://eips.ethereum.org/EIPS/eip-6372#clock). - -Requirements: - -- This function MUST always be non-decreasing. - - - -Returns a description of the clock the contract is operating in. See [ERC-6372#CLOCK\_MODE](https://eips.ethereum.org/EIPS/eip-6372#clock_mode). - -Requirements: - -- The output MUST be formatted like a URL query string, decodable in standard JavaScript. - - -#### Internal functions [!toc] [#VotesComponent-InternalFunctions] - - -Returns the current total supply of votes. - - - -Moves delegated votes from one delegate to another. - -May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. - - - -Transfers, mints, or burns voting units. - -To register a mint, `from` should be zero. To register a burn, `to` should be zero. Total supply of voting units will be adjusted with mints and burns. - -If voting units are based on an underlying transferable asset (like a token), you must call this function every time the asset is transferred to keep the internal voting power accounting in sync. For ERC20 and ERC721 tokens, this is typically handled using hooks. - -May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. - - - -Returns the number of checkpoints for `account`. - - - -Returns the `pos`-th checkpoint for `account`. - - - -Delegates all of `account`'s voting units to `delegatee`. - -Emits a [DelegateChanged](#VotesComponent-DelegateChanged) event. - -May emit one or two [DelegateVotesChanged](#VotesComponent-DelegateVotesChanged) events. - - -#### Events [!toc] [#VotesComponent-Events] - - -Emitted when an account changes their delegate. - - - -Emitted when a token transfer or delegate change results in changes to a delegate's number of votes. - - -### `VotingUnitsTrait` [toc] [#VotingUnitsTrait] - - - -```rust -pub trait VotingUnitsTrait { - fn get_voting_units(self: @TState, account: ContractAddress) -> u256; -} -``` - -A trait that must be implemented when integrating [VotesComponent](#VotesComponent) into a contract. It offers a mechanism to retrieve the number of voting units for a given account at the current time. - -Functions - -- [`get_voting_units(self, account)`](#VotingUnitsTrait-get_voting_units) - -#### Functions [!toc] [#VotingUnitsTrait-Functions] - - -Returns the number of voting units for a given account. For ERC20, this is typically the token balance. For ERC721, this is typically the number of tokens owned. - -While any formula can be used as a measure of voting units, the internal vote accounting of the contract may be compromised if voting units are transferred in any external flow by following a different formula. -For example, when implementing the hook for ERC20, the number of voting units transferred should match the formula given by the `get_voting_units` implementation. - diff --git a/content/contracts-cairo/alpha/api/introspection.mdx b/content/contracts-cairo/alpha/api/introspection.mdx deleted file mode 100644 index 32a0450c..00000000 --- a/content/contracts-cairo/alpha/api/introspection.mdx +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: Introspection ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This crate handles [type introspection](https://en.wikipedia.org/wiki/Type_introspection) of contracts. In other words, it examines which functions can be called on a given contract. This is referred to as the contract's interface. - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -### `ISRC5` [toc] [#ISRC5] - - - -```rust -use openzeppelin_interfaces::introspection::ISRC5; -``` - -Interface of the SRC5 Introspection Standard as defined in [SNIP-5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md). - -[SRC5 ID](#ISRC5) - -```text -0x3f918d17e5ee77373b56385708f855659a07f75997f365cf87748628532a055 -``` - -Functions - -- [`supports_interface(interface_id)`](#ISRC5-supports_interface) - -#### Functions [!toc] [#ISRC5-Functions] - - -Checks whether the contract implements the given interface. - -Check [Computing the Interface ID](../introspection#computing-the-interface-id) for more information on how to compute this ID. - - -## [](#core)Core - -### `SRC5Component` [toc] [#SRC5Component] - - - -```rust -use openzeppelin_introspection::src5::SRC5Component; -``` - -SRC5 component extending [`ISRC5`](#ISRC5). - -Embeddable Implementations - -#### SRC5Impl [!toc] [#SRC5Component-SRC5Impl] - -- [`supports_interface(self, interface_id)`](#SRC5Component-supports_interface) - -Internal Implementations - -#### InternalImpl [!toc] [#SRC5Component-InternalImpl] - -- [`register_interface(self, interface_id)`](#SRC5Component-register_interface) -- [`deregister_interface(self, interface_id)`](#SRC5Component-deregister_interface) - -#### Embeddable functions [!toc] [#SRC5Component-EmbeddableFunctions] - - -See [`ISRC5::supports_interface`](#ISRC5-supports_interface). - - -#### Internal functions [!toc] [#SRC5Component-InternalFunctions] - - -Registers support for the given `interface_id`. - - - -Deregisters support for the given `interface_id`. - diff --git a/content/contracts-cairo/alpha/api/merkle-tree.mdx b/content/contracts-cairo/alpha/api/merkle-tree.mdx deleted file mode 100644 index 5772e073..00000000 --- a/content/contracts-cairo/alpha/api/merkle-tree.mdx +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: Merkle Tree ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This crate provides a set of utilities for verifying Merkle Tree proofs on-chain. The tree and the proofs can be generated using this [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). - -This module provides: - -- `verify` - can prove that some value is part of a Merkle tree. -- `verify_multi_proof` - can prove multiple values are part of a Merkle tree. - -`openzeppelin_merkle_tree` doesn’t have dependencies outside of `corelib`, and can be used in projects that are not Starknet-related. - -To use it as a standalone package, you can add it in your `Scarb.toml` as follows: - -`openzeppelin_merkle_tree = "3.0.0-alpha.1"` - -## [](#modules)Modules - -### [](#merkle_proof)`merkle_proof` [toc] [#merkle_proof] - - - -```rust -use openzeppelin_merkle_tree::merkle_proof; -``` - -These functions deal with verification of Merkle Tree proofs. - -The tree and the proofs can be generated using this [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). You will find a quickstart guide in the readme. - -You should avoid using leaf values that are two felt252 values long prior to hashing, or use a hash function other than the one used to hash internal nodes for hashing leaves. This is because the concatenation of a sorted pair of internal nodes in the Merkle tree could be reinterpreted as a leaf value. The JavaScript library generates Merkle trees that are safe against this attack out of the box. - -Functions - -- [`verify(proof, root, leaf)`](#merkle_proof-verify) -- [`verify_pedersen(proof, root, leaf)`](#merkle_proof-verify_pedersen) -- [`verify_poseidon(proof, root, leaf)`](#merkle_proof-verify_poseidon) -- [`process_proof(proof, leaf)`](#merkle_proof-process_proof) -- [`verify_multi_proof(proof, proof_flags, root, leaves)`](#merkle_proof-verify_multi_proof) -- [`process_multi_proof(proof, proof_flags, leaf)`](#merkle_proof-process_multi_proof) - -#### [](#merkle_proof-Functions)Functions [!toc] - - -Returns true if a `leaf` can be proved to be a part of a Merkle tree defined by `root`. - -For this, a `proof` must be provided, containing sibling hashes on the branch from the leaf to the root of the tree. - -Each pair of leaves and each pair of pre-images are assumed to be sorted. - -This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. - -`verify_pedersen` and `verify_poseidon` already include the corresponding `Hasher` implementations. - - - -Version of `verify` using Pedersen as the hashing function. - - - -Version of `verify` using Poseidon as the hashing function. - - - -Returns the rebuilt hash obtained by traversing a Merkle tree up from `leaf` using `proof`. - -A `proof` is valid if and only if the rebuilt hash matches the root of the tree. - -When processing the proof, the pairs of leaves & pre-images are assumed to be sorted. - -This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. - - - -Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by `root`, according to `proof` and `proof_flags` as described in `process_multi_proof`. - -The `leaves` must be validated independently. - -Not all Merkle trees admit multiproofs. See `process_multi_proof` for details. - -Consider the case where `root == proof.at(0) && leaves.len() == 0` as it will return `true`. - -This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. - - - -Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. - -The reconstruction proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another leaf/inner node or a proof sibling node, depending on whether each `proof_flags` item is true or false respectively. - -Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: - -1. The tree is complete (but not necessarily perfect). -2. The leaves to be proven are in the opposite order than they are in the tree. (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). - -The *empty set* (i.e. the case where `proof.len() == 1 && leaves.len() == 0`) is considered a no-op, and therefore a valid multiproof (i.e. it returns `proof.at(0)`). Consider disallowing this case if you're not validating the leaves elsewhere. - -This function expects a `CommutativeHasher` implementation. See [hashes::CommutativeHasher](#hashes-CommutativeHasher) for more information. - - -### [](#hashes)`hashes` [toc] [#hashes] - - - -```rust -use openzeppelin_merkle_tree::hashes; -``` - -Module providing the trait and default implementations for the commutative hash functions used in [`merkle_proof`](#merkle_proof). - -The `PedersenCHasher` implementation matches the default node hashing function used in the [JavaScript library](https://github.com/ericnordelo/strk-merkle-tree). - -Traits - -- [`CommutativeHasher`](#hashes-CommutativeHasher) - -Impls - -- [`PedersenCHasher`](#hashes-PedersenCHasher) -- [`PoseidonCHasher`](#hashes-PoseidonCHasher) - -#### [](#hashes-Traits)Traits [!toc] - - -Declares a commutative hash function with the following signature: - -`commutative_hash(a: felt252, b: felt252) → felt252;` - -which computes a commutative hash of a sorted pair of felt252 values. - -This is usually implemented as an extension of a non-commutative hash function, like Pedersen or Poseidon, returning the hash of the concatenation of the two values by first sorting them. - -Frequently used when working with merkle proofs. - -The `commutative_hash` function MUST follow the invariant that `commutative_hash(a, b) == commutative_hash(b, a)`. - - -#### [](#hashes-Impls)Impls [!toc] - - -Implementation of the `CommutativeHasher` trait which computes the Pedersen hash of chaining the two input values with the len (2), sorting the pair first. - - - -Implementation of the `CommutativeHasher` trait which computes the Poseidon hash of the concatenation of two values, sorting the pair first. - diff --git a/content/contracts-cairo/alpha/api/security.mdx b/content/contracts-cairo/alpha/api/security.mdx deleted file mode 100644 index 12e3a01f..00000000 --- a/content/contracts-cairo/alpha/api/security.mdx +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: Security ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This crate provides components to handle common security-related tasks. - -## [](#initializable)Initializable - -### [](#InitializableComponent)`InitializableComponent` [toc] [#InitializableComponent] - - - -```rust -use openzeppelin_security::InitializableComponent; -``` - -Component enabling one-time initialization for contracts. - -Embeddable Implementations - -InitializableImpl - -- [`is_initialized(self)`](#InitializableComponent-is_initialized) - -Internal Implementations - -InternalImpl - -- [`initialize(self)`](#InitializableComponent-initialize) - -#### [](#InitializableComponent-Embeddable-Functions)Embeddable functions [!toc] - - -Returns whether the contract has been initialized. - - -#### [](#InitializableComponent-Internal-Functions)Internal functions [!toc] - - -Initializes the contract. Can only be called once. - -Requirements: - -- the contract must not have been initialized before. - - -## [](#pausable)Pausable - -### [](#PausableComponent)`PausableComponent` [toc] [#PausableComponent] - - - -```rust -use openzeppelin_security::PausableComponent; -``` - -Component to implement an emergency stop mechanism. - -Embeddable Implementations - -PausableImpl - -- [`is_paused(self)`](#PausableComponent-is_paused) - -Internal Implementations - -InternalImpl - -- [`assert_not_paused(self)`](#PausableComponent-assert_not_paused) -- [`assert_paused(self)`](#PausableComponent-assert_paused) -- [`pause(self)`](#PausableComponent-pause) -- [`unpause(self)`](#PausableComponent-unpause) - -Events - -- [`Paused(account)`](#PausableComponent-Paused) -- [`Unpaused(account)`](#PausableComponent-Unpaused) - -#### [](#PausableComponent-Embeddable-Functions)Embeddable functions [!toc] - - -Returns whether the contract is currently paused. - - -#### [](#PausableComponent-Internal-Functions)Internal functions [!toc] - - -Panics if the contract is paused. - - - -Panics if the contract is not paused. - - - -Pauses the contract. - -Requirements: - -- the contract must not be paused. - -Emits a [Paused](#PausableComponent-Paused) event. - - - -Unpauses the contract. - -Requirements: - -- the contract must be paused. - -Emits an [Unpaused](#PausableComponent-Unpaused) event. - - -#### [](#PausableComponent-Events)Events [!toc] - - -Emitted when the contract is paused by `account`. - - - -Emitted when the contract is unpaused by `account`. - - -## [](#reentrancyguard)ReentrancyGuard - -### [](#ReentrancyGuardComponent)`ReentrancyGuardComponent` [toc] [#ReentrancyGuardComponent] - - - -```rust -use openzeppelin_security::ReentrancyGuardComponent; -``` - -Component to help prevent reentrant calls. - -Internal Implementations - -InternalImpl - -- [`start(self)`](#ReentrancyGuardComponent-start) -- [`end(self)`](#ReentrancyGuardComponent-end) - -#### [](#ReentrancyGuardComponent-Internal-Functions)Internal functions [!toc] - - -Prevents a contract's function from calling itself or another protected function, directly or indirectly. - -Requirements: - -- the guard must not be currently enabled. - - - -Removes the reentrant guard. - diff --git a/content/contracts-cairo/alpha/api/testing.mdx b/content/contracts-cairo/alpha/api/testing.mdx deleted file mode 100644 index 7a9b3cc6..00000000 --- a/content/contracts-cairo/alpha/api/testing.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Testing ---- - - -The `openzeppelin_testing` package version is now decoupled from the `cairo-contracts` version. - - -You can find the documentation for the `openzeppelin_testing` package in the README for the corresponding version in the -[scarb registry](https://scarbs.xyz/packages/openzeppelin_testing). diff --git a/content/contracts-cairo/alpha/api/token_common.mdx b/content/contracts-cairo/alpha/api/token_common.mdx deleted file mode 100644 index de34561d..00000000 --- a/content/contracts-cairo/alpha/api/token_common.mdx +++ /dev/null @@ -1,539 +0,0 @@ ---- -title: Common (Token) ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This module provides extensions and utilities that are common to multiple token standards. - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -### [](#IERC2981)`IERC2981` [toc] [#IERC2981] - - - -```rust -use openzeppelin_interfaces::erc2981::IERC2981; -``` - -[SRC5 ID](./introspection#ISRC5) - -```text -0x2d3414e45a8700c29f119a54b9f11dca0e29e06ddcb214018fc37340e165ed6 -``` - -Interface of the ERC2981 standard as defined in [EIP-2981](https://eips.ethereum.org/EIPS/eip-2981). - -Functions - -- [`royalty_info(token_id, sale_price)`](#IERC2981-royalty_info) - -#### [](#IERC2981-Functions)Functions [!toc] - - -Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of exchange. The royalty amount is denominated and must be paid in that same unit of exchange. - - -### [](#IERC2981Info)`IERC2981Info` [toc] [#IERC2981Info] - - - -```rust -use openzeppelin_interfaces::erc2981::IERC2981Info; -``` - -Interface providing external read functions for discovering the state of ERC2981 component. - -Functions - -- [`default_royalty()`](#IERC2981Info-default_royalty) -- [`token_royalty(token_id)`](#IERC2981Info-token_royalty) - -#### [](#IERC2981Info-Functions)Functions [!toc] - - -Returns the royalty information that all ids in this contract will default to. - -The returned tuple contains: - -- `t.0`: The receiver of the royalty payment. -- `t.1`: The numerator of the royalty fraction. -- `t.2`: The denominator of the royalty fraction. - - - -Returns the royalty information specific to a token. - -The returned tuple contains: - -- `t.0`: The receiver of the royalty payment. -- `t.1`: The numerator of the royalty fraction. -- `t.2`: The denominator of the royalty fraction. - - -### [](#IERC2981Admin)`IERC2981Admin` [toc] [#IERC2981Admin] - - - -```rust -use openzeppelin_interfaces::erc2981::IERC2981Admin; -``` - -Interface providing external admin functions for managing the settings of ERC2981 component. - -Functions - -- [`set_default_royalty(receiver, fee_numerator)`](#IERC2981Admin-set_default_royalty) -- [`delete_default_royalty()`](#IERC2981Admin-delete_default_royalty) -- [`set_token_royalty(token_id, receiver, fee_numerator)`](#IERC2981Admin-set_token_royalty) -- [`reset_token_royalty(token_id)`](#IERC2981Admin-reset_token_royalty) - -#### [](#IERC2981Admin-Functions)Functions [!toc] - - -Sets the royalty information that all ids in this contract will default to. - - - -Sets the default royalty percentage and receiver to zero. - - - -Sets the royalty information for a specific token id that takes precedence over the global default. - - - -Resets royalty information for the token id back to unset. - - -## [](#erc2981)ERC2981 - -### [](#ERC2981Component)`ERC2981Component` [toc] [#ERC2981Component] - - - -```rust -use openzeppelin_token::common::erc2981::ERC2981Component; -``` - -ERC2981 component extending [IERC2981](#IERC2981). - -[Immutable Component Config](../components#immutable-config) - -constants - -- [`FEE_DENOMINATOR`](#ERC2981Component-IC-FEE_DENOMINATOR) - -functions - -- [`validate()`](#ERC2981Component-IC-validate) - -Embeddable Implementations - -ERC2981Impl - -- [`royalty_info(self, token_id, sale_price)`](#ERC2981Component-royalty_info) - -ERC2981InfoImpl - -- [`default_royalty(self)`](#ERC2981InfoImpl-default_royalty) -- [`token_royalty(self, token_id)`](#ERC2981InfoImpl-token_royalty) - -ERC2981AdminOwnableImpl - -- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminOwnableImpl-set_default_royalty) -- [`delete_default_royalty(self)`](#ERC2981AdminOwnableImpl-delete_default_royalty) -- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminOwnableImpl-set_token_royalty) -- [`reset_token_royalty(self, token_id)`](#ERC2981AdminOwnableImpl-reset_token_royalty) - -ERC2981AdminAccessControlImpl - -- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminAccessControlImpl-set_default_royalty) -- [`delete_default_royalty(self)`](#ERC2981AdminAccessControlImpl-delete_default_royalty) -- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminAccessControlImpl-set_token_royalty) -- [`reset_token_royalty(self, token_id)`](#ERC2981AdminAccessControlImpl-reset_token_royalty) - -ERC2981AdminAccessControlDefaultAdminRulesImpl - -- [`set_default_royalty(self, receiver, fee_numerator)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-set_default_royalty) -- [`delete_default_royalty(self)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-delete_default_royalty) -- [`set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-set_token_royalty) -- [`reset_token_royalty(self, token_id)`](#ERC2981AdminAccessControlDefaultAdminRulesImpl-reset_token_royalty) - -Internal implementations - -InternalImpl - -- [`initializer(self, default_receiver, default_royalty_fraction)`](#ERC2981Component-initializer) -- [`_default_royalty(self)`](#ERC2981Component-_default_royalty) -- [`_set_default_royalty(self, receiver, fee_numerator)`](#ERC2981Component-_set_default_royalty) -- [`_delete_default_royalty(self)`](#ERC2981Component-_delete_default_royalty) -- [`_token_royalty(self, token_id)`](#ERC2981Component-_token_royalty) -- [`_set_token_royalty(self, token_id, receiver, fee_numerator)`](#ERC2981Component-_set_token_royalty) -- [`_reset_token_royalty(self, token_id)`](#ERC2981Component-_reset_token_royalty) - -#### [](#ERC2981Component-Immutable-Config)Immutable Config constants [!toc] - - -The denominator with which to interpret the fee set in `_set_token_royalty` and `_set_default_royalty` as a fraction of the sale price. - - - -Validates the given implementation of the contract's configuration. - -Requirements: - -- `FEE_DENOMINATOR` must be greater than 0. - -This function is called by the contract's initializer. - - -#### [](#ERC2981Component-Embeddable-functions)Embeddable functions [!toc] - - -Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of exchange. The royalty amount is denominated and should be paid in that same unit of exchange. - -The returned tuple contains: - -- `t.0`: The receiver of the royalty payment. -- `t.1`: The amount of royalty payment. - - - -Returns the royalty information that all ids in this contract will default to. - -The returned tuple contains: - -- `t.0`: The receiver of the royalty payment. -- `t.1`: The numerator of the royalty fraction. -- `t.2`: The denominator of the royalty fraction. - - - -Returns the royalty information specific to a token. If no specific royalty information is set for the token, the default is returned. - -The returned tuple contains: - -- `t.0`: The receiver of the royalty payment. -- `t.1`: The numerator of the royalty fraction. -- `t.2`: The denominator of the royalty fraction. - - -#### [](#ERC2981Component-ERC2981AdminOwnableImpl)ERC2981AdminOwnableImpl [!toc] - -Provides admin functions for managing royalty settings that are restricted to be called only by the contract's owner. Requires the contract to implement [OwnableComponent](./access#OwnableComponent). - - -Sets the royalty information that all ids in this contract will default to. - -Requirements: - -- The caller is the contract owner. -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Sets the default royalty percentage and receiver to zero. - -Requirements: - -- The caller is the contract owner. - - - -Sets the royalty information for a specific token id that takes precedence over the global default. - -Requirements: - -- The caller is the contract owner. -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Resets royalty information for the token id back to unset. - -Requirements: - -- The caller is the contract owner. - - -#### [](#ERC2981Component-ERC2981AdminAccessControlImpl)ERC2981AdminAccessControlImpl [!toc] - -Provides admin functions for managing royalty settings that require `ROYALTY_ADMIN_ROLE` to be granted to the caller. Requires the contract to implement [AccessControlComponent](./access#AccessControlComponent). - - -Role for the admin responsible for managing royalty settings. - - - -Sets the royalty information that all ids in this contract will default to. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Sets the default royalty percentage and receiver to zero. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. - - - -Sets the royalty information for a specific token id that takes precedence over the global default. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Resets royalty information for the token id back to unset. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. - - -#### [](#ERC2981Component-ERC2981AdminAccessControlDefaultAdminRulesImpl)ERC2981AdminAccessControlDefaultAdminRulesImpl [!toc] - -An alternative implementation of [IERC2981Admin](#IERC2981Admin). Provides admin functions for managing royalty settings -that require `ROYALTY_ADMIN_ROLE` to be granted to the caller. Requires the contract to implement [AccessControlDefaultAdminRulesComponent](./access#AccessControlDefaultAdminRulesComponent). - - - -Sets the royalty information that all ids in this contract will default to. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Sets the default royalty percentage and receiver to zero. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. - - - -Sets the royalty information for a specific token id that takes precedence over the global default. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Resets royalty information for the token id back to unset. - -Requirements: - -- The caller must have `ROYALTY_ADMIN_ROLE` role. - - -#### [](#ERC2981Component-Internal-functions)Internal functions [!toc] - - -Initializes the contract by setting the default royalty and registering the supported interface. - -Requirements: - -- `default_receiver` cannot be the zero address. -- `default_royalty_fraction` cannot be greater than the fee denominator. -- The fee denominator must be greater than 0. - -The fee denominator is set by the contract using the [Immutable Component Config](../components#immutable-config). - - - -Returns the royalty information that all ids in this contract will default to. - -The returned tuple contains: - -- `t.0`: The receiver of the royalty payment. -- `t.1`: The numerator of the royalty fraction. -- `t.2`: The denominator of the royalty fraction. - - - -Sets the royalty information that all ids in this contract will default to. - -Requirements: - -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Sets the default royalty percentage and receiver to zero. - - - -Returns the royalty information that all ids in this contract will default to. - -The returned tuple contains: - -- `t.0`: The receiver of the royalty payment. -- `t.1`: The numerator of the royalty fraction. -- `t.2`: The denominator of the royalty fraction. - - - -Sets the royalty information for a specific token id that takes precedence over the global default. - -Requirements: - -- `receiver` cannot be the zero address. -- `fee_numerator` cannot be greater than the fee denominator. - - - -Resets royalty information for the token id back to unset. - diff --git a/content/contracts-cairo/alpha/api/udc.mdx b/content/contracts-cairo/alpha/api/udc.mdx deleted file mode 100644 index 742fd907..00000000 --- a/content/contracts-cairo/alpha/api/udc.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Universal Deployer ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -Reference of the Universal Deployer Contract (UDC) interface and preset. - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -### [](#IUniversalDeployer)`IUniversalDeployer` [toc] [#IUniversalDeployer] - - - -```rust -use openzeppelin_interfaces::deployments::IUniversalDeployer; -``` - -Functions - -- [`deploy_contract(class_hash, salt, not_from_zero, calldata)`](#IUniversalDeployer-deploy_contract) - -Events - -- [`ContractDeployed(address, deployer, not_from_zero, class_hash, calldata, salt)`](#IUniversalDeployer-ContractDeployed) - -#### [](#IUniversalDeployer-Functions)Functions [!toc] - - -Deploys a contract through the Universal Deployer Contract. - - -#### [](#IUniversalDeployer-Events)Events [!toc] - - -Emitted when `deployer` deploys a contract through the Universal Deployer Contract. - - -## [](#presets)Presets - -### [](#UniversalDeployer)`UniversalDeployer` [toc] [#UniversalDeployer] - - - -```rust -use openzeppelin_presets::UniversalDeployer; -``` - -The standard Universal Deployer Contract. - -[Sierra class hash](../presets) - -```text -{{UniversalDeployerClassHash}} -``` - -Embedded Implementations - -UniversalDeployerImpl - -- [`deploy_contract(self, address, deployer, not_from_zero, class_hash, calldata, salt)`](#UniversalDeployer-deploy_contract) - -#### [](#UniversalDeployer-External-functions)External functions [!toc] - - -Deploys a contract through the Universal Deployer Contract. - -When `not_from_zero` is `true`, `salt` is hashed with the caller address and the modified salt is passed to the inner `deploy_syscall`. This type of deployment is [origin-dependent](../udc#origin-dependent). - -When `not_from_zero` is `false`, the deployment type is [origin-independent](../udc#origin-independent). - -Emits an [ContractDeployed](#IUniversalDeployer-ContractDeployed) event. - diff --git a/content/contracts-cairo/alpha/api/upgrades.mdx b/content/contracts-cairo/alpha/api/upgrades.mdx deleted file mode 100644 index 0fffeec6..00000000 --- a/content/contracts-cairo/alpha/api/upgrades.mdx +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: Upgrades ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This crate provides interfaces and utilities related to upgradeability. - -## [](#interfaces)Interfaces - - -Starting from version `3.x.x`, the interfaces are no longer part of the `openzeppelin_access` package. The references -documented here are contained in the `openzeppelin_interfaces` package version `{{openzeppelin_interfaces_version}}`. - - -### [](#IUpgradeable)`IUpgradeable` [toc] [#IUpgradeable] - - - -```rust -use openzeppelin_interfaces::upgrades::IUpgradeable; -``` - -Interface of an upgradeable contract. - -Functions - -- [`upgrade(new_class_hash)`](#IUpgradeable-upgrade) - -#### [](#IUpgradeable-Functions)Functions [!toc] - - -Upgrades the contract code by updating its [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/). - -This function is usually protected by an [Access Control](../access) mechanism. - - -### [](#IUpgradeAndCall)`IUpgradeAndCall` [toc] [#IUpgradeAndCall] - - - -```rust -use openzeppelin_interfaces::upgrades::IUpgradeAndCall; -``` - -Interface for an upgradeable contract that couples an upgrade with a function call in the upgraded context. - -Functions - -- [`upgrade_and_call(new_class_hash, selector, calldata)`](#IUpgradeAndCall-upgrade_and_call) - -#### [](#IUpgradeAndCall-Functions)Functions [!toc] - - -Upgrades the contract code by updating its [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/) and calls `selector` with the upgraded context. - -This function is usually protected by an [Access Control](../access) mechanism. - - -## [](#core)Core - -### [](#UpgradeableComponent)`UpgradeableComponent` [toc] [#UpgradeableComponent] - - - -```rust -use openzeppelin_upgrades::upgradeable::UpgradeableComponent; -``` - -Upgradeable component. - -Internal Implementations - -InternalImpl - -- [`upgrade(self, new_class_hash)`](#UpgradeableComponent-upgrade) -- [`upgrade_and_call(self, new_class_hash, selector, calldata)`](#UpgradeableComponent-upgrade_and_call) - -Events - -- [`Upgraded(class_hash)`](#UpgradeableComponent-Upgraded) - -#### [](#UpgradeableComponent-Internal-Functions)Internal Functions [!toc] - - -Upgrades the contract by updating the contract [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/). - -Requirements: - -- `new_class_hash` must be different from zero. - -Emits an [Upgraded](#UpgradeableComponent-Upgraded) event. - - - -Replaces the contract's class hash with `new_class_hash` and then calls `selector` from the upgraded context. This function returns the unwrapped `call_contract_syscall` return value(s), if available, of the `selector` call. - -Requirements: - -- `new_class_hash` must be different from zero. - -The function call comes from the upgraded contract itself and not the account. - -A similar behavior to `upgrade_and_call` can also be achieved with a list of calls from an account since the [SNIP-6](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-6.md) account standard supports multicall. An account can execute a list of calls with [upgrade](#IUpgradeable-upgrade) being the first element in the list and the extra function call as the second. With this approach, the calls will execute from the account's context and can't be front-ran. - -Emits an [Upgraded](#UpgradeableComponent-Upgraded) event. - - -#### [](#UpgradeableComponent-Events)Events [!toc] - - -Emitted when the [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/) is upgraded. - diff --git a/content/contracts-cairo/alpha/api/utilities.mdx b/content/contracts-cairo/alpha/api/utilities.mdx deleted file mode 100644 index 6a6380a5..00000000 --- a/content/contracts-cairo/alpha/api/utilities.mdx +++ /dev/null @@ -1,417 +0,0 @@ ---- -title: Utilities ---- - -import { UMBRELLA_VERSION } from "../utils/constants.js"; - -This crate provides miscellaneous components and libraries containing utility functions to handle common tasks. - -## Core - -### `utils` [toc] [#utils] - - - -```rust -use openzeppelin_utils; -``` - -Module containing core utilities of the library. - -Members - -Inner modules - -- [`cryptography`](#utils-cryptography) -- [`deployments`](#utils-deployments) -- [`math`](#utils-math) -- [`contract_clock`](#utils-contract_clock) -- [`serde`](#utils-serde) - -#### Inner modules [!toc] [#utils-Inner-Modules] - - -See [`openzeppelin_utils::cryptography`](#cryptography). - - - -See [`openzeppelin_utils::deployments`](#deployments). - - - -See [`openzeppelin_utils::math`](#math). - - - -See [`openzeppelin_utils::contract_clock`](#contract_clock). - - - -See [`openzeppelin_utils::serde`](#serde). - - -### `cryptography` [toc] [#cryptography] - - - -```rust -use openzeppelin_utils::cryptography; -``` - -Module containing utilities related to cryptography. - -Members - -Inner modules - -- [`nonces`](#cryptography-nonces) -- [`snip12`](#cryptography-snip12) - -#### Inner modules [!toc] [#cryptography-Inner-Modules] - - -See [`openzeppelin_utils::cryptography::nonces::NoncesComponent`](#NoncesComponent). - - - -See [`openzeppelin_utils::cryptography::snip12`](#snip12). - - -### `deployments` [toc] [#deployments] - - - -```rust -use openzeppelin_utils::deployments; -``` - -Module containing utility functions for calculating contract addresses through [deploy\_syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) and the [Universal Deployer Contract](../udc) (UDC). - -Members - -Structs - -- [`DeployerInfo(caller_address, udc_address)`](#deployments-DeployerInfo) - -Functions - -- [`calculate_contract_address_from_deploy_syscall(salt, class_hash, constructor_calldata, deployer_address)`](#deployments-calculate_contract_address_from_deploy_syscall) -- [`compute_hash_on_elements(data)`](#deployments-compute_hash_on_elements) -- [`calculate_contract_address_from_udc(salt, class_hash, constructor_calldata, deployer_info)`](#deployments-calculate_contract_address_from_udc) - -#### Structs [!toc] [#deployments-Structs] - - -Struct containing arguments necessary in [utils::calculate\_contract\_address\_from\_udc](#deployments-calculate_contract_address_from_udc) for origin-dependent deployment calculations. - - -#### Functions [!toc] [#deployments-Functions] - - -Returns the contract address when passing the given arguments to [deploy\_syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall). - - - -Creates a Pedersen hash chain with the elements of `data` and returns the finalized hash. - - - -Returns the calculated contract address for UDC deployments. - -Origin-independent deployments (deployed from zero) should pass `Option::None` as `deployer_info`. - -Origin-dependent deployments hash `salt` with `caller_address` (member of [DeployerInfo](#deployments-DeployerInfo)) and pass the hashed salt to the inner [deploy\_syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) as the `contract_address_salt` argument. - - -### `execution` [toc] [#execution] - - - -```rust -use openzeppelin_utils::execution; -``` - -Module containing utilities related to execution. - -#### Functions [!toc] [#execution-Functions] - - -Executes a list of calls and returns an array containing the return values from each call. - - - -Executes a single call and returns its return value. - - - -Validates a signature using SRC6 `is_valid_signature` and asserts it's valid. Checks both 'VALID' (`starknet::VALIDATED`) and true (1) for backwards compatibility. Reverts with `invalid_signature_error` if signature is invalid. - - -### `math` [toc] [#math] - - - -```rust -use openzeppelin_utils::math; -``` - -Module containing math utilities. - -Members - -Functions - -- [`average(a, b)`](#math-average) - -#### Functions [!toc] [#math-Functions] - - -Returns the average of two unsigned integers. The result is rounded down. - -`T` is a generic value matching different numeric implementations. - - -### `contract_clock` [toc] [#contract_clock] - - -```rust -use openzeppelin_utils::contract_clock; -``` - -Module providing a trait for the [EIP-6372](https://eips.ethereum.org/EIPS/eip-6372) standard along with default clock implementations based on either block number or block timestamp. - -Traits - -- [`ERC6372Clock`](#ERC6372Clock) - -Implementations - -- [`ERC6372BlockNumberClock`](#contract_clock-ERC6372BlockNumberClock) -- [`ERC6372TimestampClock`](#contract_clock-ERC6372TimestampClock) - -#### `ERC6372Clock` [toc] [#ERC6372Clock] - - -```rust -use openzeppelin_utils::contract_clock::ERC6372Clock; -``` - -A trait for the [EIP-6372](https://eips.ethereum.org/EIPS/eip-6372) standard that allows flexible internal clock implementation — based on block timestamp, block number, or a custom logic. - -Functions - -- [`clock()`](#ERC6372Clock-clock) -- [`CLOCK_MODE()`](#ERC6372Clock-CLOCK_MODE) - -#### Functions [!toc] [#ERC6372Clock-Functions] - - -Returns the current timepoint determined by the contract's operational mode, intended for use in time-sensitive logic. - -Requirements: - -- This function MUST always be non-decreasing. - - - -Returns a description of the clock the contract is operating in. - -Requirements: - -- The output MUST be formatted like a URL query string, decodable in standard JavaScript. - - -#### Implementations [!toc] [#contract_clock-Impls] - - -Implementation of the `ERC6372Clock` trait that uses the block number as its clock reference. - - - -Implementation of the `ERC6372Clock` trait that uses the block timestamp as its clock reference. - - -### `serde` [toc] [#serde] - - - -```rust -use openzeppelin_utils::serde; -``` - -Module containing utilities related to serialization and deserialization of Cairo data structures. - -Members - -Traits - -- [`SerializedAppend`](#serde-SerializedAppend) - -#### Traits [!toc] [#serde-Traits] - - -Importing this trait allows the ability to append a serialized representation of a Cairo data structure already implementing the `Serde` trait to a `felt252` buffer. - -Usage example: - -```rust -use openzeppelin_utils::serde::SerializedAppend; -use starknet::ContractAddress; - -fn to_calldata(recipient: ContractAddress, amount: u256) -> Array { - let mut calldata = array![]; - calldata.append_serde(recipient); - calldata.append_serde(amount); - calldata -} -``` - -Note that the `append_serde` method is automatically available for arrays of felts, and it accepts any data structure that implements the `Serde` trait. - - -## Cryptography [#cryptography-toc] - -### `NoncesComponent` [toc] [#NoncesComponent] - - - -```rust -use openzeppelin_utils::cryptography::nonces::NoncesComponent; -``` - -This component provides a simple mechanism for handling incremental nonces for a set of addresses. It is commonly used to prevent replay attacks when contracts accept signatures as input. - -Embeddable Implementations - -NoncesImpl - -- [`nonces(self, owner)`](#NoncesComponent-nonces) - -Internal Implementations - -InternalImpl - -- [`use_nonce(self, owner)`](#NoncesComponent-use_nonce) -- [`use_checked_nonce(self, owner, nonce)`](#NoncesComponent-use_checked_nonce) - -#### Embeddable functions [!toc] [#NoncesComponent-Embeddable-Functions] - - -Returns the next unused nonce for an `owner`. - - -#### Internal functions [!toc] [#NoncesComponent-Internal-Functions] - - -Consumes a nonce, returns the current value, and increments nonce. - -For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be decremented or reset. This guarantees that the nonce never overflows. - - - -Same as `use_nonce` but checking that `nonce` is the next valid one for `owner`. - - -### `snip12` [toc] [#snip12] - - - -```rust -use openzeppelin_utils::snip12; -``` - -Supports on-chain generation of message hashes compliant with [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md). - -For a full walkthrough on how to use this module, see the [SNIP12 and Typed Messages](../guides/snip12) guide. diff --git a/content/contracts-cairo/alpha/backwards-compatibility.mdx b/content/contracts-cairo/alpha/backwards-compatibility.mdx deleted file mode 100644 index 3c63bfcd..00000000 --- a/content/contracts-cairo/alpha/backwards-compatibility.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Backwards Compatibility ---- - -OpenZeppelin Contracts uses semantic versioning to communicate backwards compatibility of its API and storage layout. Patch and minor updates will generally be backwards compatible, with rare exceptions as detailed below. Major updates should be assumed incompatible with previous releases. On this page, we provide details about these guarantees. - -Bear in mind that while releasing versions, we treat minors as majors and patches as minors, in accordance with semantic versioning. This means that `v2.1.0` could be adding features to `v2.0.0`, while `v3.0.0` would be considered a breaking release. - -## API - -In backwards compatible releases, all changes should be either additions or modifications to internal implementation details. Most code should continue to compile and behave as expected. The exceptions to this rule are listed below. - -### Security - -Infrequently, a patch or minor update will remove or change an API in a breaking way but only if the previous API is considered insecure. These breaking changes will be noted in the changelog and release notes, and published along with a security advisory. - -### Errors - -The specific error format and data that is included with reverts should not be assumed stable unless otherwise specified. - -### Major releases - -Major releases should be assumed incompatible. Nevertheless, the external interfaces of contracts will remain compatible if they are standardized, or if the maintainers judge that changing them would cause significant strain on the ecosystem. - -An important aspect that major releases may break is "upgrade compatibility", in particular storage layout compatibility. It will never be safe for a live contract to upgrade from one major release to another. - -In the case of breaking "upgrade compatibility", an entry to the changelog will be added listing those breaking changes. - -## Storage layout - -Patch updates will always preserve storage layout compatibility, and after `1.0.0` minors will too. This means that a live contract can be upgraded from one minor to another without corrupting the storage layout. In some cases it may be necessary to initialize new state variables when upgrading, although we expect this to be infrequent. - -## Cairo version - -The minimum Cairo version required to compile the contracts will remain unchanged for patch updates, but it may change for minors. diff --git a/content/contracts-cairo/alpha/components.mdx b/content/contracts-cairo/alpha/components.mdx deleted file mode 100644 index d78deab7..00000000 --- a/content/contracts-cairo/alpha/components.mdx +++ /dev/null @@ -1,667 +0,0 @@ ---- -title: Components ---- - -The following documentation provides reasoning and examples on how to use Contracts for Cairo components. - -Starknet components are separate modules that contain storage, events, and implementations that can be integrated into a contract. -Components themselves cannot be declared or deployed. -Another way to think of components is that they are abstract modules that must be instantiated. - -[shamans_post]: https://community.starknet.io/t/cairo-components/101136#components-1 -[cairo_book]: https://book.cairo-lang.org/ch103-02-00-composability-and-components.html - - -For more information on the construction and design of Starknet components, see the [Starknet Shamans post][shamans_post] and the [Cairo book][cairo_book]. - - -## Building a contract - -### Setup - -The contract should first import the component and declare it with the `component!` macro: - -```rust -#[starknet::contract] -mod MyContract { - // Import the component - use openzeppelin_security::InitializableComponent; - - // Declare the component - component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); -} -``` - -The `path` argument should be the imported component itself (in this case, [InitializableComponent](./security#initializable)). -The `storage` and `event` arguments are the variable names that will be set in the `Storage` struct and `Event` enum, respectively. -Note that even if the component doesn’t define any events, the compiler will still create an empty event enum inside the component module. - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_security::InitializableComponent; - - component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); - - #[storage] - struct Storage { - #[substorage(v0)] - initializable: InitializableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - InitializableEvent: InitializableComponent::Event - } -} -``` - -The `#[substorage(v0)]` attribute must be included for each component in the `Storage` trait. -This allows the contract to have indirect access to the component’s storage. -See [Accessing component storage](#accessing-component-storage) for more on this. - -The `#[flat]` attribute for events in the `Event` enum, however, is not required. -For component events, the first key in the event log is the component ID. -Flattening the component event removes it, leaving the event ID as the first key. - -### Implementations - -Components come with granular implementations of different interfaces. -This allows contracts to integrate only the implementations that they’ll use and avoid unnecessary bloat. -Integrating an implementation looks like this: - -```rust -mod MyContract { - use openzeppelin_security::InitializableComponent; - - component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); - - (...) - - // Gives the contract access to the implementation methods - impl InitializableImpl = - InitializableComponent::InitializableImpl; -} -``` - -Defining an `impl` gives the contract access to the methods within the implementation from the component. -For example, `is_initialized` is defined in the `InitializableImpl`. -A function on the contract level can expose it like this: - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_security::InitializableComponent; - - component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); - - (...) - - impl InitializableImpl = - InitializableComponent::InitializableImpl; - - #[external(v0)] - fn is_initialized(ref self: ContractState) -> bool { - self.initializable.is_initialized() - } -} -``` - -While there’s nothing wrong with manually exposing methods like in the previous example, this process can be tedious for implementations with many methods. -Fortunately, a contract can embed implementations which will expose all of the methods of the implementation. -To embed an implementation, add the `#[abi(embed_v0)]` attribute above the `impl`: - -```rust -#[starknet::contract] -mod MyContract { - (...) - - // This attribute exposes the methods of the `impl` - #[abi(embed_v0)] - impl InitializableImpl = - InitializableComponent::InitializableImpl; -} -``` - -`InitializableImpl` defines the `is_initialized` method in the component. -By adding the embed attribute, `is_initialized` becomes a contract entrypoint for `MyContract`. - - -Embeddable implementations, when available in this library’s components, are segregated from the internal component implementation which makes it easier to safely expose. -Components also separate granular implementations from [mixin](#mixins) implementations. -The API documentation design reflects these groupings. -See [ERC20Component](/contracts-cairo/alpha/api/erc20#erc20component) as an example which includes: - -* **Embeddable Mixin Implementation** -* **Embeddable Implementations** -* **Internal Implementations** -* **Events** - - - -### Mixins - -Mixins are impls made of a combination of smaller, more specific impls. -While separating components into granular implementations offers flexibility, -integrating components with many implementations can appear crowded especially if the contract uses all of them. -Mixins simplify this by allowing contracts to embed groups of implementations with a single directive. - -Compare the following code blocks to see the benefit of using a mixin when creating an account contract. - -#### Account without mixin - -```rust -component!(path: AccountComponent, storage: account, event: AccountEvent); -component!(path: SRC5Component, storage: src5, event: SRC5Event); - -#[abi(embed_v0)] -impl SRC6Impl = AccountComponent::SRC6Impl; -#[abi(embed_v0)] -impl DeclarerImpl = AccountComponent::DeclarerImpl; -#[abi(embed_v0)] -impl DeployableImpl = AccountComponent::DeployableImpl; -#[abi(embed_v0)] -impl PublicKeyImpl = AccountComponent::PublicKeyImpl; -#[abi(embed_v0)] -impl SRC6CamelOnlyImpl = AccountComponent::SRC6CamelOnlyImpl; -#[abi(embed_v0)] -impl PublicKeyCamelImpl = AccountComponent::PublicKeyCamelImpl; -impl AccountInternalImpl = AccountComponent::InternalImpl; - -#[abi(embed_v0)] -impl SRC5Impl = SRC5Component::SRC5Impl; -``` - -#### Account with mixin - -```rust -component!(path: AccountComponent, storage: account, event: AccountEvent); -component!(path: SRC5Component, storage: src5, event: SRC5Event); - -#[abi(embed_v0)] -impl AccountMixinImpl = AccountComponent::AccountMixinImpl; -impl AccountInternalImpl = AccountComponent::InternalImpl; -``` - -The rest of the setup for the contract, however, does not change. -This means that component dependencies must still be included in the `Storage` struct and `Event` enum. -Here’s a full example of an account contract that embeds the `AccountMixinImpl`: - -```rust -#[starknet::contract] -mod Account { - use openzeppelin_account::AccountComponent; - use openzeppelin_introspection::src5::SRC5Component; - - component!(path: AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // This embeds all of the methods from the many AccountComponent implementations - // and also includes `supports_interface` from `SRC5Impl` - #[abi(embed_v0)] - impl AccountMixinImpl = AccountComponent::AccountMixinImpl; - impl AccountInternalImpl = AccountComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - account: AccountComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccountEvent: AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState, public_key: felt252) { - self.account.initializer(public_key); - } -} -``` - -### Initializers - - -Failing to use a component’s `initializer` can result in irreparable contract deployments. - -Always read the API Reference documentation for each integrated component. - - - -Some components require some sort of setup upon construction. -Usually, this would be a job for a constructor; however, components themselves cannot implement constructors. -Components instead offer ``initializer``s within their `InternalImpl` to call from the contract’s constructor. -Let’s look at how a contract would integrate [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent): - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_access::ownable::OwnableComponent; - use starknet::ContractAddress; - - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - - // Instantiate `InternalImpl` to give the contract access to the `initializer` - impl InternalImpl = OwnableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - ownable: OwnableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - OwnableEvent: OwnableComponent::Event - } - - #[constructor] - fn constructor(ref self: ContractState, owner: ContractAddress) { - // Invoke ownable's `initializer` - self.ownable.initializer(owner); - } -} -``` - -### Immutable Config - -While initializers help set up the component’s initial state, some require configuration that may be defined -as constants, saving gas by avoiding the necessity of reading from storage each time the variable needs to be used. The -Immutable Component Config pattern helps with this matter by allowing the implementing contract to define a set of -constants declared in the component, customizing its functionality. - - -The Immutable Component Config standard is defined in the SRC-107. - - -Here’s an example of how to use the Immutable Component Config pattern with the [ERC2981Component](/contracts-cairo/alpha/api/token_common#erc2981component): - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::common::erc2981::ERC2981Component; - use starknet::contract_address_const; - - component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // Instantiate `InternalImpl` to give the contract access to the `initializer` - impl InternalImpl = ERC2981Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc2981: ERC2981Component::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC2981Event: ERC2981Component::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - // Define the immutable config - pub impl ERC2981ImmutableConfig of ERC2981Component::ImmutableConfig { - const FEE_DENOMINATOR: u128 = 10_000; - } - - #[constructor] - fn constructor(ref self: ContractState) { - let default_receiver = contract_address_const::<'RECEIVER'>(); - let default_royalty_fraction = 1000; - // Invoke erc2981's `initializer` - self.erc2981.initializer(default_receiver, default_royalty_fraction); - } -} -``` - -#### Default config - -Sometimes, components implementing the Immutable Component Config pattern provide a default configuration that can be -directly used without implementing the `ImmutableConfig` trait locally. When provided, this implementation will be named -`DefaultConfig` and will be available in the same module containing the component, as a sibling. - -In the following example, the `DefaultConfig` trait is used to define the `FEE_DENOMINATOR` config constant. - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_introspection::src5::SRC5Component; - // Bring the DefaultConfig trait into scope - use openzeppelin_token::common::erc2981::{ERC2981Component, DefaultConfig}; - use starknet::contract_address_const; - - component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - // Instantiate `InternalImpl` to give the contract access to the `initializer` - impl InternalImpl = ERC2981Component::InternalImpl; - - #[storage] - struct Storage { - (...) - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - (...) - } - - #[constructor] - fn constructor(ref self: ContractState) { - let default_receiver = contract_address_const::<'RECEIVER'>(); - let default_royalty_fraction = 1000; - // Invoke erc2981's `initializer` - self.erc2981.initializer(default_receiver, default_royalty_fraction); - } -} -``` - -#### `validate` function - -The `ImmutableConfig` trait may also include a `validate` function with a default implementation, which -asserts that the configuration is correct, and must not be overridden by the implementing contract. For more information -on how to use this function, refer to the [validate section of the SRC-107](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-107.md#validate-function). - -### Dependencies - -Some components include dependencies of other components. -Contracts that integrate components with dependencies must also include the component dependency. -For instance, [AccessControlComponent](/contracts-cairo/alpha/api/access#accesscontrolcomponent) depends on [SRC5Component](/contracts-cairo/alpha/api/introspection#src5component). -Creating a contract with `AccessControlComponent` should look like this: - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_access::accesscontrol::AccessControlComponent; - use openzeppelin_introspection::src5::SRC5Component; - - component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // AccessControl - #[abi(embed_v0)] - impl AccessControlImpl = - AccessControlComponent::AccessControlImpl; - #[abi(embed_v0)] - impl AccessControlCamelImpl = - AccessControlComponent::AccessControlCamelImpl; - impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - #[storage] - struct Storage { - #[substorage(v0)] - accesscontrol: AccessControlComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccessControlEvent: AccessControlComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - (...) -} -``` - -## Customization - - -Customizing implementations and accessing component storage can potentially corrupt the state, bypass security checks, and undermine the component logic. - -**Exercise extreme caution**. See [Security](#security). - - - -### Hooks - -Hooks are entrypoints to the business logic of a token component that are accessible at the contract level. -This allows contracts to insert additional behaviors before and/or after token transfers (including mints and burns). -Prior to hooks, extending functionality required contracts to create [custom implementations](#custom-implementations). - -All token components include a generic hooks trait that include empty default functions. -When creating a token contract, the using contract must create an implementation of the hooks trait. -Suppose an ERC20 contract wanted to include Pausable functionality on token transfers. -The following snippet leverages the `before_update` hook to include this behavior. - -```rust -#[starknet::contract] -mod MyToken { - use openzeppelin_security::pausable::PausableComponent::InternalTrait; - use openzeppelin_security::pausable::PausableComponent; - use openzeppelin_token::erc20::{ERC20Component, DefaultConfig}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - component!(path: PausableComponent, storage: pausable, event: PausableEvent); - - // ERC20 Mixin - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[abi(embed_v0)] - impl PausableImpl = PausableComponent::PausableImpl; - impl PausableInternalImpl = PausableComponent::InternalImpl; - - // Create the hooks implementation - impl ERC20HooksImpl of ERC20Component::ERC20HooksTrait { - // Occurs before token transfers - fn before_update( - ref self: ERC20Component::ComponentState, - from: ContractAddress, - recipient: ContractAddress, - amount: u256 - ) { - // Access local state from component state - let contract_state = self.get_contract(); - // Call function from integrated component - contract_state.pausable.assert_not_paused(); - } - - // Omitting the `after_update` hook because the default behavior - // is already implemented in the trait - } - - (...) -} -``` - -Notice that the `self` parameter expects a component state type. -Instead of passing the component state, the using contract’s state can be passed which simplifies the syntax. -The hook then moves the scope up with the Cairo-generated `get_contract` through the `HasComponent` trait (as illustrated with ERC20Component in this example). -From here, the hook can access the using contract’s integrated components, storage, and implementations. - -Be advised that even if a token contract does not require hooks, the hooks trait must still be implemented. -The using contract may instantiate an empty impl of the trait; -however, the Contracts for Cairo library already provides the instantiated impl to abstract this away from contracts. -The using contract just needs to bring the implementation into scope like this: - -```rust -#[starknet::contract] -mod MyToken { - use openzeppelin_token::erc20::{ERC20Component, DefaultConfig}; - use openzeppelin_token::erc20::ERC20HooksEmptyImpl; - - (...) -} -``` - - -For a more in-depth guide on hooks, see [Extending Cairo Contracts with Hooks](https://fleming-andrew.medium.com/extending-cairo-contracts-with-hooks-c3ca21d1d6b8). - - -### Custom implementations - -There are instances where a contract requires different or amended behaviors from a component implementation. -In these scenarios, a contract must create a custom implementation of the interface. -Let’s break down a pausable ERC20 contract to see what that looks like. -Here’s the setup: - -```rust -#[starknet::contract] -mod ERC20Pausable { - use openzeppelin_security::pausable::PausableComponent; - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; - // Import the ERC20 interfaces to create custom implementations - use openzeppelin_interfaces::erc20::{IERC20, IERC20CamelOnly}; - use starknet::ContractAddress; - - component!(path: PausableComponent, storage: pausable, event: PausableEvent); - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - #[abi(embed_v0)] - impl PausableImpl = PausableComponent::PausableImpl; - impl PausableInternalImpl = PausableComponent::InternalImpl; - - // `ERC20MetadataImpl` can keep the embed directive because the implementation - // will not change - #[abi(embed_v0)] - impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; - // Do not add the embed directive to these implementations because - // these will be customized - impl ERC20Impl = ERC20Component::ERC20Impl; - impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; - - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - (...) -} -``` - -The first thing to notice is that the contract imports the interfaces of the implementations that will be customized. -These will be used in the next code example. - -Next, the contract includes the [ERC20Component](/contracts-cairo/alpha/api/erc20#erc20component) implementations; however, `ERC20Impl` and `ERC20CamelOnlyImpl` are **not** embedded. -Instead, we want to expose our custom implementation of an interface. -The following example shows the pausable logic integrated into the ERC20 implementations: - -```rust -#[starknet::contract] -mod ERC20Pausable { - (...) - - // Custom ERC20 implementation - #[abi(embed_v0)] - impl CustomERC20Impl of IERC20 { - fn transfer( - ref self: ContractState, recipient: ContractAddress, amount: u256 - ) -> bool { - // Add the custom logic - self.pausable.assert_not_paused(); - // Add the original implementation method from `IERC20Impl` - self.erc20.transfer(recipient, amount) - } - - fn total_supply(self: @ContractState) -> u256 { - // This method's behavior does not change from the component - // implementation, but this method must still be defined. - // Simply add the original implementation method from `IERC20Impl` - self.erc20.total_supply() - } - - (...) - } - - // Custom ERC20CamelOnly implementation - #[abi(embed_v0)] - impl CustomERC20CamelOnlyImpl of IERC20CamelOnly { - fn totalSupply(self: @ContractState) -> u256 { - self.erc20.total_supply() - } - - fn balanceOf(self: @ContractState, account: ContractAddress) -> u256 { - self.erc20.balance_of(account) - } - - fn transferFrom( - ref self: ContractState, - sender: ContractAddress, - recipient: ContractAddress, - amount: u256 - ) -> bool { - self.pausable.assert_not_paused(); - self.erc20.transfer_from(sender, recipient, amount) - } - } -} -``` - -Notice that in the `CustomERC20Impl`, the `transfer` method integrates `pausable.assert_not_paused` as well as `erc20.transfer` from `PausableImpl` and `ERC20Impl` respectively. -This is why the contract defined the `ERC20Impl` from the component in the previous example. - -Creating a custom implementation of an interface must define **all** methods from that interface. -This is true even if the behavior of a method does not change from the component implementation (as `total_supply` exemplifies in this example). - -### Accessing component storage - -There may be cases where the contract must read or write to an integrated component’s storage. -To do so, use the same syntax as calling an implementation method except replace the name of the method with the storage variable like this: - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_security::InitializableComponent; - - component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); - - #[storage] - struct Storage { - #[substorage(v0)] - initializable: InitializableComponent::Storage - } - - (...) - - fn write_to_comp_storage(ref self: ContractState) { - self.initializable.Initializable_initialized.write(true); - } - - fn read_from_comp_storage(self: @ContractState) -> bool { - self.initializable.Initializable_initialized.read() - } -} -``` - -## Security - -The maintainers of OpenZeppelin Contracts for Cairo are mainly concerned with the correctness and security of the code as published in the library. - -Customizing implementations and manipulating the component state may break some important assumptions and introduce vulnerabilities. -While we try to ensure the components remain secure in the face of a wide range of potential customizations, this is done in a best-effort manner. -Any and all customizations to the component logic should be carefully reviewed and checked against the source code of the component they are customizing so as to fully understand their impact and guarantee their security. diff --git a/content/contracts-cairo/alpha/erc1155.mdx b/content/contracts-cairo/alpha/erc1155.mdx deleted file mode 100644 index 45ed4c4a..00000000 --- a/content/contracts-cairo/alpha/erc1155.mdx +++ /dev/null @@ -1,239 +0,0 @@ ---- -title: ERC1155 ---- - -The ERC1155 multi-token standard is a specification for [fungibility-agnostic](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens) token contracts. -The ERC1155 library implements an approximation of [EIP-1155](https://eips.ethereum.org/EIPS/eip-1155) in Cairo for StarkNet. - -## Multi Token Standard - -The distinctive feature of ERC1155 is that it uses a single smart contract to represent multiple tokens at once. This -is why its [balance_of](/contracts-cairo/alpha/api/erc1155#IERC1155-balance_of) function differs from ERC20’s and ERC777’s: it has an additional ID argument for the -identifier of the token that you want to query the balance of. - -This is similar to how ERC721 does things, but in that standard a token ID has no concept of balance: each token is -non-fungible and exists or doesn’t. The ERC721 [balance_of](/contracts-cairo/alpha/api/erc721#IERC721-balance_of) function refers to how many different tokens an account -has, not how many of each. On the other hand, in ERC1155 accounts have a distinct balance for each token ID, and -non-fungible tokens are implemented by simply minting a single one of them. - -This approach leads to massive gas savings for projects that require multiple tokens. Instead of deploying a new -contract for each token type, a single ERC1155 token contract can hold the entire system state, reducing deployment -costs and complexity. - -## Usage - -Using Contracts for Cairo, constructing an ERC1155 contract requires integrating both `ERC1155Component` and `SRC5Component`. -The contract should also set up the constructor to initialize the token’s URI and interface support. -Here’s an example of a basic contract: - -```rust -#[starknet::contract] -mod MyERC1155 { - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl}; - use starknet::ContractAddress; - - component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // ERC1155 Mixin - #[abi(embed_v0)] - impl ERC1155MixinImpl = ERC1155Component::ERC1155MixinImpl; - impl ERC1155InternalImpl = ERC1155Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc1155: ERC1155Component::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC1155Event: ERC1155Component::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - token_uri: ByteArray, - recipient: ContractAddress, - token_ids: Span, - values: Span - ) { - self.erc1155.initializer(token_uri); - self - .erc1155 - .batch_mint_with_acceptance_check(recipient, token_ids, values, array![].span()); - } -} -``` - -## Interface - -The following interface represents the full ABI of the Contracts for Cairo [ERC1155Component](/contracts-cairo/alpha/api/erc1155#ERC1155Component). -The interface includes the [IERC1155](/contracts-cairo/alpha/api/erc1155#IERC1155) standard interface and the optional [IERC1155MetadataURI](/contracts-cairo/alpha/api/erc1155#IERC1155MetadataURI) interface together with [ISRC5](/contracts-cairo/alpha/api/introspection#ISRC5). - -To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. - -```rust -#[starknet::interface] -pub trait ERC1155ABI { - // IERC1155 - fn balance_of(account: ContractAddress, token_id: u256) -> u256; - fn balance_of_batch( - accounts: Span, token_ids: Span - ) -> Span; - fn safe_transfer_from( - from: ContractAddress, - to: ContractAddress, - token_id: u256, - value: u256, - data: Span - ); - fn safe_batch_transfer_from( - from: ContractAddress, - to: ContractAddress, - token_ids: Span, - values: Span, - data: Span - ); - fn is_approved_for_all( - owner: ContractAddress, operator: ContractAddress - ) -> bool; - fn set_approval_for_all(operator: ContractAddress, approved: bool); - - // IERC1155MetadataURI - fn uri(token_id: u256) -> ByteArray; - - // ISRC5 - fn supports_interface(interface_id: felt252) -> bool; - - // IERC1155Camel - fn balanceOf(account: ContractAddress, tokenId: u256) -> u256; - fn balanceOfBatch( - accounts: Span, tokenIds: Span - ) -> Span; - fn safeTransferFrom( - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - value: u256, - data: Span - ); - fn safeBatchTransferFrom( - from: ContractAddress, - to: ContractAddress, - tokenIds: Span, - values: Span, - data: Span - ); - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; - fn setApprovalForAll(operator: ContractAddress, approved: bool); -} -``` - -## ERC1155 Compatibility - -Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC1155 standard but some differences can still be found, such as: - -* The optional `data` argument in both `safe_transfer_from` and `safe_batch_transfer_from` is implemented as `Span`. -* `IERC1155Receiver` compliant contracts must implement SRC5 and register the `IERC1155Receiver` interface ID. -* `IERC1155Receiver::on_erc1155_received` must return that interface ID on success. - -## Batch operations - -Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, [balance_of_batch](/contracts-cairo/alpha/api/erc1155#IERC1155-balance_of_batch) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from), that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive. We also have [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) for non-batch operations. - -In the spirit of the standard, we’ve also included batch operations in the non-standard functions, such as -[batch_mint_with_acceptance_check](/contracts-cairo/alpha/api/erc1155#ERC1155Component-batch_mint_with_acceptance_check). - - -While [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from) prevent loss by checking the receiver can handle the -tokens, this yields execution to the receiver which can result in a [reentrant call](./security#reentrancy-guard). - - -## Receiving tokens - -In order to be sure a non-account contract can safely accept ERC1155 tokens, the contract must implement the `IERC1155Receiver` interface. -The recipient contract must also implement the [SRC5](./introspection#src5) interface which supports interface introspection. - -### IERC1155Receiver - -```rust -#[starknet::interface] -pub trait IERC1155Receiver { - fn on_erc1155_received( - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - value: u256, - data: Span - ) -> felt252; - fn on_erc1155_batch_received( - operator: ContractAddress, - from: ContractAddress, - token_ids: Span, - values: Span, - data: Span - ) -> felt252; -} -``` - -Implementing the `IERC1155Receiver` interface exposes the [on_erc1155_received](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver-on_erc1155_received) and [on_erc1155_batch_received](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver-on_erc1155_batch_received) methods. -When [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from) are called, they invoke the recipient contract’s `on_erc1155_received` or `on_erc1155_batch_received` methods respectively which **must** return the [IERC1155Receiver interface ID](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver). -Otherwise, the transaction will fail. - - -For information on how to calculate interface IDs, see [Computing the interface ID](./introspection#computing-the-interface-id). - - -### Creating a token receiver contract - -The Contracts for Cairo ERC1155ReceiverComponent already returns the correct interface ID for safe token transfers. -To integrate the `IERC1155Receiver` interface into a contract, simply include the ABI embed directive to the implementations and add the `initializer` in the contract’s constructor. -Here’s an example of a simple token receiver contract: - -```rust -#[starknet::contract] -mod MyTokenReceiver { - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc1155::ERC1155ReceiverComponent; - use starknet::ContractAddress; - - component!(path: ERC1155ReceiverComponent, storage: erc1155_receiver, event: ERC1155ReceiverEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // ERC1155Receiver Mixin - #[abi(embed_v0)] - impl ERC1155ReceiverMixinImpl = ERC1155ReceiverComponent::ERC1155ReceiverMixinImpl; - impl ERC1155ReceiverInternalImpl = ERC1155ReceiverComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc1155_receiver: ERC1155ReceiverComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC1155ReceiverEvent: ERC1155ReceiverComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.erc1155_receiver.initializer(); - } -} -``` diff --git a/content/contracts-cairo/alpha/erc20.mdx b/content/contracts-cairo/alpha/erc20.mdx deleted file mode 100644 index 4c0c5ba1..00000000 --- a/content/contracts-cairo/alpha/erc20.mdx +++ /dev/null @@ -1,252 +0,0 @@ ---- -title: ERC20 ---- - -The ERC20 token standard is a specification for [fungible tokens](https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens), a type of token where all the units are exactly equal to each other. -`token::erc20::ERC20Component` provides an approximation of [EIP-20](https://eips.ethereum.org/EIPS/eip-20) in Cairo for Starknet. - - -Prior to [Contracts v0.7.0](https://github.com/OpenZeppelin/cairo-contracts/releases/tag/v0.7.0), ERC20 contracts store and read `decimals` from storage; however, this implementation returns a static `18`. -If upgrading an older ERC20 contract that has a decimals value other than `18`, the upgraded contract **must** use a custom `decimals` implementation. -See the [Customizing decimals](#customizing-decimals) guide. - - -## Usage - -Using Contracts for Cairo, constructing an ERC20 contract requires setting up the constructor and instantiating the token implementation. -Here’s what that looks like: - -```rust -#[starknet::contract] -mod MyToken { - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - // ERC20 Mixin - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc20: ERC20Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC20Event: ERC20Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - initial_supply: u256, - recipient: ContractAddress - ) { - let name = "MyToken"; - let symbol = "MTK"; - - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, initial_supply); - } -} -``` - -`MyToken` integrates both the `ERC20Impl` and `ERC20MetadataImpl` with the embed directive which marks the implementations as external in the contract. -While the `ERC20MetadataImpl` is optional, it’s generally recommended to include it because the vast majority of ERC20 tokens provide the metadata methods. -The above example also includes the `ERC20InternalImpl` instance. -This allows the contract’s constructor to initialize the contract and create an initial supply of tokens. - - -For a more complete guide on ERC20 token mechanisms, see [Creating ERC20 Supply](./guides/erc20-supply). - - -## Interface - -The following interface represents the full ABI of the Contracts for Cairo [ERC20Component](/contracts-cairo/alpha/api/erc20#ERC20Component). -The interface includes the [IERC20](/contracts-cairo/alpha/api/erc20#IERC20) standard interface as well as the optional [IERC20Metadata](/contracts-cairo/alpha/api/erc20#IERC20Metadata). - -To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes an implementation of the interface written in camelCase. - -```rust -#[starknet::interface] -pub trait ERC20ABI { - // IERC20 - fn total_supply() -> u256; - fn balance_of(account: ContractAddress) -> u256; - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - fn transfer_from( - sender: ContractAddress, recipient: ContractAddress, amount: u256 - ) -> bool; - fn approve(spender: ContractAddress, amount: u256) -> bool; - - // IERC20Metadata - fn name() -> ByteArray; - fn symbol() -> ByteArray; - fn decimals() -> u8; - - // IERC20Camel - fn totalSupply() -> u256; - fn balanceOf(account: ContractAddress) -> u256; - fn transferFrom( - sender: ContractAddress, recipient: ContractAddress, amount: u256 - ) -> bool; -} -``` - -## ERC20 compatibility - -Although Starknet is not EVM compatible, this component aims to be as close as possible to the ERC20 token standard. -Some notable differences, however, can still be found, such as: - -* The `ByteArray` type is used to represent strings in Cairo. -* The component offers a [dual interface](./guides/interfaces-and-dispatchers#dual-interfaces) which supports both snake_case and camelCase methods, as opposed to just camelCase in Solidity. -* `transfer`, `transfer_from` and `approve` will never return anything different from `true` because they will revert on any error. -* Function selectors are calculated differently between [Cairo](https://github.com/starkware-libs/cairo/blob/7dd34f6c57b7baf5cd5a30c15e00af39cb26f7e1/crates/cairo-lang-starknet/src/contract.rs#L39-L48) and [Solidity](https://solidity-by-example.org/function-selector/). - -## Customizing decimals - -Cairo, like Solidity, does not support [floating-point numbers](https://en.wikipedia.org//wiki/Floating-point_arithmetic). -To get around this limitation, ERC20 token contracts may offer a `decimals` field which communicates to outside interfaces (wallets, exchanges, etc.) how the token should be displayed. -For instance, suppose a token had a `decimals` value of `3` and the total token supply was `1234`. -An outside interface would display the token supply as `1.234`. -In the actual contract, however, the supply would still be the integer `1234`. -In other words, **the decimals field in no way changes the actual arithmetic** because all operations are still performed on integers. - -Most contracts use `18` decimals and this was even proposed to be compulsory (see the [EIP discussion](https://github.com/ethereum/EIPs/issues/724)). - -### The static approach (SRC-107) - -The Contracts for Cairo `ERC20` component leverages SRC-107 to allow for a static and configurable number of decimals. -To use the default `18` decimals, you can use the `DefaultConfig` implementation by just importing it: - -```rust -#[starknet::contract] -mod MyToken { - // Importing the DefaultConfig implementation would make decimals 18 by default. - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - (...) -} -``` - -To customize this value, you can implement the ImmutableConfig trait locally in the contract. -The following example shows how to set the decimals to `6`: - -```rust -mod MyToken { - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - (...) - - // Custom implementation of the ERC20Component ImmutableConfig. - impl ERC20ImmutableConfig of ERC20Component::ImmutableConfig { - const DECIMALS: u8 = 6; - } -} -``` - -### The storage approach - -For more complex scenarios, such as a factory deploying multiple tokens with differing values for decimals, a flexible solution might be appropriate. - - -Note that we are not using the MixinImpl or the DefaultConfig in this case, since we need to customize the IERC20Metadata implementation. - - -```rust -#[starknet::contract] -mod MyToken { - use openzeppelin_interfaces::erc20 as interface; - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc20: ERC20Component::Storage, - // The decimals value is stored locally - decimals: u8, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC20Event: ERC20Component::Event, - } - - #[constructor] - fn constructor( - ref self: ContractState, decimals: u8, initial_supply: u256, recipient: ContractAddress, - ) { - // Call the internal function that writes decimals to storage - self._set_decimals(decimals); - - // Initialize ERC20 - let name = "MyToken"; - let symbol = "MTK"; - - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, initial_supply); - } - - #[abi(embed_v0)] - impl ERC20CustomMetadataImpl of interface::IERC20Metadata { - fn name(self: @ContractState) -> ByteArray { - self.erc20.ERC20_name.read() - } - - fn symbol(self: @ContractState) -> ByteArray { - self.erc20.ERC20_symbol.read() - } - - fn decimals(self: @ContractState) -> u8 { - self.decimals.read() - } - } - - #[generate_trait] - impl InternalImpl of InternalTrait { - fn _set_decimals(ref self: ContractState, decimals: u8) { - self.decimals.write(decimals); - } - } -} -``` - -This contract expects a `decimals` argument in the constructor and uses an internal function to write the decimals to storage. -Note that the `decimals` state variable must be defined in the contract’s storage because this variable does not exist in the component offered by OpenZeppelin Contracts for Cairo. -It’s important to include a custom ERC20 metadata implementation and NOT use the Contracts for Cairo `ERC20MetadataImpl` in this specific case since the `decimals` method will always return `18`. diff --git a/content/contracts-cairo/alpha/erc4626.mdx b/content/contracts-cairo/alpha/erc4626.mdx deleted file mode 100644 index bcacf612..00000000 --- a/content/contracts-cairo/alpha/erc4626.mdx +++ /dev/null @@ -1,433 +0,0 @@ ---- -title: ERC4626 ---- - -[ERC4626](https://eips.ethereum.org/EIPS/eip-4626) is an extension of [ERC20](./erc20) that proposes a standard interface for token vaults. -This standard interface can be used by widely different contracts (including lending markets, aggregators, and intrinsically interest bearing tokens), -which brings a number of subtleties. Navigating these potential issues is essential to implementing a compliant and composable token vault. - -We provide a base component of ERC4626 which is designed to allow developers to easily re-configure the vault’s behavior, using traits and hooks, while -staying compliant. In this guide, we will discuss some security considerations that affect ERC4626. We will also discuss common customizations of the vault. - -## Security concern: Inflation attack - -### Visualizing the vault - -In exchange for the assets deposited into an ERC4626 vault, a user receives shares. These shares can later be burned to redeem the corresponding underlying assets. -The number of shares a user gets depends on the amount of assets they put in and on the exchange rate of the vault. This exchange rate is defined by the -current liquidity held by the vault. - -* If a vault has 100 tokens to back 200 shares, then each share is worth 0.5 assets. -* If a vault has 200 tokens to back 100 shares, then each share is worth 2.0 assets. - -In other words, the exchange rate can be defined as the slope of the line that passes through the origin and the current number of assets and shares in the vault. -Deposits and withdrawals move the vault in this line. - -![Exchange rates in linear scale](/erc4626-rate-linear.png) - -When plotted in log-log scale, the rate is defined similarly, but appears differently (because the point (0,0) is infinitely far away). Rates are represented by "diagonal" lines with different offsets. - -![Exchange rates in logarithmic scale](/erc4626-rate-loglog.png) - -In such a representation, widely different rates can be clearly visible in the same graph. This wouldn’t be the case in linear scale. - -![More exchange rates in logarithmic scale](/erc4626-rate-loglogext.png) - -### The attack - -When depositing tokens, the number of shares a user gets is rounded towards zero. This rounding takes away value from the user in favor of -the vault (i.e. in favor of all the current shareholders). This rounding is often negligible because of the amount at stake. If you deposit 1e9 shares -worth of tokens, the rounding will have you lose at most 0.0000001% of your deposit. However if you deposit 10 shares worth of tokens, you could -lose 10% of your deposit. Even worse, if you deposit less than 1 share worth of tokens, you will receive 0 shares, effectively making a donation. - -For a given amount of assets, the more shares you receive the safer you are. If you want to limit your losses to at most 1%, you need to receive at least 100 shares. - -![Depositing assets](/erc4626-deposit.png) - -In the figure we can see that for a given deposit of 500 assets, the number of shares we get and the corresponding rounding losses depend on the exchange rate. -If the exchange rate is that of the orange curve, we are getting less than a share, so we lose 100% of our deposit. However, if the exchange rate -is that of the green curve, we get 5000 shares, which limits our rounding losses to at most 0.02%. - -![Minting shares](/erc4626-mint.png) - -Symmetrically, if we focus on limiting our losses to a maximum of 0.5%, we need to get at least 200 shares. With the green exchange rate that requires -just 20 tokens, but with the orange rate that requires 200000 tokens. - -We can clearly see that the blue and green curves correspond to vaults that are safer than the yellow and orange curves. - -The idea of an inflation attack is that an attacker can donate assets to the vault to move the rate curve to the right, and make the vault unsafe. - -![Inflation attack without protection](/erc4626-attack.png) - -Figure 6 shows how an attacker can manipulate the rate of an empty vault. First the attacker must deposit a small amount of tokens (1 token) and follow up with -a donation of 1e5 tokens directly to the vault to move the exchange rate "right". This puts the vault in a state where any deposit smaller than 1e5 would be -completely lost to the vault. Given that the attacker is the only shareholder (from their donation), the attacker would steal all the tokens deposited. - -An attacker would typically wait for a user to do the first deposit into the vault, and would frontrun that operation with the attack described above. The risk is -low, and the size of the "donation" required to manipulate the vault is equivalent to the size of the deposit that is being attacked. - -In math that gives: - -* $a_0$ the attacker deposit -* $a_1$ the attacker donation -* $u$ the user deposit - -| | Assets | Shares | Rate | -| --- | --- | --- | --- | -| initial | $0$ | $0$ | - | -| after attacker's deposit | $a_0$ | $a_0$ | $1$ | -| after attacker's donation | $a_0 + a_1$ | $a_0$ | $\frac{a_0}{a_1 + a_0}$ | - -This means a deposit of $u$ will give this number of shares: - -```math -\frac{u \times a_0}{a_0 + a_1} -``` - -For the attacker to dilute that deposit to 0 shares, causing the user to lose all its deposit, it must ensure that - -```math -\frac{u \times a_0}{a_0+a_1} < 1 \iff u < 1 + \frac{a_1}{a_0} -``` - -Using $a_0 = 1$ and $a_1 = u$ is enough. So the attacker only needs $u+1$ assets to perform a successful attack. - -It is easy to generalize the above results to scenarios where the attacker is going after a smaller fraction of the user’s deposit. In order to target $\frac{u}{n}$, the user needs to suffer rounding of a similar fraction, which means the user must receive at most $n$ shares. This results in: - -```math -\frac{u \times a_0}{a_0+a_1} < n \iff \frac{u}{n} < 1 + \frac{a_1}{a_0} -``` - -In this scenario, the attack is $n$ times less powerful (in how much it is stealing) and costs $n$ times less to execute. In both cases, the amount of funds the attacker needs to commit is equivalent to its potential earnings. - -### Defending with a virtual offset - -The defense we propose is based on the approach used in [YieldBox](https://github.com/boringcrypto/YieldBox). It consists of two parts: - -* Use an offset between the "precision" of the representation of shares and assets. Said otherwise, we use more decimal places to represent the shares than the underlying token does to represent the assets. -* Include virtual shares and virtual assets in the exchange rate computation. These virtual assets enforce the conversion rate when the vault is empty. - -These two parts work together in enforcing the security of the vault. First, the increased precision corresponds to a high rate, which we saw is safer as it reduces the rounding error when computing the amount of shares. Second, the virtual assets and shares (in addition to simplifying a lot of the computations) capture part of the donation, making it unprofitable to perform an attack. - -Following the previous math definitions, we have: - -* $\delta$ the vault offset -* $a_0$ the attacker deposit -* $a_1$ the attacker donation -* $u$ the user deposit - -| | Assets | Shares | Rate | -| --- | --- | --- | --- | -| initial | $1$ | $10^\delta$ | $10^\delta$ | -| after attacker's deposit | $1+a_0$ | $10^\delta \times (1+a_0)$ | $10^\delta$ | -| after attacker's donation | $1+a_0+a_1$ | $10^\delta \times (1+a_0)$ | $10^\delta$ | - -One important thing to note is that the attacker only owns a fraction $\frac{a_0}{1 + a_0}$ of the shares, so when doing the donation, he will only be able -to recover that fraction $\frac{a_1 * a_0}{1 + a_0}$ of the donation. The remaining $\frac{a_1}{1+a_0}$ are captured by the vault. - -```math -\mathit{loss} = \frac{a_1}{1+a_0} -``` - -When the user deposits $u$, he receives - -```math -10^\delta \times u \times \frac{1+a_0}{1+a_0+a_1} -``` - -For the attacker to dilute that deposit to 0 shares, causing the user to lose all its deposit, it must ensure that - -```math -10^\delta \times u \times \frac{1+a_0}{1+a_0+a_1} < 1 -``` - -```math -\iff 10^\delta \times u < \frac{1+a_0+a_1}{1+a_0} -``` - -```math -\iff 10^\delta \times u < 1 + \frac{a_1}{1+a_0} -``` - -```math -\iff 10^\delta \times u \le \mathit{loss} -``` - -* If the offset is 0, the attacker loss is at least equal to the user’s deposit. -* If the offset is greater than 0, the attacker will have to suffer losses that are orders of magnitude bigger than the amount of value that can hypothetically be stolen from the user. - -This shows that even with an offset of 0, the virtual shares and assets make this attack non profitable for the attacker. Bigger offsets increase the security even further by making any attack on the user extremely wasteful. - -The following figure shows how the offset impacts the initial rate and limits the ability of an attacker with limited funds to inflate it effectively. - -![Inflation attack without offset=3](/erc4626-attack-3a.png) -$\delta = 3$, $a_0 = 1$, $a_1 = 10^5$ - -![Inflation attack without offset=3 and an attacker deposit that limits its losses](/erc4626-attack-3b.png) -$\delta = 3$, $a_0 = 100$, $a_1 = 10^5$ - -![Inflation attack without offset=6](/erc4626-attack-6.png) -$\delta = 6$, $a_0 = 1$, $a_1 = 10^5$ - -## Usage - -### Custom behavior: Adding fees to the vault - -In ERC4626 vaults, fees can be captured during deposit/mint and/or withdraw/redeem operations. It is essential to remain -compliant with the ERC4626 requirements regarding the preview functions. Fees are calculated through the [FeeConfigTrait](/contracts-cairo/alpha/api/erc20#ERC4626Component-FeeConfigTrait) -implementation. By default, the ERC4626 component charges no fees. If this is the desired behavior, you can use the default -[ERC4626DefaultNoFees](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/token/src/erc20/extensions/erc4626/erc4626.cairo#L899) implementation. - - -Starting from v3.0.0, fees can be charged in either assets or shares. Prior versions only supported fees taken in assets. -See the updated [FeeConfigTrait](/contracts-cairo/alpha/api/erc20#ERC4626Component-FeeConfigTrait) and implementation examples in [ERC4626 mocks](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo). - - -For example, if calling `deposit(100, receiver)`, the caller should deposit exactly 100 underlying tokens, including fees, and the receiver should receive a number of shares that matches the value returned by `preview_deposit(100)`. -Similarly, `preview_mint` should account for the fees that the user will have to pay on top of share’s cost. - -As for the `Deposit` event, while this is less clear in the EIP spec itself, -there seems to be consensus that it should include the number of assets paid for by the user, including the fees. - -On the other hand, when withdrawing assets, the number given by the user should correspond to what the user receives. -Any fees should be added to the quote (in shares) performed by `preview_withdraw`. - -The `Withdraw` event should include the number of shares the user burns (including fees) and the number of assets the user actually receives (after fees are deducted). - -The consequence of this design is that both the `Deposit` and `Withdraw` events will describe two exchange rates. -The spread between the "Buy-in" and the "Exit" prices correspond to the fees taken by the vault. - -The following example describes how fees taken in assets on deposits/withdrawals and in shares on mints/redemptions -proportional to the deposited/withdrawn amount can be implemented: - -```rust -#[starknet::contract] -#[with_components(./erc20, ERC4626)] -pub mod ERC4626Fees { - use openzeppelin_interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; - use openzeppelin_token::erc20::extensions::erc4626::ERC4626Component::{Fee, FeeConfigTrait}; - use openzeppelin_token::erc20::extensions::erc4626::{ - DefaultConfig, ERC4626DefaultNoLimits, ERC4626SelfAssetsManagement, - }; - use openzeppelin_token::erc20::{DefaultConfig as ERC20DefaultConfig, ERC20HooksEmptyImpl}; - use openzeppelin_utils::math; - use openzeppelin_utils::math::Rounding; - use starknet::ContractAddress; - use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - - const _BASIS_POINT_SCALE: u256 = 10_000; - const FEE_TRANSFER_FAILED: felt252 = 0x1; - - // ERC4626 - #[abi(embed_v0)] - impl ERC4626ComponentImpl = ERC4626Component::ERC4626Impl; - // ERC4626MetadataImpl is a custom impl of IERC20Metadata - #[abi(embed_v0)] - impl ERC4626MetadataImpl = ERC4626Component::ERC4626MetadataImpl; - - // ERC20 - #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; - - #[storage] - pub struct Storage { - pub entry_fee_basis_point_value: u256, - pub entry_fee_recipient: ContractAddress, - pub exit_fee_basis_point_value: u256, - pub exit_fee_recipient: ContractAddress, - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - underlying_asset: ContractAddress, - initial_supply: u256, - recipient: ContractAddress, - entry_fee: u256, - entry_treasury: ContractAddress, - exit_fee: u256, - exit_treasury: ContractAddress, - ) { - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, initial_supply); - self.erc4626.initializer(underlying_asset); - - self.entry_fee_basis_point_value.write(entry_fee); - self.entry_fee_recipient.write(entry_treasury); - self.exit_fee_basis_point_value.write(exit_fee); - self.exit_fee_recipient.write(exit_treasury); - } - - /// Hooks - impl ERC4626HooksImpl of ERC4626Component::ERC4626HooksTrait { - fn after_deposit( - ref self: ERC4626Component::ComponentState, - caller: ContractAddress, - receiver: ContractAddress, - assets: u256, - shares: u256, - fee: Option, - ) { - if let Option::Some(fee) = fee { - let mut contract_state = self.get_contract_mut(); - let fee_recipient = contract_state.entry_fee_recipient.read(); - match fee { - Fee::Assets(fee) => { - let asset_address = contract_state.asset(); - let asset_dispatcher = IERC20Dispatcher { contract_address: asset_address }; - assert( - asset_dispatcher.transfer(fee_recipient, fee), FEE_TRANSFER_FAILED, - ); - }, - Fee::Shares(fee) => contract_state.erc20.mint(fee_recipient, fee), - }; - } - } - - fn before_withdraw( - ref self: ERC4626Component::ComponentState, - caller: ContractAddress, - receiver: ContractAddress, - owner: ContractAddress, - assets: u256, - shares: u256, - fee: Option, - ) { - if let Option::Some(fee) = fee { - let mut contract_state = self.get_contract_mut(); - let fee_recipient = contract_state.exit_fee_recipient.read(); - match fee { - Fee::Assets(fee) => { - let asset_address = contract_state.asset(); - let asset_dispatcher = IERC20Dispatcher { contract_address: asset_address }; - assert( - asset_dispatcher.transfer(fee_recipient, fee), FEE_TRANSFER_FAILED, - ); - }, - Fee::Shares(fee) => { - if caller != owner { - contract_state.erc20._spend_allowance(owner, caller, fee); - } - contract_state.erc20._transfer(owner, fee_recipient, fee); - }, - }; - } - } - } - - /// Calculate fees - impl FeeConfigImpl of FeeConfigTrait { - fn calculate_deposit_fee( - self: @ERC4626Component::ComponentState, assets: u256, shares: u256, - ) -> Option { - let contract_state = self.get_contract(); - let fee = fee_on_total(assets, contract_state.entry_fee_basis_point_value.read()); - Option::Some(Fee::Assets(fee)) - } - - fn calculate_mint_fee( - self: @ERC4626Component::ComponentState, assets: u256, shares: u256, - ) -> Option { - let contract_state = self.get_contract(); - let fee = fee_on_raw(shares, contract_state.entry_fee_basis_point_value.read()); - Option::Some(Fee::Shares(fee)) - } - - fn calculate_withdraw_fee( - self: @ERC4626Component::ComponentState, assets: u256, shares: u256, - ) -> Option { - let contract_state = self.get_contract(); - let fee = fee_on_raw(assets, contract_state.exit_fee_basis_point_value.read()); - Option::Some(Fee::Assets(fee)) - } - - fn calculate_redeem_fee( - self: @ERC4626Component::ComponentState, assets: u256, shares: u256, - ) -> Option { - let contract_state = self.get_contract(); - let fee = fee_on_total(shares, contract_state.exit_fee_basis_point_value.read()); - Option::Some(Fee::Shares(fee)) - } - } - - /// Calculates the fees that should be added to an amount `assets` that does not already - /// include fees. - /// Used in IERC4626::mint and IERC4626::withdraw operations. - fn fee_on_raw( - assets: u256, - fee_basis_points: u256, - ) -> u256 { - math::u256_mul_div(assets, fee_basis_points, _BASIS_POINT_SCALE, Rounding::Ceil) - } - - /// Calculates the fee part of an amount `assets` that already includes fees. - /// Used in IERC4626::deposit and IERC4626::redeem operations. - fn fee_on_total( - assets: u256, - fee_basis_points: u256, - ) -> u256 { - math::u256_mul_div( - assets, fee_basis_points, fee_basis_points + _BASIS_POINT_SCALE, Rounding::Ceil, - ) - } -} -``` - -## Interface - -The following interface represents the full ABI of the Contracts for Cairo [ERC4626Component](/contracts-cairo/alpha/api/erc20#ERC4626Component). -The full interface includes the [IERC4626](/contracts-cairo/alpha/api/erc20#IERC4626), [IERC20](/contracts-cairo/alpha/api/erc20#IERC20), and [IERC20Metadata](/contracts-cairo/alpha/api/erc20#IERC20Metadata) interfaces. -Note that implementing the IERC20Metadata interface is a requirement of IERC4626. - -```rust -#[starknet::interface] -pub trait ERC4626ABI { - // IERC4626 - fn asset() -> ContractAddress; - fn total_assets() -> u256; - fn convert_to_shares(assets: u256) -> u256; - fn convert_to_assets(shares: u256) -> u256; - fn max_deposit(receiver: ContractAddress) -> u256; - fn preview_deposit(assets: u256) -> u256; - fn deposit(assets: u256, receiver: ContractAddress) -> u256; - fn max_mint(receiver: ContractAddress) -> u256; - fn preview_mint(shares: u256) -> u256; - fn mint(shares: u256, receiver: ContractAddress) -> u256; - fn max_withdraw(owner: ContractAddress) -> u256; - fn preview_withdraw(assets: u256) -> u256; - fn withdraw( - assets: u256, receiver: ContractAddress, owner: ContractAddress, - ) -> u256; - fn max_redeem(owner: ContractAddress) -> u256; - fn preview_redeem(shares: u256) -> u256; - fn redeem( - shares: u256, receiver: ContractAddress, owner: ContractAddress, - ) -> u256; - - // IERC20 - fn total_supply() -> u256; - fn balance_of(account: ContractAddress) -> u256; - fn allowance(owner: ContractAddress, spender: ContractAddress) -> u256; - fn transfer(recipient: ContractAddress, amount: u256) -> bool; - fn transfer_from( - sender: ContractAddress, recipient: ContractAddress, amount: u256, - ) -> bool; - fn approve(spender: ContractAddress, amount: u256) -> bool; - - // IERC20Metadata - fn name() -> ByteArray; - fn symbol() -> ByteArray; - fn decimals() -> u8; - - // IERC20CamelOnly - fn totalSupply() -> u256; - fn balanceOf(account: ContractAddress) -> u256; - fn transferFrom( - sender: ContractAddress, recipient: ContractAddress, amount: u256, - ) -> bool; -} -``` diff --git a/content/contracts-cairo/alpha/erc721.mdx b/content/contracts-cairo/alpha/erc721.mdx deleted file mode 100644 index a2310b39..00000000 --- a/content/contracts-cairo/alpha/erc721.mdx +++ /dev/null @@ -1,213 +0,0 @@ ---- -title: ERC721 ---- - -The ERC721 token standard is a specification for [non-fungible tokens](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens), or more colloquially: NFTs. -`token::erc721::ERC721Component` provides an approximation of [EIP-721](https://eips.ethereum.org/EIPS/eip-721) in Cairo for Starknet. - -## Usage - -Using Contracts for Cairo, constructing an ERC721 contract requires integrating both `ERC721Component` and `SRC5Component`. -The contract should also set up the constructor to initialize the token’s name, symbol, and interface support. -Here’s an example of a basic contract: - -```rust -#[starknet::contract] -mod MyNFT { - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc721::{ERC721Component, ERC721HooksEmptyImpl}; - use starknet::ContractAddress; - - component!(path: ERC721Component, storage: erc721, event: ERC721Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // ERC721 Mixin - #[abi(embed_v0)] - impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; - impl ERC721InternalImpl = ERC721Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc721: ERC721Component::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC721Event: ERC721Component::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - recipient: ContractAddress - ) { - let name = "MyNFT"; - let symbol = "NFT"; - let base_uri = "https://api.example.com/v1/"; - let token_id = 1; - - self.erc721.initializer(name, symbol, base_uri); - self.erc721.mint(recipient, token_id); - } -} -``` - -## Interface - -The following interface represents the full ABI of the Contracts for Cairo [ERC721Component](/contracts-cairo/alpha/api/erc721#ERC721Component). -The interface includes the [IERC721](/contracts-cairo/alpha/api/erc721#IERC721) standard interface and the optional [IERC721Metadata](/contracts-cairo/alpha/api/erc721#IERC721Metadata) interface. - -To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. - -```rust -#[starknet::interface] -pub trait ERC721ABI { - // IERC721 - fn balance_of(account: ContractAddress) -> u256; - fn owner_of(token_id: u256) -> ContractAddress; - fn safe_transfer_from( - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ); - fn transfer_from(from: ContractAddress, to: ContractAddress, token_id: u256); - fn approve(to: ContractAddress, token_id: u256); - fn set_approval_for_all(operator: ContractAddress, approved: bool); - fn get_approved(token_id: u256) -> ContractAddress; - fn is_approved_for_all(owner: ContractAddress, operator: ContractAddress) -> bool; - - // IERC721Metadata - fn name() -> ByteArray; - fn symbol() -> ByteArray; - fn token_uri(token_id: u256) -> ByteArray; - - // IERC721CamelOnly - fn balanceOf(account: ContractAddress) -> u256; - fn ownerOf(tokenId: u256) -> ContractAddress; - fn safeTransferFrom( - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ); - fn transferFrom(from: ContractAddress, to: ContractAddress, tokenId: u256); - fn setApprovalForAll(operator: ContractAddress, approved: bool); - fn getApproved(tokenId: u256) -> ContractAddress; - fn isApprovedForAll(owner: ContractAddress, operator: ContractAddress) -> bool; - - // IERC721MetadataCamelOnly - fn tokenURI(tokenId: u256) -> ByteArray; -} -``` - -## ERC721 compatibility - -Although Starknet is not EVM compatible, this implementation aims to be as close as possible to the ERC721 standard. -This implementation does, however, include a few notable differences such as: - -* ``interface_id``s are hardcoded and initialized by the constructor. -The hardcoded values derive from Starknet’s selector calculations. -See the [Introspection](./introspection) docs. -* `safe_transfer_from` can only be expressed as a single function in Cairo as opposed to the two functions declared in EIP721, because function overloading is currently not possible in Cairo. -The difference between both functions consists of accepting `data` as an argument. -`safe_transfer_from` by default accepts the `data` argument which is interpreted as `Span`. -If `data` is not used, simply pass an empty array. -* ERC721 utilizes [SRC5](./introspection#src5) to declare and query interface support on Starknet as opposed to Ethereum’s [EIP165](https://eips.ethereum.org/EIPS/eip-165). -The design for `SRC5` is similar to OpenZeppelin’s [ERC165Storage](https://docs.openzeppelin.com/contracts/4.xapi/utils#ERC165Storage). -* `IERC721Receiver` compliant contracts return a hardcoded interface ID according to Starknet selectors (as opposed to selector calculation in Solidity). - -## Token transfers - -This library includes [transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-transfer_from) and [safe_transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-safe_transfer_from) to transfer NFTs. -If using `transfer_from`, **the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.** -The `safe_transfer_from` method mitigates this risk by querying the recipient contract’s interface support. - - -Usage of `safe_transfer_from` prevents loss, though the caller must understand this adds an external call which potentially creates a reentrancy vulnerability. - - -## Receiving tokens - -In order to be sure a non-account contract can safely accept ERC721 tokens, said contract must implement the `IERC721Receiver` interface. -The recipient contract must also implement the [SRC5](./introspection#src5) interface which, as described earlier, supports interface introspection. - -### IERC721Receiver - -```rust -#[starknet::interface] -pub trait IERC721Receiver { - fn on_erc721_received( - operator: ContractAddress, - from: ContractAddress, - token_id: u256, - data: Span - ) -> felt252; -} -``` - -Implementing the `IERC721Receiver` interface exposes the [on_erc721_received](/contracts-cairo/alpha/api/erc721#IERC721Receiver-on_erc721_received) method. -When safe methods such as [safe_transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-safe_transfer_from) and [safe_mint](/contracts-cairo/alpha/api/erc721#ERC721-safe_mint) are called, they invoke the recipient contract’s `on_erc721_received` method which **must** return the [IERC721Receiver interface ID](/contracts-cairo/alpha/api/erc721#IERC721Receiver). -Otherwise, the transaction will fail. - - -For information on how to calculate interface IDs, see [Computing the interface ID](./introspection#computing-the-interface-id). - - -### Creating a token receiver contract - -The Contracts for Cairo `IERC721ReceiverImpl` already returns the correct interface ID for safe token transfers. -To integrate the `IERC721Receiver` interface into a contract, simply include the ABI embed directive to the implementation and add the `initializer` in the contract’s constructor. -Here’s an example of a simple token receiver contract: - -```rust -#[starknet::contract] -mod MyTokenReceiver { - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc721::ERC721ReceiverComponent; - use starknet::ContractAddress; - - component!(path: ERC721ReceiverComponent, storage: erc721_receiver, event: ERC721ReceiverEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // ERC721Receiver Mixin - #[abi(embed_v0)] - impl ERC721ReceiverMixinImpl = ERC721ReceiverComponent::ERC721ReceiverMixinImpl; - impl ERC721ReceiverInternalImpl = ERC721ReceiverComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc721_receiver: ERC721ReceiverComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC721ReceiverEvent: ERC721ReceiverComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.erc721_receiver.initializer(); - } -} -``` - -## Storing ERC721 URIs - -Token URIs were previously stored as single field elements prior to Cairo v0.2.5. -ERC721Component now stores only the base URI as a `ByteArray` and the full token URI is returned as the `ByteArray` concatenation of the base URI and the token ID through the [token_uri](/contracts-cairo/alpha/api/erc721#IERC721Metadata-token_uri) method. -This design mirrors OpenZeppelin’s default [Solidity implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/932fddf69a699a9a80fd2396fd1a2ab91cdda123/contracts/token/ERC721/ERC721.sol#L85-L93) for ERC721. diff --git a/content/contracts-cairo/alpha/finance.mdx b/content/contracts-cairo/alpha/finance.mdx deleted file mode 100644 index 124efeb6..00000000 --- a/content/contracts-cairo/alpha/finance.mdx +++ /dev/null @@ -1,216 +0,0 @@ ---- -title: Finance ---- - -This module includes primitives for financial systems. - -## Vesting component - -The [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) manages the gradual release of ERC-20 tokens to a designated beneficiary based on a predefined vesting schedule. -The implementing contract must implement the [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent), where the contract owner is regarded as the vesting beneficiary. -This structure allows ownership rights of both the contract and the vested tokens to be assigned and transferred. - - -Any assets transferred to this contract will follow the vesting schedule as if they were locked from the beginning of the vesting period. -As a result, if the vesting has already started, a portion of the newly transferred tokens may become immediately releasable. - - - -By setting the duration to 0, it’s possible to configure this contract to behave like an asset timelock that holds tokens -for a beneficiary until a specified date. - - -### Vesting schedule - -The [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait defines the logic for calculating the vested amount based on a given timestamp. This -logic is not part of the [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent), so any contract implementing the [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) must provide its own -implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait. - - -There’s a ready-made implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait available named [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule). -It incorporates a cliff period by returning 0 vested amount until the cliff ends. After the cliff, the vested amount -is calculated as directly proportional to the time elapsed since the beginning of the vesting schedule. - - -### Usage - -The contract must integrate [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) and [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent) as dependencies. The contract’s constructor -should initialize both components. Core vesting parameters, such as `beneficiary`, `start`, `duration` -and `cliff_duration`, are passed as arguments to the constructor and set at the time of deployment. - -The implementing contract must provide an implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait. This can be achieved either by importing -a ready-made [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule) implementation or by defining a custom one. - -Here’s an example of a simple vesting wallet contract with a [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule), where the vested amount -is calculated as being directly proportional to the time elapsed since the start of the vesting period. - -```rust -#[starknet::contract] -mod LinearVestingWallet { - use openzeppelin_access::ownable::OwnableComponent; - use openzeppelin_finance::vesting::{VestingComponent, LinearVestingSchedule}; - use starknet::ContractAddress; - - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - component!(path: VestingComponent, storage: vesting, event: VestingEvent); - - #[abi(embed_v0)] - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; - impl OwnableInternalImpl = OwnableComponent::InternalImpl; - - #[abi(embed_v0)] - impl VestingImpl = VestingComponent::VestingImpl; - impl VestingInternalImpl = VestingComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - ownable: OwnableComponent::Storage, - #[substorage(v0)] - vesting: VestingComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - OwnableEvent: OwnableComponent::Event, - #[flat] - VestingEvent: VestingComponent::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - beneficiary: ContractAddress, - start: u64, - duration: u64, - cliff_duration: u64 - ) { - self.ownable.initializer(beneficiary); - self.vesting.initializer(start, duration, cliff_duration); - } -} -``` - -A vesting schedule will often follow a custom formula. In such cases, the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait is useful. -To support a custom vesting schedule, the contract must provide an implementation of the -[calculate_vested_amount](/contracts-cairo/alpha/api/finance#VestingComponent-calculate_vested_amount) function based on the desired formula. - - -When using a custom [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) implementation, the [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule) must be excluded from the imports. - - - -If there are additional parameters required for calculations, which are stored in the contract’s storage, you can access them using `self.get_contract()`. - - -Here’s an example of a vesting wallet contract with a custom [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) implementation, where tokens -are vested in a number of steps. - -```rust -#[starknet::contract] -mod StepsVestingWallet { - use openzeppelin_access::ownable::OwnableComponent; - use openzeppelin_finance::vesting::VestingComponent::VestingScheduleTrait; - use openzeppelin_finance::vesting::VestingComponent; - use starknet::ContractAddress; - use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - component!(path: VestingComponent, storage: vesting, event: VestingEvent); - - #[abi(embed_v0)] - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; - impl OwnableInternalImpl = OwnableComponent::InternalImpl; - - #[abi(embed_v0)] - impl VestingImpl = VestingComponent::VestingImpl; - impl VestingInternalImpl = VestingComponent::InternalImpl; - - #[storage] - struct Storage { - total_steps: u64, - #[substorage(v0)] - ownable: OwnableComponent::Storage, - #[substorage(v0)] - vesting: VestingComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - OwnableEvent: OwnableComponent::Event, - #[flat] - VestingEvent: VestingComponent::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - total_steps: u64, - beneficiary: ContractAddress, - start: u64, - duration: u64, - cliff: u64, - ) { - self.total_steps.write(total_steps); - self.ownable.initializer(beneficiary); - self.vesting.initializer(start, duration, cliff); - } - - impl VestingSchedule of VestingScheduleTrait { - fn calculate_vested_amount( - self: @VestingComponent::ComponentState, - token: ContractAddress, - total_allocation: u256, - timestamp: u64, - start: u64, - duration: u64, - cliff: u64, - ) -> u256 { - if timestamp < cliff { - 0 - } else if timestamp >= start + duration { - total_allocation - } else { - let total_steps = self.get_contract().total_steps.read(); - let vested_per_step = total_allocation / total_steps.into(); - let step_duration = duration / total_steps; - let current_step = (timestamp - start) / step_duration; - let vested_amount = vested_per_step * current_step.into(); - vested_amount - } - } - } -} -``` - -### Interface - -Here is the full interface of a standard contract implementing the vesting functionality: - -```rust -#[starknet::interface] -pub trait VestingABI { - // IVesting - fn start(self: @TState) -> u64; - fn cliff(self: @TState) -> u64; - fn duration(self: @TState) -> u64; - fn end(self: @TState) -> u64; - fn released(self: @TState, token: ContractAddress) -> u256; - fn releasable(self: @TState, token: ContractAddress) -> u256; - fn vested_amount(self: @TState, token: ContractAddress, timestamp: u64) -> u256; - fn release(ref self: TState, token: ContractAddress) -> u256; - - // IOwnable - fn owner(self: @TState) -> ContractAddress; - fn transfer_ownership(ref self: TState, new_owner: ContractAddress); - fn renounce_ownership(ref self: TState); - - // IOwnableCamelOnly - fn transferOwnership(ref self: TState, newOwner: ContractAddress); - fn renounceOwnership(ref self: TState); -} -``` diff --git a/content/contracts-cairo/alpha/governance/governor.mdx b/content/contracts-cairo/alpha/governance/governor.mdx deleted file mode 100644 index cfad3946..00000000 --- a/content/contracts-cairo/alpha/governance/governor.mdx +++ /dev/null @@ -1,443 +0,0 @@ ---- -title: Governor ---- - -Decentralized protocols are in constant evolution from the moment they are publicly released. Often, -the initial team retains control of this evolution in the first stages, but eventually delegates it -to a community of stakeholders. The process by which this community makes decisions is called -on-chain governance, and it has become a central component of decentralized protocols, fueling -varied decisions such as parameter tweaking, smart contract upgrades, integrations with other -protocols, treasury management, grants, etc. - -This governance protocol is generally implemented in a special-purpose contract called “Governor”. In -OpenZeppelin Contracts for Cairo, we set out to build a modular system of Governor components where different -requirements can be accommodated by implementing specific traits. You will find the most common requirements out of the box, -but writing additional ones is simple, and we will be adding new features as requested by the community in future releases. - -## Usage and setup - -### Token - -The voting power of each account in our governance setup will be determined by an ERC20 or an ERC721 token. The token has -to implement the [VotesComponent](../api/governance#VotesComponent) extension. This extension will keep track of historical balances so that voting power -is retrieved from past snapshots rather than current balance, which is an important protection that prevents double voting. - -If your project already has a live token that does not include Votes and is not upgradeable, you can wrap it in a -governance token by using a wrapper. This will allow token holders to participate in governance by wrapping their tokens 1-to-1. - - -The library currently does not include a wrapper for tokens, but it will be added in a future release. - - - -Currently, the clock mode is fixed to block timestamps, since the Votes component uses the block timestamp to track -checkpoints. We plan to add support for more flexible clock modes in Votes in a future release, allowing to use, for example, -block numbers instead. - - -### Governor - -We will initially build a Governor without a timelock. The core logic is given by the [GovernorComponent](../api/governance#GovernorComponent), but we -still need to choose: - -1) how voting power is determined, - -2) how many votes are needed for quorum, - -3) what options people have when casting a vote and how those votes are counted, and - -4) the execution mechanism that should be used. - -Each of these aspects is customizable by writing your own extensions, -or more easily choosing one from the library. - -***For 1)*** we will use the GovernorVotes extension, which hooks to an [IVotes](../api/governance#IVotes) instance to determine the voting power -of an account based on the token balance they hold when a proposal becomes active. -This module requires the address of the token to be passed as an argument to the initializer. - -***For 2)*** we will use GovernorVotesQuorumFraction. This works together with the [IVotes](../api/governance#IVotes) instance to define the quorum as a -percentage of the total supply at the block when a proposal’s voting power is retrieved. This requires an initializer -parameter to set the percentage besides the votes token address. Most Governors nowadays use 4%. Since the quorum denominator -is 1000 for precision, we initialize the module with a numerator of 40, resulting in a 4% quorum (40/1000 = 0.04 or 4%). - -***For 3)*** we will use GovernorCountingSimple, an extension that offers 3 options to voters: For, Against, and Abstain, -and where only For and Abstain votes are counted towards quorum. - -***For 4)*** we will use GovernorCoreExecution, an extension that allows proposal execution directly through the governor. - - -Another option is GovernorTimelockExecution. An example can be found in the next section. - - -Besides these, we also need an implementation for the GovernorSettingsTrait defining the voting delay, voting period, -and proposal threshold. While we can use the GovernorSettings extension which allows to set these parameters by the -governor itself, we will implement the trait locally in the contract and set the voting delay, voting period, -and proposal threshold as constant values. - -__voting_delay__: How long after a proposal is created should voting power be fixed. A large voting delay gives -users time to unstake tokens if necessary. - -__voting_period__: How long does a proposal remain open to votes. - - -These parameters are specified in the unit defined in the token’s clock, which is for now always timestamps. - - -__proposal_threshold__: This restricts proposal creation to accounts who have enough voting power. - -An implementation of `GovernorComponent::ImmutableConfig` is also required. For the example below, we have used -the `DefaultConfig`. Check the immutable-config guide for more details. - -The last missing step is to add an `SNIP12Metadata` implementation used to retrieve the name and version of the governor. - -```rust -#[starknet::contract] -mod MyGovernor { - use openzeppelin_governance::governor::GovernorComponent::InternalTrait as GovernorInternalTrait; - use openzeppelin_governance::governor::extensions::GovernorVotesQuorumFractionComponent::InternalTrait; - use openzeppelin_governance::governor::extensions::{ - GovernorVotesQuorumFractionComponent, GovernorCountingSimpleComponent, - GovernorCoreExecutionComponent, - }; - use openzeppelin_governance::governor::{GovernorComponent, DefaultConfig}; - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; - use starknet::ContractAddress; - - pub const VOTING_DELAY: u64 = 86400; // 1 day - pub const VOTING_PERIOD: u64 = 604800; // 1 week - pub const PROPOSAL_THRESHOLD: u256 = 10; - pub const QUORUM_NUMERATOR: u256 = 40; // 4% - - component!(path: GovernorComponent, storage: governor, event: GovernorEvent); - component!( - path: GovernorVotesQuorumFractionComponent, - storage: governor_votes, - event: GovernorVotesEvent - ); - component!( - path: GovernorCountingSimpleComponent, - storage: governor_counting_simple, - event: GovernorCountingSimpleEvent - ); - component!( - path: GovernorCoreExecutionComponent, - storage: governor_core_execution, - event: GovernorCoreExecutionEvent - ); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // Governor - #[abi(embed_v0)] - impl GovernorImpl = GovernorComponent::GovernorImpl; - - // Extensions external - #[abi(embed_v0)] - impl QuorumFractionImpl = - GovernorVotesQuorumFractionComponent::QuorumFractionImpl; - - // Extensions internal - impl GovernorQuorumImpl = GovernorVotesQuorumFractionComponent::GovernorQuorum; - impl GovernorVotesImpl = GovernorVotesQuorumFractionComponent::GovernorVotes; - impl GovernorCountingSimpleImpl = - GovernorCountingSimpleComponent::GovernorCounting; - impl GovernorCoreExecutionImpl = - GovernorCoreExecutionComponent::GovernorExecution; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - #[storage] - struct Storage { - #[substorage(v0)] - pub governor: GovernorComponent::Storage, - #[substorage(v0)] - pub governor_votes: GovernorVotesQuorumFractionComponent::Storage, - #[substorage(v0)] - pub governor_counting_simple: GovernorCountingSimpleComponent::Storage, - #[substorage(v0)] - pub governor_core_execution: GovernorCoreExecutionComponent::Storage, - #[substorage(v0)] - pub src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - GovernorEvent: GovernorComponent::Event, - #[flat] - GovernorVotesEvent: GovernorVotesQuorumFractionComponent::Event, - #[flat] - GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event, - #[flat] - GovernorCoreExecutionEvent: GovernorCoreExecutionComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - } - - #[constructor] - fn constructor(ref self: ContractState, votes_token: ContractAddress) { - self.governor.initializer(); - self.governor_votes.initializer(votes_token, QUORUM_NUMERATOR); - } - - // - // SNIP12 Metadata - // - - pub impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { - 'DAPP_NAME' - } - - fn version() -> felt252 { - 'DAPP_VERSION' - } - } - - // - // Locally implemented extensions - // - - pub impl GovernorSettings of GovernorComponent::GovernorSettingsTrait { - /// See `GovernorComponent::GovernorSettingsTrait::voting_delay`. - fn voting_delay(self: @GovernorComponent::ComponentState) -> u64 { - VOTING_DELAY - } - - /// See `GovernorComponent::GovernorSettingsTrait::voting_period`. - fn voting_period(self: @GovernorComponent::ComponentState) -> u64 { - VOTING_PERIOD - } - - /// See `GovernorComponent::GovernorSettingsTrait::proposal_threshold`. - fn proposal_threshold(self: @GovernorComponent::ComponentState) -> u256 { - PROPOSAL_THRESHOLD - } - } -} -``` - -### Timelock - -It is good practice to add a timelock to governance decisions. This allows users to exit the system if they disagree -with a decision before it is executed. We will use OpenZeppelin’s [TimelockController](#timelock) in combination with the -GovernorTimelockExecution extension. - - -When using a timelock, it is the timelock that will execute proposals and thus the timelock that should -hold any funds, ownership, and access control roles. - - -TimelockController uses an [AccessControl](../access#role-based-accesscontrol) setup that we need to understand in order to set up roles. - -The Proposer role is in charge of queueing operations: this is the role the Governor instance must be granted, -and it MUST be the only proposer (and canceller) in the system. - -The Executor role is in charge of executing already available operations: we can assign this role to the special -zero address to allow anyone to execute (if operations can be particularly time sensitive, the Governor should be made Executor instead). - -The Canceller role is in charge of canceling operations: the Governor instance must be granted this role, -and it MUST be the only canceller in the system. - -Lastly, there is the Admin role, which can grant and revoke the two previous roles: this is a very sensitive role that will be granted automatically to the timelock itself, and optionally to a second account, which can be used for ease of setup but should promptly renounce the role. - -The following example uses the GovernorTimelockExecution extension, together with GovernorSettings, and uses a -fixed quorum value instead of a percentage: - -```rust -#[starknet::contract] -pub mod MyTimelockedGovernor { - use openzeppelin_governance::governor::GovernorComponent::InternalTrait as GovernorInternalTrait; - use openzeppelin_governance::governor::extensions::GovernorSettingsComponent::InternalTrait as GovernorSettingsInternalTrait; - use openzeppelin_governance::governor::extensions::GovernorTimelockExecutionComponent::InternalTrait as GovernorTimelockExecutionInternalTrait; - use openzeppelin_governance::governor::extensions::GovernorVotesComponent::InternalTrait as GovernorVotesInternalTrait; - use openzeppelin_governance::governor::extensions::{ - GovernorVotesComponent, GovernorSettingsComponent, GovernorCountingSimpleComponent, - GovernorTimelockExecutionComponent - }; - use openzeppelin_governance::governor::{GovernorComponent, DefaultConfig}; - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; - use starknet::ContractAddress; - - pub const VOTING_DELAY: u64 = 86400; // 1 day - pub const VOTING_PERIOD: u64 = 604800; // 1 week - pub const PROPOSAL_THRESHOLD: u256 = 10; - pub const QUORUM: u256 = 100_000_000; - - component!(path: GovernorComponent, storage: governor, event: GovernorEvent); - component!(path: GovernorVotesComponent, storage: governor_votes, event: GovernorVotesEvent); - component!( - path: GovernorSettingsComponent, storage: governor_settings, event: GovernorSettingsEvent - ); - component!( - path: GovernorCountingSimpleComponent, - storage: governor_counting_simple, - event: GovernorCountingSimpleEvent - ); - component!( - path: GovernorTimelockExecutionComponent, - storage: governor_timelock_execution, - event: GovernorTimelockExecutionEvent - ); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // Governor - #[abi(embed_v0)] - impl GovernorImpl = GovernorComponent::GovernorImpl; - - // Extensions external - #[abi(embed_v0)] - impl VotesTokenImpl = GovernorVotesComponent::VotesTokenImpl; - #[abi(embed_v0)] - impl GovernorSettingsAdminImpl = - GovernorSettingsComponent::GovernorSettingsAdminImpl; - #[abi(embed_v0)] - impl TimelockedImpl = - GovernorTimelockExecutionComponent::TimelockedImpl; - - // Extensions internal - impl GovernorVotesImpl = GovernorVotesComponent::GovernorVotes; - impl GovernorSettingsImpl = GovernorSettingsComponent::GovernorSettings; - impl GovernorCountingSimpleImpl = - GovernorCountingSimpleComponent::GovernorCounting; - impl GovernorTimelockExecutionImpl = - GovernorTimelockExecutionComponent::GovernorExecution; - - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - #[storage] - struct Storage { - #[substorage(v0)] - pub governor: GovernorComponent::Storage, - #[substorage(v0)] - pub governor_votes: GovernorVotesComponent::Storage, - #[substorage(v0)] - pub governor_settings: GovernorSettingsComponent::Storage, - #[substorage(v0)] - pub governor_counting_simple: GovernorCountingSimpleComponent::Storage, - #[substorage(v0)] - pub governor_timelock_execution: GovernorTimelockExecutionComponent::Storage, - #[substorage(v0)] - pub src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - GovernorEvent: GovernorComponent::Event, - #[flat] - GovernorVotesEvent: GovernorVotesComponent::Event, - #[flat] - GovernorSettingsEvent: GovernorSettingsComponent::Event, - #[flat] - GovernorCountingSimpleEvent: GovernorCountingSimpleComponent::Event, - #[flat] - GovernorTimelockExecutionEvent: GovernorTimelockExecutionComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - } - - #[constructor] - fn constructor( - ref self: ContractState, votes_token: ContractAddress, timelock_controller: ContractAddress - ) { - self.governor.initializer(); - self.governor_votes.initializer(votes_token); - self.governor_settings.initializer(VOTING_DELAY, VOTING_PERIOD, PROPOSAL_THRESHOLD); - self.governor_timelock_execution.initializer(timelock_controller); - } - - // - // SNIP12 Metadata - // - - pub impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { - 'DAPP_NAME' - } - - fn version() -> felt252 { - 'DAPP_VERSION' - } - } - - // - // Locally implemented extensions - // - - impl GovernorQuorum of GovernorComponent::GovernorQuorumTrait { - /// See `GovernorComponent::GovernorQuorumTrait::quorum`. - fn quorum(self: @GovernorComponent::ComponentState, timepoint: u64) -> u256 { - QUORUM - } - } -} -``` - -## Interface - -This is the full interface of the `Governor` implementation: -```rust -#[starknet::interface] -pub trait IGovernor { - fn name(self: @TState) -> felt252; - fn version(self: @TState) -> felt252; - fn COUNTING_MODE(self: @TState) -> ByteArray; - fn hash_proposal(self: @TState, calls: Span, description_hash: felt252) -> felt252; - fn state(self: @TState, proposal_id: felt252) -> ProposalState; - fn proposal_threshold(self: @TState) -> u256; - fn proposal_snapshot(self: @TState, proposal_id: felt252) -> u64; - fn proposal_deadline(self: @TState, proposal_id: felt252) -> u64; - fn proposal_proposer(self: @TState, proposal_id: felt252) -> ContractAddress; - fn proposal_eta(self: @TState, proposal_id: felt252) -> u64; - fn proposal_needs_queuing(self: @TState, proposal_id: felt252) -> bool; - fn voting_delay(self: @TState) -> u64; - fn voting_period(self: @TState) -> u64; - fn quorum(self: @TState, timepoint: u64) -> u256; - fn get_votes(self: @TState, account: ContractAddress, timepoint: u64) -> u256; - fn get_votes_with_params( - self: @TState, account: ContractAddress, timepoint: u64, params: Span - ) -> u256; - fn has_voted(self: @TState, proposal_id: felt252, account: ContractAddress) -> bool; - fn propose(ref self: TState, calls: Span, description: ByteArray) -> felt252; - fn queue(ref self: TState, calls: Span, description_hash: felt252) -> felt252; - fn execute(ref self: TState, calls: Span, description_hash: felt252) -> felt252; - fn cancel(ref self: TState, calls: Span, description_hash: felt252) -> felt252; - fn cast_vote(ref self: TState, proposal_id: felt252, support: u8) -> u256; - fn cast_vote_with_reason( - ref self: TState, proposal_id: felt252, support: u8, reason: ByteArray - ) -> u256; - fn cast_vote_with_reason_and_params( - ref self: TState, - proposal_id: felt252, - support: u8, - reason: ByteArray, - params: Span - ) -> u256; - fn cast_vote_by_sig( - ref self: TState, - proposal_id: felt252, - support: u8, - voter: ContractAddress, - signature: Span - ) -> u256; - fn cast_vote_with_reason_and_params_by_sig( - ref self: TState, - proposal_id: felt252, - support: u8, - voter: ContractAddress, - reason: ByteArray, - params: Span, - signature: Span - ) -> u256; - fn nonces(self: @TState, voter: ContractAddress) -> felt252; - fn relay(ref self: TState, call: Call); -} -``` diff --git a/content/contracts-cairo/alpha/governance/multisig.mdx b/content/contracts-cairo/alpha/governance/multisig.mdx deleted file mode 100644 index c98fdf9d..00000000 --- a/content/contracts-cairo/alpha/governance/multisig.mdx +++ /dev/null @@ -1,150 +0,0 @@ ---- -title: Multisig ---- - -The Multisig component implements a multi-signature mechanism to enhance the security and -governance of smart contract transactions. It ensures that no single signer can unilaterally -execute critical actions, requiring multiple registered signers to approve and collectively -execute transactions. - -This component is designed to secure operations such as fund management or protocol governance, -where collective decision-making is essential. The Multisig Component is self-administered, -meaning that changes to signers or quorum must be approved through the multisig process itself. - -## Key features - -* **Multi-Signature Security**: transactions must be approved by multiple signers, ensuring -distributed governance. -* **Quorum Enforcement**: defines the minimum number of approvals required for transaction execution. -* **Self-Administration**: all modifications to the component (e.g., adding or removing signers) -must pass through the multisig process. -* **Event Logging**: provides comprehensive event logging for transparency and auditability. - -## Signer management - -The Multisig component introduces the concept of signers and quorum: - -* **Signers**: only registered signers can submit, confirm, revoke, or execute transactions. The Multisig -Component supports adding, removing, or replacing signers. -* **Quorum**: the quorum defines the minimum number of confirmations required to approve a transaction. - - -To prevent unauthorized modifications, only the contract itself can add, remove, or replace signers or change the quorum. -This ensures that all modifications pass through the multisig approval process. - - -## Transaction lifecycle - -The state of a transaction is represented by the `TransactionState` enum and can be retrieved -by calling the `get_transaction_state` function with the transaction’s identifier. - -The identifier of a multisig transaction is a `felt252` value, computed as the Pedersen hash -of the transaction’s calls and salt. It can be computed by invoking the implementing contract’s -`hash_transaction` method for single-call transactions or `hash_transaction_batch` for multi-call -transactions. Submitting a transaction with identical calls and the same salt value a second time -will fail, as transaction identifiers must be unique. To resolve this, use a different salt value -to generate a unique identifier. - -A transaction in the Multisig component follows a specific lifecycle: - -`NotFound` → `Pending` → `Confirmed` → `Executed` - -* **NotFound**: the transaction does not exist. -* **Pending**: the transaction exists but has not reached the required confirmations. -* **Confirmed**: the transaction has reached the quorum but has not yet been executed. -* **Executed**: the transaction has been successfully executed. - -## Usage - -Integrating the Multisig functionality into a contract requires implementing [MultisigComponent](../api/governance#MultisigComponent). -The contract’s constructor should initialize the component with a quorum value and a list of initial signers. - -Here’s an example of a simple wallet contract featuring the Multisig functionality: - -```rust -#[starknet::contract] -mod MultisigWallet { - use openzeppelin_governance::multisig::MultisigComponent; - use starknet::ContractAddress; - - component!(path: MultisigComponent, storage: multisig, event: MultisigEvent); - - #[abi(embed_v0)] - impl MultisigImpl = MultisigComponent::MultisigImpl; - impl MultisigInternalImpl = MultisigComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - multisig: MultisigComponent::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - MultisigEvent: MultisigComponent::Event, - } - - #[constructor] - fn constructor(ref self: ContractState, quorum: u32, signers: Span) { - self.multisig.initializer(quorum, signers); - } -} -``` - -## Interface - -This is the interface of a contract implementing the [MultisigComponent](../api/governance#MultisigComponent): - -```rust -#[starknet::interface] -pub trait MultisigABI { - // Read functions - fn get_quorum(self: @TState) -> u32; - fn is_signer(self: @TState, signer: ContractAddress) -> bool; - fn get_signers(self: @TState) -> Span; - fn is_confirmed(self: @TState, id: TransactionID) -> bool; - fn is_confirmed_by(self: @TState, id: TransactionID, signer: ContractAddress) -> bool; - fn is_executed(self: @TState, id: TransactionID) -> bool; - fn get_submitted_block(self: @TState, id: TransactionID) -> u64; - fn get_transaction_state(self: @TState, id: TransactionID) -> TransactionState; - fn get_transaction_confirmations(self: @TState, id: TransactionID) -> u32; - fn hash_transaction( - self: @TState, - to: ContractAddress, - selector: felt252, - calldata: Span, - salt: felt252, - ) -> TransactionID; - fn hash_transaction_batch(self: @TState, calls: Span, salt: felt252) -> TransactionID; - - // Write functions - fn add_signers(ref self: TState, new_quorum: u32, signers_to_add: Span); - fn remove_signers(ref self: TState, new_quorum: u32, signers_to_remove: Span); - fn replace_signer( - ref self: TState, signer_to_remove: ContractAddress, signer_to_add: ContractAddress, - ); - fn change_quorum(ref self: TState, new_quorum: u32); - fn submit_transaction( - ref self: TState, - to: ContractAddress, - selector: felt252, - calldata: Span, - salt: felt252, - ) -> TransactionID; - fn submit_transaction_batch( - ref self: TState, calls: Span, salt: felt252, - ) -> TransactionID; - fn confirm_transaction(ref self: TState, id: TransactionID); - fn revoke_confirmation(ref self: TState, id: TransactionID); - fn execute_transaction( - ref self: TState, - to: ContractAddress, - selector: felt252, - calldata: Span, - salt: felt252, - ); - fn execute_transaction_batch(ref self: TState, calls: Span, salt: felt252); -} -``` diff --git a/content/contracts-cairo/alpha/governance/timelock.mdx b/content/contracts-cairo/alpha/governance/timelock.mdx deleted file mode 100644 index d3404511..00000000 --- a/content/contracts-cairo/alpha/governance/timelock.mdx +++ /dev/null @@ -1,198 +0,0 @@ ---- -title: Timelock Controller ---- - -The Timelock Controller provides a means of enforcing time delays on the execution of transactions. This is considered good practice regarding governance systems because it allows users the opportunity to exit the system if they disagree with a decision before it is executed. - - -The Timelock contract itself executes transactions, not the user. The Timelock should, therefore, hold associated funds, ownership, and access control roles. - - -## Operation lifecycle - -The state of an operation is represented by the `OperationState` enum and can be retrieved -by calling the `get_operation_state` function with the operation’s identifier. - -The identifier of an operation is a `felt252` value, computed as the Pedersen hash of the -operation’s call or calls, its predecessor, and salt. It can be computed by invoking the -implementing contract’s `hash_operation` function for single-call operations or -`hash_operation_batch` for multi-call operations. Submitting an operation with identical calls, -predecessor, and the same salt value a second time will fail, as operation identifiers must be -unique. To resolve this, use a different salt value to generate a unique identifier. - -Timelocked operations follow a specific lifecycle: - -`Unset` → `Waiting` → `Ready` → `Done` - -* `Unset`: the operation has not been scheduled or has been canceled. -* `Waiting`: the operation has been scheduled and is pending the scheduled delay. -* `Ready`: the timer has expired, and the operation is eligible for execution. -* `Done`: the operation has been executed. - -## Timelock flow - -### Schedule - -When a proposer calls [schedule](../api/governance#ITimelock-schedule), the `OperationState` moves from `Unset` to `Waiting`. -This starts a timer that must be greater than or equal to the minimum delay. -The timer expires at a timestamp accessible through [get_timestamp](../api/governance#ITimelock-get_timestamp). -Once the timer expires, the `OperationState` automatically moves to the `Ready` state. -At this point, it can be executed. - -### Execute - -By calling [execute](../api/governance#ITimelock-execute), an executor triggers the operation’s underlying transactions and moves it to the `Done` state. If the operation has a predecessor, the predecessor’s operation must be in the `Done` state for this transaction to succeed. - -### Cancel - -The [cancel](../api/governance#ITimelock-cancel) function allows cancellers to cancel any pending operations. -This resets the operation to the `Unset` state. -It is therefore possible for a proposer to re-schedule an operation that has been cancelled. -In this case, the timer restarts when the operation is re-scheduled. - -## Roles - -[TimelockControllerComponent](../api/governance#TimelockControllerComponent) leverages an [AccessControlComponent](../api/access#AccessControlComponent) setup that we need to understand in order to set up roles. - -* `PROPOSER_ROLE` - in charge of queueing operations. -* `CANCELLER_ROLE` - may cancel scheduled operations. -During initialization, accounts granted with `PROPOSER_ROLE` will also be granted `CANCELLER_ROLE`. -Therefore, the initial proposers may also cancel operations after they are scheduled. -* `EXECUTOR_ROLE` - in charge of executing already available operations. -* `DEFAULT_ADMIN_ROLE` - can grant and revoke the three previous roles. - - -The `DEFAULT_ADMIN_ROLE` is a sensitive role that will be granted automatically to the timelock itself and optionally to a second account. -The latter case may be required to ease a contract’s initial configuration; however, this role should promptly be renounced. - - -Furthermore, the timelock component supports the concept of open roles for the `EXECUTOR_ROLE`. -This allows anyone to execute an operation once it’s in the `Ready` OperationState. -To enable the `EXECUTOR_ROLE` to be open, grant the zero address with the `EXECUTOR_ROLE`. - - -Be very careful with enabling open roles as _anyone_ can call the function. - - -## Minimum delay - -The minimum delay of the timelock acts as a buffer from when a proposer schedules an operation to the earliest point at which an executor may execute that operation. -The idea is for users, should they disagree with a scheduled proposal, to have options such as exiting the system or making their case for cancellers to cancel the operation. - -After initialization, the only way to change the timelock’s minimum delay is to schedule it and execute it with the same flow as any other operation. - -The minimum delay of a contract is accessible through [get_min_delay](../api/governance#ITimelock-get_min_delay). - -## Usage - -Integrating the timelock into a contract requires integrating [TimelockControllerComponent](../api/governance#TimelockControllerComponent) as well as [SRC5Component](../api/introspection#SRC5Component) and [AccessControlComponent](../api/access#AccessControlComponent) as dependencies. -The contract’s constructor should initialize the timelock which consists of setting the: - -* Proposers and executors. -* Minimum delay between scheduling and executing an operation. -* Optional admin if additional configuration is required. - - -The optional admin should renounce their role once configuration is complete. - - -Here’s an example of a simple timelock contract: - -```rust -#[starknet::contract] -mod TimelockControllerContract { - use openzeppelin_access::accesscontrol::AccessControlComponent; - use openzeppelin_governance::timelock::TimelockControllerComponent; - use openzeppelin_introspection::src5::SRC5Component; - use starknet::ContractAddress; - - component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent); - component!(path: TimelockControllerComponent, storage: timelock, event: TimelockEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // Timelock Mixin - #[abi(embed_v0)] - impl TimelockMixinImpl = - TimelockControllerComponent::TimelockMixinImpl; - impl TimelockInternalImpl = TimelockControllerComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - access_control: AccessControlComponent::Storage, - #[substorage(v0)] - timelock: TimelockControllerComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccessControlEvent: AccessControlComponent::Event, - #[flat] - TimelockEvent: TimelockControllerComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - min_delay: u64, - proposers: Span, - executors: Span, - admin: ContractAddress - ) { - self.timelock.initializer(min_delay, proposers, executors, admin); - } -} -``` - -## Interface - -This is the full interface of the TimelockMixinImpl implementation: - -```rust -#[starknet::interface] -pub trait TimelockABI { - // ITimelock - fn is_operation(self: @TState, id: felt252) -> bool; - fn is_operation_pending(self: @TState, id: felt252) -> bool; - fn is_operation_ready(self: @TState, id: felt252) -> bool; - fn is_operation_done(self: @TState, id: felt252) -> bool; - fn get_timestamp(self: @TState, id: felt252) -> u64; - fn get_operation_state(self: @TState, id: felt252) -> OperationState; - fn get_min_delay(self: @TState) -> u64; - fn hash_operation(self: @TState, call: Call, predecessor: felt252, salt: felt252) -> felt252; - fn hash_operation_batch( - self: @TState, calls: Span, predecessor: felt252, salt: felt252 - ) -> felt252; - fn schedule(ref self: TState, call: Call, predecessor: felt252, salt: felt252, delay: u64); - fn schedule_batch( - ref self: TState, calls: Span, predecessor: felt252, salt: felt252, delay: u64 - ); - fn cancel(ref self: TState, id: felt252); - fn execute(ref self: TState, call: Call, predecessor: felt252, salt: felt252); - fn execute_batch(ref self: TState, calls: Span, predecessor: felt252, salt: felt252); - fn update_delay(ref self: TState, new_delay: u64); - - // ISRC5 - fn supports_interface(self: @TState, interface_id: felt252) -> bool; - - // IAccessControl - fn has_role(self: @TState, role: felt252, account: ContractAddress) -> bool; - fn get_role_admin(self: @TState, role: felt252) -> felt252; - fn grant_role(ref self: TState, role: felt252, account: ContractAddress); - fn revoke_role(ref self: TState, role: felt252, account: ContractAddress); - fn renounce_role(ref self: TState, role: felt252, account: ContractAddress); - - // IAccessControlCamel - fn hasRole(self: @TState, role: felt252, account: ContractAddress) -> bool; - fn getRoleAdmin(self: @TState, role: felt252) -> felt252; - fn grantRole(ref self: TState, role: felt252, account: ContractAddress); - fn revokeRole(ref self: TState, role: felt252, account: ContractAddress); - fn renounceRole(ref self: TState, role: felt252, account: ContractAddress); -} -``` diff --git a/content/contracts-cairo/alpha/governance/votes.mdx b/content/contracts-cairo/alpha/governance/votes.mdx deleted file mode 100644 index 44d9e731..00000000 --- a/content/contracts-cairo/alpha/governance/votes.mdx +++ /dev/null @@ -1,222 +0,0 @@ ---- -title: Votes ---- - -The [VotesComponent](../api/governance#VotesComponent) provides a flexible system for tracking and delegating voting power. This system allows users to delegate their voting power to other addresses, enabling more active participation in governance. - - -By default, token balance does not account for voting power. This makes transfers cheaper. The downside is that it requires users to delegate to themselves in order to activate checkpoints and have their voting power tracked. - - - -The transferring of voting units must be handled by the implementing contract. In the case of `ERC20` and `ERC721` this is usually done via the hooks. You can check the [usage](#usage) section for examples of how to implement this. - - -## Key features - -1. **Delegation**: Users can delegate their voting power to any address, including themselves. Vote power can be delegated either by calling the [delegate](../api/governance#VotesComponent-delegate) function directly, or by providing a signature to be used with [delegate_by_sig](../api/governance#VotesComponent-delegate_by_sig). -2. **Historical lookups**: The system keeps track of historical snapshots for each account, which allows the voting power of an account to be queried at a specific timestamp.\ -This can be used for example to determine the voting power of an account when a proposal was created, rather than using the current balance. - -## Usage - -When integrating the `VotesComponent`, the [VotingUnitsTrait](../api/governance#VotingUnitsTrait) must be implemented to get the voting units for a given account as a function of the implementing contract.\ -For simplicity, this module already provides two implementations for `ERC20` and `ERC721` tokens, which will work out of the box if the respective components are integrated.\ -Additionally, you must implement the [NoncesComponent](../api/utilities#NoncesComponent) and the [SNIP12Metadata](../api/utilities#snip12) trait to enable delegation by signatures. - -Here’s an example of how to structure a simple ERC20Votes contract: - -```rust -#[starknet::contract] -mod ERC20VotesContract { - use openzeppelin_governance::votes::VotesComponent; - use openzeppelin_token::erc20::{ERC20Component, DefaultConfig}; - use openzeppelin_utils::cryptography::nonces::NoncesComponent; - use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; - use starknet::ContractAddress; - - component!(path: VotesComponent, storage: erc20_votes, event: ERC20VotesEvent); - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - component!(path: NoncesComponent, storage: nonces, event: NoncesEvent); - - // Votes - #[abi(embed_v0)] - impl VotesImpl = VotesComponent::VotesImpl; - impl VotesInternalImpl = VotesComponent::InternalImpl; - - // ERC20 - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - // Nonces - #[abi(embed_v0)] - impl NoncesImpl = NoncesComponent::NoncesImpl; - - #[storage] - pub struct Storage { - #[substorage(v0)] - pub erc20_votes: VotesComponent::Storage, - #[substorage(v0)] - pub erc20: ERC20Component::Storage, - #[substorage(v0)] - pub nonces: NoncesComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC20VotesEvent: VotesComponent::Event, - #[flat] - ERC20Event: ERC20Component::Event, - #[flat] - NoncesEvent: NoncesComponent::Event - } - - // Required for hash computation. - pub impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { - 'DAPP_NAME' - } - fn version() -> felt252 { - 'DAPP_VERSION' - } - } - - // We need to call the `transfer_voting_units` function after - // every mint, burn and transfer. - // For this, we use the `after_update` hook of the `ERC20Component::ERC20HooksTrait`. - impl ERC20VotesHooksImpl of ERC20Component::ERC20HooksTrait { - fn after_update( - ref self: ERC20Component::ComponentState, - from: ContractAddress, - recipient: ContractAddress, - amount: u256 - ) { - let mut contract_state = self.get_contract_mut(); - contract_state.erc20_votes.transfer_voting_units(from, recipient, amount); - } - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.erc20.initializer("MyToken", "MTK"); - } -} -``` - -And here’s an example of how to structure a simple ERC721Votes contract: - -```rust -#[starknet::contract] -pub mod ERC721VotesContract { - use openzeppelin_governance::votes::VotesComponent; - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_token::erc721::ERC721Component; - use openzeppelin_utils::cryptography::nonces::NoncesComponent; - use openzeppelin_utils::cryptography::snip12::SNIP12Metadata; - use starknet::ContractAddress; - - component!(path: VotesComponent, storage: erc721_votes, event: ERC721VotesEvent); - component!(path: ERC721Component, storage: erc721, event: ERC721Event); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: NoncesComponent, storage: nonces, event: NoncesEvent); - - // Votes - #[abi(embed_v0)] - impl VotesImpl = VotesComponent::VotesImpl; - impl VotesInternalImpl = VotesComponent::InternalImpl; - - // ERC721 - #[abi(embed_v0)] - impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; - impl ERC721InternalImpl = ERC721Component::InternalImpl; - - // Nonces - #[abi(embed_v0)] - impl NoncesImpl = NoncesComponent::NoncesImpl; - - #[storage] - pub struct Storage { - #[substorage(v0)] - pub erc721_votes: VotesComponent::Storage, - #[substorage(v0)] - pub erc721: ERC721Component::Storage, - #[substorage(v0)] - pub src5: SRC5Component::Storage, - #[substorage(v0)] - pub nonces: NoncesComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC721VotesEvent: VotesComponent::Event, - #[flat] - ERC721Event: ERC721Component::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - NoncesEvent: NoncesComponent::Event - } - - /// Required for hash computation. - pub impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { - 'DAPP_NAME' - } - fn version() -> felt252 { - 'DAPP_VERSION' - } - } - - // We need to call the `transfer_voting_units` function after - // every mint, burn and transfer. - // For this, we use the `before_update` hook of the - //`ERC721Component::ERC721HooksTrait`. - // This hook is called before the transfer is executed. - // This gives us access to the previous owner. - impl ERC721VotesHooksImpl of ERC721Component::ERC721HooksTrait { - fn before_update( - ref self: ERC721Component::ComponentState, - to: ContractAddress, - token_id: u256, - auth: ContractAddress - ) { - let mut contract_state = self.get_contract_mut(); - - // We use the internal function here since it does not check if the token - // id exists which is necessary for mints - let previous_owner = self._owner_of(token_id); - contract_state.erc721_votes.transfer_voting_units(previous_owner, to, 1); - } - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.erc721.initializer("MyToken", "MTK", ""); - } -} -``` - -## Interface - -This is the full interface of the `VotesImpl` implementation: - -```rust -#[starknet::interface] -pub trait VotesABI { - // IVotes - fn get_votes(self: @TState, account: ContractAddress) -> u256; - fn get_past_votes(self: @TState, account: ContractAddress, timepoint: u64) -> u256; - fn get_past_total_supply(self: @TState, timepoint: u64) -> u256; - fn delegates(self: @TState, account: ContractAddress) -> ContractAddress; - fn delegate(ref self: TState, delegatee: ContractAddress); - fn delegate_by_sig(ref self: TState, delegator: ContractAddress, delegatee: ContractAddress, nonce: felt252, expiry: u64, signature: Span); - - // INonces - fn nonces(self: @TState, owner: ContractAddress) -> felt252; -} -``` diff --git a/content/contracts-cairo/alpha/guides/deploy-udc.mdx b/content/contracts-cairo/alpha/guides/deploy-udc.mdx deleted file mode 100644 index 5e20ac1e..00000000 --- a/content/contracts-cairo/alpha/guides/deploy-udc.mdx +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: UDC Appchain Deployment ---- - -While the Universal Deployer Contract (UDC) is deployed on Starknet public networks, appchains may need to deploy -their own instance of the UDC for their own use. This guide will walk you through this process while keeping the -same final address on all networks. - -## Prerequisites - -This guide assumes you have: - -* Familiarity with [Scarb](https://docs.swmansion.com/scarb/docs.html) and Starknet development environment. -* A functional account available on the network you’re deploying to. -* Familiarity with the process of declaring contracts through the [declare transaction](https://docs.starknet.io/resources/transactions-reference/#declare_transaction). - - -For declaring contracts on Starknet, you can use the [sncast](https://foundry-rs.github.io/starknet-foundry/starknet/declare.html) tool from the [starknet-foundry](https://foundry-rs.github.io/starknet-foundry/index.html) project. - - -## Note on the UDC final address - -It is important that the Universal Deployer Contract (UDC) in Starknet maintains the same address across all -networks because essential developer tools like **starkli** and **sncast** rely on this address by default when deploying contracts. -These tools are widely used in the Starknet ecosystem to streamline and standardize contract deployment workflows. - -If the UDC address is consistent, developers can write deployment scripts, CI/CD pipelines, and integrations that work seamlessly -across testnets, mainnet, and appchains without needing to update configuration files or handle special cases for each -environment. - -In the following sections, we’ll walk you through the process of deploying the UDC on appchains while keeping the same address, -under one important assumption: **the declared UDC class hash MUST be the same across all networks**. -Different compiler versions may produce different class hashes for the same contract, so you need to make -sure you are using the same compiler version to build the UDC class (and the release profile). - -The latest version of the UDC available in the `openzeppelin_presets` package was compiled with **Cairo v2.11.4** (release profile) and the resulting class hash is `0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8`. - - -If you are using a different compiler version, you need to make sure the class hash is the same as the one above in order to keep the same address across all networks. - - - -To avoid potential issues by using a different compiler version, you can directly import the contract class deployed on Starknet mainnet and declare it on your appchain. At -the time of writing, this is not easily achievable with the `sncast` tool, but you can leverage `[starkli](https://book.starkli.rs/declaring-classes)` to do it. - -Quick reference: - -```bash -starkli class-by-hash --parse \ - 0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8 \ - --network mainnet \ - > udc.json -``` - -This will output a `udc.json` file that you can use to declare the UDC on your appchain. - -```bash -starkli declare udc.json --rpc -``` - - - -## Madara Appchains - -[Madara](https://github.com/madara-alliance/madara/blob/main/README.md) is a popular Starknet node implementation that has a friendly and robust interface for building appchains. If -you are using it for this purpose, you are probably familiar with the [Madara Bootstrapper](https://github.com/madara-alliance/madara/tree/main/bootstrapper#readme), which already declares and -deploys a few contracts for you when you create a new appchain, including accounts and the UDC. - -However, since the UDC was migrated to a new version in June 2025, it’s possible that the appchain was created before -this change, meaning the UDC on the appchain is an older version. If that’s the case, you can follow the steps below to -deploy the new UDC. - -### 1. Declare and deploy the Bootstrapper - -In the Starknet ecosystem, contracts need to be declared before they can be deployed, and deployments can only happen -either via the `deploy_syscall`, or using a `deploy_account` transaction. The latter would require adding account -functionality to the UDC, which is not optimal, so we’ll use the `deploy_syscall`, which requires having an account -with this functionality enabled. - - -Madara declares an account with this functionality enabled as part of the bootstrapping process. You may be able to -use that implementation directly to skip this step. - - -#### Bootstrapper Contract - -The bootstrapper contract is a simple contract that declares the UDC and allows for its deployment via the `deploy_syscall`. -You can find a reference implementation below: - - -This reference implementation targets Cairo v2.11.4. If you are using a different version of Cairo, you may need to update the code to match your compiler version. - - -```rust -#[starknet::contract(account)] -mod UniversalDeployerBootstrapper { - use core::num::traits::Zero; - use openzeppelin_account::AccountComponent; - use openzeppelin_introspection::src5::SRC5Component; - use openzeppelin_utils::deployments::calculate_contract_address_from_deploy_syscall; - use starknet::{ClassHash, ContractAddress, SyscallResultTrait}; - - component!(path: AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - // - // Account features (deployable, declarer, and invoker) - // - - #[abi(embed_v0)] - pub(crate) impl DeployableImpl = - AccountComponent::DeployableImpl; - #[abi(embed_v0)] - impl DeclarerImpl = AccountComponent::DeclarerImpl; - #[abi(embed_v0)] - impl SRC6Impl = AccountComponent::SRC6Impl; - impl AccountInternalImpl = AccountComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - pub account: AccountComponent::Storage, - #[substorage(v0)] - pub src5: SRC5Component::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - pub(crate) enum Event { - #[flat] - AccountEvent: AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - } - - #[constructor] - pub fn constructor(ref self: ContractState, public_key: felt252) { - self.account.initializer(public_key); - } - - #[abi(per_item)] - #[generate_trait] - impl ExternalImpl of ExternalTrait { - #[external(v0)] - fn deploy_udc(ref self: ContractState, udc_class_hash: ClassHash) { - self.account.assert_only_self(); - starknet::syscalls::deploy_syscall(udc_class_hash, 0, array![].span(), true) - .unwrap_syscall(); - } - - #[external(v0)] - fn get_udc_address(ref self: ContractState, udc_class_hash: ClassHash) -> ContractAddress { - calculate_contract_address_from_deploy_syscall( - 0, udc_class_hash, array![].span(), Zero::zero(), - ) - } - -} -``` - -#### Deploying the Bootstrapper - -This guide assumes you have a functional account available on the network you’re deploying to, and familiarity -with the process of declaring contracts through the `declare` transaction. To recap, the reason we are deploying -this bootstrapper account contract is to be able to deploy the UDC via the `deploy_syscall`. - - -sncast v0.45.0 was used in the examples below. - - -As a quick example, if your account is configured for **sncast**, you can declare the bootstrapper contract with the following command: - -```bash -sncast -p declare \ - --contract-name UniversalDeployerBootstrapper -``` - -The bootstrapper implements the `IDeployable` trait, meaning it can be counterfactually deployed. Check out the -[Counterfactual Deployments](./deployment) guide. Continuing with the **sncast** examples, you can create and deploy the bootstrapper with the following commands: - -##### Create the account - -```bash -sncast account create --name bootstrapper \ - --network \ - --class-hash \ - --type oz -``` - -##### Deploy it to the network - - -You need to prefund the account with enough funds before you can deploy it. - - -```bash -sncast account deploy \ - --network \ - --name bootstrapper -``` - -### 2. Declare and deploy the UDC - -Once the bootstrapper is deployed, you can declare and deploy the UDC through it. - -#### Declaring the UDC - -The UDC source code is available in the `openzeppelin_presets` package. You can copy it to your project and declare it with the following command: - -```bash -sncast -p declare \ - --contract-name UniversalDeployer -``` - - -If you followed the [Note on the UDC final address](#note-on-the-udc-final-address) section, your declared class hash should be -`0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8`. - - -#### Previewing the UDC address - -You can preview the UDC address with the following command: - -```bash -sncast call \ - --network \ - --contract-address \ - --function "get_udc_address" \ - --arguments '' -``` - -If the UDC class hash is the same as the one in the [Note on the UDC final address](#note-on-the-udc-final-address) section, -the output should be `0x2ceed65a4bd731034c01113685c831b01c15d7d432f71afb1cf1634b53a2125`. - -#### Deploying the UDC - -Now everything is set up to deploy the UDC. You can use the following command to deploy it: - - -Note that the bootstrapper contract MUST call itself to successfully deploy the UDC, since the `deploy_udc` function is protected. - - -```bash -sncast \ - --account bootstrapper \ - invoke \ - --network \ - --contract-address \ - --function "deploy_udc" \ - --arguments '' -``` - -## Other Appchain providers - -If you are using an appchain provider different from Madara, you can follow the same steps to deploy the UDC -as long as you have access to an account that can declare contracts. - -Summarizing, the steps to follow are: - -1. Declare the Bootstrapper -2. Counterfactually deploy the Bootstrapper -3. Declare the UDC -4. Preview the UDC address -5. Deploy the UDC from the Bootstrapper - -## Conclusion - -By following this guide, you have successfully deployed the Universal Deployer Contract on your appchain while ensuring consistency with -Starknet’s public networks. Maintaining the same UDC address and class hash across all environments is crucial for seamless contract deployment -and tooling compatibility, allowing developers to leverage tools like **sncast** and **starkli** without additional configuration. This process not only -improves the reliability of your deployment workflows but also ensures that your appchain remains compatible with the broader Starknet ecosystem. -With the UDC correctly deployed, you are now ready to take full advantage of streamlined contract -deployments and robust developer tooling on your appchain. diff --git a/content/contracts-cairo/alpha/guides/deployment.mdx b/content/contracts-cairo/alpha/guides/deployment.mdx deleted file mode 100644 index 42a924db..00000000 --- a/content/contracts-cairo/alpha/guides/deployment.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Counterfactual deployments ---- - -A counterfactual contract is a contract we can interact with even before actually deploying it on-chain. -For example, we can send funds or assign privileges to a contract that doesn’t yet exist. -Why? Because deployments in Starknet are deterministic, allowing us to predict the address where our contract will be deployed. -We can leverage this property to make a contract pay for its own deployment by simply sending funds in advance. We call this a counterfactual deployment. - -This process can be described with the following steps: - - -For testing this flow you can check the [Starknet Foundry](https://foundry-rs.github.io/starknet-foundry/starknet/account.html) or the [Starkli](https://book.starkli.rs/accounts#account-deployment) guides for deploying accounts. - - -1. Deterministically precompute the `contract_address` given a `class_hash`, `salt`, and constructor `calldata`. -Note that the `class_hash` must be previously declared for the deployment to succeed. -2. Send funds to the `contract_address`. Usually you will estimate the fee of the transaction first. Existing -tools usually do this for you. -3. Send a `DeployAccount` type transaction to the network. -4. The protocol will then validate the transaction with the `__validate_deploy__` entrypoint of the contract to be deployed. -5. If the validation succeeds, the protocol will charge the fee and then register the contract as deployed. - - -Although this method is very popular to deploy accounts, this works for any kind of contract. - - -## Deployment validation - -To be counterfactually deployed, the deploying contract must implement the `__validate_deploy__` entrypoint, -called by the protocol when a `DeployAccount` transaction is sent to the network. - -```rust -trait IDeployable { - /// Must return 'VALID' when the validation is successful. - fn __validate_deploy__( - class_hash: felt252, contract_address_salt: felt252, public_key: felt252 - ) -> felt252; -} -``` diff --git a/content/contracts-cairo/alpha/guides/erc20-permit.mdx b/content/contracts-cairo/alpha/guides/erc20-permit.mdx deleted file mode 100644 index 28259458..00000000 --- a/content/contracts-cairo/alpha/guides/erc20-permit.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: ERC20Permit ---- - -The [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) standard, commonly referred to as ERC20Permit, is designed to support gasless token approvals. This is achieved with an off-chain -signature following the [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) standard, rather than with an on-chain transaction. The [permit](../api/erc20#ERC20Component-permit) function verifies the signature and sets -the spender’s allowance if the signature is valid. This approach improves user experience and reduces gas costs. - -## Differences from Solidity - -Although this extension is mostly similar to the [Solidity implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/ERC20Permit.sol) of [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612), there are some notable differences in the parameters of the [permit](../api/erc20#ERC20Component-permit) function: - -* The `deadline` parameter is represented by `u64` rather than `u256`. -* The `signature` parameter is represented by a span of felts rather than `v`, `r`, and `s` values. - - -Unlike Solidity, there is no enforced format for signatures on Starknet. A signature is represented by an array or span of felts, -and there is no universal method for validating signatures of unknown formats. Consequently, a signature provided to the [permit](../api/erc20#ERC20Component-permit) function -is validated through an external `is_valid_signature` call to the contract at the `owner` address. - - - -## Usage - -The functionality is provided as an embeddable [ERC20Permit](../api/erc20#ERC20Component-Embeddable-Impls-ERC20PermitImpl) trait of the [ERC20Component](../api/erc20#ERC20Component). - -```rust -#[abi(embed_v0)] -impl ERC20PermitImpl = ERC20Component::ERC20PermitImpl; -``` - -A contract must meet the following requirements to be able to use the [ERC20Permit](../api/erc20#ERC20Component-Embeddable-Impls-ERC20PermitImpl) trait: - -* Implement [ERC20Component](../api/erc20#ERC20Component). -* Implement [NoncesComponent](../api/utilities#NoncesComponent). -* Implement [SNIP12Metadata](../api/utilities#snip12) trait (used in signature generation). - -## Typed message - -To safeguard against replay attacks and ensure the uniqueness of each approval via [permit](../api/erc20#ERC20Component-permit), the data signed includes: - -* The address of the `owner`. -* The parameters specified in the [approve](../api/erc20#ERC20Component-approve) function (`spender` and `amount`) -* The address of the `token` contract itself. -* A `nonce`, which must be unique for each operation. -* The `chain_id`, which protects against cross-chain replay attacks. - -The format of the `Permit` structure in a signed permit message is as follows: -```rust -struct Permit { - token: ContractAddress, - spender: ContractAddress, - amount: u256, - nonce: felt252, - deadline: u64, -} -``` - - -The owner of the tokens is also part of the signed message. It is used as the `signer` parameter in the `get_message_hash` call. - - -Further details on preparing and signing a typed message can be found in the [SNIP12 guide](./snip12). diff --git a/content/contracts-cairo/alpha/guides/erc20-supply.mdx b/content/contracts-cairo/alpha/guides/erc20-supply.mdx deleted file mode 100644 index 51465637..00000000 --- a/content/contracts-cairo/alpha/guides/erc20-supply.mdx +++ /dev/null @@ -1,148 +0,0 @@ ---- -title: Creating ERC20 Supply ---- - -The standard interface implemented by tokens built on Starknet comes from the popular token standard on Ethereum called ERC20. -[EIP20](https://eips.ethereum.org/EIPS/eip-20), from which ERC20 contracts are derived, does not specify how tokens are created. -This guide will go over strategies for creating both a fixed and dynamic token supply. - -## Fixed Supply - -Let’s say we want to create a token named `MyToken` with a fixed token supply. -We can achieve this by setting the token supply in the constructor which will execute upon deployment. - -```rust -#[starknet::contract] -mod MyToken { - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - // ERC20 Mixin - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc20: ERC20Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC20Event: ERC20Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - fixed_supply: u256, - recipient: ContractAddress - ) { - let name = "MyToken"; - let symbol = "MTK"; - - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, fixed_supply); - } -} -``` - -In the constructor, we’re first calling the ERC20 initializer to set the token name and symbol. -Next, we’re calling the internal `mint` function which creates `fixed_supply` of tokens and allocates them to `recipient`. -Since the internal `mint` is not exposed in our contract, it will not be possible to create any more tokens. -In other words, we’ve implemented a fixed token supply! - -## Dynamic Supply - -ERC20 contracts with a dynamic supply include a mechanism for creating or destroying tokens. -Let’s make a few changes to the almighty `MyToken` contract and create a minting mechanism. - -```rust -#[starknet::contract] -mod MyToken { - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - // ERC20 Mixin - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc20: ERC20Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC20Event: ERC20Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - let name = "MyToken"; - let symbol = "MTK"; - - self.erc20.initializer(name, symbol); - } - - #[external(v0)] - fn mint( - ref self: ContractState, - recipient: ContractAddress, - amount: u256 - ) { - // This function is NOT protected which means - // ANYONE can mint tokens - self.erc20.mint(recipient, amount); - } -} -``` - -The exposed `mint` above will create `amount` tokens and allocate them to `recipient`. -We now have our minting mechanism! - -There is, however, a big problem. -`mint` does not include any restrictions on who can call this function. -For the sake of good practices, let’s implement a simple permissioning mechanism with `Ownable`. - -```rust -#[starknet::contract] -mod MyToken { - - (...) - - // Integrate Ownable - - #[external(v0)] - fn mint( - ref self: ContractState, - recipient: ContractAddress, - amount: u256 - ) { - // Set permissions with Ownable - self.ownable.assert_only_owner(); - - // Mint tokens if called by the contract owner - self.erc20.mint(recipient, amount); - } -} -``` - -In the constructor, we pass the owner address to set the owner of the `MyToken` contract. -The `mint` function includes `assert_only_owner` which will ensure that only the contract owner can call this function. -Now, we have a protected ERC20 minting mechanism to create a dynamic token supply. - - -For a more thorough explanation of permission mechanisms, see [Access Control](../access). - diff --git a/content/contracts-cairo/alpha/guides/interfaces-and-dispatchers.mdx b/content/contracts-cairo/alpha/guides/interfaces-and-dispatchers.mdx deleted file mode 100644 index fbabbc32..00000000 --- a/content/contracts-cairo/alpha/guides/interfaces-and-dispatchers.mdx +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: 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 `openzeppelin_interfaces` package modules, such as `openzeppelin_interfaces::erc20`. - - -Starting from version `3.x.x`, OpenZeppelin Contracts for Cairo interfaces have been separated from their implementation modules into a dedicated package. -This architectural change brings several important benefits. Check the [Interfaces module](../interfaces) section for more information. - - -For example: - -```rust -use openzeppelin_interfaces::erc20::IERC20; -``` - -or - -```rust -use openzeppelin_interfaces::erc20::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. - -```rust -#[starknet::interface] -pub trait 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; -} -``` - -### 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 standards 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. - - -```rust -#[starknet::interface] -pub trait ERC20ABI { - // 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: - -```rust -use openzeppelin_interfaces::erc20::{IERC20Dispatcher, IERC20DispatcherTrait}; -``` - -Other types of dispatchers are also auto-generated from the annotated trait. See the -[Interacting with another contract](https://book.cairo-lang.org/ch15-02-interacting-with-another-contract.html) 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 - - -The `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](https://community.starknet.io/t/the-great-interface-migration/92107) 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: - -1. `camelCase` interfaces, which are the ones we’ve been using so far. -2. `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](https://github.com/OpenZeppelin/cairo-contracts/releases/tag/v0.6.1) and below). - - -### `IERC20` - -The default version of the ERC20 interface trait exposes `snake_case` functions: - -```rust -#[starknet::interface] -pub trait IERC20 { - 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: - -```rust -#[starknet::interface] -pub trait IERC20Camel { - 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; -} -``` diff --git a/content/contracts-cairo/alpha/guides/snip12.mdx b/content/contracts-cairo/alpha/guides/snip12.mdx deleted file mode 100644 index a86aae6b..00000000 --- a/content/contracts-cairo/alpha/guides/snip12.mdx +++ /dev/null @@ -1,344 +0,0 @@ ---- -title: SNIP12 and Typed Messages ---- - -Similar to [EIP712](https://eips.ethereum.org/EIPS/eip-712), [SNIP12](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md) is a standard for secure off-chain signature verification on Starknet. -It provides a way to hash and sign generic typed structs rather than just strings. When building decentralized -applications, usually you might need to sign a message with complex data. The purpose of signature verification -is then to ensure that the received message was indeed signed by the expected signer, and it hasn’t been tampered with. - -OpenZeppelin Contracts for Cairo provides a set of utilities to make the implementation of this standard -as easy as possible, and in this guide we will walk you through the process of generating the hashes of typed messages -using these utilities for on-chain signature verification. For that, let’s build an example with a custom [ERC20](/contracts-cairo/alpha/api/erc20#ERC20) contract -adding an extra `transfer_with_signature` method. - - -This is an educational example, and it is not intended to be used in production environments. - - -## CustomERC20 - -Let’s start with a basic ERC20 contract leveraging the [ERC20Component](/contracts-cairo/alpha/api/erc20#ERC20Component), and let’s add the new function. -Note that some declarations are omitted for brevity. The full example will be available at the end of the guide. - -```rust -#[starknet::contract] -mod CustomERC20 { - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - (...) - - #[constructor] - fn constructor( - ref self: ContractState, - initial_supply: u256, - recipient: ContractAddress - ) { - self.erc20.initializer("MyToken", "MTK"); - self.erc20.mint(recipient, initial_supply); - } - - #[external(v0)] - fn transfer_with_signature( - ref self: ContractState, - recipient: ContractAddress, - amount: u256, - nonce: felt252, - expiry: u64, - signature: Array - ) { - (...) - } -} -``` - -The `transfer_with_signature` function will allow a user to transfer tokens to another account by providing a signature. -The signature will be generated off-chain, and it will be used to verify the message on-chain. Note that the message -we need to verify is a struct with the following fields: - -* `recipient`: The address of the recipient. -* `amount`: The amount of tokens to transfer. -* `nonce`: A unique number to prevent replay attacks. -* `expiry`: The timestamp when the signature expires. - -Note that generating the hash of this message on-chain is a requirement to verify the signature, because if we accept -the message as a parameter, it could be easily tampered with. - -## Generating the Typed Message Hash - -To generate the hash of the message, we need to follow these steps: - -### 1. Define the message struct. - -In this particular example, the message struct looks like this: - -```rust -struct Message { - recipient: ContractAddress, - amount: u256, - nonce: felt252, - expiry: u64 -} -``` - -### 2. Get the message type hash. - -This is the `starknet_keccak(encode_type(message))` as defined in the [SNIP](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md#how-to-work-with-each-type). - -In this case it can be computed as follows: - -```rust -// Since there's no u64 type in SNIP-12, we use u128 for `expiry` in the type hash generation. -let message_type_hash = selector!( - "\"Message\"(\"recipient\":\"ContractAddress\",\"amount\":\"u256\",\"nonce\":\"felt\",\"expiry\":\"u128\")\"u256\"(\"low\":\"u128\",\"high\":\"u128\")" -); -``` - -which is the same as: - -```rust -let message_type_hash = 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; -``` - - -In practice it’s better to compute the type hash off-chain and hardcode it in the contract, since it is a constant value. - - -### 3. Implement the `StructHash` trait for the struct. - -You can import the trait from: `openzeppelin_utils::snip12::StructHash`. And this implementation -is nothing more than the encoding of the message as defined in the [SNIP](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-12.md#how-to-work-with-each-type). - -```rust -use core::hash::{HashStateExTrait, HashStateTrait}; -use core::poseidon::PoseidonTrait; -use openzeppelin_utils::snip12::StructHash; -use starknet::ContractAddress; - -const MESSAGE_TYPE_HASH: felt252 = - 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; - -#[derive(Copy, Drop, Hash)] -struct Message { - recipient: ContractAddress, - amount: u256, - nonce: felt252, - expiry: u64 -} - -impl StructHashImpl of StructHash { - fn hash_struct(self: @Message) -> felt252 { - let hash_state = PoseidonTrait::new(); - hash_state.update_with(MESSAGE_TYPE_HASH).update_with(*self).finalize() - } -} -``` - -### 4. Implement the `SNIP12Metadata` trait. - -This implementation determines the values of the domain separator. Only the `name` and `version` fields are required -because the `chain_id` is obtained on-chain, and the `revision` is hardcoded to `1`. - -```rust -use openzeppelin_utils::snip12::SNIP12Metadata; - -impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { 'DAPP_NAME' } - fn version() -> felt252 { 'v1' } -} -``` - -In the above example, no storage reads are required which avoids unnecessary extra gas costs, but in -some cases we may need to read from storage to get the domain separator values. This can be accomplished even when -the trait is not bounded to the ContractState, like this: - -```rust -use openzeppelin_utils::snip12::SNIP12Metadata; - -impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { - let state = unsafe_new_contract_state(); - - // Some logic to get the name from storage - state.erc20.name().at(0).unwrap().into() - } - - fn version() -> felt252 { 'v1' } -} -``` - -### 5. Generate the hash. - -The final step is to use the `OffchainMessageHashImpl` implementation to generate the hash of the message -using the `get_message_hash` function. The implementation is already available as a utility. - -```rust -use core::hash::{HashStateExTrait, HashStateTrait}; -use core::poseidon::PoseidonTrait; -use openzeppelin_utils::snip12::{SNIP12Metadata, StructHash, OffchainMessageHash}; -use starknet::ContractAddress; - -const MESSAGE_TYPE_HASH: felt252 = - 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; - -#[derive(Copy, Drop, Hash)] -struct Message { - recipient: ContractAddress, - amount: u256, - nonce: felt252, - expiry: u64 -} - -impl StructHashImpl of StructHash { - fn hash_struct(self: @Message) -> felt252 { - let hash_state = PoseidonTrait::new(); - hash_state.update_with(MESSAGE_TYPE_HASH).update_with(*self).finalize() - } -} - -impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { - 'DAPP_NAME' - } - fn version() -> felt252 { - 'v1' - } -} - -fn get_hash( - account: ContractAddress, recipient: ContractAddress, amount: u256, nonce: felt252, expiry: u64 -) -> felt252 { - let message = Message { recipient, amount, nonce, expiry }; - message.get_message_hash(account) -} -``` - - -The expected parameter for the `get_message_hash` function is the address of account that signed the message. - - -## Full Implementation - -Finally, the full implementation of the `CustomERC20` contract looks like this: - - -We are using the [`ISRC6Dispatcher`](/contracts-cairo/alpha/api/account#ISRC6) to verify the signature, -and the [`NoncesComponent`](/contracts-cairo/alpha/api/utilities#NoncesComponent) to handle nonces to prevent replay attacks. - - -```rust -use core::hash::{HashStateExTrait, HashStateTrait}; -use core::poseidon::PoseidonTrait; -use openzeppelin_utils::snip12::{SNIP12Metadata, StructHash, OffchainMessageHash}; -use starknet::ContractAddress; - -const MESSAGE_TYPE_HASH: felt252 = - 0x28bf13f11bba405c77ce010d2781c5903cbed100f01f72fcff1664f98343eb6; - -#[derive(Copy, Drop, Hash)] -struct Message { - recipient: ContractAddress, - amount: u256, - nonce: felt252, - expiry: u64 -} - -impl StructHashImpl of StructHash { - fn hash_struct(self: @Message) -> felt252 { - let hash_state = PoseidonTrait::new(); - hash_state.update_with(MESSAGE_TYPE_HASH).update_with(*self).finalize() - } -} - -#[starknet::contract] -mod CustomERC20 { - use openzeppelin_interfaces::accounts::{ISRC6Dispatcher, ISRC6DispatcherTrait}; - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; - use openzeppelin_utils::cryptography::nonces::NoncesComponent; - use starknet::ContractAddress; - - use super::{Message, OffchainMessageHash, SNIP12Metadata}; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - component!(path: NoncesComponent, storage: nonces, event: NoncesEvent); - - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[abi(embed_v0)] - impl NoncesImpl = NoncesComponent::NoncesImpl; - impl NoncesInternalImpl = NoncesComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc20: ERC20Component::Storage, - #[substorage(v0)] - nonces: NoncesComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC20Event: ERC20Component::Event, - #[flat] - NoncesEvent: NoncesComponent::Event - } - - #[constructor] - fn constructor(ref self: ContractState, initial_supply: u256, recipient: ContractAddress) { - self.erc20.initializer("MyToken", "MTK"); - self.erc20.mint(recipient, initial_supply); - } - - /// Required for hash computation. - impl SNIP12MetadataImpl of SNIP12Metadata { - fn name() -> felt252 { - 'CustomERC20' - } - fn version() -> felt252 { - 'v1' - } - } - - #[external(v0)] - fn transfer_with_signature( - ref self: ContractState, - recipient: ContractAddress, - amount: u256, - nonce: felt252, - expiry: u64, - signature: Array - ) { - assert(starknet::get_block_timestamp() <= expiry, 'Expired signature'); - let owner = starknet::get_caller_address(); - - // Check and increase nonce - self.nonces.use_checked_nonce(owner, nonce); - - // Build hash for calling `is_valid_signature` - let message = Message { recipient, amount, nonce, expiry }; - let hash = message.get_message_hash(owner); - - let is_valid_signature_felt = ISRC6Dispatcher { contract_address: owner } - .is_valid_signature(hash, signature); - - // Check either 'VALID' or true for backwards compatibility - let is_valid_signature = is_valid_signature_felt == starknet::VALIDATED - || is_valid_signature_felt == 1; - assert(is_valid_signature, 'Invalid signature'); - - // Transfer tokens - self.erc20._transfer(owner, recipient, amount); - } -} -``` diff --git a/content/contracts-cairo/alpha/index.mdx b/content/contracts-cairo/alpha/index.mdx deleted file mode 100644 index 9c768d64..00000000 --- a/content/contracts-cairo/alpha/index.mdx +++ /dev/null @@ -1,156 +0,0 @@ ---- -title: Contracts for Cairo ---- - -[starknet]:https://starkware.co/product/starknet/ -[scarb]:https://docs.swmansion.com/scarb/ -[scarb-install]:https://docs.swmansion.com/scarb/download.html - - -**A library for secure smart contract development** written in Cairo for [Starknet][starknet]. This library consists of a set of -[reusable components](/contracts-cairo/alpha/components) to build custom smart contracts, as well as ready-to-deploy [presets](/contracts-cairo/alpha/presets). You can also -find other [utilities](/contracts-cairo/alpha/api/utilities) including [interfaces and dispatchers](/contracts-cairo/alpha/interfaces) and [test utilities](/contracts-cairo/alpha/api/testing) -that facilitate testing with Starknet Foundry. - - -You can track our roadmap and future milestones in our [Github Project](https://github.com/orgs/OpenZeppelin/projects/29/). - - -## Installation - -The library is available as a [Scarb][scarb] package. Follow [this guide][scarb-install] for installing Cairo and Scarb on your machine -before proceeding, and run the following command to check that the installation was successful: - -```bash -$ scarb --version - -scarb 2.12.2 (dc0dbfd50 2025-09-15) -cairo: 2.12.2 (https://crates.io/crates/cairo-lang-compiler/2.12.2) -sierra: 1.7.0 -``` - -### Set up your project - -Create an empty directory, and `cd` into it: - -```bash -mkdir my_project/ && cd my_project/ -``` - -Initialize a new Scarb project: - -```bash -scarb init -``` - -The contents of `my_project/` should now look like this: - -```bash -$ ls - -Scarb.toml src -``` - -### Install the library - - - -The `openzeppelin` package is an umbrella (meta) package that aggregates all library subpackages. Prior to v3.x, the umbrella and its subpackages were -versioned in lockstep—their versions always matched. Starting with v3.x, following the introduction of `openzeppelin_interfaces`, the umbrella is -versioned independently from some of the subpackages. - -See the [Versioning of the sub-packages](/contracts-cairo#versioning-of-the-sub-packages) section for more information. - - - -Install the library by declaring it as a dependency in the project’s `Scarb.toml` file: - -```javascript -[dependencies] -openzeppelin = "{{umbrella_version}}" -``` - -The previous example would import the entire library. We can also add each package as a separate dependency to -improve the building time by not including modules that won’t be used: - -```toml -[dependencies] -openzeppelin_access = "{{umbrella_version}}" -openzeppelin_token = "{{umbrella_version}}" -openzeppelin_interfaces = "{{openzeppelin_interfaces_version}}" -``` - -## Versioning of the sub-packages - -Here you can find a reference of the versioning of the sub-packages for this umbrella version: - -```javascript -[dependencies] -openzeppelin_access = "{{umbrella_version}}" -openzeppelin_token = "{{umbrella_version}}" -openzeppelin_access = "{{umbrella_version}}" -openzeppelin_account = "{{umbrella_version}}" -openzeppelin_finance = "{{umbrella_version}}" -openzeppelin_interfaces = "{{openzeppelin_interfaces_version}}" -openzeppelin_governance = "{{umbrella_version}}" -openzeppelin_introspection = "{{umbrella_version}}" -openzeppelin_merkle_tree = "{{umbrella_version}}" -openzeppelin_presets = "{{umbrella_version}}" -openzeppelin_security = "{{umbrella_version}}" -openzeppelin_token = "{{umbrella_version}}" -openzeppelin_upgrades = "{{umbrella_version}}" -openzeppelin_utils = "{{openzeppelin_utils_version}}" -``` - -## Basic usage - -This is how it looks to build an ERC20 contract using the [ERC20 component](/contracts-cairo/alpha/erc20). -Copy the code into `src/lib.cairo`. - -```rust -#[starknet::contract] -mod MyERC20Token { - // NOTE: If you added the entire library as a dependency, - // use `openzeppelin::token` instead. - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl, DefaultConfig}; - use starknet::ContractAddress; - - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - - // ERC20 Mixin - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - erc20: ERC20Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ERC20Event: ERC20Component::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - fixed_supply: u256, - recipient: ContractAddress - ) { - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, fixed_supply); - } -} -``` - -You can now compile it: - -```bash -scarb build -``` diff --git a/content/contracts-cairo/alpha/introspection.mdx b/content/contracts-cairo/alpha/introspection.mdx deleted file mode 100644 index 09fd89b4..00000000 --- a/content/contracts-cairo/alpha/introspection.mdx +++ /dev/null @@ -1,137 +0,0 @@ ---- -title: Introspection ---- - -To smooth interoperability, often standards require smart contracts to implement [introspection mechanisms](https://en.wikipedia.org/wiki/Type_introspection). - -In Ethereum, the [EIP165](https://eips.ethereum.org/EIPS/eip-165) standard defines how contracts should declare -their support for a given interface, and how other contracts may query this support. - -Starknet offers a similar mechanism for interface introspection defined by the [SRC5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md) standard. - -## SRC5 - -Similar to its Ethereum counterpart, the [SRC5](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md) standard requires contracts to implement the `supports_interface` function, -which can be used by others to query if a given interface is supported. - -### Usage - -To expose this functionality, the contract must implement the [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component), which defines the `supports_interface` function. -Here is an example contract: - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_introspection::src5::SRC5Component; - - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - impl SRC5InternalImpl = SRC5Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - self.src5.register_interface(selector!("some_interface")); - } -} -``` - -### Interface - -```rust -#[starknet::interface] -pub trait ISRC5 { - /// Query if a contract implements an interface. - /// Receives the interface identifier as specified in SRC-5. - /// Returns `true` if the contract implements `interface_id`, `false` otherwise. - fn supports_interface(interface_id: felt252) -> bool; -} -``` - -## Computing the interface ID - -The interface ID, as specified in the standard, is the [XOR](https://en.wikipedia.org/wiki/Exclusive_or) of all the -[Extended Function Selectors](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md#extended-function-selector) -of the interface. We strongly advise reading the SNIP to understand the specifics of computing these -extended function selectors. There are tools such as [src5-rs](https://github.com/ericnordelo/src5-rs) that can help with this process. - -## Registering interfaces - -For a contract to declare its support for a given interface, we recommend using the SRC5 component to register support upon contract deployment through a constructor either directly or indirectly (as an initializer) like this: - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_interfaces::accounts as interface; - use openzeppelin_introspection::src5::SRC5Component; - - component!(path: SRC5Component, storage: src5, event: SRC5Event); - - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - impl InternalImpl = SRC5Component::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - src5: SRC5Component::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - SRC5Event: SRC5Component::Event - } - - #[constructor] - fn constructor(ref self: ContractState) { - // Register the contract's support for the ISRC6 interface - self.src5.register_interface(interface::ISRC6_ID); - } - - (...) -} -``` - -## Querying interfaces - -Use the `supports_interface` function to query a contract’s support for a given interface. - -```rust -#[starknet::contract] -mod MyContract { - use openzeppelin_interfaces::accounts as interface; - use openzeppelin_interfaces::introspection::ISRC5DispatcherTrait; - use openzeppelin_interfaces::introspection::ISRC5Dispatcher; - use starknet::ContractAddress; - - #[storage] - struct Storage {} - - #[external(v0)] - fn query_is_account(self: @ContractState, target: ContractAddress) -> bool { - let dispatcher = ISRC5Dispatcher { contract_address: target }; - dispatcher.supports_interface(interface::ISRC6_ID) - } -} -``` - - -If you are unsure whether a contract implements SRC5 or not, you can follow the process described in -[here](https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md#how-to-detect-if-a-contract-implements-src-5). - diff --git a/content/contracts-cairo/alpha/macros.mdx b/content/contracts-cairo/alpha/macros.mdx deleted file mode 100644 index 13482da4..00000000 --- a/content/contracts-cairo/alpha/macros.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Macros ---- - -This crate provides a collection of macros that streamline and simplify development with the library. -To use them, you need to add the `openzeppelin_macros` crate as a dependency in your `Scarb.toml` file: - -```toml -openzeppelin_macros = "{{umbrella_version}}" -``` - -## Attribute macros - -* [with_components](./macros/with_components) -* [type_hash](./macros/type_hash) diff --git a/content/contracts-cairo/alpha/macros/type_hash.mdx b/content/contracts-cairo/alpha/macros/type_hash.mdx deleted file mode 100644 index 279090a2..00000000 --- a/content/contracts-cairo/alpha/macros/type_hash.mdx +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: type_hash ---- - -This macro generates a SNIP-12-compatible type hash for a given struct or enum. - - -This macro is fully compatible with the SNIP-12 standard revision 1. - - -## Usage - -```rust -#[type_hash(name: "My Struct", debug: true)] -struct MyStruct { - #[snip12(name: "My Field")] - my_field: felt252, -} -``` - -This will generate a type hash for the struct. - -```rust -pub const MY_STRUCT_TYPE_HASH: felt252 = 0x1735aa9819941b96c651b740b792a96c854565eaff089b7e293d996828b88a8; -``` - -And because of the `debug` argument, it will generate the following code: - -```rust -pub fn __MY_STRUCT_encoded_type() { - println!("\"My Struct\"(\"My Field\":\"felt\")"); -} -``` - -## Basic types - -The list of supported basic types as defined in the SNIP-12 standard is: - -* felt252 -* shortstring -* ClassHash -* ContractAddress -* timestamp -* selector -* merkletree -* u128 -* i128 - -### Examples - -Struct with basic types and custom names and kinds: - -```rust -#[type_hash(name: "My Struct", debug: true)] -pub struct MyStruct { - #[snip12(name: "Simple Felt")] // Optional custom name - pub simple_felt: felt252, - #[snip12(name: "Class Hash")] - pub class_hash: ClassHash, - #[snip12(name: "Target Token")] - pub target: ContractAddress, - #[snip12(name: "Timestamp", kind: "timestamp")] - pub timestamp: u128, - #[snip12(name: "Selector", kind: "selector")] - pub selector: felt252, -} - -pub const MY_STRUCT_TYPE_HASH: felt252 - = 0x522e0c3dc5e13b0978f4645760a436b1e119fd335842523fee8fbae6057b8c; - -``` - -Enum with basic types and custom names and kinds: - -```rust -#[type_hash(name: "My Enum", debug: true)] -pub enum MyEnum { - #[snip12(name: "Simple Felt")] - SimpleFelt: felt252, - #[snip12(name: "Class Hash")] - ClassHash: ClassHash, - #[snip12(name: "Target Token")] - ContractAddress: ContractAddress, - #[snip12(name: "Timestamp", kind: "timestamp")] - Timestamp: u128, - #[snip12(name: "Selector", kind: "selector")] - Selector: felt252, -} - -pub const MY_ENUM_TYPE_HASH: felt252 - = 0x3f30aaa6cda9f699d4131940b10602b78b986feb88f28a19f3b48567cb4b566; -``` - -## Collection types - -The list of supported collection types as defined in the SNIP-12 standard is: - -* Array -* Tuple ***(Only supported for enums)*** -* Span ***(Treated as an array)*** - - -While Span is not directly supported by the SNIP-12 standard, it is treated as an array for the purposes of this macro, since -it is sometimes helpful to use `Span` instead of `Array` in order to save on gas. - - -### Examples - -Struct with collection types: - -```rust -#[type_hash(name: "My Struct", debug: true)] -pub struct MyStruct { - #[snip12(name: "Member 1")] - pub member1: Array, - #[snip12(name: "Member 2")] - pub member2: Span, - #[snip12(name: "Timestamps", kind: "Array")] - pub timestamps: Array, -} - -pub const MY_STRUCT_TYPE_HASH: felt252 - = 0x369cdec45d8c55e70986aed44da0e330375171ba6e25b58e741c0ce02fa8ac; -``` - -Enum with collection types: - -```rust -#[type_hash(name: "My Enum", debug: true)] -pub enum MyEnum { - #[snip12(name: "Member 1")] - Member1: Array, - #[snip12(name: "Member 2")] - Member2: Span, - #[snip12(name: "Timestamps", kind: "Array")] - Timestamps: Array, - #[snip12(name: "Name and Last Name", kind: "(shortstring, shortstring)")] - NameAndLastName: (felt252, felt252), -} - -pub const MY_ENUM_TYPE_HASH: felt252 - = 0x9e3e1ebad4448a8344b3318f9cfda5df237588fd8328e1c2968635f09c735d; -``` - -## Preset types - -The list of supported preset types as defined in the SNIP-12 standard is: - -* TokenAmount -* NftId -* u256 - -### Examples - -Struct with preset types: - -```rust -#[type_hash(name: "My Struct", debug: true)] -pub struct MyStruct { - #[snip12(name: "Token Amount")] - pub token_amount: TokenAmount, - #[snip12(name: "NFT ID")] - pub nft_id: NftId, - #[snip12(name: "Number")] - pub number: u256, -} - -pub const MY_STRUCT_TYPE_HASH: felt252 - = 0x19f63528d68c4f44b7d9003a5a6b7793f5bb6ffc8a22bdec82b413ddf4f9412; -``` - -Enum with preset types: - -```rust -#[type_hash(name: "My Enum", debug: true)] -pub enum MyEnum { - #[snip12(name: "Token Amount")] - TokenAmount: TokenAmount, - #[snip12(name: "NFT ID")] - NftId: NftId, - #[snip12(name: "Number")] - Number: u256, -} - -pub const MY_ENUM_TYPE_HASH: felt252 - = 0x39dd19c7e5c5f89e084b78a26200b712c6ae3265f2bae774471c588858421b7; -``` - -## User-defined types - -User-defined types are currently ***NOT SUPPORTED*** since the macro doesn’t have access to scope outside of the -target struct/enum. In the future it may be supported by extending the syntax to explicitly declare the custom type -definition. diff --git a/content/contracts-cairo/alpha/macros/with_components.mdx b/content/contracts-cairo/alpha/macros/with_components.mdx deleted file mode 100644 index ba7a6dee..00000000 --- a/content/contracts-cairo/alpha/macros/with_components.mdx +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: with_components ---- - -This macro simplifies the syntax for adding a set of components to a contract. It: - -* _Imports the corresponding components into the contract._ -* _Adds the corresponding `component!` macro entries._ -* _Adds the storage entries for each component to the Storage struct._ -* _Adds the event entries for each component to the Event struct, or creates the struct if it is missing._ -* _Brings the corresponding internal implementations into scope._ -* _Provides some diagnostics for each specific component to help the developer avoid common mistakes._ - - -Since the macro does not expose any external implementations, developers must make sure to specify explicitly -the ones required by the contract. - - -## Security considerations - -The macro was designed to be simple and effective while still being very hard to misuse. For this reason, the features -that it provides are limited, and things that might make the contract behave in unexpected ways must be -explicitly specified by the developer. It does not specify external implementations, so contracts won’t find -themselves in a situation where external functions are exposed without the developer’s knowledge. It brings -the internal implementations into scope so these functions are available by default, but if they are not used, -they won’t have any effect on the contract’s behavior. - -## Usage - -This is how a contract with multiple components looks when using the macro. - -```rust -#[with_components(Account, SRC5, SRC9, Upgradeable)] -#[starknet::contract(account)] -mod OutsideExecutionAccountUpgradeable { - use openzeppelin_interfaces::upgrades::IUpgradeable; - use starknet::{ClassHash, ContractAddress}; - - // External - #[abi(embed_v0)] - impl AccountMixinImpl = AccountComponent::AccountMixinImpl; - #[abi(embed_v0)] - impl OutsideExecutionV2Impl = - SRC9Component::OutsideExecutionV2Impl; - - #[storage] - struct Storage {} - - #[constructor] - fn constructor(ref self: ContractState, public_key: felt252) { - self.account.initializer(public_key); - self.src9.initializer(); - } - - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { - self.account.assert_only_self(); - self.upgradeable.upgrade(new_class_hash); - } - } -} -``` - -This is how the same contract looks using regular syntax. - -```rust -#[starknet::contract(account)] -mod OutsideExecutionAccountUpgradeable { - use openzeppelin::account::AccountComponent; - use openzeppelin::account::extensions::SRC9Component; - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::upgrades::UpgradeableComponent; - use openzeppelin::upgrades::interface::IUpgradeable; - use starknet::ClassHash; - - component!(path: AccountComponent, storage: account, event: AccountEvent); - component!(path: SRC5Component, storage: src5, event: SRC5Event); - component!(path: SRC9Component, storage: src9, event: SRC9Event); - component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); - - // External - #[abi(embed_v0)] - impl AccountMixinImpl = AccountComponent::AccountMixinImpl; - #[abi(embed_v0)] - impl OutsideExecutionV2Impl = - SRC9Component::OutsideExecutionV2Impl; - - // Internal - impl AccountInternalImpl = AccountComponent::InternalImpl; - impl OutsideExecutionInternalImpl = SRC9Component::InternalImpl; - impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - account: AccountComponent::Storage, - #[substorage(v0)] - src5: SRC5Component::Storage, - #[substorage(v0)] - src9: SRC9Component::Storage, - #[substorage(v0)] - upgradeable: UpgradeableComponent::Storage, - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - AccountEvent: AccountComponent::Event, - #[flat] - SRC5Event: SRC5Component::Event, - #[flat] - SRC9Event: SRC9Component::Event, - #[flat] - UpgradeableEvent: UpgradeableComponent::Event, - } - - #[constructor] - fn constructor(ref self: ContractState, public_key: felt252) { - self.account.initializer(public_key); - self.src9.initializer(); - } - - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { - self.account.assert_only_self(); - self.upgradeable.upgrade(new_class_hash); - } - } -} -``` diff --git a/content/contracts-cairo/alpha/presets.mdx b/content/contracts-cairo/alpha/presets.mdx deleted file mode 100644 index 5511457f..00000000 --- a/content/contracts-cairo/alpha/presets.mdx +++ /dev/null @@ -1,141 +0,0 @@ ---- -title: Presets ---- - -Presets are ready-to-deploy contracts provided by the library. Since presets are intended to be very simple -and as generic as possible, there’s no support for custom or complex contracts such as `ERC20Pausable` or `ERC721Mintable`. - - -For contract customization and combination of modules you can use [Wizard for Cairo](https://wizard.openzeppelin.com), our code-generation tool. - - -## Available presets - -List of available presets and their corresponding [Sierra class hashes](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/). Like Contracts for Cairo, -use of preset contracts are subject to the terms of the -[MIT License](https://github.com/OpenZeppelin/cairo-contracts?tab=MIT-1-ov-file#readme). - - -Class hashes were computed using scarb `v{{class_hash_scarb_version}}` and the `scarb --release` profile. - - - -Before version 2.x, class hashes were computed using the `scarb --dev` profile. - - -| Name | Sierra Class Hash | -| --- | --- | -| [`AccountUpgradeable`](/contracts-cairo/alpha/api/account#AccountUpgradeable) | `{{AccountUpgradeableClassHash}}` | -| [`ERC20Upgradeable`](/contracts-cairo/alpha/api/erc20#ERC20Upgradeable) | `{{ERC20UpgradeableClassHash}}` | -| [`ERC721Upgradeable`](/contracts-cairo/alpha/api/erc721#ERC721Upgradeable) | `{{ERC721UpgradeableClassHash}}` | -| [`ERC1155Upgradeable`](/contracts-cairo/alpha/api/erc1155#ERC1155Upgradeable) | `{{ERC1155UpgradeableClassHash}}` | -| [`EthAccountUpgradeable`](/contracts-cairo/alpha/api/account#EthAccountUpgradeable) | `{{EthAccountUpgradeableClassHash}}` | -| [`UniversalDeployer`](/contracts-cairo/alpha/api/udc#UniversalDeployer) | `{{UniversalDeployerClassHash}}` | -| [`VestingWallet`](/contracts-cairo/alpha/api/finance#VestingWallet) | `{{VestingWalletClassHash}}` | - - -[starkli](https://book.starkli.rs/introduction) class-hash command can be used to compute the class hash from a Sierra artifact. - - -## Usage - -These preset contracts are ready-to-deploy which means they should already be declared on the Sepolia network. -Simply deploy the preset class hash and add the appropriate constructor arguments. -Deploying the ERC20Upgradeable preset with [starkli](https://book.starkli.rs/introduction), for example, will look like this: - -```bash -starkli deploy {ERC20Upgradeable-class-hash} \ - \ - --network="sepolia" -``` - -If a class hash has yet to be declared, copy/paste the preset contract code and declare it locally. -Start by [setting up a project](/contracts-cairo#set-up-your-project) and [installing the Contracts for Cairo library](/contracts-cairo#install-the-library). -Copy the target preset contract from the [presets directory](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v{{umbrella_version}}/packages/presets/src) -and paste it in the new project’s `src/lib.cairo` like this: - -```rust -// src/lib.cairo - -#[starknet::contract] -mod ERC20Upgradeable { - use openzeppelin_access::ownable::OwnableComponent; - use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; - use openzeppelin_upgrades::UpgradeableComponent; - use openzeppelin_interfaces::upgrades::IUpgradeable; - use starknet::{ContractAddress, ClassHash}; - - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - component!(path: ERC20Component, storage: erc20, event: ERC20Event); - component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); - - // Ownable Mixin - #[abi(embed_v0)] - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; - impl OwnableInternalImpl = OwnableComponent::InternalImpl; - - // ERC20 Mixin - #[abi(embed_v0)] - impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; - impl ERC20InternalImpl = ERC20Component::InternalImpl; - - // Upgradeable - impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - ownable: OwnableComponent::Storage, - #[substorage(v0)] - erc20: ERC20Component::Storage, - #[substorage(v0)] - upgradeable: UpgradeableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - OwnableEvent: OwnableComponent::Event, - #[flat] - ERC20Event: ERC20Component::Event, - #[flat] - UpgradeableEvent: UpgradeableComponent::Event - } - - #[constructor] - fn constructor( - ref self: ContractState, - name: ByteArray, - symbol: ByteArray, - fixed_supply: u256, - recipient: ContractAddress, - owner: ContractAddress - ) { - self.ownable.initializer(owner); - self.erc20.initializer(name, symbol); - self.erc20.mint(recipient, fixed_supply); - } - - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { - self.ownable.assert_only_owner(); - self.upgradeable.upgrade(new_class_hash); - } - } -} -``` - -Next, compile the contract. - -```bash -scarb build -``` - -Finally, declare the preset. - -```bash -starkli declare target/dev/my_project_ERC20Upgradeable.contract_class.json \ - --network="sepolia" -``` diff --git a/content/contracts-cairo/alpha/security.mdx b/content/contracts-cairo/alpha/security.mdx deleted file mode 100644 index 13c32d96..00000000 --- a/content/contracts-cairo/alpha/security.mdx +++ /dev/null @@ -1,218 +0,0 @@ ---- -title: Security ---- - -The following documentation provides context, reasoning, and examples of modules found under `openzeppelin_security`. - - -Expect these modules to evolve. - - -## Initializable - -The [Initializable](/contracts-cairo/alpha/api/security#InitializableComponent) component provides a simple mechanism that mimics -the functionality of a constructor. -More specifically, it enables logic to be performed once and only once which is useful to set up a contract’s initial state when a constructor cannot be used, for example when there are circular dependencies at construction time. - -### Usage - -You can use the component in your contracts like this: - -```rust -#[starknet::contract] -mod MyInitializableContract { - use openzeppelin_security::InitializableComponent; - - component!(path: InitializableComponent, storage: initializable, event: InitializableEvent); - - impl InternalImpl = InitializableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - initializable: InitializableComponent::Storage, - param: felt252 - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - InitializableEvent: InitializableComponent::Event - } - - fn initializer(ref self: ContractState, some_param: felt252) { - // Makes the method callable only once - self.initializable.initialize(); - - // Initialization logic - self.param.write(some_param); - } -} -``` - - -This Initializable pattern should only be used in one function. - - -### Interface - -The component provides the following external functions as part of the `InitializableImpl` implementation: - -```rust -#[starknet::interface] -pub trait InitializableABI { - fn is_initialized() -> bool; -} -``` - -## Pausable - -The [Pausable](/contracts-cairo/alpha/api/security#PausableComponent) component allows contracts to implement an emergency stop mechanism. -This can be useful for scenarios such as preventing trades until the end of an evaluation period or having an emergency switch to freeze all transactions in the event of a large bug. - -To become pausable, the contract should include `pause` and `unpause` functions (which should be protected). -For methods that should be available only when paused or not, insert calls to `[assert_paused](/contracts-cairo/alpha/api/security#PausableComponent-assert_paused)` and `[assert_not_paused](/contracts-cairo/alpha/api/security#PausableComponent-assert_not_paused)` -respectively. - -### Usage - -For example (using the [Ownable](/contracts-cairo/alpha/api/access#OwnableComponent) component for access control): - -```rust -#[starknet::contract] -mod MyPausableContract { - use openzeppelin_access::ownable::OwnableComponent; - use openzeppelin_security::PausableComponent; - use starknet::ContractAddress; - - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - component!(path: PausableComponent, storage: pausable, event: PausableEvent); - - // Ownable Mixin - #[abi(embed_v0)] - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; - impl OwnableInternalImpl = OwnableComponent::InternalImpl; - - // Pausable - #[abi(embed_v0)] - impl PausableImpl = PausableComponent::PausableImpl; - impl PausableInternalImpl = PausableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - ownable: OwnableComponent::Storage, - #[substorage(v0)] - pausable: PausableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - OwnableEvent: OwnableComponent::Event, - #[flat] - PausableEvent: PausableComponent::Event - } - - #[constructor] - fn constructor(ref self: ContractState, owner: ContractAddress) { - self.ownable.initializer(owner); - } - - #[external(v0)] - fn pause(ref self: ContractState) { - self.ownable.assert_only_owner(); - self.pausable.pause(); - } - - #[external(v0)] - fn unpause(ref self: ContractState) { - self.ownable.assert_only_owner(); - self.pausable.unpause(); - } - - #[external(v0)] - fn when_not_paused(ref self: ContractState) { - self.pausable.assert_not_paused(); - // Do something - } - - #[external(v0)] - fn when_paused(ref self: ContractState) { - self.pausable.assert_paused(); - // Do something - } -} -``` - -### Interface - -The component provides the following external functions as part of the `PausableImpl` implementation: - -```rust -#[starknet::interface] -pub trait PausableABI { - fn is_paused() -> bool; -} -``` - -## Reentrancy Guard - -A [reentrancy attack](https://gus-tavo-guim.medium.com/reentrancy-attack-on-smart-contracts-how-to-identify-the-exploitable-and-an-example-of-an-attack-4470a2d8dfe4) occurs when the caller is able to obtain more resources than allowed by recursively calling a target’s function. - -### Usage - -Since Cairo does not support modifiers like Solidity, the [ReentrancyGuard](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent) -component exposes two methods `[start](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent-start)` and `[end](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent-end)` to protect functions against reentrancy attacks. -The protected function must call `start` before the first function statement, and `end` before the return statement, as shown below: - -```rust -#[starknet::contract] -mod MyReentrancyContract { - use openzeppelin_security::ReentrancyGuardComponent; - - component!( - path: ReentrancyGuardComponent, storage: reentrancy_guard, event: ReentrancyGuardEvent - ); - - impl InternalImpl = ReentrancyGuardComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - reentrancy_guard: ReentrancyGuardComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - ReentrancyGuardEvent: ReentrancyGuardComponent::Event - } - - #[external(v0)] - fn protected_function(ref self: ContractState) { - self.reentrancy_guard.start(); - - // Do something - - self.reentrancy_guard.end(); - } - - #[external(v0)] - fn another_protected_function(ref self: ContractState) { - self.reentrancy_guard.start(); - - // Do something - - self.reentrancy_guard.end(); - } -} -``` - - -The guard prevents the execution flow occurring inside `protected_function` -to call itself or `another_protected_function`, and vice versa. - diff --git a/content/contracts-cairo/alpha/udc.mdx b/content/contracts-cairo/alpha/udc.mdx deleted file mode 100644 index dd25f5e8..00000000 --- a/content/contracts-cairo/alpha/udc.mdx +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: Universal Deployer Contract ---- - -The Universal Deployer Contract (UDC) is a singleton smart contract that wraps the [deploy syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) to expose it to any contract that doesn’t implement it, such as account contracts. You can think of it as a standardized generic factory for Starknet contracts. - -Since Starknet has no deployment transaction type, it offers a standardized way to deploy smart contracts by following the [Standard Deployer Interface](https://community.starknet.io/t/snip-deployer-contract-interface/2772) and emitting a [ContractDeployed](/contracts-cairo/alpha/api/udc#IUniversalDeployer-ContractDeployed) event. - -For details on the motivation and the decision making process, see the [Universal Deployer Contract proposal](https://community.starknet.io/t/universal-deployer-contract-proposal/1864). - -## UDC contract address - -The UDC is deployed at address `0x02ceed65a4bd731034c01113685c831b01c15d7d432f71afb1cf1634b53a2125` on Starknet sepolia and mainnet. - -## Interface - -```rust -#[starknet::interface] -pub trait IUniversalDeployer { - fn deploy_contract( - class_hash: ClassHash, - salt: felt252, - not_from_zero: bool, - calldata: Span - ) -> ContractAddress; -} -``` - -## Deploying a contract with the UDC - -First, [declare](https://docs.starknet.io/learn/cheatsheets/transactions-reference#declare-v3) the target contract (if it’s not already declared). -Next, call the UDC’s `deploy_contract` method. -Here’s an implementation example in Cairo: - -```rust -use openzeppelin_utils::interfaces::{IUniversalDeployerDispatcher, IUniversalDeployerDispatcherTrait}; - -const UDC_ADDRESS: felt252 = 0x04...; - -fn deploy() -> ContractAddress { - let dispatcher = IUniversalDeployerDispatcher { - contract_address: UDC_ADDRESS.try_into().unwrap() - }; - - // Deployment parameters - let class_hash = class_hash_const::< - 0x5c478ee27f2112411f86f207605b2e2c58cdb647bac0df27f660ef2252359c6 - >(); - let salt = 1234567879; - let not_from_zero = true; - let calldata = array![]; - - // The UDC returns the deployed contract address - dispatcher.deploy_contract(class_hash, salt, not_from_zero, calldata.span()) -} -``` - -## Deployment types - -The Universal Deployer Contract offers two types of addresses to deploy: origin-dependent and origin-independent. -As the names suggest, the origin-dependent type includes the deployer’s address in the address calculation, -whereas, the origin-independent type does not. -The `not_from_zero` boolean parameter ultimately determines the type of deployment. - - - - -When deploying a contract that uses `get_caller_address` in the constructor calldata, remember that the UDC, not the account, deploys that contract. -Therefore, querying `get_caller_address` in a contract’s constructor returns the UDC’s address, _not the account’s address_. - - - -### Origin-dependent - -By making deployments dependent upon the origin address, users can reserve a whole address space to prevent someone else from taking ownership of the address. - -Only the owner of the origin address can deploy to those addresses. - -Achieving this type of deployment necessitates that the origin sets `not_from_zero` to `true` in the [deploy_contract](/contracts-cairo/alpha/api/udc#UniversalDeployer-deploy_contract) call. -Under the hood, the function passes a modified salt to the `deploy_syscall`, which is the hash of the origin’s address with the given salt. - -To deploy a unique contract address pass: - -```js -let deployed_addr = udc.deploy_contract(class_hash, salt, true, calldata.span()); -``` - -### Origin-independent - -Origin-independent contract deployments create contract addresses independent of the deployer and the UDC instance. -Instead, only the class hash, salt, and constructor arguments determine the address. -This type of deployment enables redeployments of accounts and known systems across multiple networks. -To deploy a reproducible deployment, set `not_from_zero` to `false`. - -```rust -let deployed_addr = udc.deploy_contract(class_hash, salt, false, calldata.span()); -``` - -## Version changes - - -See the [previous Universal Deployer API](https://docs.starknet.io/learn/protocol/accounts#deploying-a-new-account) for the initial spec. - - -The latest iteration of the UDC includes some notable changes to the API which include: - -* `deployContract` method is replaced with the snake_case [deploy_contract](/contracts-cairo/alpha/api/udc#UniversalDeployer-deploy_contract). -* `unique` parameter is replaced with `not_from_zero` in both the `deploy_contract` method and [ContractDeployed](/contracts-cairo/alpha/api/udc#IUniversalDeployer-ContractDeployed) event. - -## Precomputing contract addresses - -This library offers utility functions written in Cairo to precompute contract addresses. -They include the generic [calculate_contract_address_from_deploy_syscall](/contracts-cairo/alpha/api/utilities#deployments-calculate_contract_address_from_deploy_syscall) as well as the UDC-specific [calculate_contract_address_from_udc](/contracts-cairo/alpha/api/utilities#deployments-calculate_contract_address_from_udc). -Check out the [deployments](/contracts-cairo/alpha/api/utilities#deployments) for more information. diff --git a/content/contracts-cairo/alpha/upgrades.mdx b/content/contracts-cairo/alpha/upgrades.mdx deleted file mode 100644 index 811738cd..00000000 --- a/content/contracts-cairo/alpha/upgrades.mdx +++ /dev/null @@ -1,127 +0,0 @@ ---- -title: Upgrades ---- - -In different blockchains, multiple patterns have been developed for making a contract upgradeable including the widely adopted proxy patterns. - -Starknet has native upgradeability through a syscall that updates the contract source code, removing [the need for proxies](#proxies-in-starknet). - - -Make sure you follow [our security recommendations](#security) before upgrading. - - -## Replacing contract classes - -To better comprehend how upgradeability works in Starknet, it’s important to understand the difference between a contract and its contract class. - -[Contract Classes](https://docs.starknet.io/build/starknet-by-example/applications/upgradeable-contract#upgradeable-contract) represent the source code of a program. All -contracts are associated to a class, and many contracts can be instances of the same one. Classes are usually represented by -a [class hash](https://docs.starknet.io/build/starknet-by-example/applications/factory#class-hash-and-contract-instance/), and before a contract of a class can be deployed, -the class hash needs to be declared. - -### `replace_class_syscall` - -The `[replace_class](https://docs.starknet.io/build/corelib/core-starknet-syscalls-replace_class_syscall#core-starknet-syscalls-replace-class-syscall)` syscall allows a contract to -update its source code by replacing its class hash once deployed. - -```rust -/// Upgrades the contract source code to the new contract class. -fn upgrade(new_class_hash: ClassHash) { - let CLASS_HASH_CANNOT_BE_ZERO: felt252 = 0x1; - assert(!new_class_hash.is_zero(), CLASS_HASH_CANNOT_BE_ZERO); - starknet::replace_class_syscall(new_class_hash).unwrap_syscall(); -} -``` - - -If a contract is deployed without this mechanism, its class hash can still be replaced through [library calls](https://docs.starknet.io/build/starknet-by-example/advanced/library-calls#library-calls). - - -## `Upgradeable` component - -OpenZeppelin Contracts for Cairo provides [Upgradeable](https://github.com/OpenZeppelin/cairo-contracts/blob/release-v{{umbrella_version}}/packages/upgrades/src/upgradeable.cairo) to add upgradeability support to your contracts. - -### Usage - -Upgrades are often very sensitive operations, and some form of access control is usually required to -avoid unauthorized upgrades. The [Ownable](./access#ownership-and-ownable) module is used in this example. - - -We will be using the following module to implement the [IUpgradeable](/contracts-cairo/alpha/api/upgrades#IUpgradeable) interface described in the API Reference section. - - -```rust -#[starknet::contract] -mod UpgradeableContract { - use openzeppelin_access::ownable::OwnableComponent; - use openzeppelin_upgrades::UpgradeableComponent; - use openzeppelin_interfaces::upgrades::IUpgradeable; - use starknet::ClassHash; - use starknet::ContractAddress; - - component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); - component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); - - // Ownable Mixin - #[abi(embed_v0)] - impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; - impl OwnableInternalImpl = OwnableComponent::InternalImpl; - - // Upgradeable - impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; - - #[storage] - struct Storage { - #[substorage(v0)] - ownable: OwnableComponent::Storage, - #[substorage(v0)] - upgradeable: UpgradeableComponent::Storage - } - - #[event] - #[derive(Drop, starknet::Event)] - enum Event { - #[flat] - OwnableEvent: OwnableComponent::Event, - #[flat] - UpgradeableEvent: UpgradeableComponent::Event - } - - #[constructor] - fn constructor(ref self: ContractState, owner: ContractAddress) { - self.ownable.initializer(owner); - } - - #[abi(embed_v0)] - impl UpgradeableImpl of IUpgradeable { - fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { - // This function can only be called by the owner - self.ownable.assert_only_owner(); - - // Replace the class hash upgrading the contract - self.upgradeable.upgrade(new_class_hash); - } - } -} -``` - -## Security - -Upgrades can be very sensitive operations, and security should always be top of mind while performing one. Please make sure you thoroughly review the changes and their consequences before upgrading. Some aspects to consider are: - -* API changes that might affect integration. For example, changing an external function’s arguments might break existing contracts or offchain systems calling your contract. -* Storage changes that might result in lost data (e.g. changing a storage slot name, making existing storage inaccessible). -* Collisions (e.g. mistakenly reusing the same storage slot from another component) are also possible, although less likely if best practices are followed, for example prepending storage variables with the component’s name (e.g. `ERC20_balances`). -* Always check for [backwards compatibility](./backwards-compatibility) before upgrading between versions of OpenZeppelin Contracts. - -## Proxies in Starknet - -Proxies enable different patterns such as upgrades and clones. But since Starknet achieves the same in different ways is that there’s no support to implement them. - -In the case of contract upgrades, it is achieved by simply changing the contract’s class hash. As of clones, contracts already are like clones of the class they implement. - -Implementing a proxy pattern in Starknet has an important limitation: there is no fallback mechanism to be used -for redirecting every potential function call to the implementation. This means that a generic proxy contract -can’t be implemented. Instead, a limited proxy contract can implement specific functions that forward -their execution to another contract class. -This can still be useful for example to upgrade the logic of some functions. diff --git a/content/contracts-cairo/alpha/utils/constants.js b/content/contracts-cairo/alpha/utils/constants.js deleted file mode 100644 index d3ef174d..00000000 --- a/content/contracts-cairo/alpha/utils/constants.js +++ /dev/null @@ -1,21 +0,0 @@ -export const OPENZEPPELIN_INTERFACES_VERSION = "2.1.0-alpha.0"; -export const OPENZEPPELIN_UTILS_VERSION = "2.1.0-alpha.0"; -export const UMBRELLA_VERSION = "3.0.0-alpha.3"; -export const CLASS_HASH_SCARB_VERSION = "2.12.2"; - -export const CLASS_HASHES = { - AccountUpgradeableClassHash: - "0x072b2479c3bf45bfc2391b0a04bb0fb4806b93a61a8fede391081535cad65038", - ERC20UpgradeableClassHash: - "0x0435835a8002b39bf6eb827678b32a75ed3e0bec580ef71a7c29a068d1a96d24", - ERC721UpgradeableClassHash: - "0x062212af6bc24e478b5f1c611d2f626270e3ef5825330173afa3e02a0848bcaa", - ERC1155UpgradeableClassHash: - "0x01a312230aa2774b3271204bfd41e8633d3b2ab48f10f4b56be1ef17806599c4", - EthAccountUpgradeableClassHash: - "0x071fa21092599f6ffdaed5da83961d895668c668a488465251d88606c26a34b7", - UniversalDeployerClassHash: - "0x038a75a9c5a0203e5fa94bb181850a4e6a349fc3fcda6a0ccbcaeb5c2f50c7c3", - VestingWalletClassHash: - "0x03e7d05eb2325c2e10d219f6b70468aef9e915352ac31521ec2950ebf0e3acb4", -}; diff --git a/content/contracts-cairo/alpha/utils/replacements.ts b/content/contracts-cairo/alpha/utils/replacements.ts deleted file mode 100644 index 32791a2b..00000000 --- a/content/contracts-cairo/alpha/utils/replacements.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { CLASS_HASHES, CLASS_HASH_SCARB_VERSION, OPENZEPPELIN_INTERFACES_VERSION, OPENZEPPELIN_UTILS_VERSION, UMBRELLA_VERSION } from "./constants"; - -export const REPLACEMENTS = { - include: ['**/content/contracts-cairo/alpha/**/*.mdx'], - replacements: { - umbrella_version: UMBRELLA_VERSION, - openzeppelin_interfaces_version: OPENZEPPELIN_INTERFACES_VERSION, - openzeppelin_utils_version: OPENZEPPELIN_UTILS_VERSION, - class_hash_scarb_version: CLASS_HASH_SCARB_VERSION, - ...CLASS_HASHES, - } -} diff --git a/content/contracts-cairo/alpha/wizard.mdx b/content/contracts-cairo/alpha/wizard.mdx deleted file mode 100644 index 7b07a3a3..00000000 --- a/content/contracts-cairo/alpha/wizard.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: Wizard for Cairo ---- - -Not sure where to start? Use the interactive generator below to bootstrap your -contract and learn about the components offered in OpenZeppelin Contracts for Cairo. - - -We strongly recommend checking the [Components](./components) section to understand how to extend from our library. - - -import { UMBRELLA_VERSION } from "./utils/constants.js"; - - From 3e5bd21182cb59d857720f7a8f6e440994fd6291 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 23 Dec 2025 19:28:57 +0300 Subject: [PATCH 03/11] Update Cairo 3.x version and replacements --- .../contracts-cairo/3.x/utils/constants.js | 22 +++++++++---------- content/contracts-cairo/cairo-replacements.ts | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/content/contracts-cairo/3.x/utils/constants.js b/content/contracts-cairo/3.x/utils/constants.js index d3ef174d..53c10df7 100644 --- a/content/contracts-cairo/3.x/utils/constants.js +++ b/content/contracts-cairo/3.x/utils/constants.js @@ -1,21 +1,21 @@ -export const OPENZEPPELIN_INTERFACES_VERSION = "2.1.0-alpha.0"; -export const OPENZEPPELIN_UTILS_VERSION = "2.1.0-alpha.0"; -export const UMBRELLA_VERSION = "3.0.0-alpha.3"; -export const CLASS_HASH_SCARB_VERSION = "2.12.2"; +export const OPENZEPPELIN_INTERFACES_VERSION = "3.0.0"; +export const OPENZEPPELIN_UTILS_VERSION = "3.0.0"; +export const UMBRELLA_VERSION = "3.0.0"; +export const CLASS_HASH_SCARB_VERSION = "2.13.1"; export const CLASS_HASHES = { AccountUpgradeableClassHash: - "0x072b2479c3bf45bfc2391b0a04bb0fb4806b93a61a8fede391081535cad65038", + "0x01d1777db36cdd06dd62cfde77b1b6ae06412af95d57a13dc40ac77b8a702381", ERC20UpgradeableClassHash: - "0x0435835a8002b39bf6eb827678b32a75ed3e0bec580ef71a7c29a068d1a96d24", + "0x0385b75aa04cfee201d2e2de42daf00a839367cfd740e9042766b447d891fea0", ERC721UpgradeableClassHash: - "0x062212af6bc24e478b5f1c611d2f626270e3ef5825330173afa3e02a0848bcaa", + "0x0356fbb89f6a6574ff97c4ecc5fe97808785ce0b093c8e864b87a184f48dbd9e", ERC1155UpgradeableClassHash: - "0x01a312230aa2774b3271204bfd41e8633d3b2ab48f10f4b56be1ef17806599c4", + "0x05a6d419c7a599c9731e8e3a8a710b1f49b4857c65bd4d697a391772f96373c1", EthAccountUpgradeableClassHash: - "0x071fa21092599f6ffdaed5da83961d895668c668a488465251d88606c26a34b7", + "0x000b5bcc16b8b0d86c24996e22206f6071bb8d7307837a02720f0ce2fa1b3d7c", UniversalDeployerClassHash: - "0x038a75a9c5a0203e5fa94bb181850a4e6a349fc3fcda6a0ccbcaeb5c2f50c7c3", + "0x01b2df6d8861670d4a8ca4670433b2418d78169c2947f46dc614e69f333745c8", VestingWalletClassHash: - "0x03e7d05eb2325c2e10d219f6b70468aef9e915352ac31521ec2950ebf0e3acb4", + "0x00540c7f907539e1a283318fb3da16f1bf9d9e60ad10c20d0557a0185043b08f", }; diff --git a/content/contracts-cairo/cairo-replacements.ts b/content/contracts-cairo/cairo-replacements.ts index c6f03ba3..76952d1a 100644 --- a/content/contracts-cairo/cairo-replacements.ts +++ b/content/contracts-cairo/cairo-replacements.ts @@ -1,4 +1,4 @@ -import { REPLACEMENTS as alphaReplacements } from "./alpha/utils/replacements"; import { REPLACEMENTS as twoXReplacements } from "./2.x/utils/replacements"; +import { REPLACEMENTS as threeXReplacements } from "./3.x/utils/replacements"; -export const CAIRO_REPLACEMENTS = [alphaReplacements, twoXReplacements]; +export const CAIRO_REPLACEMENTS = [twoXReplacements, threeXReplacements]; From c756d089b8e05ec84f58f1f9c2740705dfc639ee Mon Sep 17 00:00:00 2001 From: immrsd Date: Fri, 19 Dec 2025 21:49:22 +0300 Subject: [PATCH 04/11] Use umbrella version in wizard.mdx --- content/contracts-cairo/2.x/wizard.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/contracts-cairo/2.x/wizard.mdx b/content/contracts-cairo/2.x/wizard.mdx index 84f581f5..7b07a3a3 100644 --- a/content/contracts-cairo/2.x/wizard.mdx +++ b/content/contracts-cairo/2.x/wizard.mdx @@ -9,4 +9,6 @@ contract and learn about the components offered in OpenZeppelin Contracts for Ca We strongly recommend checking the [Components](./components) section to understand how to extend from our library. - +import { UMBRELLA_VERSION } from "./utils/constants.js"; + + From 4c1fb44b496a1ccd75a7b061dc34bdbe58859ba5 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 23 Dec 2025 19:32:05 +0300 Subject: [PATCH 05/11] Change "/alpha/" to "/3.x/" --- content/contracts-cairo/3.x/access.mdx | 26 ++++++++--------- content/contracts-cairo/3.x/accounts.mdx | 10 +++---- content/contracts-cairo/3.x/components.mdx | 10 +++---- content/contracts-cairo/3.x/erc1155.mdx | 18 ++++++------ content/contracts-cairo/3.x/erc20.mdx | 4 +-- content/contracts-cairo/3.x/erc4626.mdx | 8 +++--- content/contracts-cairo/3.x/erc721.mdx | 12 ++++---- content/contracts-cairo/3.x/finance.mdx | 28 +++++++++---------- content/contracts-cairo/3.x/guides/snip12.mdx | 8 +++--- content/contracts-cairo/3.x/index.mdx | 6 ++-- content/contracts-cairo/3.x/introspection.mdx | 2 +- content/contracts-cairo/3.x/presets.mdx | 14 +++++----- content/contracts-cairo/3.x/security.mdx | 12 ++++---- content/contracts-cairo/3.x/udc.mdx | 12 ++++---- content/contracts-cairo/3.x/upgrades.mdx | 2 +- .../contracts-cairo/3.x/utils/replacements.ts | 2 +- 16 files changed, 87 insertions(+), 87 deletions(-) diff --git a/content/contracts-cairo/3.x/access.mdx b/content/contracts-cairo/3.x/access.mdx index cf78465c..6defd17b 100644 --- a/content/contracts-cairo/3.x/access.mdx +++ b/content/contracts-cairo/3.x/access.mdx @@ -13,13 +13,13 @@ The most common and basic form of access control is the concept of ownership: th of a contract and can do administrative tasks on it. This approach is perfectly reasonable for contracts that have a single administrative user. -OpenZeppelin Contracts for Cairo provides [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent) for implementing ownership in your contracts. +OpenZeppelin Contracts for Cairo provides [OwnableComponent](/contracts-cairo/3.x/api/access#OwnableComponent) for implementing ownership in your contracts. ### Usage Integrating this component into a contract first requires assigning an owner. The implementing contract’s constructor should set the initial owner by passing the owner’s address to Ownable’s -[`initializer`](/contracts-cairo/alpha/api/access#OwnableComponent-initializer) like this: +[`initializer`](/contracts-cairo/3.x/api/access#OwnableComponent-initializer) like this: ```rust #[starknet::contract] @@ -106,7 +106,7 @@ will no longer be callable! ### Two step transfer The component also offers a more robust way of transferring ownership via the -[OwnableTwoStepImpl](/contracts-cairo/alpha/api/access#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) implementation. A two step transfer mechanism helps +[OwnableTwoStepImpl](/contracts-cairo/3.x/api/access#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl) implementation. A two step transfer mechanism helps to prevent unintended and irreversible owner transfers. Simply replace the `OwnableMixinImpl` with its respective two step variant: @@ -146,7 +146,7 @@ flexibility in this regard. In essence, we will be defining multiple roles, each allowed to perform different sets of actions. An account may have, for example, 'moderator', 'minter' or 'admin' roles, which you will then check for -instead of simply using [`assert_only_owner`](/contracts-cairo/alpha/api/access#OwnableComponent-assert_only_owner). This check can be enforced through [`assert_only_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-assert_only_role). +instead of simply using [`assert_only_owner`](/contracts-cairo/3.x/api/access#OwnableComponent-assert_only_owner). This check can be enforced through [`assert_only_role`](/contracts-cairo/3.x/api/access#AccessControlComponent-assert_only_role). Separately, you will be able to define rules for how accounts can be granted a role, have it revoked, and more. Most software uses access control systems that are role-based: some users are regular users, some may be supervisors @@ -158,7 +158,7 @@ For each role that you want to define, you will create a new _role identifier_ t check if an account has that role. See [Creating role identifiers](#creating-role-identifiers) for information on creating identifiers. -Here’s a simple example of implementing [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) on a portion of an ERC20 token contract which defines +Here’s a simple example of implementing [AccessControl](/contracts-cairo/3.x/api/access#AccessControlComponent) on a portion of an ERC20 token contract which defines and sets a 'minter' role: ```rust @@ -242,12 +242,12 @@ mod MyContract { ``` -Make sure you fully understand how [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) works before +Make sure you fully understand how [AccessControl](/contracts-cairo/3.x/api/access#AccessControlComponent) works before using it on your system, or copy-pasting the examples from this guide. While clear and explicit, this isn’t anything we wouldn’t have been able to achieve with -[Ownable](/contracts-cairo/alpha/api/access#OwnableComponent). Where [AccessControl](/contracts-cairo/alpha/api/access#AccessControlComponent) shines the most is in scenarios where granular +[Ownable](/contracts-cairo/3.x/api/access#OwnableComponent). Where [AccessControl](/contracts-cairo/3.x/api/access#AccessControlComponent) shines the most is in scenarios where granular permissions are required, which can be implemented by defining _multiple_ roles. Let’s augment our ERC20 token example by also defining a 'burner' role, which lets accounts destroy tokens: @@ -350,16 +350,16 @@ security practice. Note that each account may still have more than one role, if ### Granting and revoking roles -The ERC20 token example above uses [`_grant_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-_grant_role), +The ERC20 token example above uses [`_grant_role`](/contracts-cairo/3.x/api/access#AccessControlComponent-_grant_role), an `internal` function that is useful when programmatically assigning roles (such as during construction). But what if we later want to grant the 'minter' role to additional accounts? By default, **accounts with a role cannot grant it or revoke it from other accounts**: all having a role does is making -the [`assert_only_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-assert_only_role) check pass. To grant and revoke roles dynamically, you will need help from the role’s _admin_. +the [`assert_only_role`](/contracts-cairo/3.x/api/access#AccessControlComponent-assert_only_role) check pass. To grant and revoke roles dynamically, you will need help from the role’s _admin_. Every role has an associated admin role, which grants permission to call the -[`grant_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-grant_role) and -[`revoke_role`](/contracts-cairo/alpha/api/access#AccessControlComponent-revoke_role) functions. +[`grant_role`](/contracts-cairo/3.x/api/access#AccessControlComponent-grant_role) and +[`revoke_role`](/contracts-cairo/3.x/api/access#AccessControlComponent-revoke_role) functions. A role can be granted or revoked by using these if the calling account has the corresponding admin role. Multiple roles may have the same admin role to make management easier. A role’s admin can even be the same role itself, which would cause accounts with that role to be able @@ -369,9 +369,9 @@ This mechanism can be used to create complex permissioning structures resembling provides an easy way to manage simpler applications. `AccessControl` includes a special role with the role identifier of `0`, called `DEFAULT_ADMIN_ROLE`, which acts as the **default admin role for all roles**. An account with this role will be able to manage any other role, unless -[`set_role_admin`](/contracts-cairo/alpha/api/access#AccessControlComponent-set_role_admin) is used to select a new admin role. +[`set_role_admin`](/contracts-cairo/3.x/api/access#AccessControlComponent-set_role_admin) is used to select a new admin role. -Since it is the admin for all roles by default, and in fact it is also its own admin, this role carries significant risk. To mitigate this risk we provide [AccessControlDefaultAdminRules](/contracts-cairo/alpha/api/access#AccessControlDefaultAdminRulesComponent), a recommended extension of AccessControl that adds a number of enforced security measures for this role: the admin is restricted to a single account, with a 2-step transfer procedure with a delay in between steps. +Since it is the admin for all roles by default, and in fact it is also its own admin, this role carries significant risk. To mitigate this risk we provide [AccessControlDefaultAdminRules](/contracts-cairo/3.x/api/access#AccessControlDefaultAdminRulesComponent), a recommended extension of AccessControl that adds a number of enforced security measures for this role: the admin is restricted to a single account, with a 2-step transfer procedure with a delay in between steps. Let’s take a look at the ERC20 token example, this time taking advantage of the default admin role: diff --git a/content/contracts-cairo/3.x/accounts.mdx b/content/contracts-cairo/3.x/accounts.mdx index b699c4b9..32c4927f 100644 --- a/content/contracts-cairo/3.x/accounts.mdx +++ b/content/contracts-cairo/3.x/accounts.mdx @@ -13,7 +13,7 @@ A more detailed discussion on the topic can be found in [Starknet Shaman’s forum](https://community.starknet.io/t/starknet-account-abstraction-model-part-1/781). -For detailed information on the usage and implementation check the [API Reference](/contracts-cairo/alpha/api/account) section. +For detailed information on the usage and implementation check the [API Reference](/contracts-cairo/3.x/api/account) section. ## What is an account? @@ -97,11 +97,11 @@ Starknet native account abstraction pattern allows for the creation of custom ac usually most account implementations validate transactions using the [Stark curve](https://docs.starknet.io/learn/protocol/cryptography#the-stark-curve) which is the most efficient way of validating signatures since it is a STARK-friendly curve. -OpenZeppelin Contracts for Cairo provides [AccountComponent](/contracts-cairo/alpha/api/account#AccountComponent) for implementing this validation scheme. +OpenZeppelin Contracts for Cairo provides [AccountComponent](/contracts-cairo/3.x/api/account#AccountComponent) for implementing this validation scheme. ### Usage -Constructing an account contract requires integrating both [AccountComponent](/contracts-cairo/alpha/api/account#AccountComponent) and [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: +Constructing an account contract requires integrating both [AccountComponent](/contracts-cairo/3.x/api/account#AccountComponent) and [SRC5Component](/contracts-cairo/3.x/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: ```rust #[starknet::contract(account)] @@ -180,11 +180,11 @@ pub trait AccountABI { ## Ethereum Account Besides the Stark-curve account, OpenZeppelin Contracts for Cairo also offers Ethereum-flavored accounts that use the [secp256k1](https://en.bitcoin.it/wiki/Secp256k1) curve for signature validation. -For this the [EthAccountComponent](/contracts-cairo/alpha/api/account#EthAccountComponent) must be used. +For this the [EthAccountComponent](/contracts-cairo/3.x/api/account#EthAccountComponent) must be used. ### Usage -Constructing a secp256k1 account contract also requires integrating both [EthAccountComponent](/contracts-cairo/alpha/api/account#EthAccountComponent) and [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component). +Constructing a secp256k1 account contract also requires integrating both [EthAccountComponent](/contracts-cairo/3.x/api/account#EthAccountComponent) and [SRC5Component](/contracts-cairo/3.x/api/introspection#SRC5Component). The contract should also set up the constructor to initialize the public key that will be used as the account’s signer. Here’s an example of a basic contract: diff --git a/content/contracts-cairo/3.x/components.mdx b/content/contracts-cairo/3.x/components.mdx index d78deab7..27aaa40d 100644 --- a/content/contracts-cairo/3.x/components.mdx +++ b/content/contracts-cairo/3.x/components.mdx @@ -132,7 +132,7 @@ By adding the embed attribute, `is_initialized` becomes a contract entrypoint fo Embeddable implementations, when available in this library’s components, are segregated from the internal component implementation which makes it easier to safely expose. Components also separate granular implementations from [mixin](#mixins) implementations. The API documentation design reflects these groupings. -See [ERC20Component](/contracts-cairo/alpha/api/erc20#erc20component) as an example which includes: +See [ERC20Component](/contracts-cairo/3.x/api/erc20#erc20component) as an example which includes: * **Embeddable Mixin Implementation** * **Embeddable Implementations** @@ -240,7 +240,7 @@ Always read the API Reference documentation for each integrated component. Some components require some sort of setup upon construction. Usually, this would be a job for a constructor; however, components themselves cannot implement constructors. Components instead offer ``initializer``s within their `InternalImpl` to call from the contract’s constructor. -Let’s look at how a contract would integrate [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent): +Let’s look at how a contract would integrate [OwnableComponent](/contracts-cairo/3.x/api/access#OwnableComponent): ```rust #[starknet::contract] @@ -285,7 +285,7 @@ constants declared in the component, customizing its functionality. The Immutable Component Config standard is defined in the SRC-107. -Here’s an example of how to use the Immutable Component Config pattern with the [ERC2981Component](/contracts-cairo/alpha/api/token_common#erc2981component): +Here’s an example of how to use the Immutable Component Config pattern with the [ERC2981Component](/contracts-cairo/3.x/api/token_common#erc2981component): ```rust #[starknet::contract] @@ -393,7 +393,7 @@ on how to use this function, refer to the [validate section of the SRC-107](http Some components include dependencies of other components. Contracts that integrate components with dependencies must also include the component dependency. -For instance, [AccessControlComponent](/contracts-cairo/alpha/api/access#accesscontrolcomponent) depends on [SRC5Component](/contracts-cairo/alpha/api/introspection#src5component). +For instance, [AccessControlComponent](/contracts-cairo/3.x/api/access#accesscontrolcomponent) depends on [SRC5Component](/contracts-cairo/3.x/api/introspection#src5component). Creating a contract with `AccessControlComponent` should look like this: ```rust @@ -567,7 +567,7 @@ mod ERC20Pausable { The first thing to notice is that the contract imports the interfaces of the implementations that will be customized. These will be used in the next code example. -Next, the contract includes the [ERC20Component](/contracts-cairo/alpha/api/erc20#erc20component) implementations; however, `ERC20Impl` and `ERC20CamelOnlyImpl` are **not** embedded. +Next, the contract includes the [ERC20Component](/contracts-cairo/3.x/api/erc20#erc20component) implementations; however, `ERC20Impl` and `ERC20CamelOnlyImpl` are **not** embedded. Instead, we want to expose our custom implementation of an interface. The following example shows the pausable logic integrated into the ERC20 implementations: diff --git a/content/contracts-cairo/3.x/erc1155.mdx b/content/contracts-cairo/3.x/erc1155.mdx index 45ed4c4a..825c7610 100644 --- a/content/contracts-cairo/3.x/erc1155.mdx +++ b/content/contracts-cairo/3.x/erc1155.mdx @@ -8,11 +8,11 @@ The ERC1155 library implements an approximation of [EIP-1155](https://eips.ether ## Multi Token Standard The distinctive feature of ERC1155 is that it uses a single smart contract to represent multiple tokens at once. This -is why its [balance_of](/contracts-cairo/alpha/api/erc1155#IERC1155-balance_of) function differs from ERC20’s and ERC777’s: it has an additional ID argument for the +is why its [balance_of](/contracts-cairo/3.x/api/erc1155#IERC1155-balance_of) function differs from ERC20’s and ERC777’s: it has an additional ID argument for the identifier of the token that you want to query the balance of. This is similar to how ERC721 does things, but in that standard a token ID has no concept of balance: each token is -non-fungible and exists or doesn’t. The ERC721 [balance_of](/contracts-cairo/alpha/api/erc721#IERC721-balance_of) function refers to how many different tokens an account +non-fungible and exists or doesn’t. The ERC721 [balance_of](/contracts-cairo/3.x/api/erc721#IERC721-balance_of) function refers to how many different tokens an account has, not how many of each. On the other hand, in ERC1155 accounts have a distinct balance for each token ID, and non-fungible tokens are implemented by simply minting a single one of them. @@ -76,8 +76,8 @@ mod MyERC1155 { ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC1155Component](/contracts-cairo/alpha/api/erc1155#ERC1155Component). -The interface includes the [IERC1155](/contracts-cairo/alpha/api/erc1155#IERC1155) standard interface and the optional [IERC1155MetadataURI](/contracts-cairo/alpha/api/erc1155#IERC1155MetadataURI) interface together with [ISRC5](/contracts-cairo/alpha/api/introspection#ISRC5). +The following interface represents the full ABI of the Contracts for Cairo [ERC1155Component](/contracts-cairo/3.x/api/erc1155#ERC1155Component). +The interface includes the [IERC1155](/contracts-cairo/3.x/api/erc1155#IERC1155) standard interface and the optional [IERC1155MetadataURI](/contracts-cairo/3.x/api/erc1155#IERC1155MetadataURI) interface together with [ISRC5](/contracts-cairo/3.x/api/introspection#ISRC5). To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. @@ -148,13 +148,13 @@ Although Starknet is not EVM compatible, this implementation aims to be as close ## Batch operations -Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, [balance_of_batch](/contracts-cairo/alpha/api/erc1155#IERC1155-balance_of_batch) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from), that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive. We also have [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) for non-batch operations. +Because all state is held in a single contract, it is possible to operate over multiple tokens in a single transaction very efficiently. The standard provides two functions, [balance_of_batch](/contracts-cairo/3.x/api/erc1155#IERC1155-balance_of_batch) and [safe_batch_transfer_from](/contracts-cairo/3.x/api/erc1155#IERC1155-safe_batch_transfer_from), that make querying multiple balances and transferring multiple tokens simpler and less gas-intensive. We also have [safe_transfer_from](/contracts-cairo/3.x/api/erc1155#IERC1155-safe_transfer_from) for non-batch operations. In the spirit of the standard, we’ve also included batch operations in the non-standard functions, such as -[batch_mint_with_acceptance_check](/contracts-cairo/alpha/api/erc1155#ERC1155Component-batch_mint_with_acceptance_check). +[batch_mint_with_acceptance_check](/contracts-cairo/3.x/api/erc1155#ERC1155Component-batch_mint_with_acceptance_check). -While [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from) prevent loss by checking the receiver can handle the +While [safe_transfer_from](/contracts-cairo/3.x/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/3.x/api/erc1155#IERC1155-safe_batch_transfer_from) prevent loss by checking the receiver can handle the tokens, this yields execution to the receiver which can result in a [reentrant call](./security#reentrancy-guard). @@ -185,8 +185,8 @@ pub trait IERC1155Receiver { } ``` -Implementing the `IERC1155Receiver` interface exposes the [on_erc1155_received](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver-on_erc1155_received) and [on_erc1155_batch_received](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver-on_erc1155_batch_received) methods. -When [safe_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/alpha/api/erc1155#IERC1155-safe_batch_transfer_from) are called, they invoke the recipient contract’s `on_erc1155_received` or `on_erc1155_batch_received` methods respectively which **must** return the [IERC1155Receiver interface ID](/contracts-cairo/alpha/api/erc1155#IERC1155Receiver). +Implementing the `IERC1155Receiver` interface exposes the [on_erc1155_received](/contracts-cairo/3.x/api/erc1155#IERC1155Receiver-on_erc1155_received) and [on_erc1155_batch_received](/contracts-cairo/3.x/api/erc1155#IERC1155Receiver-on_erc1155_batch_received) methods. +When [safe_transfer_from](/contracts-cairo/3.x/api/erc1155#IERC1155-safe_transfer_from) and [safe_batch_transfer_from](/contracts-cairo/3.x/api/erc1155#IERC1155-safe_batch_transfer_from) are called, they invoke the recipient contract’s `on_erc1155_received` or `on_erc1155_batch_received` methods respectively which **must** return the [IERC1155Receiver interface ID](/contracts-cairo/3.x/api/erc1155#IERC1155Receiver). Otherwise, the transaction will fail. diff --git a/content/contracts-cairo/3.x/erc20.mdx b/content/contracts-cairo/3.x/erc20.mdx index 4c0c5ba1..b788dce5 100644 --- a/content/contracts-cairo/3.x/erc20.mdx +++ b/content/contracts-cairo/3.x/erc20.mdx @@ -68,8 +68,8 @@ For a more complete guide on ERC20 token mechanisms, see [Creating ERC20 Supply] ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC20Component](/contracts-cairo/alpha/api/erc20#ERC20Component). -The interface includes the [IERC20](/contracts-cairo/alpha/api/erc20#IERC20) standard interface as well as the optional [IERC20Metadata](/contracts-cairo/alpha/api/erc20#IERC20Metadata). +The following interface represents the full ABI of the Contracts for Cairo [ERC20Component](/contracts-cairo/3.x/api/erc20#ERC20Component). +The interface includes the [IERC20](/contracts-cairo/3.x/api/erc20#IERC20) standard interface as well as the optional [IERC20Metadata](/contracts-cairo/3.x/api/erc20#IERC20Metadata). To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes an implementation of the interface written in camelCase. diff --git a/content/contracts-cairo/3.x/erc4626.mdx b/content/contracts-cairo/3.x/erc4626.mdx index bcacf612..b56ba562 100644 --- a/content/contracts-cairo/3.x/erc4626.mdx +++ b/content/contracts-cairo/3.x/erc4626.mdx @@ -174,13 +174,13 @@ $\delta = 6$, $a_0 = 1$, $a_1 = 10^5$ ### Custom behavior: Adding fees to the vault In ERC4626 vaults, fees can be captured during deposit/mint and/or withdraw/redeem operations. It is essential to remain -compliant with the ERC4626 requirements regarding the preview functions. Fees are calculated through the [FeeConfigTrait](/contracts-cairo/alpha/api/erc20#ERC4626Component-FeeConfigTrait) +compliant with the ERC4626 requirements regarding the preview functions. Fees are calculated through the [FeeConfigTrait](/contracts-cairo/3.x/api/erc20#ERC4626Component-FeeConfigTrait) implementation. By default, the ERC4626 component charges no fees. If this is the desired behavior, you can use the default [ERC4626DefaultNoFees](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/token/src/erc20/extensions/erc4626/erc4626.cairo#L899) implementation. Starting from v3.0.0, fees can be charged in either assets or shares. Prior versions only supported fees taken in assets. -See the updated [FeeConfigTrait](/contracts-cairo/alpha/api/erc20#ERC4626Component-FeeConfigTrait) and implementation examples in [ERC4626 mocks](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo). +See the updated [FeeConfigTrait](/contracts-cairo/3.x/api/erc20#ERC4626Component-FeeConfigTrait) and implementation examples in [ERC4626 mocks](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo). For example, if calling `deposit(100, receiver)`, the caller should deposit exactly 100 underlying tokens, including fees, and the receiver should receive a number of shares that matches the value returned by `preview_deposit(100)`. @@ -379,8 +379,8 @@ pub mod ERC4626Fees { ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC4626Component](/contracts-cairo/alpha/api/erc20#ERC4626Component). -The full interface includes the [IERC4626](/contracts-cairo/alpha/api/erc20#IERC4626), [IERC20](/contracts-cairo/alpha/api/erc20#IERC20), and [IERC20Metadata](/contracts-cairo/alpha/api/erc20#IERC20Metadata) interfaces. +The following interface represents the full ABI of the Contracts for Cairo [ERC4626Component](/contracts-cairo/3.x/api/erc20#ERC4626Component). +The full interface includes the [IERC4626](/contracts-cairo/3.x/api/erc20#IERC4626), [IERC20](/contracts-cairo/3.x/api/erc20#IERC20), and [IERC20Metadata](/contracts-cairo/3.x/api/erc20#IERC20Metadata) interfaces. Note that implementing the IERC20Metadata interface is a requirement of IERC4626. ```rust diff --git a/content/contracts-cairo/3.x/erc721.mdx b/content/contracts-cairo/3.x/erc721.mdx index a2310b39..63eff4ed 100644 --- a/content/contracts-cairo/3.x/erc721.mdx +++ b/content/contracts-cairo/3.x/erc721.mdx @@ -61,8 +61,8 @@ mod MyNFT { ## Interface -The following interface represents the full ABI of the Contracts for Cairo [ERC721Component](/contracts-cairo/alpha/api/erc721#ERC721Component). -The interface includes the [IERC721](/contracts-cairo/alpha/api/erc721#IERC721) standard interface and the optional [IERC721Metadata](/contracts-cairo/alpha/api/erc721#IERC721Metadata) interface. +The following interface represents the full ABI of the Contracts for Cairo [ERC721Component](/contracts-cairo/3.x/api/erc721#ERC721Component). +The interface includes the [IERC721](/contracts-cairo/3.x/api/erc721#IERC721) standard interface and the optional [IERC721Metadata](/contracts-cairo/3.x/api/erc721#IERC721Metadata) interface. To support older token deployments, as mentioned in [Dual interfaces](./guides/interfaces-and-dispatchers#dual-interfaces), the component also includes implementations of the interface written in camelCase. @@ -126,7 +126,7 @@ The design for `SRC5` is similar to OpenZeppelin’s [ERC165Storage](https://doc ## Token transfers -This library includes [transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-transfer_from) and [safe_transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-safe_transfer_from) to transfer NFTs. +This library includes [transfer_from](/contracts-cairo/3.x/api/erc721#IERC721-transfer_from) and [safe_transfer_from](/contracts-cairo/3.x/api/erc721#IERC721-safe_transfer_from) to transfer NFTs. If using `transfer_from`, **the caller is responsible to confirm that the recipient is capable of receiving NFTs or else they may be permanently lost.** The `safe_transfer_from` method mitigates this risk by querying the recipient contract’s interface support. @@ -153,8 +153,8 @@ pub trait IERC721Receiver { } ``` -Implementing the `IERC721Receiver` interface exposes the [on_erc721_received](/contracts-cairo/alpha/api/erc721#IERC721Receiver-on_erc721_received) method. -When safe methods such as [safe_transfer_from](/contracts-cairo/alpha/api/erc721#IERC721-safe_transfer_from) and [safe_mint](/contracts-cairo/alpha/api/erc721#ERC721-safe_mint) are called, they invoke the recipient contract’s `on_erc721_received` method which **must** return the [IERC721Receiver interface ID](/contracts-cairo/alpha/api/erc721#IERC721Receiver). +Implementing the `IERC721Receiver` interface exposes the [on_erc721_received](/contracts-cairo/3.x/api/erc721#IERC721Receiver-on_erc721_received) method. +When safe methods such as [safe_transfer_from](/contracts-cairo/3.x/api/erc721#IERC721-safe_transfer_from) and [safe_mint](/contracts-cairo/3.x/api/erc721#ERC721-safe_mint) are called, they invoke the recipient contract’s `on_erc721_received` method which **must** return the [IERC721Receiver interface ID](/contracts-cairo/3.x/api/erc721#IERC721Receiver). Otherwise, the transaction will fail. @@ -209,5 +209,5 @@ mod MyTokenReceiver { ## Storing ERC721 URIs Token URIs were previously stored as single field elements prior to Cairo v0.2.5. -ERC721Component now stores only the base URI as a `ByteArray` and the full token URI is returned as the `ByteArray` concatenation of the base URI and the token ID through the [token_uri](/contracts-cairo/alpha/api/erc721#IERC721Metadata-token_uri) method. +ERC721Component now stores only the base URI as a `ByteArray` and the full token URI is returned as the `ByteArray` concatenation of the base URI and the token ID through the [token_uri](/contracts-cairo/3.x/api/erc721#IERC721Metadata-token_uri) method. This design mirrors OpenZeppelin’s default [Solidity implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/932fddf69a699a9a80fd2396fd1a2ab91cdda123/contracts/token/ERC721/ERC721.sol#L85-L93) for ERC721. diff --git a/content/contracts-cairo/3.x/finance.mdx b/content/contracts-cairo/3.x/finance.mdx index 124efeb6..aa717f9e 100644 --- a/content/contracts-cairo/3.x/finance.mdx +++ b/content/contracts-cairo/3.x/finance.mdx @@ -6,8 +6,8 @@ This module includes primitives for financial systems. ## Vesting component -The [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) manages the gradual release of ERC-20 tokens to a designated beneficiary based on a predefined vesting schedule. -The implementing contract must implement the [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent), where the contract owner is regarded as the vesting beneficiary. +The [VestingComponent](/contracts-cairo/3.x/api/finance#VestingComponent) manages the gradual release of ERC-20 tokens to a designated beneficiary based on a predefined vesting schedule. +The implementing contract must implement the [OwnableComponent](/contracts-cairo/3.x/api/access#OwnableComponent), where the contract owner is regarded as the vesting beneficiary. This structure allows ownership rights of both the contract and the vested tokens to be assigned and transferred. @@ -22,26 +22,26 @@ for a beneficiary until a specified date. ### Vesting schedule -The [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait defines the logic for calculating the vested amount based on a given timestamp. This -logic is not part of the [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent), so any contract implementing the [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) must provide its own -implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait. +The [VestingSchedule](/contracts-cairo/3.x/api/finance#VestingComponent-Vesting-Schedule) trait defines the logic for calculating the vested amount based on a given timestamp. This +logic is not part of the [VestingComponent](/contracts-cairo/3.x/api/finance#VestingComponent), so any contract implementing the [VestingComponent](/contracts-cairo/3.x/api/finance#VestingComponent) must provide its own +implementation of the [VestingSchedule](/contracts-cairo/3.x/api/finance#VestingComponent-Vesting-Schedule) trait. -There’s a ready-made implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait available named [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule). +There’s a ready-made implementation of the [VestingSchedule](/contracts-cairo/3.x/api/finance#VestingComponent-Vesting-Schedule) trait available named [LinearVestingSchedule](/contracts-cairo/3.x/api/finance#LinearVestingSchedule). It incorporates a cliff period by returning 0 vested amount until the cliff ends. After the cliff, the vested amount is calculated as directly proportional to the time elapsed since the beginning of the vesting schedule. ### Usage -The contract must integrate [VestingComponent](/contracts-cairo/alpha/api/finance#VestingComponent) and [OwnableComponent](/contracts-cairo/alpha/api/access#OwnableComponent) as dependencies. The contract’s constructor +The contract must integrate [VestingComponent](/contracts-cairo/3.x/api/finance#VestingComponent) and [OwnableComponent](/contracts-cairo/3.x/api/access#OwnableComponent) as dependencies. The contract’s constructor should initialize both components. Core vesting parameters, such as `beneficiary`, `start`, `duration` and `cliff_duration`, are passed as arguments to the constructor and set at the time of deployment. -The implementing contract must provide an implementation of the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait. This can be achieved either by importing -a ready-made [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule) implementation or by defining a custom one. +The implementing contract must provide an implementation of the [VestingSchedule](/contracts-cairo/3.x/api/finance#VestingComponent-Vesting-Schedule) trait. This can be achieved either by importing +a ready-made [LinearVestingSchedule](/contracts-cairo/3.x/api/finance#LinearVestingSchedule) implementation or by defining a custom one. -Here’s an example of a simple vesting wallet contract with a [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule), where the vested amount +Here’s an example of a simple vesting wallet contract with a [LinearVestingSchedule](/contracts-cairo/3.x/api/finance#LinearVestingSchedule), where the vested amount is calculated as being directly proportional to the time elapsed since the start of the vesting period. ```rust @@ -93,19 +93,19 @@ mod LinearVestingWallet { } ``` -A vesting schedule will often follow a custom formula. In such cases, the [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) trait is useful. +A vesting schedule will often follow a custom formula. In such cases, the [VestingSchedule](/contracts-cairo/3.x/api/finance#VestingComponent-Vesting-Schedule) trait is useful. To support a custom vesting schedule, the contract must provide an implementation of the -[calculate_vested_amount](/contracts-cairo/alpha/api/finance#VestingComponent-calculate_vested_amount) function based on the desired formula. +[calculate_vested_amount](/contracts-cairo/3.x/api/finance#VestingComponent-calculate_vested_amount) function based on the desired formula. -When using a custom [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) implementation, the [LinearVestingSchedule](/contracts-cairo/alpha/api/finance#LinearVestingSchedule) must be excluded from the imports. +When using a custom [VestingSchedule](/contracts-cairo/3.x/api/finance#VestingComponent-Vesting-Schedule) implementation, the [LinearVestingSchedule](/contracts-cairo/3.x/api/finance#LinearVestingSchedule) must be excluded from the imports. If there are additional parameters required for calculations, which are stored in the contract’s storage, you can access them using `self.get_contract()`. -Here’s an example of a vesting wallet contract with a custom [VestingSchedule](/contracts-cairo/alpha/api/finance#VestingComponent-Vesting-Schedule) implementation, where tokens +Here’s an example of a vesting wallet contract with a custom [VestingSchedule](/contracts-cairo/3.x/api/finance#VestingComponent-Vesting-Schedule) implementation, where tokens are vested in a number of steps. ```rust diff --git a/content/contracts-cairo/3.x/guides/snip12.mdx b/content/contracts-cairo/3.x/guides/snip12.mdx index a86aae6b..d92926a9 100644 --- a/content/contracts-cairo/3.x/guides/snip12.mdx +++ b/content/contracts-cairo/3.x/guides/snip12.mdx @@ -9,7 +9,7 @@ is then to ensure that the received message was indeed signed by the expected si OpenZeppelin Contracts for Cairo provides a set of utilities to make the implementation of this standard as easy as possible, and in this guide we will walk you through the process of generating the hashes of typed messages -using these utilities for on-chain signature verification. For that, let’s build an example with a custom [ERC20](/contracts-cairo/alpha/api/erc20#ERC20) contract +using these utilities for on-chain signature verification. For that, let’s build an example with a custom [ERC20](/contracts-cairo/3.x/api/erc20#ERC20) contract adding an extra `transfer_with_signature` method. @@ -18,7 +18,7 @@ This is an educational example, and it is not intended to be used in production ## CustomERC20 -Let’s start with a basic ERC20 contract leveraging the [ERC20Component](/contracts-cairo/alpha/api/erc20#ERC20Component), and let’s add the new function. +Let’s start with a basic ERC20 contract leveraging the [ERC20Component](/contracts-cairo/3.x/api/erc20#ERC20Component), and let’s add the new function. Note that some declarations are omitted for brevity. The full example will be available at the end of the guide. ```rust @@ -229,8 +229,8 @@ The expected parameter for the `get_message_hash` function is the address of acc Finally, the full implementation of the `CustomERC20` contract looks like this: -We are using the [`ISRC6Dispatcher`](/contracts-cairo/alpha/api/account#ISRC6) to verify the signature, -and the [`NoncesComponent`](/contracts-cairo/alpha/api/utilities#NoncesComponent) to handle nonces to prevent replay attacks. +We are using the [`ISRC6Dispatcher`](/contracts-cairo/3.x/api/account#ISRC6) to verify the signature, +and the [`NoncesComponent`](/contracts-cairo/3.x/api/utilities#NoncesComponent) to handle nonces to prevent replay attacks. ```rust diff --git a/content/contracts-cairo/3.x/index.mdx b/content/contracts-cairo/3.x/index.mdx index 9c768d64..5e82cb51 100644 --- a/content/contracts-cairo/3.x/index.mdx +++ b/content/contracts-cairo/3.x/index.mdx @@ -8,8 +8,8 @@ title: Contracts for Cairo **A library for secure smart contract development** written in Cairo for [Starknet][starknet]. This library consists of a set of -[reusable components](/contracts-cairo/alpha/components) to build custom smart contracts, as well as ready-to-deploy [presets](/contracts-cairo/alpha/presets). You can also -find other [utilities](/contracts-cairo/alpha/api/utilities) including [interfaces and dispatchers](/contracts-cairo/alpha/interfaces) and [test utilities](/contracts-cairo/alpha/api/testing) +[reusable components](/contracts-cairo/3.x/components) to build custom smart contracts, as well as ready-to-deploy [presets](/contracts-cairo/3.x/presets). You can also +find other [utilities](/contracts-cairo/3.x/api/utilities) including [interfaces and dispatchers](/contracts-cairo/3.x/interfaces) and [test utilities](/contracts-cairo/3.x/api/testing) that facilitate testing with Starknet Foundry. @@ -104,7 +104,7 @@ openzeppelin_utils = "{{openzeppelin_utils_version}}" ## Basic usage -This is how it looks to build an ERC20 contract using the [ERC20 component](/contracts-cairo/alpha/erc20). +This is how it looks to build an ERC20 contract using the [ERC20 component](/contracts-cairo/3.x/erc20). Copy the code into `src/lib.cairo`. ```rust diff --git a/content/contracts-cairo/3.x/introspection.mdx b/content/contracts-cairo/3.x/introspection.mdx index 09fd89b4..03bc547f 100644 --- a/content/contracts-cairo/3.x/introspection.mdx +++ b/content/contracts-cairo/3.x/introspection.mdx @@ -16,7 +16,7 @@ which can be used by others to query if a given interface is supported. ### Usage -To expose this functionality, the contract must implement the [SRC5Component](/contracts-cairo/alpha/api/introspection#SRC5Component), which defines the `supports_interface` function. +To expose this functionality, the contract must implement the [SRC5Component](/contracts-cairo/3.x/api/introspection#SRC5Component), which defines the `supports_interface` function. Here is an example contract: ```rust diff --git a/content/contracts-cairo/3.x/presets.mdx b/content/contracts-cairo/3.x/presets.mdx index 5511457f..c4b02116 100644 --- a/content/contracts-cairo/3.x/presets.mdx +++ b/content/contracts-cairo/3.x/presets.mdx @@ -25,13 +25,13 @@ Before version 2.x, class hashes were computed using the `scarb --dev` profile. | Name | Sierra Class Hash | | --- | --- | -| [`AccountUpgradeable`](/contracts-cairo/alpha/api/account#AccountUpgradeable) | `{{AccountUpgradeableClassHash}}` | -| [`ERC20Upgradeable`](/contracts-cairo/alpha/api/erc20#ERC20Upgradeable) | `{{ERC20UpgradeableClassHash}}` | -| [`ERC721Upgradeable`](/contracts-cairo/alpha/api/erc721#ERC721Upgradeable) | `{{ERC721UpgradeableClassHash}}` | -| [`ERC1155Upgradeable`](/contracts-cairo/alpha/api/erc1155#ERC1155Upgradeable) | `{{ERC1155UpgradeableClassHash}}` | -| [`EthAccountUpgradeable`](/contracts-cairo/alpha/api/account#EthAccountUpgradeable) | `{{EthAccountUpgradeableClassHash}}` | -| [`UniversalDeployer`](/contracts-cairo/alpha/api/udc#UniversalDeployer) | `{{UniversalDeployerClassHash}}` | -| [`VestingWallet`](/contracts-cairo/alpha/api/finance#VestingWallet) | `{{VestingWalletClassHash}}` | +| [`AccountUpgradeable`](/contracts-cairo/3.x/api/account#AccountUpgradeable) | `{{AccountUpgradeableClassHash}}` | +| [`ERC20Upgradeable`](/contracts-cairo/3.x/api/erc20#ERC20Upgradeable) | `{{ERC20UpgradeableClassHash}}` | +| [`ERC721Upgradeable`](/contracts-cairo/3.x/api/erc721#ERC721Upgradeable) | `{{ERC721UpgradeableClassHash}}` | +| [`ERC1155Upgradeable`](/contracts-cairo/3.x/api/erc1155#ERC1155Upgradeable) | `{{ERC1155UpgradeableClassHash}}` | +| [`EthAccountUpgradeable`](/contracts-cairo/3.x/api/account#EthAccountUpgradeable) | `{{EthAccountUpgradeableClassHash}}` | +| [`UniversalDeployer`](/contracts-cairo/3.x/api/udc#UniversalDeployer) | `{{UniversalDeployerClassHash}}` | +| [`VestingWallet`](/contracts-cairo/3.x/api/finance#VestingWallet) | `{{VestingWalletClassHash}}` | [starkli](https://book.starkli.rs/introduction) class-hash command can be used to compute the class hash from a Sierra artifact. diff --git a/content/contracts-cairo/3.x/security.mdx b/content/contracts-cairo/3.x/security.mdx index 13c32d96..4b314eb7 100644 --- a/content/contracts-cairo/3.x/security.mdx +++ b/content/contracts-cairo/3.x/security.mdx @@ -10,7 +10,7 @@ Expect these modules to evolve. ## Initializable -The [Initializable](/contracts-cairo/alpha/api/security#InitializableComponent) component provides a simple mechanism that mimics +The [Initializable](/contracts-cairo/3.x/api/security#InitializableComponent) component provides a simple mechanism that mimics the functionality of a constructor. More specifically, it enables logic to be performed once and only once which is useful to set up a contract’s initial state when a constructor cannot be used, for example when there are circular dependencies at construction time. @@ -68,16 +68,16 @@ pub trait InitializableABI { ## Pausable -The [Pausable](/contracts-cairo/alpha/api/security#PausableComponent) component allows contracts to implement an emergency stop mechanism. +The [Pausable](/contracts-cairo/3.x/api/security#PausableComponent) component allows contracts to implement an emergency stop mechanism. This can be useful for scenarios such as preventing trades until the end of an evaluation period or having an emergency switch to freeze all transactions in the event of a large bug. To become pausable, the contract should include `pause` and `unpause` functions (which should be protected). -For methods that should be available only when paused or not, insert calls to `[assert_paused](/contracts-cairo/alpha/api/security#PausableComponent-assert_paused)` and `[assert_not_paused](/contracts-cairo/alpha/api/security#PausableComponent-assert_not_paused)` +For methods that should be available only when paused or not, insert calls to `[assert_paused](/contracts-cairo/3.x/api/security#PausableComponent-assert_paused)` and `[assert_not_paused](/contracts-cairo/3.x/api/security#PausableComponent-assert_not_paused)` respectively. ### Usage -For example (using the [Ownable](/contracts-cairo/alpha/api/access#OwnableComponent) component for access control): +For example (using the [Ownable](/contracts-cairo/3.x/api/access#OwnableComponent) component for access control): ```rust #[starknet::contract] @@ -164,8 +164,8 @@ A [reentrancy attack](https://gus-tavo-guim.medium.com/reentrancy-attack-on-smar ### Usage -Since Cairo does not support modifiers like Solidity, the [ReentrancyGuard](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent) -component exposes two methods `[start](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent-start)` and `[end](/contracts-cairo/alpha/api/security#ReentrancyGuardComponent-end)` to protect functions against reentrancy attacks. +Since Cairo does not support modifiers like Solidity, the [ReentrancyGuard](/contracts-cairo/3.x/api/security#ReentrancyGuardComponent) +component exposes two methods `[start](/contracts-cairo/3.x/api/security#ReentrancyGuardComponent-start)` and `[end](/contracts-cairo/3.x/api/security#ReentrancyGuardComponent-end)` to protect functions against reentrancy attacks. The protected function must call `start` before the first function statement, and `end` before the return statement, as shown below: ```rust diff --git a/content/contracts-cairo/3.x/udc.mdx b/content/contracts-cairo/3.x/udc.mdx index dd25f5e8..35110ec7 100644 --- a/content/contracts-cairo/3.x/udc.mdx +++ b/content/contracts-cairo/3.x/udc.mdx @@ -4,7 +4,7 @@ title: Universal Deployer Contract The Universal Deployer Contract (UDC) is a singleton smart contract that wraps the [deploy syscall](https://docs.starknet.io/build/corelib/core-starknet-syscalls-deploy_syscall#core-starknet-syscalls-deploy-syscall) to expose it to any contract that doesn’t implement it, such as account contracts. You can think of it as a standardized generic factory for Starknet contracts. -Since Starknet has no deployment transaction type, it offers a standardized way to deploy smart contracts by following the [Standard Deployer Interface](https://community.starknet.io/t/snip-deployer-contract-interface/2772) and emitting a [ContractDeployed](/contracts-cairo/alpha/api/udc#IUniversalDeployer-ContractDeployed) event. +Since Starknet has no deployment transaction type, it offers a standardized way to deploy smart contracts by following the [Standard Deployer Interface](https://community.starknet.io/t/snip-deployer-contract-interface/2772) and emitting a [ContractDeployed](/contracts-cairo/3.x/api/udc#IUniversalDeployer-ContractDeployed) event. For details on the motivation and the decision making process, see the [Universal Deployer Contract proposal](https://community.starknet.io/t/universal-deployer-contract-proposal/1864). @@ -76,7 +76,7 @@ By making deployments dependent upon the origin address, users can reserve a who Only the owner of the origin address can deploy to those addresses. -Achieving this type of deployment necessitates that the origin sets `not_from_zero` to `true` in the [deploy_contract](/contracts-cairo/alpha/api/udc#UniversalDeployer-deploy_contract) call. +Achieving this type of deployment necessitates that the origin sets `not_from_zero` to `true` in the [deploy_contract](/contracts-cairo/3.x/api/udc#UniversalDeployer-deploy_contract) call. Under the hood, the function passes a modified salt to the `deploy_syscall`, which is the hash of the origin’s address with the given salt. To deploy a unique contract address pass: @@ -104,11 +104,11 @@ See the [previous Universal Deployer API](https://docs.starknet.io/learn/protoco The latest iteration of the UDC includes some notable changes to the API which include: -* `deployContract` method is replaced with the snake_case [deploy_contract](/contracts-cairo/alpha/api/udc#UniversalDeployer-deploy_contract). -* `unique` parameter is replaced with `not_from_zero` in both the `deploy_contract` method and [ContractDeployed](/contracts-cairo/alpha/api/udc#IUniversalDeployer-ContractDeployed) event. +* `deployContract` method is replaced with the snake_case [deploy_contract](/contracts-cairo/3.x/api/udc#UniversalDeployer-deploy_contract). +* `unique` parameter is replaced with `not_from_zero` in both the `deploy_contract` method and [ContractDeployed](/contracts-cairo/3.x/api/udc#IUniversalDeployer-ContractDeployed) event. ## Precomputing contract addresses This library offers utility functions written in Cairo to precompute contract addresses. -They include the generic [calculate_contract_address_from_deploy_syscall](/contracts-cairo/alpha/api/utilities#deployments-calculate_contract_address_from_deploy_syscall) as well as the UDC-specific [calculate_contract_address_from_udc](/contracts-cairo/alpha/api/utilities#deployments-calculate_contract_address_from_udc). -Check out the [deployments](/contracts-cairo/alpha/api/utilities#deployments) for more information. +They include the generic [calculate_contract_address_from_deploy_syscall](/contracts-cairo/3.x/api/utilities#deployments-calculate_contract_address_from_deploy_syscall) as well as the UDC-specific [calculate_contract_address_from_udc](/contracts-cairo/3.x/api/utilities#deployments-calculate_contract_address_from_udc). +Check out the [deployments](/contracts-cairo/3.x/api/utilities#deployments) for more information. diff --git a/content/contracts-cairo/3.x/upgrades.mdx b/content/contracts-cairo/3.x/upgrades.mdx index 811738cd..ab27ab32 100644 --- a/content/contracts-cairo/3.x/upgrades.mdx +++ b/content/contracts-cairo/3.x/upgrades.mdx @@ -47,7 +47,7 @@ Upgrades are often very sensitive operations, and some form of access control is avoid unauthorized upgrades. The [Ownable](./access#ownership-and-ownable) module is used in this example. -We will be using the following module to implement the [IUpgradeable](/contracts-cairo/alpha/api/upgrades#IUpgradeable) interface described in the API Reference section. +We will be using the following module to implement the [IUpgradeable](/contracts-cairo/3.x/api/upgrades#IUpgradeable) interface described in the API Reference section. ```rust diff --git a/content/contracts-cairo/3.x/utils/replacements.ts b/content/contracts-cairo/3.x/utils/replacements.ts index 32791a2b..1e339982 100644 --- a/content/contracts-cairo/3.x/utils/replacements.ts +++ b/content/contracts-cairo/3.x/utils/replacements.ts @@ -1,7 +1,7 @@ import { CLASS_HASHES, CLASS_HASH_SCARB_VERSION, OPENZEPPELIN_INTERFACES_VERSION, OPENZEPPELIN_UTILS_VERSION, UMBRELLA_VERSION } from "./constants"; export const REPLACEMENTS = { - include: ['**/content/contracts-cairo/alpha/**/*.mdx'], + include: ['**/content/contracts-cairo/3.x/**/*.mdx'], replacements: { umbrella_version: UMBRELLA_VERSION, openzeppelin_interfaces_version: OPENZEPPELIN_INTERFACES_VERSION, From 5d986afa3b38f7990750e3b4ccac29e73629da2b Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 23 Dec 2025 19:32:48 +0300 Subject: [PATCH 06/11] Fix unexpected 4.x and 5.x versions --- content/contracts-cairo/3.x/erc1155.mdx | 2 +- content/contracts-cairo/3.x/erc20.mdx | 2 +- content/contracts-cairo/3.x/erc721.mdx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/content/contracts-cairo/3.x/erc1155.mdx b/content/contracts-cairo/3.x/erc1155.mdx index 825c7610..5dafa923 100644 --- a/content/contracts-cairo/3.x/erc1155.mdx +++ b/content/contracts-cairo/3.x/erc1155.mdx @@ -2,7 +2,7 @@ title: ERC1155 --- -The ERC1155 multi-token standard is a specification for [fungibility-agnostic](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens) token contracts. +The ERC1155 multi-token standard is a specification for [fungibility-agnostic](https://docs.openzeppelin.com/contracts/3.x/tokens#different-kinds-of-tokens) token contracts. The ERC1155 library implements an approximation of [EIP-1155](https://eips.ethereum.org/EIPS/eip-1155) in Cairo for StarkNet. ## Multi Token Standard diff --git a/content/contracts-cairo/3.x/erc20.mdx b/content/contracts-cairo/3.x/erc20.mdx index b788dce5..389c138d 100644 --- a/content/contracts-cairo/3.x/erc20.mdx +++ b/content/contracts-cairo/3.x/erc20.mdx @@ -2,7 +2,7 @@ title: ERC20 --- -The ERC20 token standard is a specification for [fungible tokens](https://docs.openzeppelin.com/contracts/4.x/tokens#different-kinds-of-tokens), a type of token where all the units are exactly equal to each other. +The ERC20 token standard is a specification for [fungible tokens](https://docs.openzeppelin.com/contracts/3.x/tokens#different-kinds-of-tokens), a type of token where all the units are exactly equal to each other. `token::erc20::ERC20Component` provides an approximation of [EIP-20](https://eips.ethereum.org/EIPS/eip-20) in Cairo for Starknet. diff --git a/content/contracts-cairo/3.x/erc721.mdx b/content/contracts-cairo/3.x/erc721.mdx index 63eff4ed..cf3936f4 100644 --- a/content/contracts-cairo/3.x/erc721.mdx +++ b/content/contracts-cairo/3.x/erc721.mdx @@ -2,7 +2,7 @@ title: ERC721 --- -The ERC721 token standard is a specification for [non-fungible tokens](https://docs.openzeppelin.com/contracts/5.x/tokens#different-kinds-of-tokens), or more colloquially: NFTs. +The ERC721 token standard is a specification for [non-fungible tokens](https://docs.openzeppelin.com/contracts/3.x/tokens#different-kinds-of-tokens), or more colloquially: NFTs. `token::erc721::ERC721Component` provides an approximation of [EIP-721](https://eips.ethereum.org/EIPS/eip-721) in Cairo for Starknet. ## Usage From 3fba2e2161e0fa57a4a1e8b5a2a927e14256cff9 Mon Sep 17 00:00:00 2001 From: immrsd Date: Tue, 23 Dec 2025 19:35:43 +0300 Subject: [PATCH 07/11] Fix openzeppelin_merkle_tree package version in docs --- content/contracts-cairo/3.x/api/merkle-tree.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/contracts-cairo/3.x/api/merkle-tree.mdx b/content/contracts-cairo/3.x/api/merkle-tree.mdx index 5772e073..85f623a1 100644 --- a/content/contracts-cairo/3.x/api/merkle-tree.mdx +++ b/content/contracts-cairo/3.x/api/merkle-tree.mdx @@ -15,7 +15,7 @@ This module provides: To use it as a standalone package, you can add it in your `Scarb.toml` as follows: -`openzeppelin_merkle_tree = "3.0.0-alpha.1"` +`openzeppelin_merkle_tree = "3.0.0"` ## [](#modules)Modules From 3136414e1711c028809756fee0846aca8ce881ec Mon Sep 17 00:00:00 2001 From: immrsd Date: Fri, 19 Dec 2025 21:55:30 +0300 Subject: [PATCH 08/11] Clarify Governor voting start --- content/contracts-cairo/3.x/api/governance.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/contracts-cairo/3.x/api/governance.mdx b/content/contracts-cairo/3.x/api/governance.mdx index e5d5bbe1..d843a6c4 100644 --- a/content/contracts-cairo/3.x/api/governance.mdx +++ b/content/contracts-cairo/3.x/api/governance.mdx @@ -1674,6 +1674,7 @@ Cast a vote. Requirements: - The proposal must be active. +- The current timepoint must be greater than the proposal's snapshot timepoint. Emits a [VoteCast](#GovernorComponent-VoteCast) event. @@ -1688,6 +1689,7 @@ Cast a vote with a `reason`. Requirements: - The proposal must be active. +- The current timepoint must be greater than the proposal's snapshot timepoint. Emits a [VoteCast](#GovernorComponent-VoteCast) event. @@ -1702,6 +1704,7 @@ Cast a vote with a `reason` and additional serialized `params`. Requirements: - The proposal must be active. +- The current timepoint must be greater than the proposal's snapshot timepoint. Emits either: @@ -1719,6 +1722,7 @@ Cast a vote using the `voter`'s signature. Requirements: - The proposal must be active. +- The current timepoint must be greater than the proposal's snapshot timepoint. - The nonce in the signed message must match the account's current nonce. - `voter` must implement `SRC6::is_valid_signature`. - `signature` must be valid for the message hash. @@ -1736,6 +1740,7 @@ Cast a vote with a `reason` and additional serialized `params` using the `voter` Requirements: - The proposal must be active. +- The current timepoint must be greater than the proposal's snapshot timepoint. - The nonce in the signed message must match the account's current nonce. - `voter` must implement `SRC6::is_valid_signature`. - `signature` must be valid for the message hash. From ea646cd7c78ebb30845bdbb1b0659faef89c1b2a Mon Sep 17 00:00:00 2001 From: immrsd Date: Fri, 19 Dec 2025 22:09:53 +0300 Subject: [PATCH 09/11] Clarify EIP-6372 clock bounds --- content/contracts-cairo/3.x/api/governance.mdx | 4 ++++ content/contracts-cairo/3.x/api/utilities.mdx | 3 +++ 2 files changed, 7 insertions(+) diff --git a/content/contracts-cairo/3.x/api/governance.mdx b/content/contracts-cairo/3.x/api/governance.mdx index d843a6c4..7379a017 100644 --- a/content/contracts-cairo/3.x/api/governance.mdx +++ b/content/contracts-cairo/3.x/api/governance.mdx @@ -185,6 +185,10 @@ Whether a proposal needs to be queued before execution. This indicates if the pr Delay between when a proposal is created and when the vote starts. The unit this duration is expressed in depends on the clock (see [ERC-6372](https://eips.ethereum.org/EIPS/eip-6372)) this contract uses. This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. + +NOTE: While this function returns a u64 value, timepoints must fit into u48 according to +the EIP-6372 specification. Consequently this value must fit in a u48 (when added to the +current clock). Date: Fri, 19 Dec 2025 22:21:31 +0300 Subject: [PATCH 10/11] Add note regarding ERC4626 fees mechanics --- content/contracts-cairo/3.x/api/erc20.mdx | 2 ++ content/contracts-cairo/3.x/api/governance.mdx | 4 +--- content/contracts-cairo/3.x/api/utilities.mdx | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/content/contracts-cairo/3.x/api/erc20.mdx b/content/contracts-cairo/3.x/api/erc20.mdx index c7d28f93..99418bfc 100644 --- a/content/contracts-cairo/3.x/api/erc20.mdx +++ b/content/contracts-cairo/3.x/api/erc20.mdx @@ -1139,6 +1139,8 @@ Allows contracts to hook logic into deposit and withdraw transactions. This is w ERC4626 preview methods must be inclusive of any entry or exit fees. Fees are calculated using [FeeConfigTrait](#ERC4626Component-FeeConfigTrait) methods and automatically adjust the final asset and share amounts. Fee transfers are handled in `ERC4626HooksTrait` methods. +When a vault implements fees on deposits or withdrawals (either in shares or assets), fee transfers must be handled in these hooks by library clients. This creates a non-atomic operation flow consisting of multiple state-changing steps: transferring assets, minting or burning shares, and transferring (or minting) fees. Between these steps, the vault's state is temporarily inconsistent: the asset-to-share conversion rate does not accurately reflect the vault's final state until all steps have completed. Therefore, it is critical to avoid making any external calls (including to the vault contract itself) or querying conversion rates during hook execution. + 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](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L253) and [ERC4626SharesFeesMock](https://github.com/OpenZeppelin/cairo-contracts/tree/main/packages/test_common/src/mocks/erc4626.cairo#L426) examples. diff --git a/content/contracts-cairo/3.x/api/governance.mdx b/content/contracts-cairo/3.x/api/governance.mdx index 7379a017..6cf6664e 100644 --- a/content/contracts-cairo/3.x/api/governance.mdx +++ b/content/contracts-cairo/3.x/api/governance.mdx @@ -186,9 +186,7 @@ Delay between when a proposal is created and when the vote starts. The unit this This can be increased to leave time for users to buy voting power, or delegate it, before the voting of a proposal starts. -NOTE: While this function returns a u64 value, timepoints must fit into u48 according to -the EIP-6372 specification. Consequently this value must fit in a u48 (when added to the -current clock). +While this function returns a u64 value, timepoints must fit into u48 according to the EIP-6372 specification. Consequently this value must fit in a u48 (when added to the current clock). Date: Tue, 23 Dec 2025 19:50:39 +0300 Subject: [PATCH 11/11] Update docs to support AccessControlDefaultAdminRules fixes --- content/contracts-cairo/3.x/api/access.mdx | 34 ++++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/content/contracts-cairo/3.x/api/access.mdx b/content/contracts-cairo/3.x/api/access.mdx index 002a41fb..5cfacba1 100644 --- a/content/contracts-cairo/3.x/api/access.mdx +++ b/content/contracts-cairo/3.x/api/access.mdx @@ -241,6 +241,7 @@ Functions - [`change_default_admin_delay(new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) - [`rollback_default_admin_delay()`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) - [`default_admin_delay_increase_wait()`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) +- [`maximum_default_admin_transfer_delay()`](#IAccessControlDefaultAdminRules-maximum_default_admin_transfer_delay) Events @@ -401,11 +402,25 @@ May emit a [DefaultAdminDelayChangeCanceled](#IAccessControlDefaultAdminRules-De id="IAccessControlDefaultAdminRules-default_admin_delay_increase_wait" kind="external" > -Maximum time in seconds for an increase to [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay)) to take effect. Defaults to 5 days. - -When the [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) is scheduled to be increased, it goes into effect after the new delay has passed with the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds) that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can be overridden for a custom [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) increase scheduling. +Maximum time in seconds for an increase to [default\_admin\_delay](#IAccessControlDefaultAdminRules-default_admin_delay) (that is scheduled using [change\_default\_admin\_delay](#IAccessControlDefaultAdminRules-change_default_admin_delay)) to take effect. + Make sure to add a reasonable amount of time while overriding this value, otherwise, there’s a risk of setting a high new delay that goes into effect almost immediately without the possibility of human intervention in the case of an input error (e.g. set milliseconds instead of seconds). + + +Consider carefully the value set for `MAXIMUM_DEFAULT_ADMIN_TRANSFER_DELAY` too, since it will affect how fast you can recover from an accidental delay increase. + + + +Maximum time in seconds for a `default_admin` transfer delay. + + +If `MAXIMUM_DEFAULT_ADMIN_TRANSFER_DELAY` is set too high, you might be unable to recover from an accidental delay increase for an extended period. Too low, and it unnecessarily restricts how much security delay you can impose for `default_admin` transfers. As a best practice, consider setting it in the 30-60 day range for a good balance between security and recoverability. + #### Events [!toc] [#IAccessControlDefaultAdminRules-Events] @@ -1148,6 +1163,7 @@ Embeddable Implementations - [`change_default_admin_delay(self, new_delay)`](#IAccessControlDefaultAdminRules-change_default_admin_delay) - [`rollback_default_admin_delay(self)`](#IAccessControlDefaultAdminRules-rollback_default_admin_delay) - [`default_admin_delay_increase_wait(self)`](#IAccessControlDefaultAdminRules-default_admin_delay_increase_wait) +- [`maximum_default_admin_transfer_delay(self)`](#IAccessControlDefaultAdminRules-maximum_default_admin_transfer_delay) #### AccessControlImpl [!toc] [#AccessControlDefaultAdminRulesComponent-Embeddable-Impls-AccessControlImpl] @@ -1368,6 +1384,18 @@ Make sure to add a reasonable amount of time while overriding this value, otherw + +Maximum time in seconds for a `default_admin` transfer delay. + + +If `MAXIMUM_DEFAULT_ADMIN_TRANSFER_DELAY` is set too high, you might be unable to recover from an accidental delay increase for an extended period. Too low, and it unnecessarily restricts how much security delay you can impose for `default_admin` transfers. As a best practice, consider setting it in the 30-60 day range for a good balance between security and recoverability. + + +