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;
+}