3
3
= Macros
4
4
5
5
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:
7
7
8
8
```toml
9
9
[dependencies]
@@ -16,4 +16,132 @@ openzeppelin_macros = "0.20.0"
16
16
[[with_components]]
17
17
=== `++with_components++`
18
18
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