Skip to content

Commit c8e802d

Browse files
committed
docs: finish with_components entry
1 parent c58bae0 commit c8e802d

File tree

1 file changed

+130
-2
lines changed

1 file changed

+130
-2
lines changed

docs/modules/ROOT/pages/api/macros.adoc

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
= Macros
44

55
This crate provides a collection of macros that streamline and simplify development with the library.
6-
To use them, you need to add the `openzeppelin_macros` crate as a dependency to your `Scarb.toml` file:
6+
To use them, you need to add the `openzeppelin_macros` crate as a dependency in your `Scarb.toml` file:
77

88
```toml
99
[dependencies]
@@ -16,4 +16,132 @@ openzeppelin_macros = "0.20.0"
1616
[[with_components]]
1717
=== `++with_components++`
1818

19-
This macro simplifies the syntax for adding a set of components to a contract.
19+
This macro simplifies the syntax for adding a set of components to a contract. It:
20+
21+
- _Imports the corresponding components into the contract_
22+
- _Adds the corresponding `component!` macro entries_
23+
- _Adds the storage entries for each component to the Storage struct_
24+
- _Adds the event entries for each component to the Event struct, or creates the struct if it is missing_
25+
- _Brings the corresponding internal implementations into scope_
26+
- _Provides some diagnostics for each specific component to help the developer avoid common mistakes_
27+
28+
CAUTION: Since the macro does not expose any external implementations, developers must make sure to specify explicitly
29+
the ones required by the contract.
30+
31+
[#with_components-security]
32+
==== Security considerations
33+
34+
The macro was designed to be simple and effective while still being very hard to misuse. For this reason, the features
35+
that it provides are limited, and things that might make the contract behave in unexpected ways must be
36+
explicitly specified by the developer. It does not specify external implementations, so contracts won't find
37+
themselves in a situation where external functions are exposed without the developer's knowledge. It brings
38+
the internal implementations into scope so these functions are available by default, but if they are not used,
39+
they won't have any effect on the contract's behavior.
40+
41+
[#with_components-usage]
42+
==== Usage
43+
44+
This is how a contract with multiple components looks when using the macro.
45+
46+
```cairo
47+
#[with_components(Account, SRC5, SRC9, Upgradeable)]
48+
#[starknet::contract(account)]
49+
mod OutsideExecutionAccountUpgradeable {
50+
use openzeppelin_upgrades::interface::IUpgradeable;
51+
use starknet::{ClassHash, ContractAddress};
52+
53+
// External
54+
#[abi(embed_v0)]
55+
impl AccountMixinImpl = AccountComponent::AccountMixinImpl<ContractState>;
56+
#[abi(embed_v0)]
57+
impl OutsideExecutionV2Impl =
58+
SRC9Component::OutsideExecutionV2Impl<ContractState>;
59+
60+
#[storage]
61+
struct Storage {}
62+
63+
#[constructor]
64+
fn constructor(ref self: ContractState, public_key: felt252) {
65+
self.account.initializer(public_key);
66+
self.src9.initializer();
67+
}
68+
69+
#[abi(embed_v0)]
70+
impl UpgradeableImpl of IUpgradeable<ContractState> {
71+
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
72+
self.account.assert_only_self();
73+
self.upgradeable.upgrade(new_class_hash);
74+
}
75+
}
76+
}
77+
```
78+
79+
This is how the same contract looks using regular syntax.
80+
81+
```cairo
82+
#[starknet::contract(account)]
83+
mod OutsideExecutionAccountUpgradeable {
84+
use openzeppelin::account::AccountComponent;
85+
use openzeppelin::account::extensions::SRC9Component;
86+
use openzeppelin::introspection::src5::SRC5Component;
87+
use openzeppelin::upgrades::UpgradeableComponent;
88+
use openzeppelin::upgrades::interface::IUpgradeable;
89+
use starknet::ClassHash;
90+
91+
component!(path: AccountComponent, storage: account, event: AccountEvent);
92+
component!(path: SRC5Component, storage: src5, event: SRC5Event);
93+
component!(path: SRC9Component, storage: src9, event: SRC9Event);
94+
component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);
95+
96+
// External
97+
#[abi(embed_v0)]
98+
impl AccountMixinImpl = AccountComponent::AccountMixinImpl<ContractState>;
99+
#[abi(embed_v0)]
100+
impl OutsideExecutionV2Impl =
101+
SRC9Component::OutsideExecutionV2Impl<ContractState>;
102+
103+
// Internal
104+
impl AccountInternalImpl = AccountComponent::InternalImpl<ContractState>;
105+
impl OutsideExecutionInternalImpl = SRC9Component::InternalImpl<ContractState>;
106+
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;
107+
108+
#[storage]
109+
struct Storage {
110+
#[substorage(v0)]
111+
account: AccountComponent::Storage,
112+
#[substorage(v0)]
113+
src5: SRC5Component::Storage,
114+
#[substorage(v0)]
115+
src9: SRC9Component::Storage,
116+
#[substorage(v0)]
117+
upgradeable: UpgradeableComponent::Storage,
118+
}
119+
120+
#[event]
121+
#[derive(Drop, starknet::Event)]
122+
enum Event {
123+
#[flat]
124+
AccountEvent: AccountComponent::Event,
125+
#[flat]
126+
SRC5Event: SRC5Component::Event,
127+
#[flat]
128+
SRC9Event: SRC9Component::Event,
129+
#[flat]
130+
UpgradeableEvent: UpgradeableComponent::Event,
131+
}
132+
133+
#[constructor]
134+
fn constructor(ref self: ContractState, public_key: felt252) {
135+
self.account.initializer(public_key);
136+
self.src9.initializer();
137+
}
138+
139+
#[abi(embed_v0)]
140+
impl UpgradeableImpl of IUpgradeable<ContractState> {
141+
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
142+
self.account.assert_only_self();
143+
self.upgradeable.upgrade(new_class_hash);
144+
}
145+
}
146+
}
147+
```

0 commit comments

Comments
 (0)