diff --git a/CHANGELOG.md b/CHANGELOG.md index b0efc9caf..57f4747ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Change unwrap to unwrap_syscall (#901) +- ABI implementations (mixins) in components (#863) ## 0.9.0 (2024-02-08) diff --git a/docs/modules/ROOT/pages/api/access.adoc b/docs/modules/ROOT/pages/api/access.adoc index f30918c5a..40b9b4593 100644 --- a/docs/modules/ROOT/pages/api/access.adoc +++ b/docs/modules/ROOT/pages/api/access.adoc @@ -4,6 +4,7 @@ :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] +:mixin-impls: xref:components.adoc#mixins[Embeddable Mixin Implementations] = Access Control @@ -29,15 +30,32 @@ use openzeppelin::access::ownable::OwnableComponent; This module includes the internal `assert_only_owner` to restrict a function to be used only by the owner. -[.contract-index] +[.contract-index#OwnableComponent-Mixin-Impl] +.{mixin-impls} + +-- +.OwnableMixinImpl + +* xref:#OwnableComponent-Embeddable-Impls-OwnableImpl[`++OwnableImpl++`] +* xref:#OwnableComponent-Embeddable-Impls-OwnableCamelOnlyImpl[`++OwnableCamelOnlyImpl++`] + +.OwnableTwoStepMixinImpl + +* xref:#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl[`++OwnableTwoStepImpl++`] +* xref:#OwnableComponent-Embeddable-Impls-OwnableTwoStepCamelOnlyImpl[`++OwnableTwoStepCamelOnlyImpl++`] +-- + +[.contract-index#OwnableComponent-Embeddable-Impls] .Embeddable Implementations -- +[.sub-index#OwnableComponent-Embeddable-Impls-OwnableImpl] .OwnableImpl * xref:OwnableComponent-owner[`++owner(self)++`] * xref:OwnableComponent-transfer_ownership[`++transfer_ownership(self, new_owner)++`] * xref:OwnableComponent-renounce_ownership[`++renounce_ownership(self)++`] +[.sub-index#OwnableComponent-Embeddable-Impls-OwnableTwoStepImpl] .OwnableTwoStepImpl * xref:OwnableComponent-two-step-owner[`++owner(self)++`] @@ -45,16 +63,14 @@ This module includes the internal `assert_only_owner` to restrict a function to * xref:OwnableComponent-two-step-accept_ownership[`++accept_ownership(self)++`] * xref:OwnableComponent-two-step-transfer_ownership[`++transfer_ownership(self, new_owner)++`] * xref:OwnableComponent-two-step-renounce_ownership[`++renounce_ownership(self)++`] --- -[.contract-index] -.Embeddable Implementations (camelCase) --- +[.sub-index#OwnableComponent-Embeddable-Impls-OwnableCamelOnlyImpl] .OwnableCamelOnlyImpl * xref:OwnableComponent-transferOwnership[`++transferOwnership(self, newOwner)++`] * xref:OwnableComponent-renounceOwnership[`++renounceOwnership(self)++`] +[.sub-index#OwnableComponent-Embeddable-Impls-OwnableTwoStepCamelOnlyImpl] .OwnableTwoStepCamelOnlyImpl * xref:OwnableComponent-two-step-pendingOwner[`++pendingOwner(self)++`] @@ -83,7 +99,7 @@ This module includes the internal `assert_only_owner` to restrict a function to -- [#OwnableComponent-Embeddable-Functions] -==== Embeddable Functions +==== Embeddable functions [.contract-item] [[OwnableComponent-owner]] @@ -113,7 +129,7 @@ thereby removing any functionality that is only available to the owner. //end::renounce_ownership[] [#OwnableComponent-Embeddable-Functions-Two-Step] -==== Embeddable Functions (Two Step Transfer) +==== Embeddable functions (two step transfer) [.contract-item] [[OwnableComponent-two-step-owner]] @@ -150,9 +166,6 @@ Emits an xref:OwnableComponent-OwnershipTransferStarted[OwnershipTransferStarted ==== `[.contract-item-name]#++renounce_ownership++#++(ref self: ContractState)++` [.item-kind]#external# include::./access.adoc[tag=renounce_ownership] -[#OwnableComponent-camelCase-Support] -==== camelCase Support - [.contract-item] [[OwnableComponent-transferOwnership]] ==== `[.contract-item-name]#++transferOwnership++#++(ref self: ContractState, newOwner: ContractAddress)++` [.item-kind]#external# @@ -165,9 +178,6 @@ See xref:OwnableComponent-transfer_ownership[transfer_ownership]. See xref:OwnableComponent-renounce_ownership[renounce_ownership]. -[#OwnableComponent-camelCase-Support-Two-Step] -==== camelCase Support (Two Step Transfer) - [.contract-item] [[OwnableComponent-two-step-pendingOwner]] ==== `[.contract-item-name]#++pendingOwner++#++(self: @ContractState)++` [.item-kind]#external# @@ -193,7 +203,7 @@ See xref:OwnableComponent-two-step-transfer_ownership[transfer_ownership]. See xref:OwnableComponent-two-step-renounce_ownership[renounce_ownership]. [#OwnableComponent-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[OwnableComponent-initializer]] @@ -395,6 +405,7 @@ Emitted when `account` is revoked `role`. :assert_only_role: xref:#AccessControlComponent-assert_only_role :grant_role: xref:#AccessControlComponent-grant_role[grant_role] :revoke_role: xref:#AccessControlComponent-revoke_role[revoke_role] +:mixin-impl: xref:components.adoc#mixins[Embeddable Mixin Implementation] ```javascript use openzeppelin::access::accesscontrol::AccessControlComponent; @@ -434,9 +445,21 @@ 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 accounts that have been granted it. -[.contract-index] +[.contract-index#AccessControl-Mixin-Impl] +.{mixin-impl} + +-- +.AccessControlMixinImpl + +* xref:#AccessControlComponent-Embeddable-Impls-AccessControlImpl[`++AccessControlImpl++`] +* xref:#AccessControlComponent-Embeddable-Impls-AccessControlCamelImpl[`++AccessControlCamelImpl++`] +* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] +-- + +[.contract-index#AccessControlComponent-Embeddable-Impls] .Embeddable Implementations -- +[.sub-index#AccessControlComponent-Embeddable-Impls-AccessControlImpl] .AccessControlImpl * xref:#AccessControlComponent-has_role[`++has_role(self, role, account)++`] @@ -445,13 +468,7 @@ accounts that have been granted it. * xref:#AccessControlComponent-revoke_role[`++revoke_role(self, role, account)++`] * xref:#AccessControlComponent-renounce_role[`++renounce_role(self, role, account)++`] -.SRC5Impl -* xref:#AccessControlComponent-supports_interface[`++supports_interface(self, interface_id: felt252)++`] --- - -[.contract-index] -.Embeddable Implementations (camelCase) --- +[.sub-index#AccessControlComponent-Embeddable-Impls-AccessControlCamelImpl] .AccessControlCamelImpl * xref:#AccessControlComponent-hasRole[`++hasRole(self, role, account)++`] @@ -459,6 +476,9 @@ accounts that have been granted it. * xref:#AccessControlComponent-grantRole[`++grantRole(self, role, account)++`] * xref:#AccessControlComponent-revokeRole[`++revokeRole(self, role, account)++`] * xref:#AccessControlComponent-renounceRole[`++renounceRole(self, role, account)++`] + +.SRC5Impl +* xref:api/introspection.adoc#ISRC5-supports_interface[`supports_interface(self, interface_id: felt252)`] -- [.contract-index] @@ -483,7 +503,7 @@ accounts that have been granted it. -- [#AccessControlComponent-Embeddable-Functions] -==== Embeddable Functions +==== Embeddable functions [.contract-item] [[AccessControlComponent-has_role]] @@ -554,9 +574,6 @@ May emit a {RoleRevoked} event. See xref:api/introspection.adoc#ISRC5-supports_interface[ISRC5::supports_interface]. -[#AccessControlComponent-camelCase-Support] -==== camelCase Support - [.contract-item] [[AccessControlComponent-hasRole]] ==== `[.contract-item-name]#++hasRole++#++(self: @ContractState, role: felt252, account: ContractAddress) → bool++` [.item-kind]#external# @@ -588,7 +605,7 @@ See xref:AccessControlComponent-revoke_role[revoke_role]. See xref:AccessControlComponent-renounce_role[renounce_role]. [#AccessControlComponent-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[AccessControlComponent-initializer]] diff --git a/docs/modules/ROOT/pages/api/account.adoc b/docs/modules/ROOT/pages/api/account.adoc index efab2fa0c..f1aaf0768 100644 --- a/docs/modules/ROOT/pages/api/account.adoc +++ b/docs/modules/ROOT/pages/api/account.adoc @@ -1,6 +1,7 @@ :github-icon: pass:[] :snip6: https://github.com/ericnordelo/SNIPs/blob/feat/standard-account/SNIPS/snip-6.md[SNIP-6] :inner-src5: xref:api/introspection.adoc#ISRC5[SRC5 ID] +:mixin-impl: xref:components.adoc#mixins[Embeddable Mixin Implementation] = Account @@ -76,40 +77,60 @@ Account component implementing xref:ISRC6[`ISRC6`] for signatures over the {star NOTE: Implementing xref:api/introspection.adoc#SRC5Component[SRC5Component] is a requirement for this component to be implemented. +[.contract-index#AccountComponent-Embeddable-Mixin-Impl] +.{mixin-impl} + +-- +.AccountMixinImpl + +* xref:#AccountComponent-Embeddable-Impls-SRC6Impl[`++SRC6Impl++`] +* xref:#AccountComponent-Embeddable-Impls-DeclarerImpl[`++DeclarerImpl++`] +* xref:#AccountComponent-Embeddable-Impls-DeployableImpl[`++DeployableImpl++`] +* xref:#AccountComponent-Embeddable-Impls-PublicKeyImpl[`++PublicKeyImpl++`] +* xref:#AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl[`++SRC6CamelOnlyImpl++`] +* xref:#AccountComponent-Embeddable-Impls-PublicKeyCamelImpl[`++PublicKeyCamelImpl++`] +* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] +-- + [.contract-index#AccountComponent-Embeddable-Impls] .Embeddable Implementations -- +[.sub-index#AccountComponent-Embeddable-Impls-SRC6Impl] .SRC6Impl * xref:#AccountComponent-\\__execute__[`++__execute__(self, calls)++`] * xref:#AccountComponent-\\__validate__[`++__validate__(self, calls)++`] * xref:#AccountComponent-is_valid_signature[`++is_valid_signature(self, hash, signature)++`] +[.sub-index#AccountComponent-Embeddable-Impls-DeclarerImpl] .DeclarerImpl * xref:#AccountComponent-\\__validate_declare__[`++__validate_declare__(self, class_hash)++`] +[.sub-index#AccountComponent-Embeddable-Impls-DeployableImpl] .DeployableImpl * xref:#AccountComponent-\\__validate_deploy__[`++__validate_deploy__(self, hash, signature)++`] +[.sub-index#AccountComponent-Embeddable-Impls-PublicKeyImpl] .PublicKeyImpl * xref:#AccountComponent-get_public_key[`++get_public_key(self)++`] * xref:#AccountComponent-set_public_key[`++set_public_key(self, new_public_key)++`] --- -[.contract-index#AccountComponent-Embeddable-Impls-camelCase] -.Embeddable Implementations (camelCase) --- +[.sub-index#AccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] .SRC6CamelOnlyImpl * xref:#AccountComponent-isValidSignature[`++isValidSignature(self, hash, signature)++`] +[.sub-index#AccountComponent-Embeddable-Impls-PublicKeyCamelImpl] .PublicKeyCamelImpl * xref:#AccountComponent-getPublicKey[`++getPublicKey(self)++`] * xref:#AccountComponent-setPublicKey[`++setPublicKey(self, newPublicKey)++`] + +.SRC5Impl +* xref:api/introspection.adoc#ISRC5-supports_interface[`supports_interface(self, interface_id: felt252)`] -- [.contract-index] @@ -132,7 +153,7 @@ NOTE: Implementing xref:api/introspection.adoc#SRC5Component[SRC5Component] is a -- [#AccountComponent-Embeddable-Functions] -==== Embeddable Functions +==== Embeddable functions [.contract-item] [[AccountComponent-__execute__]] @@ -183,9 +204,6 @@ Sets a new public key for the account. Only accessible by the account calling it Emits both an {OwnerRemoved} and an {OwnerAdded} event. -[#AccountComponent-camelCase-Support] -==== camelCase Support - [.contract-item] [[AccountComponent-isValidSignature]] ==== `[.contract-item-name]#++isValidSignature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# @@ -205,7 +223,7 @@ See xref:AccountComponent-get_public_key[get_public_key]. See xref:AccountComponent-set_public_key[set_public_key]. [#AccountComponent-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[AccountComponent-initializer]] @@ -278,40 +296,60 @@ NOTE: Implementing xref:api/introspection.adoc#SRC5Component[SRC5Component] is a NOTE: The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Point`. +[.contract-index#EthAccountComponent-Embeddable-Mixin-Impl] +.{mixin-impl} + +-- +.EthAccountMixinImpl + +* xref:#EthAccountComponent-Embeddable-Impls-SRC6Impl[`++SRC6Impl++`] +* xref:#EthAccountComponent-Embeddable-Impls-DeclarerImpl[`++DeclarerImpl++`] +* xref:#EthAccountComponent-Embeddable-Impls-DeployableImpl[`++DeployableImpl++`] +* xref:#EthAccountComponent-Embeddable-Impls-PublicKeyImpl[`++PublicKeyImpl++`] +* xref:#EthAccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl[`++SRC6CamelOnlyImpl++`] +* xref:#EthAccountComponent-Embeddable-Impls-PublicKeyCamelImpl[`++PublicKeyCamelImpl++`] +* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] +-- + [.contract-index#EthAccountComponent-Embeddable-Impls] .Embeddable Implementations -- +[.sub-index#EthAccountComponent-Embeddable-Impls-SRC6Impl] .SRC6Impl * xref:#EthAccountComponent-\\__execute__[`++__execute__(self, calls)++`] * xref:#EthAccountComponent-\\__validate__[`++__validate__(self, calls)++`] * xref:#EthAccountComponent-is_valid_signature[`++is_valid_signature(self, hash, signature)++`] +[.sub-index#EthAccountComponent-Embeddable-Impls-DeclarerImpl] .DeclarerImpl * xref:#EthAccountComponent-\\__validate_declare__[`++__validate_declare__(self, class_hash)++`] +[.sub-index#EthAccountComponent-Embeddable-Impls-DeployableImpl] .DeployableImpl * xref:#EthAccountComponent-\\__validate_deploy__[`++__validate_deploy__(self, hash, signature)++`] +[.sub-index#EthAccountComponent-Embeddable-Impls-PublicKeyImpl] .PublicKeyImpl * xref:#EthAccountComponent-get_public_key[`++get_public_key(self)++`] * xref:#EthAccountComponent-set_public_key[`++set_public_key(self, new_public_key)++`] --- -[.contract-index#EthAccountComponent-Embeddable-Impls-camelCase] -.Embeddable Implementations (camelCase) --- +[.sub-index#EthAccountComponent-Embeddable-Impls-SRC6CamelOnlyImpl] .SRC6CamelOnlyImpl * xref:#EthAccountComponent-isValidSignature[`++isValidSignature(self, hash, signature)++`] +[.sub-index#EthAccountComponent-Embeddable-Impls-PublicKeyCamelImpl] .PublicKeyCamelImpl * xref:#EthAccountComponent-getPublicKey[`++getPublicKey(self)++`] * xref:#EthAccountComponent-setPublicKey[`++setPublicKey(self, newPublicKey)++`] + +.SRC5Impl +* xref:api/introspection.adoc#ISRC5-supports_interface[`supports_interface(self, interface_id: felt252)`] -- [.contract-index] @@ -334,7 +372,7 @@ NOTE: The `EthPublicKey` type is an alias for `starknet::secp256k1::Secp256k1Poi -- [#EthAccountComponent-Embeddable-Functions] -==== Embeddable Functions +==== Embeddable functions [.contract-item] [[EthAccountComponent-__execute__]] @@ -385,9 +423,6 @@ Sets a new public key for the account. Only accesible by the account calling its Emits both an {OwnerRemoved} and an {OwnerAdded} event. -[#EthAccountComponent-camelCase-Support] -==== camelCase Support - [.contract-item] [[EthAccountComponent-isValidSignature]] ==== `[.contract-item-name]#++isValidSignature++#++(self: @ContractState, hash: felt252, signature: Array) → felt252++` [.item-kind]#external# @@ -407,7 +442,7 @@ See xref:EthAccountComponent-get_public_key[get_public_key]. See xref:EthAccountComponent-set_public_key[set_public_key]. [#EthAccountComponent-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[EthAccountComponent-initializer]] @@ -496,16 +531,7 @@ include::../utils/_class_hashes.adoc[] -- .AccountComponent -* xref:#AccountComponent-Embeddable-Impls[`++SRC6Impl++`] -* xref:#AccountComponent-Embeddable-Impls[`++PublicKeyImpl++`] -* xref:#AccountComponent-Embeddable-Impls[`++DeclarerImpl++`] -* xref:#AccountComponent-Embeddable-Impls[`++DeployableImpl++`] -* xref:#AccountComponent-Embeddable-Impls-camelCase[`++SRC6CamelOnlyImpl++`] -* xref:#AccountComponent-Embeddable-Impls-camelCase[`++PublicKeyCamelImpl++`] - -.SRC5Component - -* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] +* xref:#AccountComponent-Embeddable-Mixin-Impl[`++AccountMixinImpl++`] -- [#Account-constructor-section] @@ -548,16 +574,7 @@ include::../utils/_class_hashes.adoc[] -- .EthAccountComponent -* xref:#EthAccountComponent-Embeddable-Impls[`++SRC6Impl++`] -* xref:#EthAccountComponent-Embeddable-Impls[`++PublicKeyImpl++`] -* xref:#EthAccountComponent-Embeddable-Impls[`++DeclarerImpl++`] -* xref:#EthAccountComponent-Embeddable-Impls[`++DeployableImpl++`] -* xref:#EthAccountComponent-Embeddable-Impls-camelCase[`++SRC6CamelOnlyImpl++`] -* xref:#EthAccountComponent-Embeddable-Impls-camelCase[`++PublicKeyCamelImpl++`] - -.SRC5Component - -* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] +* xref:#EthAccountComponent-Embeddable-Mixin-Impl[`++EthAccountMixinImpl++`] -- [.contract-index] @@ -576,7 +593,7 @@ include::../utils/_class_hashes.adoc[] Sets the account `public_key` and registers the interfaces the contract supports. [#EthAccountUpgradeable-external-functions] -==== External Functions +==== External functions [.contract-item] [[EthAccountUpgradeable-upgrade]] diff --git a/docs/modules/ROOT/pages/api/erc20.adoc b/docs/modules/ROOT/pages/api/erc20.adoc index c7022e7d4..21e87fcc8 100644 --- a/docs/modules/ROOT/pages/api/erc20.adoc +++ b/docs/modules/ROOT/pages/api/erc20.adoc @@ -3,6 +3,7 @@ :erc20-guide: xref:erc20.adoc[ERC20 guide] :casing-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/34[here] :custom-decimals: xref:/erc20.adoc#customizing_decimals[Customizing decimals] +:mixin-impl: xref:components.adoc#mixins[Embeddable Mixin Implementation] = ERC20 @@ -168,9 +169,20 @@ use openzeppelin::token::erc20::ERC20Component; ``` ERC20 component extending <> and <>. +[.contract-index#ERC20Component-Embeddable-Mixin-Impl] +.{mixin-impl} + +-- +.ERC20MixinImpl +* xref:#ERC20Component-Embeddable-Impls-ERC20Impl[`++ERC20Impl++`] +* xref:#ERC20Component-Embeddable-Impls-ERC20MetadataImpl[`++ERC20MetadataImpl++`] +* xref:#ERC20Component-Embeddable-Impls-ERC20CamelOnlyImpl[`++ERC20CamelOnlyImpl++`] +-- + [.contract-index#ERC20Component-Embeddable-Impls] -.Embeddable implementations +.Embeddable Implementations -- +[.sub-index#ERC20Component-Embeddable-Impls-ERC20Impl] .ERC20Impl * xref:#ERC20Component-total_supply[`++total_supply(self)++`] * xref:#ERC20Component-balance_of[`++balance_of(self, account)++`] @@ -179,15 +191,13 @@ ERC20 component extending <> and < * xref:#ERC20Component-transfer_from[`++transfer_from(self, sender, recipient, amount)++`] * xref:#ERC20Component-approve[`++approve(self, spender, amount)++`] +[.sub-index#ERC20Component-Embeddable-Impls-ERC20MetadataImpl] .ERC20MetadataImpl * xref:#ERC20Component-name[`++name(self)++`] * xref:#ERC20Component-symbol[`++symbol(self)++`] * xref:#ERC20Component-decimals[`++decimals(self)++`] --- -[.contract-index#ERC20Component-Embeddable-Impls-camelCase] -.Embeddable implementations (camelCase) --- +[.sub-index#ERC20Component-Embeddable-Impls-ERC20CamelOnlyImpl] .ERC20CamelOnlyImpl * xref:#ERC20Component-totalSupply[`++totalSupply(self)++`] * xref:#ERC20Component-balanceOf[`++balanceOf(self, account)++`] @@ -286,9 +296,6 @@ See <>. See <>. -[#ERC20Component-camelCase-support] -==== camelCase Support - [.contract-item] [[ERC20Component-totalSupply]] ==== `[.contract-item-name]#++totalSupply++#++(self: @ContractState) → u256++` [.item-kind]#external# @@ -432,11 +439,9 @@ include::../utils/_class_hashes.adoc[] [.contract-index] .Embedded Implementations -- -.ERC20Component +.ERC20MixinImpl -* xref:#ERC20Component-Embeddable-Impls[`++ERC20Impl++`] -* xref:#ERC20Component-Embeddable-Impls[`++ERC20MetadataImpl++`] -* xref:#ERC20Component-Embeddable-Impls-camelCase[`++ERC20CamelOnlyImpl++`] +* xref:#ERC20Component-Embeddable-Mixin-Impl[`++ERC20MixinImpl++`] -- [#ERC20-constructor-section] diff --git a/docs/modules/ROOT/pages/api/erc721.adoc b/docs/modules/ROOT/pages/api/erc721.adoc index 218fcde56..5995feb4b 100644 --- a/docs/modules/ROOT/pages/api/erc721.adoc +++ b/docs/modules/ROOT/pages/api/erc721.adoc @@ -3,6 +3,7 @@ :receiving-tokens: xref:/erc721.adoc#receiving_tokens[Receiving Tokens] :casing-discussion: https://github.com/OpenZeppelin/cairo-contracts/discussions/34[here] :inner-src5: xref:api/introspection.adoc#ISRC5[SRC5 ID] +:mixin-impl: xref:components.adoc#mixins[Embeddable Mixin Implementation] = ERC721 @@ -190,10 +191,23 @@ ERC721 component implementing <> and <>. See <>. -==== camelCase Support - [.contract-item] [[ERC721-balanceOf]] ==== `[.contract-item-name]#++balanceOf++#++(self: @ContractState, account: ContractAddress) -> u256++` [.item-kind]#external# @@ -627,16 +641,9 @@ include::../utils/_class_hashes.adoc[] [.contract-index] .Embedded Implementations -- -.ERC721Component +.ERC721MixinImpl -* xref:#ERC721Component-Embeddable-Impls[`++ERC721Impl++`] -* xref:#ERC721Component-Embeddable-Impls[`++ERC721MetadataImpl++`] -* xref:#ERC721Component-Embeddable-Impls-camelCase[`++ERC721CamelOnly++`] -* xref:#ERC721Component-Embeddable-Impls-camelCase[`++ERC721MetadataCamelOnly++`] - -.SRC5Component - -* xref:api/introspection.adoc#SRC5Component-Embeddable-Impls[`++SRC5Impl++`] +* xref:#ERC721Component-Embeddable-Mixin-Impl[`++ERC721MixinImpl++`] -- [#ERC721-constructor-section] diff --git a/docs/modules/ROOT/pages/api/introspection.adoc b/docs/modules/ROOT/pages/api/introspection.adoc index 589630f4a..f4742d94d 100644 --- a/docs/modules/ROOT/pages/api/introspection.adoc +++ b/docs/modules/ROOT/pages/api/introspection.adoc @@ -70,7 +70,7 @@ SRC5 component extending xref:ISRC5[`ISRC5`]. -- [#SRC5Component-Embeddable-Functions] -==== Embeddable Functions +==== Embeddable functions [.contract-item] [[SRC5Component-supports_interface]] @@ -79,7 +79,7 @@ SRC5 component extending xref:ISRC5[`ISRC5`]. See xref:ISRC5-supports_interface[`ISRC5::supports_interface`]. [#SRC5Component-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[SRC5Component-register_interface]] diff --git a/docs/modules/ROOT/pages/api/security.adoc b/docs/modules/ROOT/pages/api/security.adoc index 44a26cec0..c71cbd9a9 100644 --- a/docs/modules/ROOT/pages/api/security.adoc +++ b/docs/modules/ROOT/pages/api/security.adoc @@ -33,7 +33,7 @@ Component enabling one-time initialization for contracts. -- [#InitializableComponent-Embeddable-Functions] -==== Embeddable Functions +==== Embeddable functions [.contract-item] [[InitializableComponent-is_initialized]] @@ -42,7 +42,7 @@ Component enabling one-time initialization for contracts. Returns whether the contract has been initialized. [#InitializableComponent-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[InitializableComponent-initialize]] @@ -96,7 +96,7 @@ Component to implement an emergency stop mechanism. -- [#PausableComponent-Embeddable-Functions] -==== Embeddable Functions +==== Embeddable functions [.contract-item] [[PausableComponent-is_paused]] @@ -105,7 +105,7 @@ Component to implement an emergency stop mechanism. Returns whether the contract is currently paused. [#PausableComponent-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[PausableComponent-assert_not_paused]] @@ -181,7 +181,7 @@ Component to help prevent reentrant calls. -- [#ReentrancyGuardComponent-Internal-Functions] -==== Internal Functions +==== Internal functions [.contract-item] [[ReentrancyGuardComponent-start]] diff --git a/docs/modules/ROOT/pages/components.adoc b/docs/modules/ROOT/pages/components.adoc index 7f47f2be1..2ea18eb57 100644 --- a/docs/modules/ROOT/pages/components.adoc +++ b/docs/modules/ROOT/pages/components.adoc @@ -70,6 +70,7 @@ Flattening the component event removes it, leaving the event ID as the first key === Implementations :erc20-component: xref:/api/erc20.adoc#ERC20Component[ERC20Component] +:mixin: xref:/components.adoc#mixins[mixin] Components come with granular implementations of different interfaces. This allows contracts to integrate only the implementations that they'll use and avoid unnecessary bloat. @@ -137,15 +138,106 @@ By adding the embed attribute, `is_initialized` becomes a contract entrypoint fo [TIP] ==== Embeddable implementations, when available in this library's components, are segregated from the internal component implementation which makes it easier to safely expose. -Components also separate standard implementations (`snake_case`) from `camelCase`. -This trichotomy structures the API documentation design. +Components also separate granular implementations from {mixin} implementations. +The API documentation design reflects these groupings. See {erc20-component} as an example which includes: -- *Embeddable implementations* -- *Embeddable implementations (camelCase)* -- *Internal implementations* +- *Embeddable Mixin Implementation* +- *Embeddable Implementations* +- *Internal Implementations* +- *Events* ==== +=== Mixins + +Mixins are impls made of a combination of smaller, more specific impls. +While separating components into granular implementations offers flexibility, +integrating components with many implementations can appear crowded especially if the contract uses all of them. +Mixins simplify this by allowing contracts to embed groups of implementations with a single directive. + +Compare the following code blocks to see the benefit of using a mixin when creating an account contract. + +==== Account without mixin + +[,javascript] +---- +component!(path: AccountComponent, storage: account, event: AccountEvent); +component!(path: SRC5Component, storage: src5, event: SRC5Event); + +#[abi(embed_v0)] +impl SRC6Impl = AccountComponent::SRC6Impl; +#[abi(embed_v0)] +impl DeclarerImpl = AccountComponent::DeclarerImpl; +#[abi(embed_v0)] +impl DeployableImpl = AccountComponent::DeployableImpl; +#[abi(embed_v0)] +impl PublicKeyImpl = AccountComponent::PublicKeyImpl; +#[abi(embed_v0)] +impl SRC6CamelOnlyImpl = AccountComponent::SRC6CamelOnlyImpl; +#[abi(embed_v0)] +impl PublicKeyCamelImpl = AccountComponent::PublicKeyCamelImpl; +impl AccountInternalImpl = AccountComponent::InternalImpl; + +#[abi(embed_v0)] +impl SRC5Impl = SRC5Component::SRC5Impl; +---- + +==== Account with mixin + +[,javascript] +---- +component!(path: AccountComponent, storage: account, event: AccountEvent); +component!(path: SRC5Component, storage: src5, event: SRC5Event); + +#[abi(embed_v0)] +impl AccountMixinImpl = AccountComponent::AccountMixinImpl; +impl AccountInternalImpl = AccountComponent::InternalImpl; +---- + +The rest of the setup for the contract, however, does not change. +This means that component dependencies must still be included in the `Storage` struct and `Event` enum. +Here's a full example of an account contract that embeds the `AccountMixinImpl`: + +[,javascript] +---- +#[starknet::contract] +mod Account { + use openzeppelin::account::AccountComponent; + use openzeppelin::introspection::src5::SRC5Component; + + component!(path: AccountComponent, storage: account, event: AccountEvent); + component!(path: SRC5Component, storage: src5, event: SRC5Event); + + // This embeds all of the methods from the many AccountComponent implementations + // and also includes `supports_interface` from `SRC5Impl` + #[abi(embed_v0)] + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; + impl AccountInternalImpl = AccountComponent::InternalImpl; + + #[storage] + struct Storage { + #[substorage(v0)] + account: AccountComponent::Storage, + #[substorage(v0)] + src5: SRC5Component::Storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + AccountEvent: AccountComponent::Event, + #[flat] + SRC5Event: SRC5Component::Event + } + + #[constructor] + fn constructor(ref self: ContractState, public_key: felt252) { + self.account.initializer(public_key); + } +} +---- + === Initializers :ownable-component: xref:/api/access.adoc#OwnableComponent[OwnableComponent] diff --git a/src/access/accesscontrol/accesscontrol.cairo b/src/access/accesscontrol/accesscontrol.cairo index 44b61ba92..3d0694043 100644 --- a/src/access/accesscontrol/accesscontrol.cairo +++ b/src/access/accesscontrol/accesscontrol.cairo @@ -9,6 +9,7 @@ mod AccessControlComponent { use openzeppelin::access::accesscontrol::interface; use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5::SRC5Component; use starknet::ContractAddress; use starknet::get_caller_address; @@ -95,7 +96,7 @@ mod AccessControlComponent { fn grant_role( ref self: ComponentState, role: felt252, account: ContractAddress ) { - let admin = self.get_role_admin(role); + let admin = AccessControl::get_role_admin(@self, role); self.assert_only_role(admin); self._grant_role(role, account); } @@ -110,7 +111,7 @@ mod AccessControlComponent { fn revoke_role( ref self: ComponentState, role: felt252, account: ContractAddress ) { - let admin = self.get_role_admin(role); + let admin = AccessControl::get_role_admin(@self, role); self.assert_only_role(admin); self._revoke_role(role, account); } @@ -147,29 +148,29 @@ mod AccessControlComponent { fn hasRole( self: @ComponentState, role: felt252, account: ContractAddress ) -> bool { - self.has_role(role, account) + AccessControl::has_role(self, role, account) } fn getRoleAdmin(self: @ComponentState, role: felt252) -> felt252 { - self.get_role_admin(role) + AccessControl::get_role_admin(self, role) } fn grantRole( ref self: ComponentState, role: felt252, account: ContractAddress ) { - self.grant_role(role, account); + AccessControl::grant_role(ref self, role, account); } fn revokeRole( ref self: ComponentState, role: felt252, account: ContractAddress ) { - self.revoke_role(role, account); + AccessControl::revoke_role(ref self, role, account); } fn renounceRole( ref self: ComponentState, role: felt252, account: ContractAddress ) { - self.renounce_role(role, account); + AccessControl::renounce_role(ref self, role, account); } } @@ -189,7 +190,7 @@ mod AccessControlComponent { /// Validates that the caller has the given role. Otherwise it panics. fn assert_only_role(self: @ComponentState, role: felt252) { let caller: ContractAddress = get_caller_address(); - let authorized: bool = self.has_role(role, caller); + let authorized = AccessControl::has_role(self, role, caller); assert(authorized, Errors::MISSING_ROLE); } @@ -201,7 +202,7 @@ mod AccessControlComponent { fn _grant_role( ref self: ComponentState, role: felt252, account: ContractAddress ) { - if !self.has_role(role, account) { + if !AccessControl::has_role(@self, role, account) { let caller: ContractAddress = get_caller_address(); self.AccessControl_role_member.write((role, account), true); self.emit(RoleGranted { role, account, sender: caller }); @@ -216,7 +217,7 @@ mod AccessControlComponent { fn _revoke_role( ref self: ComponentState, role: felt252, account: ContractAddress ) { - if self.has_role(role, account) { + if AccessControl::has_role(@self, role, account) { let caller: ContractAddress = get_caller_address(); self.AccessControl_role_member.write((role, account), false); self.emit(RoleRevoked { role, account, sender: caller }); @@ -229,9 +230,83 @@ mod AccessControlComponent { fn _set_role_admin( ref self: ComponentState, role: felt252, admin_role: felt252 ) { - let previous_admin_role: felt252 = self.get_role_admin(role); + 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)] + impl AccessControlMixin< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop + > of interface::AccessControlABI> { + // IAccessControl + fn has_role( + self: @ComponentState, role: felt252, account: ContractAddress + ) -> bool { + AccessControl::has_role(self, role, account) + } + + fn get_role_admin(self: @ComponentState, role: felt252) -> felt252 { + AccessControl::get_role_admin(self, role) + } + + fn grant_role( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + AccessControl::grant_role(ref self, role, account); + } + + fn revoke_role( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + AccessControl::revoke_role(ref self, role, account); + } + + fn renounce_role( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + AccessControl::renounce_role(ref self, role, account); + } + + // IAccessControlCamel + fn hasRole( + self: @ComponentState, role: felt252, account: ContractAddress + ) -> bool { + AccessControlCamel::hasRole(self, role, account) + } + + fn getRoleAdmin(self: @ComponentState, role: felt252) -> felt252 { + AccessControlCamel::getRoleAdmin(self, role) + } + + fn grantRole( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + AccessControlCamel::grantRole(ref self, role, account); + } + + fn revokeRole( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + AccessControlCamel::revokeRole(ref self, role, account); + } + + fn renounceRole( + ref self: ComponentState, role: felt252, account: ContractAddress + ) { + AccessControlCamel::renounceRole(ref self, role, account); + } + + // ISRC5 + fn supports_interface( + self: @ComponentState, interface_id: felt252 + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) + } + } } diff --git a/src/access/accesscontrol/interface.cairo b/src/access/accesscontrol/interface.cairo index fdf093c9e..e121e0121 100644 --- a/src/access/accesscontrol/interface.cairo +++ b/src/access/accesscontrol/interface.cairo @@ -23,3 +23,23 @@ trait IAccessControlCamel { fn revokeRole(ref self: TState, role: felt252, account: ContractAddress); fn renounceRole(ref self: TState, role: felt252, account: ContractAddress); } + +#[starknet::interface] +trait AccessControlABI { + // IAccessControl + fn has_role(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn get_role_admin(self: @TState, role: felt252) -> felt252; + fn grant_role(ref self: TState, role: felt252, account: ContractAddress); + fn revoke_role(ref self: TState, role: felt252, account: ContractAddress); + fn renounce_role(ref self: TState, role: felt252, account: ContractAddress); + + // IAccessControlCamel + fn hasRole(self: @TState, role: felt252, account: ContractAddress) -> bool; + fn getRoleAdmin(self: @TState, role: felt252) -> felt252; + fn grantRole(ref self: TState, role: felt252, account: ContractAddress); + fn revokeRole(ref self: TState, role: felt252, account: ContractAddress); + fn renounceRole(ref self: TState, role: felt252, account: ContractAddress); + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; +} diff --git a/src/access/ownable/interface.cairo b/src/access/ownable/interface.cairo index d4299d336..ca42d75d9 100644 --- a/src/access/ownable/interface.cairo +++ b/src/access/ownable/interface.cairo @@ -16,6 +16,18 @@ trait IOwnableCamelOnly { fn renounceOwnership(ref self: TState); } +#[starknet::interface] +trait OwnableABI { + // IOwnable + fn owner(self: @TState) -> ContractAddress; + fn transfer_ownership(ref self: TState, new_owner: ContractAddress); + fn renounce_ownership(ref self: TState); + + // IOwnableCamelOnly + fn transferOwnership(ref self: TState, newOwner: ContractAddress); + fn renounceOwnership(ref self: TState); +} + #[starknet::interface] trait IOwnableTwoStep { fn owner(self: @TState) -> ContractAddress; @@ -32,3 +44,19 @@ trait IOwnableTwoStepCamelOnly { fn transferOwnership(ref self: TState, newOwner: ContractAddress); fn renounceOwnership(ref self: TState); } + +#[starknet::interface] +trait OwnableTwoStepABI { + // IOwnableTwoStep + fn owner(self: @TState) -> ContractAddress; + fn pending_owner(self: @TState) -> ContractAddress; + fn accept_ownership(ref self: TState); + fn transfer_ownership(ref self: TState, new_owner: ContractAddress); + fn renounce_ownership(ref self: TState); + + // IOwnableTwoStepCamelOnly + fn pendingOwner(self: @TState) -> ContractAddress; + fn acceptOwnership(ref self: TState); + fn transferOwnership(ref self: TState, newOwner: ContractAddress); + fn renounceOwnership(ref self: TState); +} diff --git a/src/access/ownable/ownable.cairo b/src/access/ownable/ownable.cairo index 851cf5505..ea2371f16 100644 --- a/src/access/ownable/ownable.cairo +++ b/src/access/ownable/ownable.cairo @@ -15,6 +15,7 @@ /// finalize the transfer. #[starknet::component] mod OwnableComponent { + use openzeppelin::access::ownable::interface::IOwnableTwoStep; use openzeppelin::access::ownable::interface; use starknet::ContractAddress; use starknet::get_caller_address; @@ -228,4 +229,78 @@ mod OwnableComponent { ); } } + + #[embeddable_as(OwnableMixinImpl)] + impl OwnableMixin< + TContractState, +HasComponent, +Drop + > of interface::OwnableABI> { + // IOwnable + fn owner(self: @ComponentState) -> ContractAddress { + Ownable::owner(self) + } + + fn transfer_ownership( + ref self: ComponentState, new_owner: ContractAddress + ) { + Ownable::transfer_ownership(ref self, new_owner); + } + + fn renounce_ownership(ref self: ComponentState) { + Ownable::renounce_ownership(ref self); + } + + // IOwnableCamelOnly + fn transferOwnership(ref self: ComponentState, newOwner: ContractAddress) { + OwnableCamelOnly::transferOwnership(ref self, newOwner); + } + + fn renounceOwnership(ref self: ComponentState) { + OwnableCamelOnly::renounceOwnership(ref self); + } + } + + #[embeddable_as(OwnableTwoStepMixinImpl)] + impl OwnableTwoStepMixin< + TContractState, +HasComponent, +Drop + > of interface::OwnableTwoStepABI> { + // IOwnableTwoStep + fn owner(self: @ComponentState) -> ContractAddress { + OwnableTwoStep::owner(self) + } + + fn pending_owner(self: @ComponentState) -> ContractAddress { + OwnableTwoStep::pending_owner(self) + } + + fn accept_ownership(ref self: ComponentState) { + OwnableTwoStep::accept_ownership(ref self); + } + + fn transfer_ownership( + ref self: ComponentState, new_owner: ContractAddress + ) { + OwnableTwoStep::transfer_ownership(ref self, new_owner); + } + + fn renounce_ownership(ref self: ComponentState) { + OwnableTwoStep::renounce_ownership(ref self); + } + + // IOwnableTwoStepCamelOnly + fn pendingOwner(self: @ComponentState) -> ContractAddress { + OwnableTwoStepCamelOnly::pendingOwner(self) + } + + fn acceptOwnership(ref self: ComponentState) { + OwnableTwoStepCamelOnly::acceptOwnership(ref self); + } + + fn transferOwnership(ref self: ComponentState, newOwner: ContractAddress) { + OwnableTwoStepCamelOnly::transferOwnership(ref self, newOwner); + } + + fn renounceOwnership(ref self: ComponentState) { + OwnableTwoStepCamelOnly::renounceOwnership(ref self); + } + } } diff --git a/src/account/account.cairo b/src/account/account.cairo index 2f720b8a4..2698a9ff6 100644 --- a/src/account/account.cairo +++ b/src/account/account.cairo @@ -10,6 +10,7 @@ mod AccountComponent { use openzeppelin::account::utils::{MIN_TRANSACTION_VERSION, QUERY_VERSION, QUERY_OFFSET}; use openzeppelin::account::utils::{execute_calls, is_valid_stark_signature}; use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5Component::{SRC5, SRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; use starknet::account::Call; use starknet::get_caller_address; @@ -174,7 +175,7 @@ mod AccountComponent { fn isValidSignature( self: @ComponentState, hash: felt252, signature: Array ) -> felt252 { - self.is_valid_signature(hash, signature) + SRC6::is_valid_signature(self, hash, signature) } } @@ -191,7 +192,7 @@ mod AccountComponent { } fn setPublicKey(ref self: ComponentState, newPublicKey: felt252) { - self.set_public_key(newPublicKey); + PublicKey::set_public_key(ref self, newPublicKey); } } @@ -245,4 +246,79 @@ mod AccountComponent { is_valid_stark_signature(hash, public_key, signature) } } + + #[embeddable_as(AccountMixinImpl)] + impl AccountMixin< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop + > of interface::AccountABI> { + // ISRC6 + fn __execute__( + self: @ComponentState, calls: Array + ) -> Array> { + SRC6::__execute__(self, calls) + } + + fn __validate__(self: @ComponentState, calls: Array) -> felt252 { + SRC6::__validate__(self, calls) + } + + fn is_valid_signature( + self: @ComponentState, hash: felt252, signature: Array + ) -> felt252 { + SRC6::is_valid_signature(self, hash, signature) + } + + // ISRC6CamelOnly + fn isValidSignature( + self: @ComponentState, hash: felt252, signature: Array + ) -> felt252 { + SRC6CamelOnly::isValidSignature(self, hash, signature) + } + + // IDeclarer + fn __validate_declare__( + self: @ComponentState, class_hash: felt252 + ) -> felt252 { + Declarer::__validate_declare__(self, class_hash) + } + + // IDeployable + fn __validate_deploy__( + self: @ComponentState, + class_hash: felt252, + contract_address_salt: felt252, + public_key: felt252 + ) -> felt252 { + Deployable::__validate_deploy__(self, class_hash, contract_address_salt, public_key) + } + + // IPublicKey + fn get_public_key(self: @ComponentState) -> felt252 { + PublicKey::get_public_key(self) + } + + fn set_public_key(ref self: ComponentState, new_public_key: felt252) { + PublicKey::set_public_key(ref self, new_public_key); + } + + // IPublicKeyCamel + fn getPublicKey(self: @ComponentState) -> felt252 { + PublicKeyCamel::getPublicKey(self) + } + + fn setPublicKey(ref self: ComponentState, newPublicKey: felt252) { + PublicKeyCamel::setPublicKey(ref self, newPublicKey); + } + + // ISRC5 + fn supports_interface( + self: @ComponentState, interface_id: felt252 + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) + } + } } diff --git a/src/account/eth_account.cairo b/src/account/eth_account.cairo index 112e0f4f5..651f8ee44 100644 --- a/src/account/eth_account.cairo +++ b/src/account/eth_account.cairo @@ -13,6 +13,7 @@ mod EthAccountComponent { use openzeppelin::account::utils::{MIN_TRANSACTION_VERSION, QUERY_VERSION, QUERY_OFFSET}; use openzeppelin::account::utils::{execute_calls, is_valid_eth_signature}; use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5Component::{SRC5, SRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; use poseidon::poseidon_hash_span; use starknet::SyscallResultTrait; @@ -183,7 +184,7 @@ mod EthAccountComponent { fn isValidSignature( self: @ComponentState, hash: felt252, signature: Array ) -> felt252 { - self.is_valid_signature(hash, signature) + SRC6::is_valid_signature(self, hash, signature) } } @@ -200,7 +201,7 @@ mod EthAccountComponent { } fn setPublicKey(ref self: ComponentState, newPublicKey: EthPublicKey) { - self.set_public_key(newPublicKey); + PublicKey::set_public_key(ref self, newPublicKey); } } @@ -260,4 +261,79 @@ mod EthAccountComponent { 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, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop + > of interface::EthAccountABI> { + // ISRC6 + fn __execute__( + self: @ComponentState, calls: Array + ) -> Array> { + SRC6::__execute__(self, calls) + } + + fn __validate__(self: @ComponentState, calls: Array) -> felt252 { + SRC6::__validate__(self, calls) + } + + fn is_valid_signature( + self: @ComponentState, hash: felt252, signature: Array + ) -> felt252 { + SRC6::is_valid_signature(self, hash, signature) + } + + // ISRC6CamelOnly + fn isValidSignature( + self: @ComponentState, hash: felt252, signature: Array + ) -> felt252 { + SRC6CamelOnly::isValidSignature(self, hash, signature) + } + + // IDeclarer + fn __validate_declare__( + self: @ComponentState, class_hash: felt252 + ) -> felt252 { + Declarer::__validate_declare__(self, class_hash) + } + + // IDeployable + fn __validate_deploy__( + self: @ComponentState, + class_hash: felt252, + contract_address_salt: felt252, + public_key: EthPublicKey + ) -> felt252 { + Deployable::__validate_deploy__(self, class_hash, contract_address_salt, public_key) + } + + // IPublicKey + fn get_public_key(self: @ComponentState) -> EthPublicKey { + PublicKey::get_public_key(self) + } + + fn set_public_key(ref self: ComponentState, new_public_key: EthPublicKey) { + PublicKey::set_public_key(ref self, new_public_key); + } + + // IPublicKeyCamel + fn getPublicKey(self: @ComponentState) -> EthPublicKey { + PublicKeyCamel::getPublicKey(self) + } + + fn setPublicKey(ref self: ComponentState, newPublicKey: EthPublicKey) { + PublicKeyCamel::setPublicKey(ref self, newPublicKey); + } + + // ISRC5 + fn supports_interface( + self: @ComponentState, interface_id: felt252 + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) + } + } } diff --git a/src/account/interface.cairo b/src/account/interface.cairo index 7ba173a8d..abed1104c 100644 --- a/src/account/interface.cairo +++ b/src/account/interface.cairo @@ -78,9 +78,6 @@ trait AccountABI { // ISRC6CamelOnly fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; - // ISRC5Camel - fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; - // IPublicKeyCamel fn getPublicKey(self: @TState) -> felt252; fn setPublicKey(ref self: TState, newPublicKey: felt252); @@ -139,9 +136,6 @@ trait EthAccountABI { // ISRC6CamelOnly fn isValidSignature(self: @TState, hash: felt252, signature: Array) -> felt252; - // ISRC5Camel - fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; - // IEthPublicKeyCamel fn getPublicKey(self: @TState) -> EthPublicKey; fn setPublicKey(ref self: TState, newPublicKey: EthPublicKey); diff --git a/src/presets/account.cairo b/src/presets/account.cairo index 5c78e5f34..7193852c8 100644 --- a/src/presets/account.cairo +++ b/src/presets/account.cairo @@ -12,25 +12,11 @@ mod Account { component!(path: AccountComponent, storage: account, event: AccountEvent); component!(path: SRC5Component, storage: src5, event: SRC5Event); - // Account + // AccountMixin #[abi(embed_v0)] - impl SRC6Impl = AccountComponent::SRC6Impl; - #[abi(embed_v0)] - impl SRC6CamelOnlyImpl = AccountComponent::SRC6CamelOnlyImpl; - #[abi(embed_v0)] - impl PublicKeyImpl = AccountComponent::PublicKeyImpl; - #[abi(embed_v0)] - impl PublicKeyCamelImpl = AccountComponent::PublicKeyCamelImpl; - #[abi(embed_v0)] - impl DeclarerImpl = AccountComponent::DeclarerImpl; - #[abi(embed_v0)] - impl DeployableImpl = AccountComponent::DeployableImpl; + impl AccountMixinImpl = AccountComponent::AccountMixinImpl; impl AccountInternalImpl = AccountComponent::InternalImpl; - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[storage] struct Storage { #[substorage(v0)] diff --git a/src/presets/erc20.cairo b/src/presets/erc20.cairo index 725959165..c0581aae8 100644 --- a/src/presets/erc20.cairo +++ b/src/presets/erc20.cairo @@ -13,12 +13,9 @@ mod ERC20 { component!(path: ERC20Component, storage: erc20, event: ERC20Event); + // ERC20Mixin #[abi(embed_v0)] - impl ERC20Impl = ERC20Component::ERC20Impl; - #[abi(embed_v0)] - impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl; - #[abi(embed_v0)] - impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl; + impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl; impl InternalImpl = ERC20Component::InternalImpl; #[storage] diff --git a/src/presets/erc721.cairo b/src/presets/erc721.cairo index 1ac53d6e3..6e71873aa 100644 --- a/src/presets/erc721.cairo +++ b/src/presets/erc721.cairo @@ -14,22 +14,11 @@ mod ERC721 { component!(path: ERC721Component, storage: erc721, event: ERC721Event); component!(path: SRC5Component, storage: src5, event: SRC5Event); - // ERC721 + // ERC721Mixin #[abi(embed_v0)] - impl ERC721Impl = ERC721Component::ERC721Impl; - #[abi(embed_v0)] - impl ERC721MetadataImpl = ERC721Component::ERC721MetadataImpl; - #[abi(embed_v0)] - impl ERC721CamelOnly = ERC721Component::ERC721CamelOnlyImpl; - #[abi(embed_v0)] - impl ERC721MetadataCamelOnly = - ERC721Component::ERC721MetadataCamelOnlyImpl; + impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl; impl ERC721InternalImpl = ERC721Component::InternalImpl; - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - #[storage] struct Storage { #[substorage(v0)] diff --git a/src/presets/eth_account.cairo b/src/presets/eth_account.cairo index 345ace630..0676ebad1 100644 --- a/src/presets/eth_account.cairo +++ b/src/presets/eth_account.cairo @@ -19,26 +19,12 @@ mod EthAccountUpgradeable { component!(path: SRC5Component, storage: src5, event: SRC5Event); component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); - // EthAccount + // EthAccountMixin #[abi(embed_v0)] - impl SRC6Impl = EthAccountComponent::SRC6Impl; - #[abi(embed_v0)] - impl SRC6CamelOnlyImpl = EthAccountComponent::SRC6CamelOnlyImpl; - #[abi(embed_v0)] - impl PublicKeyImpl = EthAccountComponent::PublicKeyImpl; - #[abi(embed_v0)] - impl PublicKeyCamelImpl = - EthAccountComponent::PublicKeyCamelImpl; - #[abi(embed_v0)] - impl DeclarerImpl = EthAccountComponent::DeclarerImpl; - #[abi(embed_v0)] - impl DeployableImpl = EthAccountComponent::DeployableImpl; + impl EthAccountMixinImpl = + EthAccountComponent::EthAccountMixinImpl; impl EthAccountInternalImpl = EthAccountComponent::InternalImpl; - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - // Upgradeable impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl; diff --git a/src/tests/access/test_accesscontrol.cairo b/src/tests/access/test_accesscontrol.cairo index df2a2b9fe..3768e13c4 100644 --- a/src/tests/access/test_accesscontrol.cairo +++ b/src/tests/access/test_accesscontrol.cairo @@ -45,7 +45,7 @@ fn setup() -> ComponentState { fn test_initializer() { let mut state = COMPONENT_STATE(); state.initializer(); - let supports_iaccesscontrol = CONTRACT_STATE().supports_interface(IACCESSCONTROL_ID); + let supports_iaccesscontrol = CONTRACT_STATE().src5.supports_interface(IACCESSCONTROL_ID); assert!(supports_iaccesscontrol); } diff --git a/src/tests/mocks/accesscontrol_mocks.cairo b/src/tests/mocks/accesscontrol_mocks.cairo index a2f4290d0..763ffeed5 100644 --- a/src/tests/mocks/accesscontrol_mocks.cairo +++ b/src/tests/mocks/accesscontrol_mocks.cairo @@ -8,20 +8,12 @@ mod DualCaseAccessControlMock { component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent); component!(path: SRC5Component, storage: src5, event: SRC5Event); - // AccessControl + // AccessControlMixin #[abi(embed_v0)] - impl AccessControlImpl = - AccessControlComponent::AccessControlImpl; - #[abi(embed_v0)] - impl AccessControlCamelImpl = - AccessControlComponent::AccessControlCamelImpl; + impl AccessControlMixinImpl = + AccessControlComponent::AccessControlMixinImpl; impl AccessControlInternalImpl = AccessControlComponent::InternalImpl; - // SRC5 - #[abi(embed_v0)] - impl SRC5Impl = SRC5Component::SRC5Impl; - - #[storage] struct Storage { #[substorage(v0)] diff --git a/src/tests/mocks/erc721_receiver_mocks.cairo b/src/tests/mocks/erc721_receiver_mocks.cairo index ed54696e1..5b5cd7cf7 100644 --- a/src/tests/mocks/erc721_receiver_mocks.cairo +++ b/src/tests/mocks/erc721_receiver_mocks.cairo @@ -215,4 +215,3 @@ mod CamelERC721ReceiverPanicMock { 3 } } - diff --git a/src/tests/mocks/ownable_mocks.cairo b/src/tests/mocks/ownable_mocks.cairo index c0a8f5fe7..9f0a5ce8e 100644 --- a/src/tests/mocks/ownable_mocks.cairo +++ b/src/tests/mocks/ownable_mocks.cairo @@ -6,10 +6,7 @@ mod DualCaseOwnableMock { component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); #[abi(embed_v0)] - impl OwnableImpl = OwnableComponent::OwnableImpl; - #[abi(embed_v0)] - impl OwnableCamelOnlyImpl = - OwnableComponent::OwnableCamelOnlyImpl; + impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl; impl InternalImpl = OwnableComponent::InternalImpl; #[storage] @@ -154,10 +151,8 @@ mod DualCaseTwoStepOwnableMock { component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); #[abi(embed_v0)] - impl OwnableTwoStepImpl = OwnableComponent::OwnableTwoStepImpl; - #[abi(embed_v0)] - impl OwnableTwoStepCamelOnlyImpl = - OwnableComponent::OwnableTwoStepCamelOnlyImpl; + impl OwnableTwoStepMixinImpl = + OwnableComponent::OwnableTwoStepMixinImpl; impl InternalImpl = OwnableComponent::InternalImpl; #[storage] diff --git a/src/tests/presets/test_account.cairo b/src/tests/presets/test_account.cairo index c92b2c98e..a15075f48 100644 --- a/src/tests/presets/test_account.cairo +++ b/src/tests/presets/test_account.cairo @@ -65,13 +65,13 @@ fn test_constructor() { assert_only_event_owner_added(ZERO(), PUBKEY); - let public_key = Account::PublicKeyImpl::get_public_key(@state); + let public_key = Account::AccountMixinImpl::get_public_key(@state); assert_eq!(public_key, PUBKEY); - let supports_isrc5 = Account::SRC5Impl::supports_interface(@state, ISRC5_ID); + let supports_isrc5 = Account::AccountMixinImpl::supports_interface(@state, ISRC5_ID); assert!(supports_isrc5); - let supports_isrc6 = Account::SRC5Impl::supports_interface(@state, ISRC6_ID); + let supports_isrc6 = Account::AccountMixinImpl::supports_interface(@state, ISRC6_ID); assert!(supports_isrc6); } diff --git a/src/tests/presets/test_erc20.cairo b/src/tests/presets/test_erc20.cairo index 6892ef2e7..cd78adafb 100644 --- a/src/tests/presets/test_erc20.cairo +++ b/src/tests/presets/test_erc20.cairo @@ -5,9 +5,6 @@ use openzeppelin::tests::utils::constants::{ }; use openzeppelin::tests::utils; use openzeppelin::token::erc20::ERC20Component::{Approval, Transfer}; -use openzeppelin::token::erc20::ERC20Component::{ERC20CamelOnlyImpl, ERC20Impl}; -use openzeppelin::token::erc20::ERC20Component::{ERC20MetadataImpl, InternalImpl}; -use openzeppelin::token::erc20::interface::ERC20ABI; use openzeppelin::token::erc20::interface::{ERC20ABIDispatcher, ERC20ABIDispatcherTrait}; use openzeppelin::utils::serde::SerializedAppend; use starknet::ContractAddress; diff --git a/src/tests/presets/test_erc721.cairo b/src/tests/presets/test_erc721.cairo index 3e2789512..ddd4998cb 100644 --- a/src/tests/presets/test_erc721.cairo +++ b/src/tests/presets/test_erc721.cairo @@ -12,10 +12,7 @@ use openzeppelin::tests::utils::constants::{ ZERO, DATA, OWNER, SPENDER, RECIPIENT, OTHER, OPERATOR, PUBKEY, NAME, SYMBOL }; use openzeppelin::tests::utils; -use openzeppelin::token::erc721::ERC721Component::InternalImpl as ERC721ComponentInternalTrait; use openzeppelin::token::erc721::ERC721Component::{Approval, ApprovalForAll, Transfer}; -use openzeppelin::token::erc721::ERC721Component::{ERC721CamelOnlyImpl, ERC721Impl}; -use openzeppelin::token::erc721::ERC721Component::{ERC721MetadataImpl, ERC721MetadataCamelOnlyImpl}; use openzeppelin::token::erc721::interface::ERC721ABI; use openzeppelin::token::erc721::interface::{ERC721ABIDispatcher, ERC721ABIDispatcherTrait}; use openzeppelin::token::erc721::interface::{IERC721_ID, IERC721_METADATA_ID}; diff --git a/src/tests/presets/test_eth_account.cairo b/src/tests/presets/test_eth_account.cairo index ae25f5f8a..e4d71a23f 100644 --- a/src/tests/presets/test_eth_account.cairo +++ b/src/tests/presets/test_eth_account.cairo @@ -95,13 +95,17 @@ fn test_constructor() { assert_only_event_owner_added(ZERO(), ETH_PUBKEY()); - let public_key = EthAccountUpgradeable::PublicKeyImpl::get_public_key(@state); + let public_key = EthAccountUpgradeable::EthAccountMixinImpl::get_public_key(@state); assert_eq!(public_key, ETH_PUBKEY()); - let supports_isrc5 = EthAccountUpgradeable::SRC5Impl::supports_interface(@state, ISRC5_ID); + let supports_isrc5 = EthAccountUpgradeable::EthAccountMixinImpl::supports_interface( + @state, ISRC5_ID + ); assert!(supports_isrc5); - let supports_isrc6 = EthAccountUpgradeable::SRC5Impl::supports_interface(@state, ISRC6_ID); + let supports_isrc6 = EthAccountUpgradeable::EthAccountMixinImpl::supports_interface( + @state, ISRC6_ID + ); assert!(supports_isrc6); } diff --git a/src/token/erc20/erc20.cairo b/src/token/erc20/erc20.cairo index 105f11c32..5d8e7ddc8 100644 --- a/src/token/erc20/erc20.cairo +++ b/src/token/erc20/erc20.cairo @@ -297,4 +297,76 @@ mod ERC20Component { } } } + + #[embeddable_as(ERC20MixinImpl)] + impl ERC20Mixin< + TContractState, +HasComponent, +Drop + > 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) -> felt252 { + ERC20Metadata::name(self) + } + + fn symbol(self: @ComponentState) -> felt252 { + 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) + } + } } diff --git a/src/token/erc721/erc721.cairo b/src/token/erc721/erc721.cairo index 9a9a3e44b..bcfdeb7fe 100644 --- a/src/token/erc721/erc721.cairo +++ b/src/token/erc721/erc721.cairo @@ -10,6 +10,7 @@ mod ERC721Component { use openzeppelin::account; use openzeppelin::introspection::dual_src5::{DualCaseSRC5, DualCaseSRC5Trait}; use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5Component::{SRC5, SRC5Camel}; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::dual721_receiver::{ DualCaseERC721Receiver, DualCaseERC721ReceiverTrait @@ -171,7 +172,8 @@ mod ERC721Component { let caller = get_caller_address(); assert( - owner == caller || self.is_approved_for_all(owner, caller), Errors::UNAUTHORIZED + owner == caller || ERC721::is_approved_for_all(@self, owner, caller), + Errors::UNAUTHORIZED ); self._approve(to, token_id); } @@ -246,11 +248,11 @@ mod ERC721Component { +Drop > of interface::IERC721CamelOnly> { fn balanceOf(self: @ComponentState, account: ContractAddress) -> u256 { - self.balance_of(account) + ERC721::balance_of(self, account) } fn ownerOf(self: @ComponentState, tokenId: u256) -> ContractAddress { - self.owner_of(tokenId) + ERC721::owner_of(self, tokenId) } fn safeTransferFrom( @@ -260,7 +262,7 @@ mod ERC721Component { tokenId: u256, data: Span ) { - self.safe_transfer_from(from, to, tokenId, data) + ERC721::safe_transfer_from(ref self, from, to, tokenId, data) } fn transferFrom( @@ -269,23 +271,23 @@ mod ERC721Component { to: ContractAddress, tokenId: u256 ) { - self.transfer_from(from, to, tokenId) + ERC721::transfer_from(ref self, from, to, tokenId) } fn setApprovalForAll( ref self: ComponentState, operator: ContractAddress, approved: bool ) { - self.set_approval_for_all(operator, approved) + ERC721::set_approval_for_all(ref self, operator, approved) } fn getApproved(self: @ComponentState, tokenId: u256) -> ContractAddress { - self.get_approved(tokenId) + ERC721::get_approved(self, tokenId) } fn isApprovedForAll( self: @ComponentState, owner: ContractAddress, operator: ContractAddress ) -> bool { - self.is_approved_for_all(owner, operator) + ERC721::is_approved_for_all(self, owner, operator) } } @@ -298,7 +300,7 @@ mod ERC721Component { +Drop > of interface::IERC721MetadataCamelOnly> { fn tokenURI(self: @ComponentState, tokenId: u256) -> felt252 { - self.token_uri(tokenId) + ERC721Metadata::token_uri(self, tokenId) } } @@ -351,8 +353,10 @@ mod ERC721Component { self: @ComponentState, spender: ContractAddress, token_id: u256 ) -> bool { let owner = self._owner_of(token_id); - let is_approved_for_all = self.is_approved_for_all(owner, spender); - owner == spender || is_approved_for_all || spender == self.get_approved(token_id) + let is_approved_for_all = ERC721::is_approved_for_all(self, owner, spender); + owner == spender + || is_approved_for_all + || spender == ERC721::get_approved(self, token_id) } /// Changes or reaffirms the approved address for an NFT. @@ -541,4 +545,131 @@ mod ERC721Component { DualCaseSRC5 { contract_address: to }.supports_interface(account::interface::ISRC6_ID) } } + + #[embeddable_as(ERC721MixinImpl)] + impl ERC721Mixin< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +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) -> felt252 { + ERC721Metadata::name(self) + } + + fn symbol(self: @ComponentState) -> felt252 { + ERC721Metadata::symbol(self) + } + + fn token_uri(self: @ComponentState, token_id: u256) -> felt252 { + 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) -> felt252 { + 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) + } + } } diff --git a/src/token/erc721/erc721_receiver.cairo b/src/token/erc721/erc721_receiver.cairo index 6435c3da5..7b19fe84b 100644 --- a/src/token/erc721/erc721_receiver.cairo +++ b/src/token/erc721/erc721_receiver.cairo @@ -9,9 +9,11 @@ #[starknet::component] mod ERC721ReceiverComponent { use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; use openzeppelin::introspection::src5::SRC5Component; use openzeppelin::token::erc721::interface::IERC721_RECEIVER_ID; use openzeppelin::token::erc721::interface::{IERC721Receiver, IERC721ReceiverCamel}; + use openzeppelin::token::erc721::interface; use starknet::ContractAddress; #[storage] @@ -75,4 +77,42 @@ mod ERC721ReceiverComponent { src5_component.register_interface(IERC721_RECEIVER_ID); } } + + #[embeddable_as(ERC721ReceiverAMixinmpl)] + impl ERC721ReceiverMixin< + TContractState, + +HasComponent, + impl SRC5: SRC5Component::HasComponent, + +Drop + > of interface::ERC721ReceiverMixin> { + // IERC721Receiver + fn on_erc721_received( + self: @ComponentState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252 { + ERC721Receiver::on_erc721_received(self, operator, from, token_id, data) + } + + // IERC721ReceiverCamel + fn onERC721Received( + self: @ComponentState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span + ) -> felt252 { + ERC721ReceiverCamel::onERC721Received(self, operator, from, tokenId, data) + } + + // ISRC5 + fn supports_interface( + self: @ComponentState, interface_id: felt252 + ) -> bool { + let src5 = get_dep_component!(self, SRC5); + src5.supports_interface(interface_id) + } + } } diff --git a/src/token/erc721/interface.cairo b/src/token/erc721/interface.cairo index b2aa22ec4..209226acc 100644 --- a/src/token/erc721/interface.cairo +++ b/src/token/erc721/interface.cairo @@ -105,9 +105,6 @@ trait ERC721ABI { fn getApproved(self: @TState, tokenId: u256) -> ContractAddress; fn isApprovedForAll(self: @TState, owner: ContractAddress, operator: ContractAddress) -> bool; - // ISRC5Camel - fn supportsInterface(self: @TState, interfaceId: felt252) -> bool; - // IERC721MetadataCamelOnly fn tokenURI(self: @TState, tokenId: u256) -> felt252; } @@ -137,3 +134,27 @@ trait IERC721ReceiverCamel { data: Span ) -> felt252; } + +#[starknet::interface] +trait ERC721ReceiverMixin { + // IERC721Receiver + fn on_erc721_received( + self: @TState, + operator: ContractAddress, + from: ContractAddress, + token_id: u256, + data: Span + ) -> felt252; + + // IERC721ReceiverCamel + fn onERC721Received( + self: @TState, + operator: ContractAddress, + from: ContractAddress, + tokenId: u256, + data: Span + ) -> felt252; + + // ISRC5 + fn supports_interface(self: @TState, interface_id: felt252) -> bool; +}