diff --git a/CHANGELOG.md b/CHANGELOG.md index 594f05659..4b33f6b68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Presets are not public anymore, since they should be copied into projects, and not directly imported. - `Trace` and `Checkpoint` structs are not public anymore, since they are intended to be used in `ERC20Votes`, and not as generic utilities. - `StorageArray` is not public anymore, since this implementation is specific to `ERC20Votes`, and is not intended as a generic utility, but as a temporary solution until Starknet native implementation arrives. +- Apply underscore pattern to modules (#993) + - AccessControlComponent + - `_set_role_admin` function renamed to `set_role_admin` + - PausableComponent + - `_pause` function renamed to `pause` + - `_unpause` function renamed to `unpause` + - UpgradeableComponent + - `_upgrade` function renamed to `upgrade` + - ERC20Component: + - `_mint` function renamed to `mint` + - `_burn` function renamed to `burn` + - `_update` function renamed to `update` + - ERC721Component: + - `_safe_transfer` function renamed to `safe_transfer` + - `_safe_mint` function renamed to `safe_mint` + - `_mint` function renamed to `mint` + - `_transfer` function renamed to `transfer` + - `_burn` function renamed to `burn` + - `_update` function renamed to `update` + - ERC1155Component: + - `set_base_uri` function renamed to `_set_base_uri` ## 0.13.0 (2024-05-20) diff --git a/README.md b/README.md index 4cd55a7a9..dd5411c42 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ mod MyToken { let symbol = "MTK"; self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } } ``` diff --git a/docs/modules/ROOT/pages/access.adoc b/docs/modules/ROOT/pages/access.adoc index cdfaf6116..63ed8ddc9 100644 --- a/docs/modules/ROOT/pages/access.adoc +++ b/docs/modules/ROOT/pages/access.adoc @@ -231,7 +231,7 @@ mod MyContract { ) { // ERC20-related initialization self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); // AccessControl-related initialization self.accesscontrol.initializer(); @@ -242,7 +242,7 @@ mod MyContract { #[external(v0)] fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { self.accesscontrol.assert_only_role(MINTER_ROLE); - self.erc20._mint(recipient, amount); + self.erc20.mint(recipient, amount); } } ---- @@ -323,7 +323,7 @@ mod MyContract { ) { // ERC20-related initialization self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); // AccessControl-related initialization self.accesscontrol.initializer(); @@ -335,14 +335,14 @@ mod MyContract { #[external(v0)] fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { self.accesscontrol.assert_only_role(MINTER_ROLE); - self.erc20._mint(recipient, amount); + 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); + self.erc20.burn(account, amount); } } ---- @@ -426,7 +426,7 @@ mod MyContract { ) { // ERC20-related initialization self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); // AccessControl-related initialization self.accesscontrol.initializer(); @@ -437,14 +437,14 @@ mod MyContract { #[external(v0)] fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { self.accesscontrol.assert_only_role(MINTER_ROLE); - self.erc20._mint(recipient, amount); + 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); + self.erc20.burn(account, amount); } } ---- diff --git a/docs/modules/ROOT/pages/api/access.adoc b/docs/modules/ROOT/pages/api/access.adoc index ad0d9e310..224b36623 100644 --- a/docs/modules/ROOT/pages/api/access.adoc +++ b/docs/modules/ROOT/pages/api/access.adoc @@ -3,7 +3,7 @@ :Ownable: xref:OwnableComponent[Ownable] :src5: https://github.com/starknet-io/SNIPs/blob/main/SNIPS/snip-5.md[SRC5] :inner-src5: xref:api/introspection.adoc#ISRC5[SRC5 ID] -:_set_role_admin: xref:#AccessControlComponent-_set_role_admin[_set_role_admin] +:set_role_admin: xref:#AccessControlComponent-set_role_admin[set_role_admin] = Access Control @@ -87,9 +87,9 @@ This module includes the internal `assert_only_owner` to restrict a function to * xref:OwnableComponent-initializer[`++initializer(self, owner)++`] * xref:OwnableComponent-assert_only_owner[`++assert_only_owner(self)++`] -* xref:OwnableComponent-_accept_ownership[`++_accept_ownership(self)++`] -* xref:OwnableComponent-_propose_owner[`++_propose_owner(self, new_owner)++`] * xref:OwnableComponent-_transfer_ownership[`++_transfer_ownership(self, new_owner)++`] +* xref:OwnableComponent-_propose_owner[`++_propose_owner(self, new_owner)++`] +* xref:OwnableComponent-_accept_ownership[`++_accept_ownership(self)++`] -- [.contract-index] @@ -221,16 +221,13 @@ Emits an xref:OwnableComponent-OwnershipTransferred[OwnershipTransferred] event. Panics if called by any account other than the owner. [.contract-item] -[[OwnableComponent-_accept_ownership]] -==== `[.contract-item-name]#++_accept_ownership++#++(ref self: ContractState)++` [.item-kind]#internal# - -Transfers ownership to the pending owner. Resets pending owner to zero address. -Calls xref:OwnableComponent-_transfer_ownership[_transfer_ownership]. +[[OwnableComponent-_transfer_ownership]] +==== `[.contract-item-name]#++_transfer_ownership++#++(ref self: ContractState, new_owner: ContractAddress)++` [.item-kind]#internal# +Transfers ownership of the contract to a new account (`new_owner`). Internal function without access restriction. -[.contract-item] -[[OwnableComponent-_transfer_ownership]] +Emits an xref:OwnableComponent-OwnershipTransferred[OwnershipTransferred] event. [.contract-item] [[OwnableComponent-_propose_owner]] @@ -243,10 +240,12 @@ Internal function without access restriction. Emits an xref:OwnableComponent-OwnershipTransferStarted[OwnershipTransferStarted] event. [.contract-item] -[[OwnableComponent-_transfer_ownership]] -==== `[.contract-item-name]#++_transfer_ownership++#++(ref self: ContractState, new_owner: ContractAddress)++` [.item-kind]#internal# +[[OwnableComponent-_accept_ownership]] +==== `[.contract-item-name]#++_accept_ownership++#++(ref self: ContractState)++` [.item-kind]#internal# + +Transfers ownership to the pending owner. Resets pending owner to zero address. +Calls xref:OwnableComponent-_transfer_ownership[_transfer_ownership]. -Transfers ownership of the contract to a new account (`new_owner`). Internal function without access restriction. Emits an xref:OwnableComponent-OwnershipTransferred[OwnershipTransferred] event. @@ -323,7 +322,7 @@ Returns `true` if `account` has been granted `role`. Returns the admin role that controls `role`. See {grant_role} and {revoke_role}. -To change a role's admin, use {_set_role_admin}. +To change a role's admin, use {set_role_admin}. [.contract-item] [[IAccessControl-grant_role]] @@ -439,7 +438,7 @@ accounts that have a role's admin role can call {grant_role} and {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}. +{set_role_admin}. WARNING: 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 @@ -488,7 +487,7 @@ accounts that have been granted it. * xref:#AccessControlComponent-initializer[`++initializer(self)++`] * xref:#AccessControlComponent-assert_only_role[`++assert_only_role(self, role)++`] -* xref:#AccessControlComponent-_set_role_admin[`++_set_role_admin(self, role, admin_role)++`] +* xref:#AccessControlComponent-set_role_admin[`++set_role_admin(self, role, admin_role)++`] * xref:#AccessControlComponent-_grant_role[`++_grant_role(self, role, account)++`] * xref:#AccessControlComponent-_revoke_role[`++_revoke_role(self, role, account)++`] -- @@ -518,7 +517,7 @@ Returns `true` if `account` has been granted `role`. Returns the admin role that controls `role`. See {grant_role} and {revoke_role}. -To change a role's admin, use {_set_role_admin}. +To change a role's admin, use {set_role_admin}. [.contract-item] [[AccessControlComponent-grant_role]] @@ -620,11 +619,13 @@ Initializes the contract by registering the xref:#IAccessControl[IAccessControl] Panics if called by any account without the given `role`. [.contract-item] -[[AccessControlComponent-_set_role_admin]] -==== `[.contract-item-name]#++_set_role_admin++#++(ref self: ContractState, role: felt252, admin_role: felt252)++` [.item-kind]#internal# +[[AccessControlComponent-set_role_admin]] +==== `[.contract-item-name]#++set_role_admin++#++(ref self: ContractState, role: felt252, admin_role: felt252)++` [.item-kind]#internal# Sets `admin_role` as ``role``'s admin role. +Internal function without access restriction. + Emits a {RoleAdminChanged} event. [.contract-item] diff --git a/docs/modules/ROOT/pages/api/erc1155.adoc b/docs/modules/ROOT/pages/api/erc1155.adoc index 07475569d..35448c48d 100644 --- a/docs/modules/ROOT/pages/api/erc1155.adoc +++ b/docs/modules/ROOT/pages/api/erc1155.adoc @@ -220,13 +220,13 @@ NOTE: See xref:#ERC1155Component-Hooks[Hooks] to understand how are hooks used. -- .InternalImpl * xref:#ERC1155Component-initializer[`++initializer(self, base_uri)++`] -* xref:#ERC1155Component-update[`++update(self, from, to, token_ids, values)++`] -* xref:#ERC1155Component-update_with_acceptance_check[`++update_with_acceptance_check(self, from, to, token_ids, values, data)++`] * xref:#ERC1155Component-mint_with_acceptance_check[`++mint_with_acceptance_check(self, to, token_id, value, data)++`] * xref:#ERC1155Component-batch_mint_with_acceptance_check[`++batch_mint_with_acceptance_check(self, to, token_ids, values, data)++`] * xref:#ERC1155Component-burn[`++burn(self, from, token_id, value)++`] * xref:#ERC1155Component-batch_burn[`++batch_burn(self, from, token_ids, values)++`] -* xref:#ERC1155Component-set_base_uri[`++set_base_uri(self, base_uri)++`] +* xref:#ERC1155Component-update_with_acceptance_check[`++update_with_acceptance_check(self, from, to, token_ids, values, data)++`] +* xref:#ERC1155Component-update[`++update(self, from, to, token_ids, values)++`] +* xref:#ERC1155Component-_set_base_uri[`++_set_base_uri(self, base_uri)++`] -- [.contract-index] @@ -401,42 +401,6 @@ See <, values: Span)++` [.item-kind]#internal# - -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 <> event if the arrays contain one element, -and <> otherwise. - -NOTE: This function can be extended using the xref:ERC1155Component-ERC1155HooksTrait[ERC1155HooksTrait], to add -functionality before and/or after the transfer, mint, or burn. - -NOTE: The ERC1155 acceptance check is not performed in this function. -See <> instead. - -[.contract-item] -[[ERC1155Component-update_with_acceptance_check]] -==== `[.contract-item-name]#++update_with_acceptance_check++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span, data: Span)++` [.item-kind]#internal# - -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 <> event if the arrays contain one element, -and <> otherwise. - [.contract-item] [[ERC1155Component-mint_with_acceptance_check]] ==== `[.contract-item-name]#++mint_with_acceptance_check++#++(ref self: ContractState, to: ContractAddress, token_id: u256, value: u256, data: Span)++` [.item-kind]#internal# @@ -494,8 +458,44 @@ Requirements: Emits a <> event. [.contract-item] -[[ERC1155Component-set_base_uri]] -==== `[.contract-item-name]#++set_base_uri++#++(ref self: ContractState, base_uri: ByteArray)++` [.item-kind]#internal# +[[ERC1155Component-update_with_acceptance_check]] +==== `[.contract-item-name]#++update_with_acceptance_check++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span, data: Span)++` [.item-kind]#internal# + +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 <> event if the arrays contain one element, +and <> otherwise. + +[.contract-item] +[[ERC1155Component-update]] +==== `[.contract-item-name]#++update++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span, values: Span)++` [.item-kind]#internal# + +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 <> event if the arrays contain one element, +and <> otherwise. + +NOTE: This function can be extended using the xref:ERC1155Component-ERC1155HooksTrait[ERC1155HooksTrait], to add +functionality before and/or after the transfer, mint, or burn. + +NOTE: The ERC1155 acceptance check is not performed in this function. +See <> instead. + +[.contract-item] +[[ERC1155Component-_set_base_uri]] +==== `[.contract-item-name]#++_set_base_uri++#++(ref self: ContractState, base_uri: ByteArray)++` [.item-kind]#internal# Sets a new URI for all token types, by relying on the token type ID substitution mechanism diff --git a/docs/modules/ROOT/pages/api/erc20.adoc b/docs/modules/ROOT/pages/api/erc20.adoc index 84c17b014..0ec307541 100644 --- a/docs/modules/ROOT/pages/api/erc20.adoc +++ b/docs/modules/ROOT/pages/api/erc20.adoc @@ -220,12 +220,12 @@ NOTE: See xref:#ERC20Component-Hooks[Hooks] to understand how are hooks used. -- .InternalImpl * xref:#ERC20Component-initializer[`++initializer(self, name, symbol)++`] +* xref:#ERC20Component-mint[`++mint(self, recipient, amount)++`] +* xref:#ERC20Component-burn[`++burn(self, account, amount)++`] +* xref:#ERC20Component-update[`++update(self, from, to, amount)++`] * xref:#ERC20Component-_transfer[`++_transfer(self, sender, recipient, amount)++`] * xref:#ERC20Component-_approve[`++_approve(self, owner, spender, amount)++`] -* xref:#ERC20Component-_mint[`++_mint(self, recipient, amount)++`] -* xref:#ERC20Component-_burn[`++_burn(self, account, amount)++`] * xref:#ERC20Component-_spend_allowance[`++_spend_allowance(self, owner, spender, amount)++`] -* xref:#ERC20Component-_update[`++_update(self, from, to, amount)++`] -- [.contract-index] @@ -249,13 +249,13 @@ for this purpose. [[ERC20Component-before_update]] ==== `[.contract-item-name]#++before_update++#++(ref self: ContractState, from: ContractAddress, recipient: ContractAddress, amount: u256)++` [.item-kind]#hook# -Function executed at the beginning of the xref:#ERC20Component-_update[_update] function prior to any other logic. +Function executed at the beginning of the xref:#ERC20Component-update[update] function prior to any other logic. [.contract-item] [[ERC20Component-after_update]] ==== `[.contract-item-name]#++after_update++#++(ref self: ContractState, from: ContractAddress, recipient: ContractAddress, amount: u256)++` [.item-kind]#hook# -Function executed at the end of the xref:#ERC20Component-_update[_update] function. +Function executed at the end of the xref:#ERC20Component-update[update] function. [#ERC20Component-Embeddable-functions] ==== Embeddable functions @@ -364,6 +364,42 @@ Supports the Cairo v0 convention of writing external methods in camelCase as dis Initializes the contract by setting the token name and symbol. This should be used inside of the contract's constructor. +[.contract-item] +[[ERC20Component-mint]] +==== `[.contract-item-name]#++mint++#++(ref self: ContractState, recipient: ContractAddress, amount: u256)++` [.item-kind]#internal# + +Creates an `amount` number of tokens and assigns them to `recipient`. + +Emits a <> event with `from` being the zero address. + +Requirements: + +- `recipient` cannot be the zero address. + +[.contract-item] +[[ERC20Component-burn]] +==== `[.contract-item-name]#++burn++#++(ref self: ContractState, account: ContractAddress, amount: u256)++` [.item-kind]#internal# + +Destroys `amount` number of tokens from `account`. + +Emits a <> event with `to` set to the zero address. + +Requirements: + +- `account` cannot be the zero address. + +[.contract-item] +[[ERC20Component-update]] +==== `[.contract-item-name]#++update++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256)++` [.item-kind]#internal# + +Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is +the zero address. + +NOTE: This function can be extended using the xref:ERC20Component-ERC20HooksTrait[ERC20HooksTrait], to add +functionality before and/or after the transfer, mint, or burn. + +Emits a <> event. + [.contract-item] [[ERC20Component-_transfer]] ==== `[.contract-item-name]#++_transfer++#++(ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256)++` [.item-kind]#internal# @@ -395,30 +431,6 @@ Requirements: - `owner` cannot be the zero address. - `spender` cannot be the zero address. -[.contract-item] -[[ERC20Component-_mint]] -==== `[.contract-item-name]#++_mint++#++(ref self: ContractState, recipient: ContractAddress, amount: u256)++` [.item-kind]#internal# - -Creates an `amount` number of tokens and assigns them to `recipient`. - -Emits a <> event with `from` being the zero address. - -Requirements: - -- `recipient` cannot be the zero address. - -[.contract-item] -[[ERC20Component-_burn]] -==== `[.contract-item-name]#++_burn++#++(ref self: ContractState, account: ContractAddress, amount: u256)++` [.item-kind]#internal# - -Destroys `amount` number of tokens from `account`. - -Emits a <> event with `to` set to the zero address. - -Requirements: - -- `account` cannot be the zero address. - [.contract-item] [[ERC20Component-_spend_allowance]] ==== `[.contract-item-name]#++_spend_allowance++#++(ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256)++` [.item-kind]#internal# @@ -429,18 +441,6 @@ This internal function does not update the allowance value in the case of infini Possibly emits an <> event. -[.contract-item] -[[ERC20Component-_update]] -==== `[.contract-item-name]#++_update++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, amount: u256)++` [.item-kind]#internal# - -Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is -the zero address. - -NOTE: This function can be extended using the xref:ERC20Component-ERC20HooksTrait[ERC20HooksTrait], to add -functionality before and/or after the transfer, mint, or burn. - -Emits a <> event. - [#ERC20Component-Events] ==== Events diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index ecd11cf4e..3f1a6e18f 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -258,22 +258,22 @@ NOTE: See xref:#ERC721Component-Hooks[Hooks] to understand how are hooks used. -- .InternalImpl * xref:#ERC721Component-initializer[`++initializer(self, name, symbol, base_uri)++`] +* xref:#ERC721Component-exists[`++exists(self, token_id)++`] +* xref:#ERC721Component-transfer[`++transfer(self, from, to, token_id)++`] +* xref:#ERC721Component-mint[`++mint(self, to, token_id)++`] +* xref:#ERC721Component-safe_transfer[`++safe_transfer(self, from, to, token_id, data)++`] +* xref:#ERC721Component-safe_mint[`++safe_mint(self, to, token_id, data)++`] +* xref:#ERC721Component-burn[`++burn(self, token_id)++`] +* xref:#ERC721Component-update[`++update(self, to, token_id, auth)++`] * xref:#ERC721Component-_owner_of[`++_owner_of(self, token_id)++`] * xref:#ERC721Component-_require_owned[`++_require_owned(self, token_id)++`] -* xref:#ERC721Component-_exists[`++_exists(self, token_id)++`] * xref:#ERC721Component-_approve[`++_approve(self, to, token_id, auth)++`] * xref:#ERC721Component-_approve_with_optional_event[`++_approve_with_optional_event(self, to, token_id, auth, emit_event)++`] * xref:#ERC721Component-_set_approval_for_all[`++_set_approval_for_all(self, owner, operator, approved)++`] -* xref:#ERC721Component-_mint[`++_mint(self, to, token_id)++`] -* xref:#ERC721Component-_transfer[`++_transfer(self, from, to, token_id)++`] -* xref:#ERC721Component-_burn[`++_burn(self, token_id)++`] -* xref:#ERC721Component-_safe_mint[`++_safe_mint(self, to, token_id, data)++`] -* xref:#ERC721Component-_safe_transfer[`++_safe_transfer(self, from, to, token_id, data)++`] * xref:#ERC721Component-_set_base_uri[`++_set_base_uri(self, base_uri)++`] * xref:#ERC721Component-_base_uri[`++_base_uri(self)++`] * xref:#ERC721Component-_is_authorized[`++_is_authorized(self, owner, spender, token_id)++`] * xref:#ERC721Component-_check_authorized[`++_check_authorized(self, owner, spender, token_id)++`] -* xref:#ERC721Component-_update[`++_update(self, to, token_id, auth)++`] -- [.contract-index] @@ -299,13 +299,13 @@ for this purpose. [[ERC721Component-before_update]] ==== `[.contract-item-name]#++before_update++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)++` [.item-kind]#hook# -Function executed at the beginning of the xref:#ERC721Component-_update[_update] function prior to any other logic. +Function executed at the beginning of the xref:#ERC721Component-update[update] function prior to any other logic. [.contract-item] [[ERC721Component-after_update]] ==== `[.contract-item-name]#++after_update++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)++` [.item-kind]#hook# -Function executed at the end of the xref:#ERC721Component-_update[_update] function. +Function executed at the end of the xref:#ERC721Component-update[update] function. ==== Embeddable functions @@ -470,102 +470,86 @@ Initializes the contract by setting the token name and symbol. This should be used inside the contract's constructor. [.contract-item] -[[ERC721Component-_owner_of]] -==== `[.contract-item-name]#++_owner_of++#++(self: @ContractState, token_id: felt252) -> ContractAddress++` [.item-kind]#internal# - -Internal function that returns the owner address of `token_id`. - -[.contract-item] -[[ERC721Component-_require_owned]] -==== `[.contract-item-name]#++_require_owned++#++(self: @ContractState, token_id: felt252) -> ContractAddress++` [.item-kind]#internal# - -Version of xref:#ERC721Component-_owner_of[_owner_of] that panics if owner is the zero address. - -[.contract-item] -[[ERC721Component-_exists]] -==== `[.contract-item-name]#++_exists++#++(self: @ContractState, token_id: u256) -> bool++` [.item-kind]#internal# +[[ERC721Component-exists]] +==== `[.contract-item-name]#++exists++#++(self: @ContractState, token_id: u256) -> bool++` [.item-kind]#internal# Internal function that returns whether `token_id` exists. -Tokens start existing when they are minted (<>), and stop existing when they are burned (<>). +Tokens start existing when they are minted (<>), and stop existing when they are burned (<>). [.contract-item] -[[ERC721Component-_approve]] -==== `[.contract-item-name]#++_approve++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)++` [.item-kind]#internal# - -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. +[[ERC721Component-transfer]] +==== `[.contract-item-name]#++transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# -Emits an <> event. - -[.contract-item] -[[ERC721Component-_approve_with_optional_event]] -==== `[.contract-item-name]#++_approve_with_optional_event++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress, emit_event: bool)++` [.item-kind]#internal# +Transfers `token_id` from `from` to `to`. -Variant of xref:#ERC721Component-_approve[_approve] with an optional flag to enable or disable the `Approval` event. -The event is not emitted in the context of transfers. +Internal function without access restriction. -WARNING: If `auth` is zero and `emit_event` is false, this function will not check that the token exists. +WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. Requirements: -- if `auth` is non-zero, it must be either the owner of the token or approved to -operate on all of its tokens. +- `to` is not the zero address. +- `from` is the token owner. +- `token_id` exists. -May emit an <> event. +Emits a <> event. [.contract-item] -[[ERC721Component-_set_approval_for_all]] -==== `[.contract-item-name]#++_set_approval_for_all++#++(ref self: ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` [.item-kind]#internal# +[[ERC721Component-mint]] +==== `[.contract-item-name]#++mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# -Enables or disables approval for `operator` to manage -all of the `owner` assets. +Mints `token_id` and transfers it to `to`. +Internal function without access restriction. + +WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. Requirements: -- `operator` is not the zero address. +- `to` is not the zero address. +- `token_id` does not exist. -Emits an <> event. +Emits a <> event. [.contract-item] -[[ERC721Component-_mint]] -==== `[.contract-item-name]#++_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# +[[ERC721Component-safe_transfer]] +==== `[.contract-item-name]#++safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# -Mints `token_id` and transfers it to `to`. -Internal function without access restriction. +Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. -WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. +`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. + +WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. Requirements: -- `to` is not the zero address. -- `token_id` does not exist. +- `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 <> event. [.contract-item] -[[ERC721Component-_transfer]] -==== `[.contract-item-name]#++_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256)++` [.item-kind]#internal# +[[ERC721Component-safe_mint]] +==== `[.contract-item-name]#++safe_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# -Transfers `token_id` from `from` to `to`. +Mints `token_id` if `to` is either an account or `IERC721Receiver`. -Internal function without access restriction. +`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. -WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. +WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. Requirements: -- `to` is not the zero address. -- `from` is the token owner. -- `token_id` exists. +- `token_id` does not exist. +- `to` is either an account contract or supports the `IERC721Receiver` interface. Emits a <> event. [.contract-item] -[[ERC721Component-_burn]] -==== `[.contract-item-name]#++_burn++#++(ref self: ContractState, token_id: u256)++` [.item-kind]#internal# +[[ERC721Component-burn]] +==== `[.contract-item-name]#++burn++#++(ref self: ContractState, token_id: u256)++` [.item-kind]#internal# Destroys `token_id`. The approval is cleared when the token is burned. @@ -579,40 +563,71 @@ Requirements: Emits a <> event. [.contract-item] -[[ERC721Component-_safe_mint]] -==== `[.contract-item-name]#++_safe_mint++#++(ref self: ContractState, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# +[[ERC721Component-update]] +==== `[.contract-item-name]#++update++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)++` [.item-kind]#internal# -Mints `token_id` if `to` is either an account or `IERC721Receiver`. +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. -`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. +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). -WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. +Emits a <> event. -Requirements: +NOTE: This function can be extended using the `ERC721HooksTrait`, to add +functionality before and/or after the transfer, mint, or burn. -- `token_id` does not exist. -- `to` is either an account contract or supports the `IERC721Receiver` interface. +[.contract-item] +[[ERC721Component-_owner_of]] +==== `[.contract-item-name]#++_owner_of++#++(self: @ContractState, token_id: felt252) -> ContractAddress++` [.item-kind]#internal# -Emits a <> event. +Internal function that returns the owner address of `token_id`. + +[.contract-item] +[[ERC721Component-_require_owned]] +==== `[.contract-item-name]#++_require_owned++#++(self: @ContractState, token_id: felt252) -> ContractAddress++` [.item-kind]#internal# + +Version of xref:#ERC721Component-_owner_of[_owner_of] that panics if owner is the zero address. [.contract-item] -[[ERC721Component-_safe_transfer]] -==== `[.contract-item-name]#++_safe_transfer++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_id: u256, data: Span)++` [.item-kind]#internal# +[[ERC721Component-_approve]] +==== `[.contract-item-name]#++_approve++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)++` [.item-kind]#internal# -Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. +Approve `to` to operate on `token_id` -`data` is additional data, it has no specified format and is forwarded in `IERC721Receiver::on_erc721_received` to `to`. +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. -WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. +Emits an <> event. + +[.contract-item] +[[ERC721Component-_approve_with_optional_event]] +==== `[.contract-item-name]#++_approve_with_optional_event++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress, emit_event: bool)++` [.item-kind]#internal# + +Variant of xref:#ERC721Component-_approve[_approve] with an optional flag to enable or disable the `Approval` event. +The event is not emitted in the context of transfers. + +WARNING: If `auth` is zero and `emit_event` is false, this function will not check that the token exists. 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. +- if `auth` is non-zero, it must be either the owner of the token or approved to +operate on all of its tokens. -Emits a <> event. +May emit an <> event. + +[.contract-item] +[[ERC721Component-_set_approval_for_all]] +==== `[.contract-item-name]#++_set_approval_for_all++#++(ref self: ContractState, owner: ContractAddress, operator: ContractAddress, approved: bool)++` [.item-kind]#internal# + +Enables or disables approval for `operator` to manage +all of the `owner` assets. + +Requirements: + +- `operator` is not the zero address. + +Emits an <> event. [.contract-item] [[ERC721Component-_set_base_uri]] @@ -654,21 +669,6 @@ Requirements: WARNING: This function assumes that `owner` is the actual owner of `token_id` and does not verify this assumption. -[.contract-item] -[[ERC721Component-_update]] -==== `[.contract-item-name]#++_update++#++(ref self: ContractState, to: ContractAddress, token_id: u256, auth: ContractAddress)++` [.item-kind]#internal# - -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 <> event. - -NOTE: This function can be extended using the `ERC721HooksTrait`, to add -functionality before and/or after the transfer, mint, or burn. - ==== Events [.contract-item] diff --git a/docs/modules/ROOT/pages/api/security.adoc b/docs/modules/ROOT/pages/api/security.adoc index b24dab60c..981b933b6 100644 --- a/docs/modules/ROOT/pages/api/security.adoc +++ b/docs/modules/ROOT/pages/api/security.adoc @@ -84,8 +84,8 @@ Component to implement an emergency stop mechanism. * xref:#PausableComponent-assert_not_paused[`++assert_not_paused(self)++`] * xref:#PausableComponent-assert_paused[`++assert_paused(self)++`] -* xref:#PausableComponent-_pause[`++_pause(self)++`] -* xref:#PausableComponent-_unpause[`++_unpause(self)++`] +* xref:#PausableComponent-pause[`++pause(self)++`] +* xref:#PausableComponent-unpause[`++unpause(self)++`] -- [.contract-index] @@ -120,8 +120,8 @@ Panics if the contract is paused. Panics if the contract is not paused. [.contract-item] -[[PausableComponent-_pause]] -==== `[.contract-item-name]#++_pause++#++(ref self: ComponentState)++` [.item-kind]#internal# +[[PausableComponent-pause]] +==== `[.contract-item-name]#++pause++#++(ref self: ComponentState)++` [.item-kind]#internal# Pauses the contract. @@ -132,8 +132,8 @@ Requirements: Emits a {Paused} event. [.contract-item] -[[PausableComponent-_unpause]] -==== `[.contract-item-name]#++_unpause++#++(ref self: ComponentState)++` [.item-kind]#internal# +[[PausableComponent-unpause]] +==== `[.contract-item-name]#++unpause++#++(ref self: ComponentState)++` [.item-kind]#internal# Unpauses the contract. diff --git a/docs/modules/ROOT/pages/api/upgrades.adoc b/docs/modules/ROOT/pages/api/upgrades.adoc index d0ebd0f7b..6a970b74f 100644 --- a/docs/modules/ROOT/pages/api/upgrades.adoc +++ b/docs/modules/ROOT/pages/api/upgrades.adoc @@ -51,7 +51,7 @@ Upgradeable component. -- .InternalImpl -* xref:#UpgradeableComponent-_upgrade[`++_upgrade(self, new_class_hash)++`] +* xref:#UpgradeableComponent-upgrade[`++upgrade(self, new_class_hash)++`] -- [.contract-index] @@ -64,8 +64,8 @@ Upgradeable component. ==== Internal Functions [.contract-item] -[[UpgradeableComponent-_upgrade]] -==== `[.contract-item-name]#++_upgrade++#++(ref self: ContractState, new_class_hash: ClassHash)++` [.item-kind]#internal# +[[UpgradeableComponent-upgrade]] +==== `[.contract-item-name]#++upgrade++#++(ref self: ContractState, new_class_hash: ClassHash)++` [.item-kind]#internal# Upgrades the contract by updating the contract {class_hash}. diff --git a/docs/modules/ROOT/pages/erc20.adoc b/docs/modules/ROOT/pages/erc20.adoc index 9d79aaa59..5aa5b5714 100644 --- a/docs/modules/ROOT/pages/erc20.adoc +++ b/docs/modules/ROOT/pages/erc20.adoc @@ -56,7 +56,7 @@ mod MyToken { let symbol = "MTK"; self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } } ---- @@ -210,7 +210,7 @@ mod MyToken { let symbol = "MTK"; self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } #[external(v0)] diff --git a/docs/modules/ROOT/pages/erc721.adoc b/docs/modules/ROOT/pages/erc721.adoc index 8c87643e0..01a1bcdf4 100644 --- a/docs/modules/ROOT/pages/erc721.adoc +++ b/docs/modules/ROOT/pages/erc721.adoc @@ -8,7 +8,7 @@ The ERC721 token standard is a specification for {token-types}, or more colloqui == Usage -:mint-api: xref:api/erc721.adoc#ERC721-_mint[_mint] +:mint-api: xref:api/erc721.adoc#ERC721-mint[mint] 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. @@ -58,7 +58,7 @@ mod MyNFT { let token_id = 1; self.erc721.initializer(name, symbol, base_uri); - self.erc721._mint(recipient, token_id); + self.erc721.mint(recipient, token_id); } } ---- @@ -156,7 +156,7 @@ WARNING: Usage of `safe_transfer_from` prevents loss, though the caller must und :on_erc721_received-api: xref:api/erc721.adoc#IERC721Receiver-on_erc721_received[on_erc721_received] :computing-interface-id: xref:introspection.adoc#computing_the_interface_id[Computing the interface ID] :safe_transfer_from-api: xref:api/erc721.adoc#IERC721-safe_transfer_from[safe_transfer_from] -:safe_mint-api: xref:api/erc721.adoc#ERC721-_safe_mint[_safe_mint] +:safe_mint-api: xref:api/erc721.adoc#ERC721-safe_mint[safe_mint] 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} interface which, as described earlier, supports interface introspection. diff --git a/docs/modules/ROOT/pages/guides/erc20-supply.adoc b/docs/modules/ROOT/pages/guides/erc20-supply.adoc index 691fbdf68..a1c86340e 100644 --- a/docs/modules/ROOT/pages/guides/erc20-supply.adoc +++ b/docs/modules/ROOT/pages/guides/erc20-supply.adoc @@ -48,14 +48,14 @@ mod MyToken { let symbol = "MTK"; self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, fixed_supply); + 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. +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 @@ -108,7 +108,7 @@ mod MyToken { ) { // This function is NOT protected which means // ANYONE can mint tokens - self.erc20._mint(recipient, amount); + self.erc20.mint(recipient, amount); } } ---- @@ -139,7 +139,7 @@ mod MyToken { self.ownable.assert_only_owner(); // Mint tokens if called by the contract owner - self.erc20._mint(recipient, amount); + self.erc20.mint(recipient, amount); } } ---- diff --git a/docs/modules/ROOT/pages/guides/snip12.adoc b/docs/modules/ROOT/pages/guides/snip12.adoc index 2f8a427f1..1ad4c134e 100644 --- a/docs/modules/ROOT/pages/guides/snip12.adoc +++ b/docs/modules/ROOT/pages/guides/snip12.adoc @@ -44,7 +44,7 @@ mod CustomERC20 { recipient: ContractAddress ) { self.erc20.initializer("MyToken", "MTK"); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } #[external(v0)] @@ -300,7 +300,7 @@ mod CustomERC20 { #[constructor] fn constructor(ref self: ContractState, initial_supply: u256, recipient: ContractAddress) { self.erc20.initializer("MyToken", "MTK"); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } /// Required for hash computation. diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 1eb91fa21..ef58e4da3 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -103,7 +103,7 @@ mod MyERC20Token { recipient: ContractAddress ) { self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, fixed_supply); + self.erc20.mint(recipient, fixed_supply); } } ---- diff --git a/docs/modules/ROOT/pages/presets.adoc b/docs/modules/ROOT/pages/presets.adoc index 90db2da1a..984477f5f 100644 --- a/docs/modules/ROOT/pages/presets.adoc +++ b/docs/modules/ROOT/pages/presets.adoc @@ -131,14 +131,14 @@ mod ERC20Upgradeable { ) { self.ownable.initializer(owner); self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, fixed_supply); + 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); + self.upgradeable.upgrade(new_class_hash); } } } diff --git a/docs/modules/ROOT/pages/security.adoc b/docs/modules/ROOT/pages/security.adoc index a87e63a9e..58af582b4 100644 --- a/docs/modules/ROOT/pages/security.adoc +++ b/docs/modules/ROOT/pages/security.adoc @@ -123,13 +123,13 @@ mod MyPausableContract { #[external(v0)] fn pause(ref self: ContractState) { self.ownable.assert_only_owner(); - self.pausable._pause(); + self.pausable.pause(); } #[external(v0)] fn unpause(ref self: ContractState) { self.ownable.assert_only_owner(); - self.pausable._unpause(); + self.pausable.unpause(); } #[external(v0)] diff --git a/docs/modules/ROOT/pages/upgrades.adoc b/docs/modules/ROOT/pages/upgrades.adoc index 4fa9e4603..99cfeaa66 100644 --- a/docs/modules/ROOT/pages/upgrades.adoc +++ b/docs/modules/ROOT/pages/upgrades.adoc @@ -28,7 +28,7 @@ The `{replace_class_syscall}` syscall allows a contract to update its source cod [,cairo] ---- /// Upgrades the contract source code to the new contract class. -fn _upgrade(new_class_hash: ClassHash) { +fn upgrade(new_class_hash: ClassHash) { assert(!new_class_hash.is_zero(), 'Class hash cannot be zero'); starknet::replace_class_syscall(new_class_hash).unwrap_syscall(); } @@ -97,7 +97,7 @@ mod UpgradeableContract { self.ownable.assert_only_owner(); // Replace the class hash upgrading the contract - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } } } diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index 7448d033c..3f320d988 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts for Cairo v0.13.0 (access/accesscontrol/accesscontrol.cairo) +// OpenZeppelin Contracts for Cairo v0.12.0 (access/accesscontrol/accesscontrol.cairo) /// # AccessControl Component /// @@ -194,6 +194,19 @@ pub mod AccessControlComponent { assert(authorized, Errors::MISSING_ROLE); } + /// Sets `admin_role` as `role`'s admin role. + /// + /// Internal function without access restriction. + /// + /// Emits a `RoleAdminChanged` event. + fn set_role_admin( + ref self: ComponentState, role: felt252, admin_role: felt252 + ) { + let previous_admin_role: felt252 = AccessControl::get_role_admin(@self, role); + self.AccessControl_role_admin.write(role, admin_role); + self.emit(RoleAdminChanged { role, previous_admin_role, new_admin_role: admin_role }); + } + /// Attempts to grant `role` to `account`. /// /// Internal function without access restriction. @@ -223,17 +236,6 @@ pub mod AccessControlComponent { self.emit(RoleRevoked { role, account, sender: caller }); } } - - /// Sets `admin_role` as `role`'s admin role. - /// - /// Emits a `RoleAdminChanged` event. - fn _set_role_admin( - ref self: ComponentState, role: felt252, admin_role: felt252 - ) { - let previous_admin_role: felt252 = AccessControl::get_role_admin(@self, role); - self.AccessControl_role_admin.write(role, admin_role); - self.emit(RoleAdminChanged { role, previous_admin_role, new_admin_role: admin_role }); - } } #[embeddable_as(AccessControlMixinImpl)] diff --git a/src/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo index 392f60ccc..9f12e900d 100644 --- a/src/access/ownable/ownable.cairo +++ b/src/access/ownable/ownable.cairo @@ -171,66 +171,6 @@ pub mod OwnableComponent { } } - #[generate_trait] - pub impl InternalImpl< - TContractState, +HasComponent - > of InternalTrait { - /// Sets the contract's initial owner. - /// - /// This function should be called at construction time. - fn initializer(ref self: ComponentState, owner: ContractAddress) { - self._transfer_ownership(owner); - } - - /// Panics if called by any account other than the owner. Use this - /// to restrict access to certain functions to the owner. - fn assert_only_owner(self: @ComponentState) { - let owner = self.Ownable_owner.read(); - let caller = get_caller_address(); - assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER); - assert(caller == owner, Errors::NOT_OWNER); - } - - /// Transfers ownership to the pending owner. - /// - /// Internal function without access restriction. - fn _accept_ownership(ref self: ComponentState) { - let pending_owner = self.Ownable_pending_owner.read(); - self.Ownable_pending_owner.write(Zero::zero()); - self._transfer_ownership(pending_owner); - } - - /// Sets a new pending owner. - /// - /// Internal function without access restriction. - fn _propose_owner(ref self: ComponentState, new_owner: ContractAddress) { - let previous_owner = self.Ownable_owner.read(); - self.Ownable_pending_owner.write(new_owner); - self - .emit( - OwnershipTransferStarted { - previous_owner: previous_owner, new_owner: new_owner - } - ); - } - - /// Transfers ownership of the contract to a new address. - /// - /// Internal function without access restriction. - /// - /// Emits an `OwnershipTransferred` event. - fn _transfer_ownership( - ref self: ComponentState, new_owner: ContractAddress - ) { - let previous_owner: ContractAddress = self.Ownable_owner.read(); - self.Ownable_owner.write(new_owner); - self - .emit( - OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner } - ); - } - } - #[embeddable_as(OwnableMixinImpl)] impl OwnableMixin< TContractState, +HasComponent, +Drop @@ -304,4 +244,68 @@ pub mod OwnableComponent { OwnableTwoStepCamelOnly::renounceOwnership(ref self); } } + + #[generate_trait] + pub impl InternalImpl< + TContractState, +HasComponent + > of InternalTrait { + /// Sets the contract's initial owner. + /// + /// This function should be called at construction time. + fn initializer(ref self: ComponentState, owner: ContractAddress) { + self._transfer_ownership(owner); + } + + /// Panics if called by any account other than the owner. Use this + /// to restrict access to certain functions to the owner. + fn assert_only_owner(self: @ComponentState) { + let owner = self.Ownable_owner.read(); + let caller = get_caller_address(); + assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER); + assert(caller == owner, Errors::NOT_OWNER); + } + + /// Transfers ownership of the contract to a new address. + /// + /// Internal function without access restriction. + /// + /// Emits an `OwnershipTransferred` event. + fn _transfer_ownership( + ref self: ComponentState, new_owner: ContractAddress + ) { + let previous_owner: ContractAddress = self.Ownable_owner.read(); + self.Ownable_owner.write(new_owner); + self + .emit( + OwnershipTransferred { previous_owner: previous_owner, new_owner: new_owner } + ); + } + + /// Sets a new pending owner. + /// + /// Internal function without access restriction. + /// + /// Emits an `OwnershipTransferStarted` event. + fn _propose_owner(ref self: ComponentState, new_owner: ContractAddress) { + let previous_owner = self.Ownable_owner.read(); + self.Ownable_pending_owner.write(new_owner); + self + .emit( + OwnershipTransferStarted { + previous_owner: previous_owner, new_owner: new_owner + } + ); + } + + /// Transfers ownership to the pending owner. + /// + /// Internal function without access restriction. + /// + /// Emits an `OwnershipTransferred` event. + fn _accept_ownership(ref self: ComponentState) { + let pending_owner = self.Ownable_pending_owner.read(); + self.Ownable_pending_owner.write(Zero::zero()); + self._transfer_ownership(pending_owner); + } + } } diff --git a/src/account/account.cairo b/src/account/account.cairo index 4914af086..06a409f06 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -51,6 +51,10 @@ pub mod AccountComponent { pub const UNAUTHORIZED: felt252 = 'Account: unauthorized'; } + // + // External + // + #[embeddable_as(SRC6Impl)] impl SRC6< TContractState, @@ -212,82 +216,6 @@ pub mod AccountComponent { } } - #[generate_trait] - pub impl InternalImpl< - TContractState, - +HasComponent, - impl SRC5: SRC5Component::HasComponent, - +Drop - > of InternalTrait { - /// Initializes the account by setting the initial public key - /// and registering the ISRC6 interface Id. - fn initializer(ref self: ComponentState, public_key: felt252) { - let mut src5_component = get_dep_component_mut!(ref self, SRC5); - src5_component.register_interface(interface::ISRC6_ID); - self._set_public_key(public_key); - } - - /// Validates that the caller is the account itself. Otherwise it reverts. - fn assert_only_self(self: @ComponentState) { - let caller = get_caller_address(); - let self = get_contract_address(); - assert(self == caller, Errors::UNAUTHORIZED); - } - - /// Validates that `new_owner` accepted the ownership of the contract. - /// - /// WARNING: This function assumes that `current_owner` is the current owner of the contract, and - /// does not validate this assumption. - /// - /// Requirements: - /// - /// - The signature must be valid for the new owner. - fn assert_valid_new_owner( - self: @ComponentState, - current_owner: felt252, - new_owner: felt252, - signature: Span - ) { - let message_hash = PoseidonTrait::new() - .update_with('StarkNet Message') - .update_with('accept_ownership') - .update_with(get_contract_address()) - .update_with(current_owner) - .finalize(); - - let is_valid = is_valid_stark_signature(message_hash, new_owner, signature); - assert(is_valid, Errors::INVALID_SIGNATURE); - } - - /// Validates the signature for the current transaction. - /// Returns the short string `VALID` if valid, otherwise it reverts. - fn validate_transaction(self: @ComponentState) -> felt252 { - let tx_info = get_tx_info().unbox(); - let tx_hash = tx_info.transaction_hash; - let signature = tx_info.signature; - assert(self._is_valid_signature(tx_hash, signature), Errors::INVALID_SIGNATURE); - starknet::VALIDATED - } - - /// Sets the public key without validating the caller. - /// The usage of this method outside the `set_public_key` function is discouraged. - /// - /// Emits an `OwnerAdded` event. - fn _set_public_key(ref self: ComponentState, new_public_key: felt252) { - self.Account_public_key.write(new_public_key); - self.emit(OwnerAdded { new_owner_guid: new_public_key }); - } - - /// Returns whether the given signature is valid for the given hash - /// using the account's current public key. - fn _is_valid_signature( - self: @ComponentState, hash: felt252, signature: Span - ) -> bool { - let public_key = self.Account_public_key.read(); - is_valid_stark_signature(hash, public_key, signature) - } - } - #[embeddable_as(AccountMixinImpl)] impl AccountMixin< TContractState, @@ -370,4 +298,84 @@ pub mod AccountComponent { src5.supports_interface(interface_id) } } + + // + // Internal + // + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop + > of InternalTrait { + /// Initializes the account by setting the initial public key + /// and registering the ISRC6 interface Id. + fn initializer(ref self: ComponentState, public_key: felt252) { + let mut src5_component = get_dep_component_mut!(ref self, SRC5); + src5_component.register_interface(interface::ISRC6_ID); + self._set_public_key(public_key); + } + + /// Validates that the caller is the account itself. Otherwise it reverts. + fn assert_only_self(self: @ComponentState) { + let caller = get_caller_address(); + let self = get_contract_address(); + assert(self == caller, Errors::UNAUTHORIZED); + } + + /// Validates that `new_owner` accepted the ownership of the contract. + /// + /// WARNING: This function assumes that `current_owner` is the current owner of the contract, and + /// does not validate this assumption. + /// + /// Requirements: + /// + /// - The signature must be valid for the new owner. + fn assert_valid_new_owner( + self: @ComponentState, + current_owner: felt252, + new_owner: felt252, + signature: Span + ) { + let message_hash = PoseidonTrait::new() + .update_with('StarkNet Message') + .update_with('accept_ownership') + .update_with(get_contract_address()) + .update_with(current_owner) + .finalize(); + + let is_valid = is_valid_stark_signature(message_hash, new_owner, signature); + assert(is_valid, Errors::INVALID_SIGNATURE); + } + + /// Validates the signature for the current transaction. + /// Returns the short string `VALID` if valid, otherwise it reverts. + fn validate_transaction(self: @ComponentState) -> felt252 { + let tx_info = get_tx_info().unbox(); + let tx_hash = tx_info.transaction_hash; + let signature = tx_info.signature; + assert(self._is_valid_signature(tx_hash, signature), Errors::INVALID_SIGNATURE); + starknet::VALIDATED + } + + /// Sets the public key without validating the caller. + /// The usage of this method outside the `set_public_key` function is discouraged. + /// + /// Emits an `OwnerAdded` event. + fn _set_public_key(ref self: ComponentState, new_public_key: felt252) { + self.Account_public_key.write(new_public_key); + self.emit(OwnerAdded { new_owner_guid: new_public_key }); + } + + /// Returns whether the given signature is valid for the given hash + /// using the account's current public key. + fn _is_valid_signature( + self: @ComponentState, hash: felt252, signature: Span + ) -> bool { + let public_key = self.Account_public_key.read(); + is_valid_stark_signature(hash, public_key, signature) + } + } } diff --git a/src/account/eth_account.cairo b/src/account/eth_account.cairo index 76a010a30..1c9e4fb28 100644 --- a/src/account/eth_account.cairo +++ b/src/account/eth_account.cairo @@ -55,6 +55,10 @@ pub mod EthAccountComponent { pub const UNAUTHORIZED: felt252 = 'EthAccount: unauthorized'; } + // + // External + // + #[embeddable_as(SRC6Impl)] impl SRC6< TContractState, @@ -218,88 +222,6 @@ pub mod EthAccountComponent { } } - #[generate_trait] - pub impl InternalImpl< - TContractState, - +HasComponent, - impl SRC5: SRC5Component::HasComponent, - +Drop - > of InternalTrait { - /// Initializes the account by setting the initial public key - /// and registering the ISRC6 interface Id. - fn initializer(ref self: ComponentState, public_key: EthPublicKey) { - let mut src5_component = get_dep_component_mut!(ref self, SRC5); - src5_component.register_interface(interface::ISRC6_ID); - self._set_public_key(public_key); - } - - /// Validates that the caller is the account itself. Otherwise it reverts. - fn assert_only_self(self: @ComponentState) { - let caller = get_caller_address(); - let self = get_contract_address(); - assert(self == caller, Errors::UNAUTHORIZED); - } - - /// Validates that `new_owner` accepted the ownership of the contract. - /// - /// WARNING: This function assumes that `current_owner` is the current owner of the contract, and - /// does not validate this assumption. - /// - /// Requirements: - /// - /// - The signature must be valid for the `new_owner`. - fn assert_valid_new_owner( - self: @ComponentState, - current_owner: EthPublicKey, - new_owner: EthPublicKey, - signature: Span - ) { - 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(); - - let is_valid = is_valid_eth_signature(message_hash, new_owner, signature); - assert(is_valid, Errors::INVALID_SIGNATURE); - } - - /// Validates the signature for the current transaction. - /// Returns the short string `VALID` if valid, otherwise it reverts. - fn validate_transaction(self: @ComponentState) -> felt252 { - let tx_info = get_tx_info().unbox(); - let tx_hash = tx_info.transaction_hash; - let signature = tx_info.signature; - assert(self._is_valid_signature(tx_hash, signature), Errors::INVALID_SIGNATURE); - starknet::VALIDATED - } - - /// Sets the public key without validating the caller. - /// The usage of this method outside the `set_public_key` function is discouraged. - /// - /// Emits an `OwnerAdded` event. - fn _set_public_key(ref self: ComponentState, new_public_key: EthPublicKey) { - self.EthAccount_public_key.write(new_public_key); - let new_owner_guid = _get_guid_from_public_key(new_public_key); - self.emit(OwnerAdded { new_owner_guid }); - } - - /// Returns whether the given signature is valid for the given hash - /// using the account's current public key. - fn _is_valid_signature( - self: @ComponentState, hash: felt252, signature: Span - ) -> bool { - let public_key: EthPublicKey = self.EthAccount_public_key.read(); - is_valid_eth_signature(hash, public_key, signature) - } - } - - fn _get_guid_from_public_key(public_key: EthPublicKey) -> felt252 { - let (x, y) = public_key.get_coordinates().unwrap_syscall(); - poseidon_hash_span(array![x.low.into(), x.high.into(), y.low.into(), y.high.into()].span()) - } - #[embeddable_as(EthAccountMixinImpl)] impl EthAccountMixin< TContractState, @@ -382,4 +304,90 @@ pub mod EthAccountComponent { src5.supports_interface(interface_id) } } + + // + // Internal + // + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop + > of InternalTrait { + /// Initializes the account by setting the initial public key + /// and registering the ISRC6 interface Id. + fn initializer(ref self: ComponentState, public_key: EthPublicKey) { + let mut src5_component = get_dep_component_mut!(ref self, SRC5); + src5_component.register_interface(interface::ISRC6_ID); + self._set_public_key(public_key); + } + + /// Validates that the caller is the account itself. Otherwise it reverts. + fn assert_only_self(self: @ComponentState) { + let caller = get_caller_address(); + let self = get_contract_address(); + assert(self == caller, Errors::UNAUTHORIZED); + } + + /// Validates that `new_owner` accepted the ownership of the contract. + /// + /// WARNING: This function assumes that `current_owner` is the current owner of the contract, and + /// does not validate this assumption. + /// + /// Requirements: + /// + /// - The signature must be valid for the `new_owner`. + fn assert_valid_new_owner( + self: @ComponentState, + current_owner: EthPublicKey, + new_owner: EthPublicKey, + signature: Span + ) { + 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(); + + let is_valid = is_valid_eth_signature(message_hash, new_owner, signature); + assert(is_valid, Errors::INVALID_SIGNATURE); + } + + /// Validates the signature for the current transaction. + /// Returns the short string `VALID` if valid, otherwise it reverts. + fn validate_transaction(self: @ComponentState) -> felt252 { + let tx_info = get_tx_info().unbox(); + let tx_hash = tx_info.transaction_hash; + let signature = tx_info.signature; + assert(self._is_valid_signature(tx_hash, signature), Errors::INVALID_SIGNATURE); + starknet::VALIDATED + } + + /// Sets the public key without validating the caller. + /// The usage of this method outside the `set_public_key` function is discouraged. + /// + /// Emits an `OwnerAdded` event. + fn _set_public_key(ref self: ComponentState, new_public_key: EthPublicKey) { + self.EthAccount_public_key.write(new_public_key); + let new_owner_guid = _get_guid_from_public_key(new_public_key); + self.emit(OwnerAdded { new_owner_guid }); + } + + /// Returns whether the given signature is valid for the given hash + /// using the account's current public key. + fn _is_valid_signature( + self: @ComponentState, hash: felt252, signature: Span + ) -> bool { + let public_key: EthPublicKey = self.EthAccount_public_key.read(); + is_valid_eth_signature(hash, public_key, signature) + } + } + + fn _get_guid_from_public_key(public_key: EthPublicKey) -> felt252 { + let (x, y) = public_key.get_coordinates().unwrap_syscall(); + poseidon_hash_span(array![x.low.into(), x.high.into(), y.low.into(), y.high.into()].span()) + } } diff --git a/src/presets/account.cairo b/src/presets/account.cairo index 38f11cdba..31143136a 100644 --- a/src/presets/account.cairo +++ b/src/presets/account.cairo @@ -55,7 +55,7 @@ pub(crate) mod AccountUpgradeable { impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { self.account.assert_only_self(); - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } } } diff --git a/src/presets/erc1155.cairo b/src/presets/erc1155.cairo index cf06c487f..13977776e 100644 --- a/src/presets/erc1155.cairo +++ b/src/presets/erc1155.cairo @@ -91,7 +91,7 @@ pub(crate) mod ERC1155Upgradeable { /// This may only be called by the contract owner. fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { self.ownable.assert_only_owner(); - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } } } diff --git a/src/presets/erc20.cairo b/src/presets/erc20.cairo index 0cd81654f..7a80aa4a8 100644 --- a/src/presets/erc20.cairo +++ b/src/presets/erc20.cairo @@ -69,7 +69,7 @@ pub(crate) mod ERC20Upgradeable { ) { self.ownable.initializer(owner); self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, fixed_supply); + self.erc20.mint(recipient, fixed_supply); } #[abi(embed_v0)] @@ -78,7 +78,7 @@ pub(crate) mod ERC20Upgradeable { /// This may only be called by the contract owner. fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { self.ownable.assert_only_owner(); - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } } } diff --git a/src/presets/erc721.cairo b/src/presets/erc721.cairo index 8b57d417e..fad33b1ff 100644 --- a/src/presets/erc721.cairo +++ b/src/presets/erc721.cairo @@ -76,7 +76,7 @@ pub(crate) mod ERC721Upgradeable { ) { self.ownable.initializer(owner); self.erc721.initializer(name, symbol, base_uri); - self._mint_assets(recipient, token_ids); + self.mint_assets(recipient, token_ids); } #[abi(embed_v0)] @@ -85,14 +85,14 @@ pub(crate) mod ERC721Upgradeable { /// This may only be called by the contract owner. fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { self.ownable.assert_only_owner(); - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } } #[generate_trait] pub(crate) impl InternalImpl of InternalTrait { /// Mints `token_ids` to `recipient`. - fn _mint_assets( + fn mint_assets( ref self: ContractState, recipient: ContractAddress, mut token_ids: Span ) { loop { @@ -100,7 +100,7 @@ pub(crate) mod ERC721Upgradeable { break; } let id = *token_ids.pop_front().unwrap(); - self.erc721._mint(recipient, id); + self.erc721.mint(recipient, id); } } } diff --git a/src/presets/eth_account.cairo b/src/presets/eth_account.cairo index 41fcb7f34..b1f31df97 100644 --- a/src/presets/eth_account.cairo +++ b/src/presets/eth_account.cairo @@ -58,7 +58,7 @@ pub(crate) mod EthAccountUpgradeable { impl UpgradeableImpl of IUpgradeable { fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { self.eth_account.assert_only_self(); - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } } } diff --git a/src/security/pausable.cairo b/src/security/pausable.cairo index 889356f0c..363961ded 100644 --- a/src/security/pausable.cairo +++ b/src/security/pausable.cairo @@ -73,7 +73,7 @@ pub mod PausableComponent { /// - The contract is not paused. /// /// Emits a `Paused` event. - fn _pause(ref self: ComponentState) { + fn pause(ref self: ComponentState) { self.assert_not_paused(); self.Pausable_paused.write(true); self.emit(Paused { account: get_caller_address() }); @@ -86,7 +86,7 @@ pub mod PausableComponent { /// - The contract is paused. /// /// Emits an `Unpaused` event. - fn _unpause(ref self: ComponentState) { + fn unpause(ref self: ComponentState) { self.assert_paused(); self.Pausable_paused.write(false); self.emit(Unpaused { account: get_caller_address() }); diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index 219e2a9fe..35a76c136 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -360,14 +360,14 @@ fn test_renounceRole_unauthorized() { } // -// _set_role_admin +// set_role_admin // #[test] -fn test__set_role_admin() { +fn test_set_role_admin() { let mut state = setup(); assert_eq!(state.get_role_admin(ROLE), DEFAULT_ADMIN_ROLE); - state._set_role_admin(ROLE, OTHER_ROLE); + state.set_role_admin(ROLE, OTHER_ROLE); assert_event_role_admin_changed(ROLE, DEFAULT_ADMIN_ROLE, OTHER_ROLE); @@ -378,7 +378,7 @@ fn test__set_role_admin() { #[test] fn test_new_admin_can_grant_roles() { let mut state = setup(); - state._set_role_admin(ROLE, OTHER_ROLE); + state.set_role_admin(ROLE, OTHER_ROLE); testing::set_caller_address(ADMIN()); state.grant_role(OTHER_ROLE, OTHER_ADMIN()); @@ -393,7 +393,7 @@ fn test_new_admin_can_grant_roles() { #[test] fn test_new_admin_can_revoke_roles() { let mut state = setup(); - state._set_role_admin(ROLE, OTHER_ROLE); + state.set_role_admin(ROLE, OTHER_ROLE); testing::set_caller_address(ADMIN()); state.grant_role(OTHER_ROLE, OTHER_ADMIN()); @@ -410,7 +410,7 @@ fn test_new_admin_can_revoke_roles() { #[should_panic(expected: ('Caller is missing role',))] fn test_previous_admin_cannot_grant_roles() { let mut state = setup(); - state._set_role_admin(ROLE, OTHER_ROLE); + state.set_role_admin(ROLE, OTHER_ROLE); testing::set_caller_address(ADMIN()); state.grant_role(ROLE, AUTHORIZED()); } @@ -419,7 +419,7 @@ fn test_previous_admin_cannot_grant_roles() { #[should_panic(expected: ('Caller is missing role',))] fn test_previous_admin_cannot_revoke_roles() { let mut state = setup(); - state._set_role_admin(ROLE, OTHER_ROLE); + state.set_role_admin(ROLE, OTHER_ROLE); testing::set_caller_address(ADMIN()); state.revoke_role(ROLE, AUTHORIZED()); } diff --git a/src/tests/mocks/erc20_mocks.cairo b/src/tests/mocks/erc20_mocks.cairo index ca00b74e6..283cd7fdf 100644 --- a/src/tests/mocks/erc20_mocks.cairo +++ b/src/tests/mocks/erc20_mocks.cairo @@ -35,7 +35,7 @@ pub(crate) mod DualCaseERC20Mock { recipient: ContractAddress ) { self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } } @@ -74,7 +74,7 @@ pub(crate) mod SnakeERC20Mock { recipient: ContractAddress ) { self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } } @@ -117,7 +117,7 @@ pub(crate) mod CamelERC20Mock { recipient: ContractAddress ) { self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, initial_supply); + self.erc20.mint(recipient, initial_supply); } #[abi(per_item)] diff --git a/src/tests/mocks/erc20_votes_mocks.cairo b/src/tests/mocks/erc20_votes_mocks.cairo index 960e8b2ec..fcc44b16a 100644 --- a/src/tests/mocks/erc20_votes_mocks.cairo +++ b/src/tests/mocks/erc20_votes_mocks.cairo @@ -96,6 +96,6 @@ pub(crate) mod DualCaseERC20VotesMock { recipient: ContractAddress ) { self.erc20.initializer(name, symbol); - self.erc20._mint(recipient, fixed_supply); + self.erc20.mint(recipient, fixed_supply); } } diff --git a/src/tests/mocks/erc721_mocks.cairo b/src/tests/mocks/erc721_mocks.cairo index ec289f8e4..770fd8312 100644 --- a/src/tests/mocks/erc721_mocks.cairo +++ b/src/tests/mocks/erc721_mocks.cairo @@ -50,7 +50,7 @@ pub(crate) mod DualCaseERC721Mock { token_id: u256 ) { self.erc721.initializer(name, symbol, base_uri); - self.erc721._mint(recipient, token_id); + self.erc721.mint(recipient, token_id); } } @@ -101,7 +101,7 @@ pub(crate) mod SnakeERC721Mock { token_id: u256 ) { self.erc721.initializer(name, symbol, base_uri); - self.erc721._mint(recipient, token_id); + self.erc721.mint(recipient, token_id); } } @@ -154,7 +154,7 @@ pub(crate) mod CamelERC721Mock { token_id: u256 ) { self.erc721.initializer(name, symbol, base_uri); - self.erc721._mint(recipient, token_id); + self.erc721.mint(recipient, token_id); } /// The following external methods are included because they are case-agnostic diff --git a/src/tests/mocks/upgrades_mocks.cairo b/src/tests/mocks/upgrades_mocks.cairo index 2c04613f6..07a7ac620 100644 --- a/src/tests/mocks/upgrades_mocks.cairo +++ b/src/tests/mocks/upgrades_mocks.cairo @@ -38,7 +38,7 @@ pub(crate) mod UpgradesV1 { #[abi(embed_v0)] impl UpgradesV1Impl of super::IUpgradesV1 { fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } fn set_value(ref self: ContractState, val: felt252) { @@ -89,7 +89,7 @@ pub(crate) mod UpgradesV2 { #[abi(embed_v0)] impl UpgradesV2Impl of super::IUpgradesV2 { fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { - self.upgradeable._upgrade(new_class_hash); + self.upgradeable.upgrade(new_class_hash); } fn set_value(ref self: ContractState, val: felt252) { diff --git a/src/tests/presets/test_account.cairo b/src/tests/presets/test_account.cairo index 476f3463c..61dc1c803 100644 --- a/src/tests/presets/test_account.cairo +++ b/src/tests/presets/test_account.cairo @@ -518,7 +518,7 @@ fn set_contract_and_caller(address: ContractAddress) { } fn get_accept_ownership_signature() -> Span { - // 0x29d33bfe2aaef69267b4f68e9d6ede5abb215aca7c46d0cf756eddf8a3f2cfd = + // 0xecfdac5cd0e60434b672a97ba94520b9acfe629d123a883005e45afa25ccea = // PoseidonTrait::new() // .update_with('StarkNet Message') // .update_with('accept_ownership') @@ -529,10 +529,10 @@ fn get_accept_ownership_signature() -> Span { // This signature was computed using starknet js sdk from the following values: // - private_key: '1234' // - public_key: 0x26da8d11938b76025862be14fdb8b28438827f73e75e86f7bfa38b196951fa7 - // - msg_hash: 0x29d33bfe2aaef69267b4f68e9d6ede5abb215aca7c46d0cf756eddf8a3f2cfd + // - msg_hash: 0xecfdac5cd0e60434b672a97ba94520b9acfe629d123a883005e45afa25ccea array![ - 0x116fda6023f35ae4a6436dbc7ed9179f78ca4852cc8538a9ffcbbd13e396304, - 0x5ab734a345374ed6fd25fb348f24f9c0c6e4a48309a4c3918072a772ca671c9 + 0x379fcc17e39513c19b5d97143e919ec3a5d9f59d4ae80fef83e037bc9275240, + 0x7719ade3541834755ed48a4373a19872c4032937344d832e2743df677b0a43 ] .span() } diff --git a/src/tests/presets/test_erc721.cairo b/src/tests/presets/test_erc721.cairo index 735589889..bf1d27d14 100644 --- a/src/tests/presets/test_erc721.cairo +++ b/src/tests/presets/test_erc721.cairo @@ -94,15 +94,15 @@ fn setup_camel_account() -> ContractAddress { } // -// _mint_assets +// mint_assets // #[test] -fn test__mint_assets() { +fn test_mint_assets() { let mut state = ERC721Upgradeable::contract_state_for_testing(); let mut token_ids = array![TOKEN_1, TOKEN_2, TOKEN_3].span(); - state._mint_assets(OWNER(), token_ids); + state.mint_assets(OWNER(), token_ids); assert_eq!(state.erc721.balance_of(OWNER()), TOKENS_LEN); loop { diff --git a/src/tests/presets/test_eth_account.cairo b/src/tests/presets/test_eth_account.cairo index cbf796a6e..b8510958a 100644 --- a/src/tests/presets/test_eth_account.cairo +++ b/src/tests/presets/test_eth_account.cairo @@ -104,7 +104,7 @@ fn test_constructor() { // #[test] -fn test_public_key_setter_and_getter() { +fn test_public_key_setter_and_getter_2() { let dispatcher = setup_dispatcher(); let new_public_key = NEW_ETH_PUBKEY(); @@ -505,7 +505,7 @@ fn set_contract_and_caller(address: ContractAddress) { fn get_accept_ownership_signature() -> Span { let mut output = array![]; - // 0x0221042e1c5eb52dd7306ab4d987f14ed795e6bafc611999eb535ef7bf2a3d80 = + // 0x054308383e1c733aa36ccf3cc62e3107b6bcb10bafcab39912108c6b52655b4c = // PoseidonTrait::new() // .update_with('StarkNet Message') // .update_with('accept_ownership') @@ -518,10 +518,10 @@ fn get_accept_ownership_signature() -> Span { // - public_key: // r: 0x829307f82a1883c2414503ba85fc85037f22c6fc6f80910801f6b01a4131da1e // s: 0x2a23f7bddf3715d11767b1247eccc68c89e11b926e2615268db6ad1af8d8da96 - // - msg_hash: 0x0221042e1c5eb52dd7306ab4d987f14ed795e6bafc611999eb535ef7bf2a3d80 + // - msg_hash: 0x054308383e1c733aa36ccf3cc62e3107b6bcb10bafcab39912108c6b52655b4c EthSignature { - r: 0x4a72dd569e1ef9fad60ef138afd25e7d9fd33b0e1b735d2c0cb1d8d94b579b12, - s: 0x39832ebc66a99d1f478e5a019e552d88216e19095fed238977e4520d5188e7c5, + r: 0xc4de7637e4206e64ddae9261782dad6d3c99eaaede20ff3fb183f751b94ee9ff, + s: 0x77c24e1ad34f5ba0627048f6a11c1e7ee1ddd3b5d518ee4c71ebd5725390f860, } .serialize(ref output); diff --git a/src/tests/security/test_pausable.cairo b/src/tests/security/test_pausable.cairo index 922750646..8fd170df0 100644 --- a/src/tests/security/test_pausable.cairo +++ b/src/tests/security/test_pausable.cairo @@ -23,10 +23,10 @@ fn test_is_paused() { let mut state = COMPONENT_STATE(); assert!(!state.is_paused()); - state._pause(); + state.pause(); assert!(state.is_paused()); - state._unpause(); + state.unpause(); assert!(!state.is_paused()); } @@ -37,7 +37,7 @@ fn test_is_paused() { #[test] fn test_assert_paused_when_paused() { let mut state = COMPONENT_STATE(); - state._pause(); + state.pause(); state.assert_paused(); } @@ -56,7 +56,7 @@ fn test_assert_paused_when_not_paused() { #[should_panic(expected: ('Pausable: paused',))] fn test_assert_not_paused_when_paused() { let mut state = COMPONENT_STATE(); - state._pause(); + state.pause(); state.assert_not_paused(); } @@ -75,7 +75,7 @@ fn test_pause_when_unpaused() { let mut state = COMPONENT_STATE(); testing::set_caller_address(CALLER()); - state._pause(); + state.pause(); assert_event_paused(CALLER()); assert!(state.is_paused()); @@ -85,8 +85,8 @@ fn test_pause_when_unpaused() { #[should_panic(expected: ('Pausable: paused',))] fn test_pause_when_paused() { let mut state = COMPONENT_STATE(); - state._pause(); - state._pause(); + state.pause(); + state.pause(); } // @@ -98,10 +98,10 @@ fn test_unpause_when_paused() { let mut state = COMPONENT_STATE(); testing::set_caller_address(CALLER()); - state._pause(); + state.pause(); utils::drop_event(ZERO()); - state._unpause(); + state.unpause(); assert_event_unpaused(CALLER()); assert!(!state.is_paused()); @@ -112,7 +112,7 @@ fn test_unpause_when_paused() { fn test_unpause_when_unpaused() { let mut state = COMPONENT_STATE(); assert!(!state.is_paused()); - state._unpause(); + state.unpause(); } // diff --git a/src/tests/token/erc20/test_erc20.cairo b/src/tests/token/erc20/test_erc20.cairo index b832a53fd..2c41a7971 100644 --- a/src/tests/token/erc20/test_erc20.cairo +++ b/src/tests/token/erc20/test_erc20.cairo @@ -27,7 +27,7 @@ fn COMPONENT_STATE() -> ComponentState { fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state.initializer(NAME(), SYMBOL()); - state._mint(OWNER(), SUPPLY); + state.mint(OWNER(), SUPPLY); utils::drop_event(ZERO()); state } @@ -54,28 +54,28 @@ fn test_initializer() { #[test] fn test_total_supply() { let mut state = COMPONENT_STATE(); - state._mint(OWNER(), SUPPLY); + state.mint(OWNER(), SUPPLY); assert_eq!(state.total_supply(), SUPPLY); } #[test] fn test_totalSupply() { let mut state = COMPONENT_STATE(); - state._mint(OWNER(), SUPPLY); + state.mint(OWNER(), SUPPLY); assert_eq!(state.totalSupply(), SUPPLY); } #[test] fn test_balance_of() { let mut state = COMPONENT_STATE(); - state._mint(OWNER(), SUPPLY); + state.mint(OWNER(), SUPPLY); assert_eq!(state.balance_of(OWNER()), SUPPLY); } #[test] fn test_balanceOf() { let mut state = COMPONENT_STATE(); - state._mint(OWNER(), SUPPLY); + state.mint(OWNER(), SUPPLY); assert_eq!(state.balanceOf(OWNER()), SUPPLY); } @@ -389,13 +389,13 @@ fn test__spend_allowance_unlimited() { } // -// _mint +// mint // #[test] -fn test__mint() { +fn test_mint() { let mut state = COMPONENT_STATE(); - state._mint(OWNER(), VALUE); + state.mint(OWNER(), VALUE); assert_only_event_transfer(ZERO(), ZERO(), OWNER(), VALUE); assert_eq!(state.balance_of(OWNER()), VALUE); @@ -404,19 +404,19 @@ fn test__mint() { #[test] #[should_panic(expected: ('ERC20: mint to 0',))] -fn test__mint_to_zero() { +fn test_mint_to_zero() { let mut state = COMPONENT_STATE(); - state._mint(ZERO(), VALUE); + state.mint(ZERO(), VALUE); } // -// _burn +// burn // #[test] -fn test__burn() { +fn test_burn() { let mut state = setup(); - state._burn(OWNER(), VALUE); + state.burn(OWNER(), VALUE); assert_only_event_transfer(ZERO(), OWNER(), ZERO(), VALUE); assert_eq!(state.total_supply(), SUPPLY - VALUE); @@ -425,7 +425,7 @@ fn test__burn() { #[test] #[should_panic(expected: ('ERC20: burn from 0',))] -fn test__burn_from_zero() { +fn test_burn_from_zero() { let mut state = setup(); - state._burn(ZERO(), VALUE); + state.burn(ZERO(), VALUE); } diff --git a/src/tests/token/erc20/test_erc20_votes.cairo b/src/tests/token/erc20/test_erc20_votes.cairo index 556f760a8..e241e891c 100644 --- a/src/tests/token/erc20/test_erc20_votes.cairo +++ b/src/tests/token/erc20/test_erc20_votes.cairo @@ -39,7 +39,7 @@ fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); let mut mock_state = CONTRACT_STATE(); - mock_state.erc20._mint(OWNER(), SUPPLY); + mock_state.erc20.mint(OWNER(), SUPPLY); state.transfer_voting_units(ZERO(), OWNER(), SUPPLY); utils::drop_event(ZERO()); state diff --git a/src/tests/token/erc721/test_erc721.cairo b/src/tests/token/erc721/test_erc721.cairo index 28e63ab2c..7dd5ae1bf 100644 --- a/src/tests/token/erc721/test_erc721.cairo +++ b/src/tests/token/erc721/test_erc721.cairo @@ -46,7 +46,7 @@ fn COMPONENT_STATE() -> ComponentState { fn setup() -> ComponentState { let mut state = COMPONENT_STATE(); state.initializer(NAME(), SYMBOL(), BASE_URI()); - state._mint(OWNER(), TOKEN_ID); + state.mint(OWNER(), TOKEN_ID); utils::drop_event(ZERO()); state } @@ -139,7 +139,7 @@ fn test_token_uri() { fn test_token_uri_not_set() { let mut state = COMPONENT_STATE(); - state._mint(OWNER(), TOKEN_ID); + state.mint(OWNER(), TOKEN_ID); let uri = state.token_uri(TOKEN_ID); let expected: ByteArray = ""; assert_eq!(uri, expected); @@ -773,7 +773,7 @@ fn test_safe_transfer_from_to_owner() { let token_id = TOKEN_ID; let owner = setup_receiver(); state.initializer(NAME(), SYMBOL(), BASE_URI()); - state._mint(owner, token_id); + state.mint(owner, token_id); utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); @@ -793,7 +793,7 @@ fn test_safeTransferFrom_to_owner() { let token_id = TOKEN_ID; let owner = setup_receiver(); state.initializer(NAME(), SYMBOL(), BASE_URI()); - state._mint(owner, token_id); + state.mint(owner, token_id); utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); @@ -813,7 +813,7 @@ fn test_safe_transfer_from_to_owner_camel() { let token_id = TOKEN_ID; let owner = setup_camel_receiver(); state.initializer(NAME(), SYMBOL(), BASE_URI()); - state._mint(owner, token_id); + state.mint(owner, token_id); utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); @@ -833,7 +833,7 @@ fn test_safeTransferFrom_to_owner_camel() { let token_id = TOKEN_ID; let owner = setup_camel_receiver(); state.initializer(NAME(), SYMBOL(), BASE_URI()); - state._mint(owner, token_id); + state.mint(owner, token_id); utils::drop_event(ZERO()); assert_eq!(state.owner_of(token_id), owner); @@ -1024,7 +1024,7 @@ fn test_safeTransferFrom_unauthorized() { } // -// _transfer +// transfer // #[test] @@ -1036,7 +1036,7 @@ fn test__transfer() { assert_state_before_transfer(owner, recipient, token_id); - state._transfer(owner, recipient, token_id); + state.transfer(owner, recipient, token_id); assert_only_event_transfer(ZERO(), owner, recipient, token_id); assert_state_after_transfer(owner, recipient, token_id); @@ -1046,35 +1046,35 @@ fn test__transfer() { #[should_panic(expected: ('ERC721: invalid token ID',))] fn test__transfer_nonexistent() { let mut state = COMPONENT_STATE(); - state._transfer(ZERO(), RECIPIENT(), TOKEN_ID); + state.transfer(ZERO(), RECIPIENT(), TOKEN_ID); } #[test] #[should_panic(expected: ('ERC721: invalid receiver',))] fn test__transfer_to_zero() { let mut state = setup(); - state._transfer(OWNER(), ZERO(), TOKEN_ID); + state.transfer(OWNER(), ZERO(), TOKEN_ID); } #[test] #[should_panic(expected: ('ERC721: invalid sender',))] fn test__transfer_from_invalid_owner() { let mut state = setup(); - state._transfer(RECIPIENT(), OWNER(), TOKEN_ID); + state.transfer(RECIPIENT(), OWNER(), TOKEN_ID); } // -// _mint +// mint // #[test] -fn test__mint() { +fn test_mint() { let mut state = COMPONENT_STATE(); let recipient = RECIPIENT(); let token_id = TOKEN_ID; assert_state_before_mint(recipient); - state._mint(recipient, TOKEN_ID); + state.mint(recipient, TOKEN_ID); assert_only_event_transfer(ZERO(), ZERO(), recipient, token_id); assert_state_after_mint(recipient, token_id); @@ -1082,20 +1082,20 @@ fn test__mint() { #[test] #[should_panic(expected: ('ERC721: invalid receiver',))] -fn test__mint_to_zero() { +fn test_mint_to_zero() { let mut state = COMPONENT_STATE(); - state._mint(ZERO(), TOKEN_ID); + state.mint(ZERO(), TOKEN_ID); } #[test] #[should_panic(expected: ('ERC721: token already minted',))] -fn test__mint_already_exist() { +fn test_mint_already_exist() { let mut state = setup(); - state._mint(RECIPIENT(), TOKEN_ID); + state.mint(RECIPIENT(), TOKEN_ID); } // -// _safe_mint +// safe_mint // #[test] @@ -1105,7 +1105,7 @@ fn test__safe_mint_to_receiver() { let token_id = TOKEN_ID; assert_state_before_mint(recipient); - state._safe_mint(recipient, token_id, DATA(true)); + state.safe_mint(recipient, token_id, DATA(true)); assert_only_event_transfer(ZERO(), ZERO(), recipient, token_id); assert_state_after_mint(recipient, token_id); @@ -1118,7 +1118,7 @@ fn test__safe_mint_to_receiver_camel() { let token_id = TOKEN_ID; assert_state_before_mint(recipient); - state._safe_mint(recipient, token_id, DATA(true)); + state.safe_mint(recipient, token_id, DATA(true)); assert_only_event_transfer(ZERO(), ZERO(), recipient, token_id); assert_state_after_mint(recipient, token_id); @@ -1131,7 +1131,7 @@ fn test__safe_mint_to_account() { let token_id = TOKEN_ID; assert_state_before_mint(account); - state._safe_mint(account, token_id, DATA(true)); + state.safe_mint(account, token_id, DATA(true)); assert_only_event_transfer(ZERO(), ZERO(), account, token_id); assert_state_after_mint(account, token_id); @@ -1144,7 +1144,7 @@ fn test__safe_mint_to_account_camel() { let token_id = TOKEN_ID; assert_state_before_mint(account); - state._safe_mint(account, token_id, DATA(true)); + state.safe_mint(account, token_id, DATA(true)); assert_only_event_transfer(ZERO(), ZERO(), account, token_id); assert_state_after_mint(account, token_id); @@ -1158,7 +1158,7 @@ fn test__safe_mint_to_non_receiver() { let token_id = TOKEN_ID; assert_state_before_mint(recipient); - state._safe_mint(recipient, token_id, DATA(true)); + state.safe_mint(recipient, token_id, DATA(true)); assert_state_after_mint(recipient, token_id); } @@ -1170,7 +1170,7 @@ fn test__safe_mint_to_receiver_failure() { let token_id = TOKEN_ID; assert_state_before_mint(recipient); - state._safe_mint(recipient, token_id, DATA(false)); + state.safe_mint(recipient, token_id, DATA(false)); assert_state_after_mint(recipient, token_id); } @@ -1182,7 +1182,7 @@ fn test__safe_mint_to_receiver_failure_camel() { let token_id = TOKEN_ID; assert_state_before_mint(recipient); - state._safe_mint(recipient, token_id, DATA(false)); + state.safe_mint(recipient, token_id, DATA(false)); assert_state_after_mint(recipient, token_id); } @@ -1190,22 +1190,22 @@ fn test__safe_mint_to_receiver_failure_camel() { #[should_panic(expected: ('ERC721: invalid receiver',))] fn test__safe_mint_to_zero() { let mut state = COMPONENT_STATE(); - state._safe_mint(ZERO(), TOKEN_ID, DATA(true)); + state.safe_mint(ZERO(), TOKEN_ID, DATA(true)); } #[test] #[should_panic(expected: ('ERC721: token already minted',))] fn test__safe_mint_already_exist() { let mut state = setup(); - state._safe_mint(RECIPIENT(), TOKEN_ID, DATA(true)); + state.safe_mint(RECIPIENT(), TOKEN_ID, DATA(true)); } // -// _burn +// burn // #[test] -fn test__burn() { +fn test_burn() { let mut state = setup(); state._approve(OTHER(), TOKEN_ID, ZERO()); @@ -1215,7 +1215,7 @@ fn test__burn() { assert_eq!(state.balance_of(OWNER()), 1); assert_eq!(state.get_approved(TOKEN_ID), OTHER()); - state._burn(TOKEN_ID); + state.burn(TOKEN_ID); assert_only_event_transfer(ZERO(), OWNER(), ZERO(), TOKEN_ID); assert_eq!(state.ERC721_owners.read(TOKEN_ID), ZERO()); @@ -1225,9 +1225,9 @@ fn test__burn() { #[test] #[should_panic(expected: ('ERC721: invalid token ID',))] -fn test__burn_nonexistent() { +fn test_burn_nonexistent() { let mut state = COMPONENT_STATE(); - state._burn(TOKEN_ID); + state.burn(TOKEN_ID); } // @@ -1293,23 +1293,23 @@ fn test__exists() { let mut state = COMPONENT_STATE(); let token_id = TOKEN_ID; - let not_exists = !state._exists(token_id); + let not_exists = !state.exists(token_id); assert!(not_exists); let mut owner = state.ERC721_owners.read(token_id); assert!(owner.is_zero()); - state._mint(RECIPIENT(), token_id); + state.mint(RECIPIENT(), token_id); - let exists = state._exists(token_id); + let exists = state.exists(token_id); assert!(exists); owner = state.ERC721_owners.read(token_id); assert_eq!(owner, RECIPIENT()); - state._burn(token_id); + state.burn(token_id); - let not_exists = !state._exists(token_id); + let not_exists = !state.exists(token_id); assert!(not_exists); owner = state.ERC721_owners.read(token_id); @@ -1483,7 +1483,7 @@ fn test__check_authorized_zero_address() { fn test_update_mint() { let mut state = setup(); - state._update(RECIPIENT(), TOKEN_ID_2, ZERO()); + state.update(RECIPIENT(), TOKEN_ID_2, ZERO()); assert_only_event_transfer(ZERO(), ZERO(), RECIPIENT(), TOKEN_ID_2); let owner = state.owner_of(TOKEN_ID_2); @@ -1497,7 +1497,7 @@ fn test_update_mint() { fn test_update_burn() { let mut state = setup(); - state._update(ZERO(), TOKEN_ID, ZERO()); + state.update(ZERO(), TOKEN_ID, ZERO()); assert_only_event_transfer(ZERO(), OWNER(), ZERO(), TOKEN_ID); let owner = state._owner_of(TOKEN_ID); @@ -1511,7 +1511,7 @@ fn test_update_burn() { fn test_update_transfer() { let mut state = setup(); - state._update(RECIPIENT(), TOKEN_ID, ZERO()); + state.update(RECIPIENT(), TOKEN_ID, ZERO()); assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } @@ -1519,7 +1519,7 @@ fn test_update_transfer() { #[test] fn test_update_auth_owner() { let mut state = setup(); - state._update(RECIPIENT(), TOKEN_ID, OWNER()); + state.update(RECIPIENT(), TOKEN_ID, OWNER()); assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } @@ -1531,7 +1531,7 @@ fn test_update_auth_approved_for_all() { state.set_approval_for_all(OPERATOR(), true); utils::drop_event(ZERO()); - state._update(RECIPIENT(), TOKEN_ID, OPERATOR()); + state.update(RECIPIENT(), TOKEN_ID, OPERATOR()); assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } @@ -1543,7 +1543,7 @@ fn test_update_auth_approved() { state.approve(OPERATOR(), TOKEN_ID); utils::drop_event(ZERO()); - state._update(RECIPIENT(), TOKEN_ID, OPERATOR()); + state.update(RECIPIENT(), TOKEN_ID, OPERATOR()); assert_only_event_transfer(ZERO(), OWNER(), RECIPIENT(), TOKEN_ID); assert_state_after_transfer(OWNER(), RECIPIENT(), TOKEN_ID); } @@ -1552,14 +1552,14 @@ fn test_update_auth_approved() { #[should_panic(expected: ('ERC721: unauthorized caller',))] fn test_update_auth_not_approved() { let mut state = setup(); - state._update(RECIPIENT(), TOKEN_ID, CALLER()); + state.update(RECIPIENT(), TOKEN_ID, CALLER()); } #[test] #[should_panic(expected: ('ERC721: invalid token ID',))] fn test_update_mint_auth_not_zero() { let mut state = setup(); - state._update(RECIPIENT(), TOKEN_ID_2, CALLER()); + state.update(RECIPIENT(), TOKEN_ID_2, CALLER()); } // diff --git a/src/token/erc1155/erc1155.cairo b/src/token/erc1155/erc1155.cairo index 4586cad01..a1cffc387 100644 --- a/src/token/erc1155/erc1155.cairo +++ b/src/token/erc1155/erc1155.cairo @@ -335,112 +335,147 @@ pub mod ERC1155Component { } } - // - // Internal - // - - #[generate_trait] - pub impl InternalImpl< + #[embeddable_as(ERC1155MixinImpl)] + impl ERC1155Mixin< TContractState, +HasComponent, impl SRC5: SRC5Component::HasComponent, - impl Hooks: ERC1155HooksTrait, + +ERC1155HooksTrait, +Drop - > of InternalTrait { - /// Initializes the contract by setting the `base_uri` for all tokens, - /// and registering the supported interfaces. - /// This should only be used inside the contract's constructor. - fn initializer(ref self: ComponentState, base_uri: ByteArray) { - self.set_base_uri(base_uri); + > of interface::ERC1155ABI> { + // IERC1155 + fn balance_of( + self: @ComponentState, account: ContractAddress, token_id: u256 + ) -> u256 { + ERC1155::balance_of(self, account, token_id) + } - let mut src5_component = get_dep_component_mut!(ref self, SRC5); - src5_component.register_interface(interface::IERC1155_ID); - src5_component.register_interface(interface::IERC1155_METADATA_URI_ID); + fn balance_of_batch( + self: @ComponentState, + accounts: Span, + token_ids: Span + ) -> Span { + ERC1155::balance_of_batch(self, accounts, token_ids) } - /// 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` event if the arrays contain one element, and `TransferBatch` otherwise. - /// - /// NOTE: This function can be extended using the `ERC1155HooksTrait`, to add - /// functionality before and/or after the transfer, mint, or burn. - /// - /// NOTE: The ERC1155 acceptance check is not performed in this function. - /// See `update_with_acceptance_check` instead. - fn update( + fn safe_transfer_from( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + value: u256, + data: Span + ) { + ERC1155::safe_transfer_from(ref self, from, to, token_id, value, data); + } + + fn safe_batch_transfer_from( ref self: ComponentState, from: ContractAddress, to: ContractAddress, token_ids: Span, - values: Span + values: Span, + data: Span ) { - Hooks::before_update(ref self, from, to, token_ids, values); + ERC1155::safe_batch_transfer_from(ref self, from, to, token_ids, values, data); + } - assert(token_ids.len() == values.len(), Errors::INVALID_ARRAY_LENGTH); + fn is_approved_for_all( + self: @ComponentState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + ERC1155::is_approved_for_all(self, owner, operator) + } - let mut index = 0; - loop { - if index == token_ids.len() { - break; - } - let token_id = *token_ids.at(index); - let value = *values.at(index); - if from.is_non_zero() { - let from_balance = self.ERC1155_balances.read((token_id, from)); - assert(from_balance >= value, Errors::INSUFFICIENT_BALANCE); - self.ERC1155_balances.write((token_id, from), from_balance - value); - } - if to.is_non_zero() { - let to_balance = self.ERC1155_balances.read((token_id, to)); - self.ERC1155_balances.write((token_id, to), to_balance + value); - } - index += 1; - }; - let operator = get_caller_address(); - if token_ids.len() == 1 { - self - .emit( - TransferSingle { - operator, from, to, id: *token_ids.at(0), value: *values.at(0) - } - ); - } else { - self.emit(TransferBatch { operator, from, to, ids: token_ids, values }); - } + fn set_approval_for_all( + ref self: ComponentState, operator: ContractAddress, approved: bool + ) { + ERC1155::set_approval_for_all(ref self, operator, approved); + } - Hooks::after_update(ref self, from, to, token_ids, values); + // ISRC5 + fn supports_interface( + self: @ComponentState, interface_id: felt252 + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) } - /// Version of `update` that performs the token acceptance check by calling - /// `IERC1155Receiver::onERC1155Received` or `IERC1155Receiver::onERC1155BatchReceived` if - /// the receiver is not recognized as 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` event if the arrays contain one element, and `TransferBatch` otherwise. - fn update_with_acceptance_check( + // IERC1155MetadataURI + fn uri(self: @ComponentState, token_id: u256) -> ByteArray { + ERC1155MetadataURI::uri(self, token_id) + } + + // IERC1155Camel + fn balanceOf( + self: @ComponentState, account: ContractAddress, tokenId: u256 + ) -> u256 { + ERC1155Camel::balanceOf(self, account, tokenId) + } + + fn balanceOfBatch( + self: @ComponentState, + accounts: Span, + tokenIds: Span + ) -> Span { + ERC1155Camel::balanceOfBatch(self, accounts, tokenIds) + } + + fn safeTransferFrom( ref self: ComponentState, from: ContractAddress, to: ContractAddress, - token_ids: Span, + tokenId: u256, + value: u256, + data: Span + ) { + ERC1155Camel::safeTransferFrom(ref self, from, to, tokenId, value, data); + } + + fn safeBatchTransferFrom( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + tokenIds: Span, values: Span, data: Span ) { - self.update(from, to, token_ids, values); - let accepted = if token_ids.len() == 1 { - _check_on_ERC1155_received(from, to, *token_ids.at(0), *values.at(0), data) - } else { - _check_on_ERC1155_batch_received(from, to, token_ids, values, data) - }; - assert(accepted, Errors::SAFE_TRANSFER_FAILED); + ERC1155Camel::safeBatchTransferFrom(ref self, from, to, tokenIds, values, data); + } + + fn isApprovedForAll( + self: @ComponentState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + ERC1155Camel::isApprovedForAll(self, owner, operator) + } + + fn setApprovalForAll( + ref self: ComponentState, operator: ContractAddress, approved: bool + ) { + ERC1155Camel::setApprovalForAll(ref self, operator, approved); + } + } + + // + // Internal + // + + #[generate_trait] + pub impl InternalImpl< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + impl Hooks: ERC1155HooksTrait, + +Drop + > of InternalTrait { + /// Initializes the contract by setting the `base_uri` for all tokens, + /// and registering the supported interfaces. + /// This should only be used inside the contract's constructor. + fn initializer(ref self: ComponentState, base_uri: ByteArray) { + self._set_base_uri(base_uri); + + let mut src5_component = get_dep_component_mut!(ref self, SRC5); + src5_component.register_interface(interface::IERC1155_ID); + src5_component.register_interface(interface::IERC1155_METADATA_URI_ID); } /// Creates a `value` amount of tokens of type `token_id`, and assigns them to `to`. @@ -527,6 +562,91 @@ pub mod ERC1155Component { self.update(from, Zero::zero(), token_ids, values); } + /// Version of `update` that performs the token acceptance check by calling + /// `IERC1155Receiver::onERC1155Received` or `IERC1155Receiver::onERC1155BatchReceived` if + /// the receiver is not recognized as 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` event if the arrays contain one element, and `TransferBatch` otherwise. + fn update_with_acceptance_check( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_ids: Span, + values: Span, + data: Span + ) { + self.update(from, to, token_ids, values); + let accepted = if token_ids.len() == 1 { + _check_on_ERC1155_received(from, to, *token_ids.at(0), *values.at(0), data) + } else { + _check_on_ERC1155_batch_received(from, to, token_ids, values, data) + }; + assert(accepted, Errors::SAFE_TRANSFER_FAILED); + } + + /// 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` event if the arrays contain one element, and `TransferBatch` otherwise. + /// + /// NOTE: This function can be extended using the `ERC1155HooksTrait`, to add + /// functionality before and/or after the transfer, mint, or burn. + /// + /// NOTE: The ERC1155 acceptance check is not performed in this function. + /// See `update_with_acceptance_check` instead. + fn update( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_ids: Span, + values: Span + ) { + Hooks::before_update(ref self, from, to, token_ids, values); + + assert(token_ids.len() == values.len(), Errors::INVALID_ARRAY_LENGTH); + + let mut index = 0; + loop { + if index == token_ids.len() { + break; + } + let token_id = *token_ids.at(index); + let value = *values.at(index); + if from.is_non_zero() { + let from_balance = self.ERC1155_balances.read((token_id, from)); + assert(from_balance >= value, Errors::INSUFFICIENT_BALANCE); + self.ERC1155_balances.write((token_id, from), from_balance - value); + } + if to.is_non_zero() { + let to_balance = self.ERC1155_balances.read((token_id, to)); + self.ERC1155_balances.write((token_id, to), to_balance + value); + } + index += 1; + }; + let operator = get_caller_address(); + if token_ids.len() == 1 { + self + .emit( + TransferSingle { + operator, from, to, id: *token_ids.at(0), value: *values.at(0) + } + ); + } else { + self.emit(TransferBatch { operator, from, to, ids: token_ids, values }); + } + + Hooks::after_update(ref self, from, to, token_ids, values); + } + /// Sets a new URI for all token types, by relying on the token type ID /// substitution mechanism defined in the ERC1155 standard. /// See https://eips.ethereum.org/EIPS/eip-1155#metadata. @@ -542,7 +662,7 @@ pub mod ERC1155Component { /// /// Because these URIs cannot be meaningfully represented by the `URI` event, /// this function emits no events. - fn set_base_uri(ref self: ComponentState, base_uri: ByteArray) { + fn _set_base_uri(ref self: ComponentState, base_uri: ByteArray) { self.ERC1155_uri.write(base_uri); } } @@ -584,126 +704,6 @@ pub mod ERC1155Component { src5_dispatcher.supports_interface(account::interface::ISRC6_ID) } } - - #[embeddable_as(ERC1155MixinImpl)] - impl ERC1155Mixin< - TContractState, - +HasComponent, - impl SRC5: SRC5Component::HasComponent, - +ERC1155HooksTrait, - +Drop - > of interface::ERC1155ABI> { - // IERC1155 - fn balance_of( - self: @ComponentState, account: ContractAddress, token_id: u256 - ) -> u256 { - ERC1155::balance_of(self, account, token_id) - } - - fn balance_of_batch( - self: @ComponentState, - accounts: Span, - token_ids: Span - ) -> Span { - ERC1155::balance_of_batch(self, accounts, token_ids) - } - - fn safe_transfer_from( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - value: u256, - data: Span - ) { - ERC1155::safe_transfer_from(ref self, from, to, token_id, value, data); - } - - fn safe_batch_transfer_from( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - token_ids: Span, - values: Span, - data: Span - ) { - ERC1155::safe_batch_transfer_from(ref self, from, to, token_ids, values, data); - } - - fn is_approved_for_all( - self: @ComponentState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - ERC1155::is_approved_for_all(self, owner, operator) - } - - fn set_approval_for_all( - ref self: ComponentState, operator: ContractAddress, approved: bool - ) { - ERC1155::set_approval_for_all(ref self, operator, approved); - } - - // ISRC5 - fn supports_interface( - self: @ComponentState, interface_id: felt252 - ) -> bool { - let src5 = get_dep_component!(self, SRC5); - src5.supports_interface(interface_id) - } - - // IERC1155MetadataURI - fn uri(self: @ComponentState, token_id: u256) -> ByteArray { - ERC1155MetadataURI::uri(self, token_id) - } - - // IERC1155Camel - fn balanceOf( - self: @ComponentState, account: ContractAddress, tokenId: u256 - ) -> u256 { - ERC1155Camel::balanceOf(self, account, tokenId) - } - - fn balanceOfBatch( - self: @ComponentState, - accounts: Span, - tokenIds: Span - ) -> Span { - ERC1155Camel::balanceOfBatch(self, accounts, tokenIds) - } - - fn safeTransferFrom( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - value: u256, - data: Span - ) { - ERC1155Camel::safeTransferFrom(ref self, from, to, tokenId, value, data); - } - - fn safeBatchTransferFrom( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - tokenIds: Span, - values: Span, - data: Span - ) { - ERC1155Camel::safeBatchTransferFrom(ref self, from, to, tokenIds, values, data); - } - - fn isApprovedForAll( - self: @ComponentState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - ERC1155Camel::isApprovedForAll(self, owner, operator) - } - - fn setApprovalForAll( - ref self: ComponentState, operator: ContractAddress, approved: bool - ) { - ERC1155Camel::setApprovalForAll(ref self, operator, approved); - } - } } /// An empty implementation of the ERC1155 hooks to be used in basic ERC1155 preset contracts. diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index b0b7d0fe4..d19f61dda 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -213,6 +213,78 @@ pub mod ERC20Component { } } + #[embeddable_as(ERC20MixinImpl)] + impl ERC20Mixin< + TContractState, +HasComponent, +ERC20HooksTrait + > of interface::ERC20ABI> { + // IERC20 + fn total_supply(self: @ComponentState) -> u256 { + ERC20::total_supply(self) + } + + fn balance_of(self: @ComponentState, account: ContractAddress) -> u256 { + ERC20::balance_of(self, account) + } + + fn allowance( + self: @ComponentState, owner: ContractAddress, spender: ContractAddress + ) -> u256 { + ERC20::allowance(self, owner, spender) + } + + fn transfer( + ref self: ComponentState, recipient: ContractAddress, amount: u256 + ) -> bool { + ERC20::transfer(ref self, recipient, amount) + } + + fn transfer_from( + ref self: ComponentState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) -> bool { + ERC20::transfer_from(ref self, sender, recipient, amount) + } + + fn approve( + ref self: ComponentState, spender: ContractAddress, amount: u256 + ) -> bool { + ERC20::approve(ref self, spender, amount) + } + + // IERC20Metadata + fn name(self: @ComponentState) -> ByteArray { + ERC20Metadata::name(self) + } + + fn symbol(self: @ComponentState) -> ByteArray { + ERC20Metadata::symbol(self) + } + + fn decimals(self: @ComponentState) -> u8 { + ERC20Metadata::decimals(self) + } + + // IERC20CamelOnly + fn totalSupply(self: @ComponentState) -> u256 { + ERC20CamelOnly::totalSupply(self) + } + + fn balanceOf(self: @ComponentState, account: ContractAddress) -> u256 { + ERC20CamelOnly::balanceOf(self, account) + } + + fn transferFrom( + ref self: ComponentState, + sender: ContractAddress, + recipient: ContractAddress, + amount: u256 + ) -> bool { + ERC20CamelOnly::transferFrom(ref self, sender, recipient, amount) + } + } + // // Internal // @@ -230,47 +302,6 @@ pub mod ERC20Component { self.ERC20_symbol.write(symbol); } - /// Internal method that moves an `amount` of tokens from `from` to `to`. - /// - /// Requirements: - /// - /// - `sender` is not the zero address. - /// - `sender` must have at least a balance of `amount`. - /// - `recipient` is not the zero address. - /// - /// Emits a `Transfer` event. - fn _transfer( - ref self: ComponentState, - sender: ContractAddress, - recipient: ContractAddress, - amount: u256 - ) { - assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO); - assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO); - self._update(sender, recipient, amount); - } - - /// Internal method that sets `amount` as the allowance of `spender` over the - /// `owner`s tokens. - /// - /// Requirements: - /// - /// - `owner` is not the zero address. - /// - `spender` is not the zero address. - /// - /// Emits an `Approval` event. - fn _approve( - ref self: ComponentState, - owner: ContractAddress, - spender: ContractAddress, - amount: u256 - ) { - assert(!owner.is_zero(), Errors::APPROVE_FROM_ZERO); - assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO); - self.ERC20_allowances.write((owner, spender), amount); - self.emit(Approval { owner, spender, value: amount }); - } - /// Creates a `value` amount of tokens and assigns them to `account`. /// /// Requirements: @@ -278,11 +309,11 @@ pub mod ERC20Component { /// - `recipient` is not the zero address. /// /// Emits a `Transfer` event with `from` set to the zero address. - fn _mint( + fn mint( ref self: ComponentState, recipient: ContractAddress, amount: u256 ) { assert(!recipient.is_zero(), Errors::MINT_TO_ZERO); - self._update(Zero::zero(), recipient, amount); + self.update(Zero::zero(), recipient, amount); } /// Destroys `amount` of tokens from `account`. @@ -293,31 +324,11 @@ pub mod ERC20Component { /// - `account` must have at least a balance of `amount`. /// /// Emits a `Transfer` event with `to` set to the zero address. - fn _burn(ref self: ComponentState, account: ContractAddress, amount: u256) { + fn burn(ref self: ComponentState, account: ContractAddress, amount: u256) { assert(!account.is_zero(), Errors::BURN_FROM_ZERO); - self._update(account, Zero::zero(), amount); + self.update(account, Zero::zero(), amount); } - /// Updates `owner`s allowance for `spender` based on spent `amount`. - /// Does not update the allowance value in case of infinite allowance. - /// - /// Requirements: - /// - /// - `spender` must have at least an allowance of `amount` from `owner`. - /// - /// Possibly emits an `Approval` event. - fn _spend_allowance( - ref self: ComponentState, - owner: ContractAddress, - spender: ContractAddress, - amount: u256 - ) { - let current_allowance = self.ERC20_allowances.read((owner, spender)); - if current_allowance != BoundedInt::max() { - assert(current_allowance >= amount, Errors::INSUFFICIENT_ALLOWANCE); - self._approve(owner, spender, current_allowance - amount); - } - } /// Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is /// the zero address. @@ -326,7 +337,7 @@ pub mod ERC20Component { /// functionality before and/or after the transfer, mint, or burn. /// /// Emits a `Transfer` event. - fn _update( + fn update( ref self: ComponentState, from: ContractAddress, to: ContractAddress, @@ -356,77 +367,67 @@ pub mod ERC20Component { Hooks::after_update(ref self, from, to, amount); } - } - - #[embeddable_as(ERC20MixinImpl)] - impl ERC20Mixin< - TContractState, +HasComponent, +ERC20HooksTrait - > of interface::ERC20ABI> { - // IERC20 - fn total_supply(self: @ComponentState) -> u256 { - ERC20::total_supply(self) - } - - fn balance_of(self: @ComponentState, account: ContractAddress) -> u256 { - ERC20::balance_of(self, account) - } - - fn allowance( - self: @ComponentState, owner: ContractAddress, spender: ContractAddress - ) -> u256 { - ERC20::allowance(self, owner, spender) - } - - fn transfer( - ref self: ComponentState, recipient: ContractAddress, amount: u256 - ) -> bool { - ERC20::transfer(ref self, recipient, amount) - } - fn transfer_from( + /// Internal method that moves an `amount` of tokens from `from` to `to`. + /// + /// Requirements: + /// + /// - `sender` is not the zero address. + /// - `sender` must have at least a balance of `amount`. + /// - `recipient` is not the zero address. + /// + /// Emits a `Transfer` event. + fn _transfer( ref self: ComponentState, sender: ContractAddress, recipient: ContractAddress, amount: u256 - ) -> bool { - ERC20::transfer_from(ref self, sender, recipient, amount) - } - - fn approve( - ref self: ComponentState, spender: ContractAddress, amount: u256 - ) -> bool { - ERC20::approve(ref self, spender, amount) - } - - // IERC20Metadata - fn name(self: @ComponentState) -> ByteArray { - ERC20Metadata::name(self) - } - - fn symbol(self: @ComponentState) -> ByteArray { - ERC20Metadata::symbol(self) - } - - fn decimals(self: @ComponentState) -> u8 { - ERC20Metadata::decimals(self) - } - - // IERC20CamelOnly - fn totalSupply(self: @ComponentState) -> u256 { - ERC20CamelOnly::totalSupply(self) + ) { + assert(!sender.is_zero(), Errors::TRANSFER_FROM_ZERO); + assert(!recipient.is_zero(), Errors::TRANSFER_TO_ZERO); + self.update(sender, recipient, amount); } - fn balanceOf(self: @ComponentState, account: ContractAddress) -> u256 { - ERC20CamelOnly::balanceOf(self, account) + /// Internal method that sets `amount` as the allowance of `spender` over the + /// `owner`s tokens. + /// + /// Requirements: + /// + /// - `owner` is not the zero address. + /// - `spender` is not the zero address. + /// + /// Emits an `Approval` event. + fn _approve( + ref self: ComponentState, + owner: ContractAddress, + spender: ContractAddress, + amount: u256 + ) { + assert(!owner.is_zero(), Errors::APPROVE_FROM_ZERO); + assert(!spender.is_zero(), Errors::APPROVE_TO_ZERO); + self.ERC20_allowances.write((owner, spender), amount); + self.emit(Approval { owner, spender, value: amount }); } - fn transferFrom( + /// Updates `owner`s allowance for `spender` based on spent `amount`. + /// Does not update the allowance value in case of infinite allowance. + /// + /// Requirements: + /// + /// - `spender` must have at least an allowance of `amount` from `owner`. + /// + /// Possibly emits an `Approval` event. + fn _spend_allowance( ref self: ComponentState, - sender: ContractAddress, - recipient: ContractAddress, + owner: ContractAddress, + spender: ContractAddress, amount: u256 - ) -> bool { - ERC20CamelOnly::transferFrom(ref self, sender, recipient, amount) + ) { + let current_allowance = self.ERC20_allowances.read((owner, spender)); + if current_allowance != BoundedInt::max() { + assert(current_allowance >= amount, Errors::INSUFFICIENT_ALLOWANCE); + self._approve(owner, spender, current_allowance - amount); + } } } } diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 75990b262..9830ad3de 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -183,7 +183,7 @@ pub mod ERC721Component { // Setting an "auth" arguments enables the `_is_authorized` check which verifies that the token exists // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. - let previous_owner = self._update(to, token_id, get_caller_address()); + let previous_owner = self.update(to, token_id, get_caller_address()); assert(from == previous_owner, Errors::INVALID_SENDER); } @@ -334,6 +334,133 @@ pub mod ERC721Component { } } + #[embeddable_as(ERC721MixinImpl)] + impl ERC721Mixin< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +ERC721HooksTrait, + +Drop + > of interface::ERC721ABI> { + // IERC721 + fn balance_of(self: @ComponentState, account: ContractAddress) -> u256 { + ERC721::balance_of(self, account) + } + + fn owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { + ERC721::owner_of(self, token_id) + } + + fn safe_transfer_from( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_id: u256, + data: Span + ) { + ERC721::safe_transfer_from(ref self, from, to, token_id, data); + } + + fn transfer_from( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + token_id: u256 + ) { + ERC721::transfer_from(ref self, from, to, token_id); + } + + fn approve(ref self: ComponentState, to: ContractAddress, token_id: u256) { + ERC721::approve(ref self, to, token_id); + } + + fn set_approval_for_all( + ref self: ComponentState, operator: ContractAddress, approved: bool + ) { + ERC721::set_approval_for_all(ref self, operator, approved); + } + + fn get_approved(self: @ComponentState, token_id: u256) -> ContractAddress { + ERC721::get_approved(self, token_id) + } + + fn is_approved_for_all( + self: @ComponentState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + ERC721::is_approved_for_all(self, owner, operator) + } + + // IERC721Metadata + fn name(self: @ComponentState) -> ByteArray { + ERC721Metadata::name(self) + } + + fn symbol(self: @ComponentState) -> ByteArray { + ERC721Metadata::symbol(self) + } + + fn token_uri(self: @ComponentState, token_id: u256) -> ByteArray { + ERC721Metadata::token_uri(self, token_id) + } + + // IERC721CamelOnly + fn balanceOf(self: @ComponentState, account: ContractAddress) -> u256 { + ERC721CamelOnly::balanceOf(self, account) + } + + fn ownerOf(self: @ComponentState, tokenId: u256) -> ContractAddress { + ERC721CamelOnly::ownerOf(self, tokenId) + } + + fn safeTransferFrom( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256, + data: Span + ) { + ERC721CamelOnly::safeTransferFrom(ref self, from, to, tokenId, data); + } + + fn transferFrom( + ref self: ComponentState, + from: ContractAddress, + to: ContractAddress, + tokenId: u256 + ) { + ERC721CamelOnly::transferFrom(ref self, from, to, tokenId); + } + + fn setApprovalForAll( + ref self: ComponentState, operator: ContractAddress, approved: bool + ) { + ERC721CamelOnly::setApprovalForAll(ref self, operator, approved); + } + + fn getApproved(self: @ComponentState, tokenId: u256) -> ContractAddress { + ERC721CamelOnly::getApproved(self, tokenId) + } + + fn isApprovedForAll( + self: @ComponentState, owner: ContractAddress, operator: ContractAddress + ) -> bool { + ERC721CamelOnly::isApprovedForAll(self, owner, operator) + } + + // IERC721MetadataCamelOnly + fn tokenURI(self: @ComponentState, tokenId: u256) -> ByteArray { + ERC721MetadataCamelOnly::tokenURI(self, tokenId) + } + + // ISRC5 + fn supports_interface( + self: @ComponentState, interface_id: felt252 + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) + } + } + // // Internal // @@ -363,95 +490,36 @@ pub mod ERC721Component { src5_component.register_interface(interface::IERC721_METADATA_ID); } - /// Returns the owner address of `token_id`. - fn _owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { - self.ERC721_owners.read(token_id) - } - - /// Returns the owner address of `token_id`. - /// - /// Requirements: - /// - /// - `token_id` exists. - fn _require_owned( - self: @ComponentState, token_id: u256 - ) -> ContractAddress { - let owner = self._owner_of(token_id); - assert(!owner.is_zero(), Errors::INVALID_TOKEN_ID); - owner - } - /// Returns whether `token_id` exists. - fn _exists(self: @ComponentState, token_id: u256) -> bool { + fn exists(self: @ComponentState, token_id: u256) -> bool { !self._owner_of(token_id).is_zero() } - /// 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. + /// Transfers `token_id` from `from` to `to`. /// - /// Emits an `Approval` event. - fn _approve( - ref self: ComponentState, - to: ContractAddress, - token_id: u256, - auth: ContractAddress - ) { - self._approve_with_optional_event(to, token_id, auth, true); - } - - /// Variant of `_approve` with an optional flag to enable or disable the `Approval` event. The event is not - /// emitted in the context of transfers. + /// Internal function without access restriction. /// - /// WARNING: If `auth` is zero and `emit_event` is false, this function will not check that the token exists. + /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. /// /// Requirements: /// - /// - If `auth` is non-zero, it must be either the owner of the token or approved to - /// operate on all of its tokens. + /// - `to` is not the zero address. + /// - `from` is the token owner. + /// - `token_id` exists. /// - /// May emit an `Approval` event. - fn _approve_with_optional_event( + /// Emits a `Transfer` event. + fn transfer( ref self: ComponentState, + from: ContractAddress, to: ContractAddress, - token_id: u256, - auth: ContractAddress, - emit_event: bool + token_id: u256 ) { - if emit_event || !auth.is_zero() { - let owner = self._require_owned(token_id); - - if !auth.is_zero() && owner != auth { - let is_approved_for_all = ERC721::is_approved_for_all(@self, owner, auth); - assert(is_approved_for_all, Errors::UNAUTHORIZED); - } - - if emit_event { - self.emit(Approval { owner, approved: to, token_id }); - } - } + assert(!to.is_zero(), Errors::INVALID_RECEIVER); - self.ERC721_token_approvals.write(token_id, to); - } + let previous_owner = self.update(to, token_id, Zero::zero()); - /// Enables or disables approval for `operator` to manage - /// all of the `owner` assets. - /// - /// Requirements: - /// - /// - `operator` is not the zero address. - /// - /// Emits an `Approval` event. - fn _set_approval_for_all( - ref self: ComponentState, - owner: ContractAddress, - operator: ContractAddress, - approved: bool - ) { - assert(!operator.is_zero(), Errors::INVALID_OPERATOR); - self.ERC721_operator_approvals.write((owner, operator), approved); - self.emit(ApprovalForAll { owner, operator, approved }); + assert(!previous_owner.is_zero(), Errors::INVALID_TOKEN_ID); + assert(from == previous_owner, Errors::INVALID_SENDER); } /// Mints `token_id` and transfers it to `to`. @@ -465,54 +533,39 @@ pub mod ERC721Component { /// - `token_id` does not exist. /// /// Emits a `Transfer` event. - fn _mint(ref self: ComponentState, to: ContractAddress, token_id: u256) { + fn mint(ref self: ComponentState, to: ContractAddress, token_id: u256) { assert(!to.is_zero(), Errors::INVALID_RECEIVER); - let previous_owner = self._update(to, token_id, Zero::zero()); + let previous_owner = self.update(to, token_id, Zero::zero()); assert(previous_owner.is_zero(), Errors::ALREADY_MINTED); } - /// Transfers `token_id` from `from` to `to`. + /// Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`. /// - /// Internal function without access restriction. + /// `data` is additional data, it has no specified format and it is sent in call to `to`. /// - /// WARNING: This method may lead to the loss of tokens if `to` is not aware of the ERC721 protocol. + /// WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. /// /// Requirements: /// - /// - `to` is not the zero address. - /// - `from` is the token owner. + /// - `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` event. - fn _transfer( + fn safe_transfer( ref self: ComponentState, from: ContractAddress, to: ContractAddress, - token_id: u256 + token_id: u256, + data: Span ) { - assert(!to.is_zero(), Errors::INVALID_RECEIVER); - - let previous_owner = self._update(to, token_id, Zero::zero()); - - assert(!previous_owner.is_zero(), Errors::INVALID_TOKEN_ID); - assert(from == previous_owner, Errors::INVALID_SENDER); - } - - /// 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` event. - fn _burn(ref self: ComponentState, token_id: u256) { - let previous_owner = self._update(Zero::zero(), token_id, Zero::zero()); - assert(!previous_owner.is_zero(), Errors::INVALID_TOKEN_ID); + self.transfer(from, to, token_id); + assert( + _check_on_erc721_received(from, to, token_id, data), Errors::SAFE_TRANSFER_FAILED + ); } /// Mints `token_id` if `to` is either an account or `IERC721Receiver`. @@ -527,97 +580,32 @@ pub mod ERC721Component { /// - `to` is either an account contract or supports the `IERC721Receiver` interface. /// /// Emits a `Transfer` event. - fn _safe_mint( + fn safe_mint( ref self: ComponentState, to: ContractAddress, token_id: u256, data: Span ) { - self._mint(to, token_id); + self.mint(to, token_id); assert( _check_on_erc721_received(Zero::zero(), to, token_id, data), Errors::SAFE_MINT_FAILED ); } - /// 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 it is sent in call to `to`. + /// Destroys `token_id`. The approval is cleared when the token is burned. /// - /// WARNING: This method makes an external call to the recipient contract, which can lead to reentrancy vulnerabilities. + /// This internal function does not check if the caller is authorized + /// to operate on the token. /// /// 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` event. - fn _safe_transfer( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - token_id: u256, - data: Span - ) { - self._transfer(from, to, token_id); - assert( - _check_on_erc721_received(from, to, token_id, data), Errors::SAFE_TRANSFER_FAILED - ); - } - - /// Sets the base URI. - fn _set_base_uri(ref self: ComponentState, base_uri: ByteArray) { - self.ERC721_base_uri.write(base_uri); - } - - /// Base URI for computing `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. - fn _base_uri(self: @ComponentState) -> ByteArray { - self.ERC721_base_uri.read() - } - - /// Returns whether `spender` is allowed to manage `owner`'s tokens, or `token_id` in - /// particular (ignoring whether it is owned by `owner`). - /// - /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does not verify this - /// assumption. - fn _is_authorized( - self: @ComponentState, - owner: ContractAddress, - spender: ContractAddress, - token_id: u256 - ) -> bool { - let is_approved_for_all = ERC721::is_approved_for_all(self, owner, spender); - - !spender.is_zero() - && (owner == spender - || is_approved_for_all - || spender == ERC721::get_approved(self, token_id)) - } - - /// 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. - /// - /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does not verify this - /// assumption. - fn _check_authorized( - self: @ComponentState, - owner: ContractAddress, - spender: ContractAddress, - token_id: u256 - ) { - // Non-existent token - assert(!owner.is_zero(), Errors::INVALID_TOKEN_ID); - assert(self._is_authorized(owner, spender, token_id), Errors::UNAUTHORIZED); + fn burn(ref self: ComponentState, token_id: u256) { + let previous_owner = self.update(Zero::zero(), token_id, Zero::zero()); + assert(!previous_owner.is_zero(), Errors::INVALID_TOKEN_ID); } /// Transfers `token_id` from its current owner to `to`, or alternatively mints (or burns) if the current owner @@ -631,7 +619,7 @@ pub mod ERC721Component { /// NOTE: This function can be extended using the `ERC721HooksTrait`, to add /// functionality before and/or after the transfer, mint, or burn. /// - fn _update( + fn update( ref self: ComponentState, to: ContractAddress, token_id: u256, @@ -662,149 +650,161 @@ pub mod ERC721Component { from } - } - - /// Checks if `to` either is an account contract or has registered support - /// for the `IERC721Receiver` interface through SRC5. - fn _check_on_erc721_received( - from: ContractAddress, to: ContractAddress, token_id: u256, data: Span - ) -> bool { - let src5_dispatcher = ISRC5Dispatcher { contract_address: to }; - if src5_dispatcher.supports_interface(interface::IERC721_RECEIVER_ID) { - DualCaseERC721Receiver { contract_address: to } - .on_erc721_received( - get_caller_address(), from, token_id, data - ) == interface::IERC721_RECEIVER_ID - } else { - src5_dispatcher.supports_interface(account::interface::ISRC6_ID) - } - } - - #[embeddable_as(ERC721MixinImpl)] - impl ERC721Mixin< - TContractState, - +HasComponent, - impl SRC5: SRC5Component::HasComponent, - +ERC721HooksTrait, - +Drop - > of interface::ERC721ABI> { - // IERC721 - fn balance_of(self: @ComponentState, account: ContractAddress) -> u256 { - ERC721::balance_of(self, account) + /// Returns the owner address of `token_id`. + fn _owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { + self.ERC721_owners.read(token_id) } - fn owner_of(self: @ComponentState, token_id: u256) -> ContractAddress { - ERC721::owner_of(self, token_id) + /// Returns the owner address of `token_id`. + /// + /// Requirements: + /// + /// - `token_id` exists. + fn _require_owned( + self: @ComponentState, token_id: u256 + ) -> ContractAddress { + let owner = self._owner_of(token_id); + assert(!owner.is_zero(), Errors::INVALID_TOKEN_ID); + owner } - fn safe_transfer_from( + /// 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` event. + fn _approve( ref self: ComponentState, - from: ContractAddress, to: ContractAddress, token_id: u256, - data: Span + auth: ContractAddress ) { - ERC721::safe_transfer_from(ref self, from, to, token_id, data); + self._approve_with_optional_event(to, token_id, auth, true); } - fn transfer_from( + /// Variant of `_approve` with an optional flag to enable or disable the `Approval` event. The event is not + /// emitted in the context of transfers. + /// + /// WARNING: 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` event. + fn _approve_with_optional_event( ref self: ComponentState, - from: ContractAddress, to: ContractAddress, - token_id: u256 - ) { - ERC721::transfer_from(ref self, from, to, token_id); - } - - fn approve(ref self: ComponentState, to: ContractAddress, token_id: u256) { - ERC721::approve(ref self, to, token_id); - } - - fn set_approval_for_all( - ref self: ComponentState, operator: ContractAddress, approved: bool + token_id: u256, + auth: ContractAddress, + emit_event: bool ) { - ERC721::set_approval_for_all(ref self, operator, approved); - } - - fn get_approved(self: @ComponentState, token_id: u256) -> ContractAddress { - ERC721::get_approved(self, token_id) - } - - fn is_approved_for_all( - self: @ComponentState, owner: ContractAddress, operator: ContractAddress - ) -> bool { - ERC721::is_approved_for_all(self, owner, operator) - } - - // IERC721Metadata - fn name(self: @ComponentState) -> ByteArray { - ERC721Metadata::name(self) - } - - fn symbol(self: @ComponentState) -> ByteArray { - ERC721Metadata::symbol(self) - } - - fn token_uri(self: @ComponentState, token_id: u256) -> ByteArray { - ERC721Metadata::token_uri(self, token_id) - } + if emit_event || !auth.is_zero() { + let owner = self._require_owned(token_id); - // IERC721CamelOnly - fn balanceOf(self: @ComponentState, account: ContractAddress) -> u256 { - ERC721CamelOnly::balanceOf(self, account) - } + if !auth.is_zero() && owner != auth { + let is_approved_for_all = ERC721::is_approved_for_all(@self, owner, auth); + assert(is_approved_for_all, Errors::UNAUTHORIZED); + } - fn ownerOf(self: @ComponentState, tokenId: u256) -> ContractAddress { - ERC721CamelOnly::ownerOf(self, tokenId) - } + if emit_event { + self.emit(Approval { owner, approved: to, token_id }); + } + } - fn safeTransferFrom( - ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256, - data: Span - ) { - ERC721CamelOnly::safeTransferFrom(ref self, from, to, tokenId, data); + self.ERC721_token_approvals.write(token_id, to); } - fn transferFrom( + /// Enables or disables approval for `operator` to manage + /// all of the `owner` assets. + /// + /// Requirements: + /// + /// - `operator` is not the zero address. + /// + /// Emits an `Approval` event. + fn _set_approval_for_all( ref self: ComponentState, - from: ContractAddress, - to: ContractAddress, - tokenId: u256 + owner: ContractAddress, + operator: ContractAddress, + approved: bool ) { - ERC721CamelOnly::transferFrom(ref self, from, to, tokenId); + assert(!operator.is_zero(), Errors::INVALID_OPERATOR); + self.ERC721_operator_approvals.write((owner, operator), approved); + self.emit(ApprovalForAll { owner, operator, approved }); } - fn setApprovalForAll( - ref self: ComponentState, operator: ContractAddress, approved: bool - ) { - ERC721CamelOnly::setApprovalForAll(ref self, operator, approved); + /// Sets the base URI. + fn _set_base_uri(ref self: ComponentState, base_uri: ByteArray) { + self.ERC721_base_uri.write(base_uri); } - fn getApproved(self: @ComponentState, tokenId: u256) -> ContractAddress { - ERC721CamelOnly::getApproved(self, tokenId) + /// Base URI for computing `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. + fn _base_uri(self: @ComponentState) -> ByteArray { + self.ERC721_base_uri.read() } - fn isApprovedForAll( - self: @ComponentState, owner: ContractAddress, operator: ContractAddress + /// Returns whether `spender` is allowed to manage `owner`'s tokens, or `token_id` in + /// particular (ignoring whether it is owned by `owner`). + /// + /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does not verify this + /// assumption. + fn _is_authorized( + self: @ComponentState, + owner: ContractAddress, + spender: ContractAddress, + token_id: u256 ) -> bool { - ERC721CamelOnly::isApprovedForAll(self, owner, operator) + let is_approved_for_all = ERC721::is_approved_for_all(self, owner, spender); + + !spender.is_zero() + && (owner == spender + || is_approved_for_all + || spender == ERC721::get_approved(self, token_id)) } - // IERC721MetadataCamelOnly - fn tokenURI(self: @ComponentState, tokenId: u256) -> ByteArray { - ERC721MetadataCamelOnly::tokenURI(self, tokenId) + /// 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. + /// + /// WARNING: This function assumes that `owner` is the actual owner of `token_id` and does not verify this + /// assumption. + fn _check_authorized( + self: @ComponentState, + owner: ContractAddress, + spender: ContractAddress, + token_id: u256 + ) { + // Non-existent token + assert(!owner.is_zero(), Errors::INVALID_TOKEN_ID); + assert(self._is_authorized(owner, spender, token_id), Errors::UNAUTHORIZED); } + } - // ISRC5 - fn supports_interface( - self: @ComponentState, interface_id: felt252 - ) -> bool { - let src5 = get_dep_component!(self, SRC5); - src5.supports_interface(interface_id) + /// Checks if `to` either is an account contract or has registered support + /// for the `IERC721Receiver` interface through SRC5. + fn _check_on_erc721_received( + from: ContractAddress, to: ContractAddress, token_id: u256, data: Span + ) -> bool { + let src5_dispatcher = ISRC5Dispatcher { contract_address: to }; + + if src5_dispatcher.supports_interface(interface::IERC721_RECEIVER_ID) { + DualCaseERC721Receiver { contract_address: to } + .on_erc721_received( + get_caller_address(), from, token_id, data + ) == interface::IERC721_RECEIVER_ID + } else { + src5_dispatcher.supports_interface(account::interface::ISRC6_ID) } } } diff --git a/src/upgrades/upgradeable.cairo b/src/upgrades/upgradeable.cairo index 15f75aebc..f268fbfdd 100644 --- a/src/upgrades/upgradeable.cairo +++ b/src/upgrades/upgradeable.cairo @@ -40,7 +40,7 @@ pub mod UpgradeableComponent { /// - `new_class_hash` is not zero. /// /// Emits an `Upgraded` event. - fn _upgrade(ref self: ComponentState, new_class_hash: ClassHash) { + fn upgrade(ref self: ComponentState, new_class_hash: ClassHash) { assert(!new_class_hash.is_zero(), Errors::INVALID_CLASS); starknet::syscalls::replace_class_syscall(new_class_hash).unwrap_syscall(); self.emit(Upgraded { class_hash: new_class_hash });