Skip to content

Commit ac098c2

Browse files
Merge branch 'main' into docs-from-attrs
2 parents 75425fa + f7bdf7e commit ac098c2

6 files changed

Lines changed: 258 additions & 25 deletions

File tree

soroban-sdk/build.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ pub fn main() {
55
// hash.
66
println!("cargo::rustc-check-cfg=cfg(soroban_sdk_internal_no_rssdkver_meta)");
77

8-
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
9-
if let Ok(version) = rustc_version::version() {
10-
if version.major == 1 && version.minor >= 82 {
11-
panic!("Rust compiler 1.82+ with target 'wasm32-unknown-unknown' is unsupported by the Soroban Environment, because the 'wasm32-unknown-unknown' target in Rust 1.82+ has features enabled that are not yet supported and not easily disabled: reference-types, multi-value. Use Rust 1.81 to build for the 'wasm32-unknown-unknown' target.");
8+
// Check if we're building for wasm32-unknown-unknown target (cross-compilation safe)
9+
if std::env::var("CARGO_CFG_TARGET_FAMILY").as_deref() == Ok("wasm")
10+
&& std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("unknown")
11+
{
12+
if let Ok(version) = rustc_version::version() {
13+
if version.major == 1 && version.minor >= 82 {
14+
panic!("Rust compiler 1.82+ with target 'wasm32-unknown-unknown' is unsupported by the Soroban Environment, use 'wasm32v1-none' available with Rust 1.84+. The 'wasm32-unknown-unknown' target in Rust 1.82+ has features enabled that are not yet supported and not easily disabled: reference-types, multi-value. If you must build for the 'wasm32-unknown-unknown' use Rust 1.81 or earlier.");
15+
}
1216
}
1317
}
1418

soroban-sdk/src/_migrating/v25_contracttrait.rs

Lines changed: 154 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,40 +48,93 @@
4848
//! ## Example: Defining and Implementing a Trait
4949
//!
5050
//! ```
51-
//! use soroban_sdk::{contract, contractimpl, contracttrait, Env};
51+
//! use soroban_sdk::{contract, contractimpl, contracttrait, Address, Env};
5252
//!
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
5459
//! #[contracttrait]
55-
//! pub trait Pausable {
60+
//! pub trait Pausable: RequireAuthForPause {
5661
//! fn is_paused(env: &Env) -> bool {
5762
//! env.storage().instance().has(&"paused")
5863
//! }
5964
//!
6065
//! fn pause(env: &Env) {
66+
//! Self::require_auth_for_pause(env);
6167
//! env.storage().instance().set(&"paused", &true);
6268
//! }
6369
//!
6470
//! fn unpause(env: &Env) {
71+
//! Self::require_auth_for_pause(env);
6572
//! env.storage().instance().remove(&"paused");
6673
//! }
6774
//! }
6875
//!
6976
//! #[contract]
7077
//! pub struct MyContract;
7178
//!
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+
//!
7286
//! // Implement the trait - default functions are automatically exported
7387
//! #[contractimpl(contracttrait)]
7488
//! impl Pausable for MyContract {}
7589
//!
7690
//! #[contractimpl]
7791
//! impl MyContract {
92+
//! pub fn __constructor(env: &Env, admin: Address) {
93+
//! env.storage().instance().set(&"admin", &admin);
94+
//! }
95+
//!
7896
//! pub fn do_something(env: &Env) {
7997
//! if Self::is_paused(env) {
8098
//! panic!("contract is paused");
8199
//! }
82100
//! // ... rest of the function
83101
//! }
84102
//! }
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"))]
85138
//! # fn main() { }
86139
//! ```
87140
//!
@@ -90,27 +143,41 @@
90143
//! Contracts can override specific functions while keeping the defaults for others:
91144
//!
92145
//! ```
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+
//! }
94152
//!
95-
//! // Define a trait with default implementations
153+
//! // Define a contracttrait with default implementations that require RequireAuthForPause
96154
//! #[contracttrait]
97-
//! pub trait Pausable {
155+
//! pub trait Pausable: RequireAuthForPause {
98156
//! fn is_paused(env: &Env) -> bool {
99157
//! env.storage().instance().has(&"paused")
100158
//! }
101159
//!
102160
//! fn pause(env: &Env) {
161+
//! Self::require_auth_for_pause(env);
103162
//! env.storage().instance().set(&"paused", &true);
104163
//! }
105164
//!
106165
//! fn unpause(env: &Env) {
166+
//! Self::require_auth_for_pause(env);
107167
//! env.storage().instance().remove(&"paused");
108168
//! }
109169
//! }
110170
//!
111171
//! #[contract]
112172
//! pub struct MyContract;
113173
//!
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+
//!
114181
//! // Implement the trait - override default implementations as needed
115182
//! #[contractimpl(contracttrait)]
116183
//! impl Pausable for MyContract {
@@ -123,13 +190,52 @@
123190
//!
124191
//! #[contractimpl]
125192
//! impl MyContract {
193+
//! pub fn __constructor(env: &Env, admin: Address) {
194+
//! env.storage().instance().set(&"admin", &admin);
195+
//! }
196+
//!
126197
//! pub fn do_something(env: &Env) {
127198
//! if Self::is_paused(env) {
128199
//! panic!("contract is paused");
129200
//! }
130201
//! // ... rest of the function
131202
//! }
132203
//! }
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"))]
133239
//! # fn main() { }
134240
//! ```
135241
//!
@@ -138,33 +244,51 @@
138244
//! The generated `{TraitName}Client` can be used to call any contract that implements the trait:
139245
//!
140246
//! ```
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+
//! }
142253
//!
143-
//! // Define a trait with default implementations
254+
//! // Define a contracttrait with default implementations that require RequireAuthForPause
144255
//! #[contracttrait]
145-
//! pub trait Pausable {
256+
//! pub trait Pausable: RequireAuthForPause {
146257
//! fn is_paused(env: &Env) -> bool {
147258
//! env.storage().instance().has(&"paused")
148259
//! }
149260
//!
150261
//! fn pause(env: &Env) {
262+
//! Self::require_auth_for_pause(env);
151263
//! env.storage().instance().set(&"paused", &true);
152264
//! }
153265
//!
154266
//! fn unpause(env: &Env) {
267+
//! Self::require_auth_for_pause(env);
155268
//! env.storage().instance().remove(&"paused");
156269
//! }
157270
//! }
158271
//!
159272
//! #[contract]
160273
//! pub struct MyContract;
161274
//!
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+
//!
162282
//! // Implement the trait - default functions are automatically exported
163283
//! #[contractimpl(contracttrait)]
164284
//! impl Pausable for MyContract {}
165285
//!
166286
//! #[contractimpl]
167287
//! impl MyContract {
288+
//! pub fn __constructor(env: &Env, admin: Address) {
289+
//! env.storage().instance().set(&"admin", &admin);
290+
//! }
291+
//!
168292
//! pub fn do_something(env: &Env) {
169293
//! if Self::is_paused(env) {
170294
//! panic!("contract is paused");
@@ -178,14 +302,32 @@
178302
//! # }
179303
//! # #[cfg(feature = "testutils")]
180304
//! # fn main() {
305+
//! use soroban_sdk::{testutils::{Address as _, MockAuth, MockAuthInvoke}, IntoVal};
181306
//! 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,));
183309
//! let client = PausableClient::new(&env, &contract_id);
184310
//!
185311
//! 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();
187321
//! 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();
189331
//! assert!(!client.is_paused());
190332
//! }
191333
//! # #[cfg(not(feature = "testutils"))]

soroban-sdk/src/iter.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
//! Iterators for use with collections like [Map], [Vec].
2+
//!
3+
//! Collections are not guaranteed to contain values of the expected type as
4+
//! they are stored on the host as [Val]s, so two iterators are provided:
5+
//!
6+
//! - **`try_iter()`** returns an iterator that yields `Result<T, E>` for each
7+
//! element, allowing the caller to handle conversion errors.
8+
//! - **`iter()`** returns an iterator that unwraps each result,
9+
//! panicking if any element cannot be converted to the declared type.
210
#[cfg(doc)]
3-
use crate::{Map, Vec};
11+
use crate::{Map, Val, Vec};
412

513
use core::fmt::Debug;
614
use core::iter::FusedIterator;

soroban-sdk/src/map.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ macro_rules! map {
5353
/// converted from [Val] back into their type.
5454
///
5555
/// The pairs of keys and values in a Map are not guaranteed to be of type
56-
/// `K`/`V` and conversion will fail if they are not. Most functions on Map
57-
/// return a `Result` due to this.
56+
/// `K`/`V` and conversion will fail if they are not. Most functions on Map have
57+
/// a try_ variation that returns a Result that will be Err if the conversion fails.
58+
/// Functions that are not prefixed with try_ will panic if conversion fails."
5859
///
5960
/// There are some cases where this lack of guarantee is important:
6061
///
@@ -445,15 +446,23 @@ where
445446
self.obj = env.map_del(self.obj, k.into_val(env)).unwrap_infallible();
446447
}
447448

448-
/// Returns a [Vec] of all keys in the map.
449+
/// Returns a [Vec] of all keys in the map, ordered in the map's key-sorted order.
450+
///
451+
/// This method does not validate that the keys in the map are of type `K`. Since [Map]
452+
/// keys are not guaranteed to be of type `K`, it is not guaranteed that all values
453+
/// in the returned [Vec] will be of type `K`.
449454
#[inline(always)]
450455
pub fn keys(&self) -> Vec<K> {
451456
let env = self.env();
452457
let vec = env.map_keys(self.obj).unwrap_infallible();
453458
Vec::<K>::try_from_val(env, &vec).unwrap()
454459
}
455460

456-
/// Returns a [Vec] of all values in the map.
461+
/// Returns a [Vec] of all values in the map, ordered in the map's key-sorted order.
462+
///
463+
/// This method does not validate that the values in the map are of type `V`. Since [Map]
464+
/// values are not guaranteed to be of type `V`, it is not guaranteed that all values
465+
/// in the returned [Vec] will be of type `V`.
457466
#[inline(always)]
458467
pub fn values(&self) -> Vec<V> {
459468
let env = self.env();
@@ -495,6 +504,14 @@ where
495504
K: IntoVal<Env, Val> + TryFromVal<Env, Val>,
496505
V: IntoVal<Env, Val> + TryFromVal<Env, Val>,
497506
{
507+
/// Returns an iterator over the key-value pairs of the map.
508+
///
509+
/// Each entry is converted from [Val] to `(K, V)` as it is yielded.
510+
///
511+
/// ### Panics
512+
///
513+
/// If any key or value cannot be converted to its declared type.
514+
/// Use [`try_iter`](Map::try_iter) to handle conversion errors.
498515
#[inline(always)]
499516
pub fn iter(&self) -> UnwrappedIter<MapTryIter<K, V>, (K, V), ConversionError>
500517
where
@@ -504,6 +521,8 @@ where
504521
self.clone().into_iter()
505522
}
506523

524+
/// Returns an iterator over the key-value pairs of the map, yielding
525+
/// `Result<(K, V), ConversionError>` for each entry.
507526
#[inline(always)]
508527
pub fn try_iter(&self) -> MapTryIter<K, V>
509528
where

0 commit comments

Comments
 (0)