|
48 | 48 | //! ## Example: Defining and Implementing a Trait |
49 | 49 | //! |
50 | 50 | //! ``` |
51 | | -//! use soroban_sdk::{contract, contractimpl, contracttrait, Env}; |
| 51 | +//! use soroban_sdk::{contract, contractimpl, contracttrait, Address, Env}; |
52 | 52 | //! |
53 | | -//! // Define a trait with default implementations |
| 53 | +//! // A regular trait for admin access control - not exported as contract functions |
| 54 | +//! pub trait RequireAuthForPause { |
| 55 | +//! fn require_auth_for_pause(env: &Env); |
| 56 | +//! } |
| 57 | +//! |
| 58 | +//! // Define a contracttrait with default implementations that require RequireAuthForPause |
54 | 59 | //! #[contracttrait] |
55 | | -//! pub trait Pausable { |
| 60 | +//! pub trait Pausable: RequireAuthForPause { |
56 | 61 | //! fn is_paused(env: &Env) -> bool { |
57 | 62 | //! env.storage().instance().has(&"paused") |
58 | 63 | //! } |
59 | 64 | //! |
60 | 65 | //! fn pause(env: &Env) { |
| 66 | +//! Self::require_auth_for_pause(env); |
61 | 67 | //! env.storage().instance().set(&"paused", &true); |
62 | 68 | //! } |
63 | 69 | //! |
64 | 70 | //! fn unpause(env: &Env) { |
| 71 | +//! Self::require_auth_for_pause(env); |
65 | 72 | //! env.storage().instance().remove(&"paused"); |
66 | 73 | //! } |
67 | 74 | //! } |
68 | 75 | //! |
69 | 76 | //! #[contract] |
70 | 77 | //! pub struct MyContract; |
71 | 78 | //! |
| 79 | +//! impl RequireAuthForPause for MyContract { |
| 80 | +//! fn require_auth_for_pause(env: &Env) { |
| 81 | +//! let admin: Address = env.storage().instance().get(&"admin").unwrap(); |
| 82 | +//! admin.require_auth(); |
| 83 | +//! } |
| 84 | +//! } |
| 85 | +//! |
72 | 86 | //! // Implement the trait - default functions are automatically exported |
73 | 87 | //! #[contractimpl(contracttrait)] |
74 | 88 | //! impl Pausable for MyContract {} |
75 | 89 | //! |
76 | 90 | //! #[contractimpl] |
77 | 91 | //! impl MyContract { |
| 92 | +//! pub fn __constructor(env: &Env, admin: Address) { |
| 93 | +//! env.storage().instance().set(&"admin", &admin); |
| 94 | +//! } |
| 95 | +//! |
78 | 96 | //! pub fn do_something(env: &Env) { |
79 | 97 | //! if Self::is_paused(env) { |
80 | 98 | //! panic!("contract is paused"); |
81 | 99 | //! } |
82 | 100 | //! // ... rest of the function |
83 | 101 | //! } |
84 | 102 | //! } |
| 103 | +//! |
| 104 | +//! #[test] |
| 105 | +//! fn test() { |
| 106 | +//! # } |
| 107 | +//! # #[cfg(feature = "testutils")] |
| 108 | +//! # fn main() { |
| 109 | +//! use soroban_sdk::{testutils::{Address as _, MockAuth, MockAuthInvoke}, IntoVal}; |
| 110 | +//! let env = Env::default(); |
| 111 | +//! let admin = Address::generate(&env); |
| 112 | +//! let contract_id = env.register(MyContract, (&admin,)); |
| 113 | +//! let client = PausableClient::new(&env, &contract_id); |
| 114 | +//! |
| 115 | +//! assert!(!client.is_paused()); |
| 116 | +//! client.mock_auths(&[MockAuth { |
| 117 | +//! address: &admin, |
| 118 | +//! invoke: &MockAuthInvoke { |
| 119 | +//! contract: &contract_id, |
| 120 | +//! fn_name: "pause", |
| 121 | +//! args: ().into_val(&env), |
| 122 | +//! sub_invokes: &[], |
| 123 | +//! }, |
| 124 | +//! }]).pause(); |
| 125 | +//! assert!(client.is_paused()); |
| 126 | +//! client.mock_auths(&[MockAuth { |
| 127 | +//! address: &admin, |
| 128 | +//! invoke: &MockAuthInvoke { |
| 129 | +//! contract: &contract_id, |
| 130 | +//! fn_name: "unpause", |
| 131 | +//! args: ().into_val(&env), |
| 132 | +//! sub_invokes: &[], |
| 133 | +//! }, |
| 134 | +//! }]).unpause(); |
| 135 | +//! assert!(!client.is_paused()); |
| 136 | +//! } |
| 137 | +//! # #[cfg(not(feature = "testutils"))] |
85 | 138 | //! # fn main() { } |
86 | 139 | //! ``` |
87 | 140 | //! |
|
90 | 143 | //! Contracts can override specific functions while keeping the defaults for others: |
91 | 144 | //! |
92 | 145 | //! ``` |
93 | | -//! use soroban_sdk::{contract, contractimpl, contracttrait, Env}; |
| 146 | +//! use soroban_sdk::{contract, contractimpl, contracttrait, Address, Env}; |
| 147 | +//! |
| 148 | +//! // A regular trait for admin access control - not exported as contract functions |
| 149 | +//! pub trait RequireAuthForPause { |
| 150 | +//! fn require_auth_for_pause(env: &Env); |
| 151 | +//! } |
94 | 152 | //! |
95 | | -//! // Define a trait with default implementations |
| 153 | +//! // Define a contracttrait with default implementations that require RequireAuthForPause |
96 | 154 | //! #[contracttrait] |
97 | | -//! pub trait Pausable { |
| 155 | +//! pub trait Pausable: RequireAuthForPause { |
98 | 156 | //! fn is_paused(env: &Env) -> bool { |
99 | 157 | //! env.storage().instance().has(&"paused") |
100 | 158 | //! } |
101 | 159 | //! |
102 | 160 | //! fn pause(env: &Env) { |
| 161 | +//! Self::require_auth_for_pause(env); |
103 | 162 | //! env.storage().instance().set(&"paused", &true); |
104 | 163 | //! } |
105 | 164 | //! |
106 | 165 | //! fn unpause(env: &Env) { |
| 166 | +//! Self::require_auth_for_pause(env); |
107 | 167 | //! env.storage().instance().remove(&"paused"); |
108 | 168 | //! } |
109 | 169 | //! } |
110 | 170 | //! |
111 | 171 | //! #[contract] |
112 | 172 | //! pub struct MyContract; |
113 | 173 | //! |
| 174 | +//! impl RequireAuthForPause for MyContract { |
| 175 | +//! fn require_auth_for_pause(env: &Env) { |
| 176 | +//! let admin: Address = env.storage().instance().get(&"admin").unwrap(); |
| 177 | +//! admin.require_auth(); |
| 178 | +//! } |
| 179 | +//! } |
| 180 | +//! |
114 | 181 | //! // Implement the trait - override default implementations as needed |
115 | 182 | //! #[contractimpl(contracttrait)] |
116 | 183 | //! impl Pausable for MyContract { |
|
123 | 190 | //! |
124 | 191 | //! #[contractimpl] |
125 | 192 | //! impl MyContract { |
| 193 | +//! pub fn __constructor(env: &Env, admin: Address) { |
| 194 | +//! env.storage().instance().set(&"admin", &admin); |
| 195 | +//! } |
| 196 | +//! |
126 | 197 | //! pub fn do_something(env: &Env) { |
127 | 198 | //! if Self::is_paused(env) { |
128 | 199 | //! panic!("contract is paused"); |
129 | 200 | //! } |
130 | 201 | //! // ... rest of the function |
131 | 202 | //! } |
132 | 203 | //! } |
| 204 | +//! |
| 205 | +//! #[test] |
| 206 | +//! fn test() { |
| 207 | +//! # } |
| 208 | +//! # #[cfg(feature = "testutils")] |
| 209 | +//! # fn main() { |
| 210 | +//! use soroban_sdk::{testutils::{Address as _, MockAuth, MockAuthInvoke}, IntoVal}; |
| 211 | +//! let env = Env::default(); |
| 212 | +//! let admin = Address::generate(&env); |
| 213 | +//! let contract_id = env.register(MyContract, (&admin,)); |
| 214 | +//! let client = PausableClient::new(&env, &contract_id); |
| 215 | +//! |
| 216 | +//! assert!(!client.is_paused()); |
| 217 | +//! client.mock_auths(&[MockAuth { |
| 218 | +//! address: &admin, |
| 219 | +//! invoke: &MockAuthInvoke { |
| 220 | +//! contract: &contract_id, |
| 221 | +//! fn_name: "pause", |
| 222 | +//! args: ().into_val(&env), |
| 223 | +//! sub_invokes: &[], |
| 224 | +//! }, |
| 225 | +//! }]).pause(); |
| 226 | +//! assert!(client.is_paused()); |
| 227 | +//! client.mock_auths(&[MockAuth { |
| 228 | +//! address: &admin, |
| 229 | +//! invoke: &MockAuthInvoke { |
| 230 | +//! contract: &contract_id, |
| 231 | +//! fn_name: "unpause", |
| 232 | +//! args: ().into_val(&env), |
| 233 | +//! sub_invokes: &[], |
| 234 | +//! }, |
| 235 | +//! }]).unpause(); |
| 236 | +//! assert!(!client.is_paused()); |
| 237 | +//! } |
| 238 | +//! # #[cfg(not(feature = "testutils"))] |
133 | 239 | //! # fn main() { } |
134 | 240 | //! ``` |
135 | 241 | //! |
|
138 | 244 | //! The generated `{TraitName}Client` can be used to call any contract that implements the trait: |
139 | 245 | //! |
140 | 246 | //! ``` |
141 | | -//! use soroban_sdk::{contract, contractimpl, contracttrait, Env}; |
| 247 | +//! use soroban_sdk::{contract, contractimpl, contracttrait, Address, Env}; |
| 248 | +//! |
| 249 | +//! // A regular trait for admin access control - not exported as contract functions |
| 250 | +//! pub trait RequireAuthForPause { |
| 251 | +//! fn require_auth_for_pause(env: &Env); |
| 252 | +//! } |
142 | 253 | //! |
143 | | -//! // Define a trait with default implementations |
| 254 | +//! // Define a contracttrait with default implementations that require RequireAuthForPause |
144 | 255 | //! #[contracttrait] |
145 | | -//! pub trait Pausable { |
| 256 | +//! pub trait Pausable: RequireAuthForPause { |
146 | 257 | //! fn is_paused(env: &Env) -> bool { |
147 | 258 | //! env.storage().instance().has(&"paused") |
148 | 259 | //! } |
149 | 260 | //! |
150 | 261 | //! fn pause(env: &Env) { |
| 262 | +//! Self::require_auth_for_pause(env); |
151 | 263 | //! env.storage().instance().set(&"paused", &true); |
152 | 264 | //! } |
153 | 265 | //! |
154 | 266 | //! fn unpause(env: &Env) { |
| 267 | +//! Self::require_auth_for_pause(env); |
155 | 268 | //! env.storage().instance().remove(&"paused"); |
156 | 269 | //! } |
157 | 270 | //! } |
158 | 271 | //! |
159 | 272 | //! #[contract] |
160 | 273 | //! pub struct MyContract; |
161 | 274 | //! |
| 275 | +//! impl RequireAuthForPause for MyContract { |
| 276 | +//! fn require_auth_for_pause(env: &Env) { |
| 277 | +//! let admin: Address = env.storage().instance().get(&"admin").unwrap(); |
| 278 | +//! admin.require_auth(); |
| 279 | +//! } |
| 280 | +//! } |
| 281 | +//! |
162 | 282 | //! // Implement the trait - default functions are automatically exported |
163 | 283 | //! #[contractimpl(contracttrait)] |
164 | 284 | //! impl Pausable for MyContract {} |
165 | 285 | //! |
166 | 286 | //! #[contractimpl] |
167 | 287 | //! impl MyContract { |
| 288 | +//! pub fn __constructor(env: &Env, admin: Address) { |
| 289 | +//! env.storage().instance().set(&"admin", &admin); |
| 290 | +//! } |
| 291 | +//! |
168 | 292 | //! pub fn do_something(env: &Env) { |
169 | 293 | //! if Self::is_paused(env) { |
170 | 294 | //! panic!("contract is paused"); |
|
178 | 302 | //! # } |
179 | 303 | //! # #[cfg(feature = "testutils")] |
180 | 304 | //! # fn main() { |
| 305 | +//! use soroban_sdk::{testutils::{Address as _, MockAuth, MockAuthInvoke}, IntoVal}; |
181 | 306 | //! let env = Env::default(); |
182 | | -//! let contract_id = env.register(MyContract, ()); |
| 307 | +//! let admin = Address::generate(&env); |
| 308 | +//! let contract_id = env.register(MyContract, (&admin,)); |
183 | 309 | //! let client = PausableClient::new(&env, &contract_id); |
184 | 310 | //! |
185 | 311 | //! assert!(!client.is_paused()); |
186 | | -//! client.pause(); |
| 312 | +//! client.mock_auths(&[MockAuth { |
| 313 | +//! address: &admin, |
| 314 | +//! invoke: &MockAuthInvoke { |
| 315 | +//! contract: &contract_id, |
| 316 | +//! fn_name: "pause", |
| 317 | +//! args: ().into_val(&env), |
| 318 | +//! sub_invokes: &[], |
| 319 | +//! }, |
| 320 | +//! }]).pause(); |
187 | 321 | //! assert!(client.is_paused()); |
188 | | -//! client.unpause(); |
| 322 | +//! client.mock_auths(&[MockAuth { |
| 323 | +//! address: &admin, |
| 324 | +//! invoke: &MockAuthInvoke { |
| 325 | +//! contract: &contract_id, |
| 326 | +//! fn_name: "unpause", |
| 327 | +//! args: ().into_val(&env), |
| 328 | +//! sub_invokes: &[], |
| 329 | +//! }, |
| 330 | +//! }]).unpause(); |
189 | 331 | //! assert!(!client.is_paused()); |
190 | 332 | //! } |
191 | 333 | //! # #[cfg(not(feature = "testutils"))] |
|
0 commit comments