soroban_sdk/env.rs
1use core::convert::Infallible;
2
3#[cfg(target_family = "wasm")]
4pub mod internal {
5 use core::convert::Infallible;
6
7 pub use soroban_env_guest::*;
8 pub type EnvImpl = Guest;
9 pub type MaybeEnvImpl = Guest;
10
11 // In the Guest case, Env::Error is already Infallible so there is no work
12 // to do to "reject an error": if an error occurs in the environment, the
13 // host will trap our VM and we'll never get here at all.
14 pub(crate) fn reject_err<T>(_env: &Guest, r: Result<T, Infallible>) -> Result<T, Infallible> {
15 r
16 }
17}
18
19#[cfg(not(target_family = "wasm"))]
20pub mod internal {
21 use core::convert::Infallible;
22
23 pub use soroban_env_host::*;
24 pub type EnvImpl = Host;
25 pub type MaybeEnvImpl = Option<Host>;
26
27 // When we have `feature="testutils"` (or are in cfg(test)) we enable feature
28 // `soroban-env-{common,host}/testutils` which in turn adds the helper method
29 // `Env::escalate_error_to_panic` to the Env trait.
30 //
31 // When this is available we want to use it, because it works in concert
32 // with a _different_ part of the host that's also `testutils`-gated: the
33 // mechanism for emulating the WASM VM error-handling semantics with native
34 // contracts. In particular when a WASM contract calls a host function that
35 // fails with some error E, the host traps the VM (not returning to it at
36 // all) and propagates E to the caller of the contract. This is simulated in
37 // the native case by returning a (nontrivial) error E to us here, which we
38 // then "reject" back to the host, which stores E in a temporary cell inside
39 // any `TestContract` frame in progress and then _panics_, unwinding back to
40 // a panic-catcher it installed when invoking the `TestContract` frame, and
41 // then extracting E from the frame and returning it to its caller. This
42 // simulates the "crash, but catching the error" behaviour of the WASM case.
43 // This only works if we panic via `escalate_error_to_panic`.
44 //
45 // (The reason we don't just panic_any() here and let the panic-catcher do a
46 // type-based catch is that there might _be_ no panic-catcher around us, and
47 // we want to print out a nice error message in that case too, which
48 // panic_any() does not do us the favor of producing. This is all very
49 // subtle. See also soroban_env_host::Host::escalate_error_to_panic.)
50 #[cfg(any(test, feature = "testutils"))]
51 pub(crate) fn reject_err<T>(env: &Host, r: Result<T, HostError>) -> Result<T, Infallible> {
52 r.map_err(|e| env.escalate_error_to_panic(e))
53 }
54
55 // When we're _not_ in a cfg enabling `soroban-env-{common,host}/testutils`,
56 // there is no `Env::escalate_error_to_panic` to call, so we just panic
57 // here. But this is ok because in that case there is also no multi-contract
58 // calling machinery set up, nor probably any panic-catcher installed that
59 // we need to hide error values for the benefit of. Any panic in this case
60 // is probably going to unwind completely anyways. No special case needed.
61 #[cfg(not(any(test, feature = "testutils")))]
62 pub(crate) fn reject_err<T>(_env: &Host, r: Result<T, HostError>) -> Result<T, Infallible> {
63 r.map_err(|e| panic!("{:?}", e))
64 }
65
66 #[doc(hidden)]
67 impl<F, T> Convert<F, T> for super::Env
68 where
69 EnvImpl: Convert<F, T>,
70 {
71 type Error = <EnvImpl as Convert<F, T>>::Error;
72 fn convert(&self, f: F) -> Result<T, Self::Error> {
73 self.env_impl.convert(f)
74 }
75 }
76}
77
78pub use internal::xdr;
79pub use internal::ConversionError;
80pub use internal::EnvBase;
81pub use internal::Error;
82pub use internal::MapObject;
83pub use internal::SymbolStr;
84pub use internal::TryFromVal;
85pub use internal::TryIntoVal;
86pub use internal::Val;
87pub use internal::VecObject;
88
89pub trait IntoVal<E: internal::Env, T> {
90 fn into_val(&self, e: &E) -> T;
91}
92
93pub trait FromVal<E: internal::Env, T> {
94 fn from_val(e: &E, v: &T) -> Self;
95}
96
97impl<E: internal::Env, T, U> FromVal<E, T> for U
98where
99 U: TryFromVal<E, T>,
100{
101 fn from_val(e: &E, v: &T) -> Self {
102 U::try_from_val(e, v).unwrap_optimized()
103 }
104}
105
106impl<E: internal::Env, T, U> IntoVal<E, T> for U
107where
108 T: FromVal<E, Self>,
109{
110 fn into_val(&self, e: &E) -> T {
111 T::from_val(e, self)
112 }
113}
114
115use crate::auth::InvokerContractAuthEntry;
116use crate::unwrap::UnwrapInfallible;
117use crate::unwrap::UnwrapOptimized;
118use crate::InvokeError;
119use crate::{
120 crypto::Crypto, deploy::Deployer, events::Events, ledger::Ledger, logs::Logs, prng::Prng,
121 storage::Storage, Address, Vec,
122};
123use internal::{
124 AddressObject, Bool, BytesObject, DurationObject, I128Object, I256Object, I256Val, I64Object,
125 StorageType, StringObject, Symbol, SymbolObject, TimepointObject, U128Object, U256Object,
126 U256Val, U32Val, U64Object, U64Val, Void,
127};
128
129#[doc(hidden)]
130#[derive(Clone)]
131pub struct MaybeEnv {
132 maybe_env_impl: internal::MaybeEnvImpl,
133 #[cfg(any(test, feature = "testutils"))]
134 test_state: Option<EnvTestState>,
135}
136
137#[cfg(target_family = "wasm")]
138impl TryFrom<MaybeEnv> for Env {
139 type Error = Infallible;
140
141 fn try_from(_value: MaybeEnv) -> Result<Self, Self::Error> {
142 Ok(Env {
143 env_impl: internal::EnvImpl {},
144 })
145 }
146}
147
148impl Default for MaybeEnv {
149 fn default() -> Self {
150 Self::none()
151 }
152}
153
154#[cfg(target_family = "wasm")]
155impl MaybeEnv {
156 // separate function to be const
157 pub const fn none() -> Self {
158 Self {
159 maybe_env_impl: internal::EnvImpl {},
160 }
161 }
162}
163
164#[cfg(not(target_family = "wasm"))]
165impl MaybeEnv {
166 // separate function to be const
167 pub const fn none() -> Self {
168 Self {
169 maybe_env_impl: None,
170 #[cfg(any(test, feature = "testutils"))]
171 test_state: None,
172 }
173 }
174}
175
176#[cfg(target_family = "wasm")]
177impl From<Env> for MaybeEnv {
178 fn from(value: Env) -> Self {
179 MaybeEnv {
180 maybe_env_impl: value.env_impl,
181 }
182 }
183}
184
185#[cfg(not(target_family = "wasm"))]
186impl TryFrom<MaybeEnv> for Env {
187 type Error = ConversionError;
188
189 fn try_from(value: MaybeEnv) -> Result<Self, Self::Error> {
190 if let Some(env_impl) = value.maybe_env_impl {
191 Ok(Env {
192 env_impl,
193 #[cfg(any(test, feature = "testutils"))]
194 test_state: value.test_state.unwrap_or_default(),
195 })
196 } else {
197 Err(ConversionError)
198 }
199 }
200}
201
202#[cfg(not(target_family = "wasm"))]
203impl From<Env> for MaybeEnv {
204 fn from(value: Env) -> Self {
205 MaybeEnv {
206 maybe_env_impl: Some(value.env_impl.clone()),
207 #[cfg(any(test, feature = "testutils"))]
208 test_state: Some(value.test_state.clone()),
209 }
210 }
211}
212
213/// The [Env] type provides access to the environment the contract is executing
214/// within.
215///
216/// The [Env] provides access to information about the currently executing
217/// contract, who invoked it, contract data, functions for signing, hashing,
218/// etc.
219///
220/// Most types require access to an [Env] to be constructed or converted.
221#[derive(Clone)]
222pub struct Env {
223 env_impl: internal::EnvImpl,
224 #[cfg(any(test, feature = "testutils"))]
225 test_state: EnvTestState,
226}
227
228impl Default for Env {
229 #[cfg(not(any(test, feature = "testutils")))]
230 fn default() -> Self {
231 Self {
232 env_impl: Default::default(),
233 }
234 }
235
236 #[cfg(any(test, feature = "testutils"))]
237 fn default() -> Self {
238 Self::new_with_config(EnvTestConfig::default())
239 }
240}
241
242#[cfg(any(test, feature = "testutils"))]
243#[derive(Clone, Default)]
244struct EnvTestState {
245 config: EnvTestConfig,
246 generators: Rc<RefCell<Generators>>,
247 auth_snapshot: Rc<RefCell<AuthSnapshot>>,
248 snapshot: Option<Rc<LedgerSnapshot>>,
249}
250
251/// Config for changing the default behavior of the Env when used in tests.
252#[cfg(any(test, feature = "testutils"))]
253#[derive(Clone)]
254pub struct EnvTestConfig {
255 /// Capture a test snapshot when the Env is dropped, causing a test snapshot
256 /// JSON file to be written to disk when the Env is no longer referenced.
257 /// Defaults to true.
258 pub capture_snapshot_at_drop: bool,
259}
260
261#[cfg(any(test, feature = "testutils"))]
262impl Default for EnvTestConfig {
263 fn default() -> Self {
264 Self {
265 capture_snapshot_at_drop: true,
266 }
267 }
268}
269
270impl Env {
271 /// Panic with the given error.
272 ///
273 /// Equivalent to `panic!`, but with an error value instead of a string.
274 #[doc(hidden)]
275 #[inline(always)]
276 pub fn panic_with_error(&self, error: impl Into<internal::Error>) -> ! {
277 _ = internal::Env::fail_with_error(self, error.into());
278 #[cfg(target_family = "wasm")]
279 core::arch::wasm32::unreachable();
280 #[cfg(not(target_family = "wasm"))]
281 unreachable!();
282 }
283
284 /// Get a [Storage] for accessing and updating persistent data owned by the
285 /// currently executing contract.
286 #[inline(always)]
287 pub fn storage(&self) -> Storage {
288 Storage::new(self)
289 }
290
291 /// Get [Events] for publishing events associated with the
292 /// currently executing contract.
293 #[inline(always)]
294 pub fn events(&self) -> Events {
295 Events::new(self)
296 }
297
298 /// Get a [Ledger] for accessing the current ledger.
299 #[inline(always)]
300 pub fn ledger(&self) -> Ledger {
301 Ledger::new(self)
302 }
303
304 /// Get a deployer for deploying contracts.
305 #[inline(always)]
306 pub fn deployer(&self) -> Deployer {
307 Deployer::new(self)
308 }
309
310 /// Get a [Crypto] for accessing the current cryptographic functions.
311 #[inline(always)]
312 pub fn crypto(&self) -> Crypto {
313 Crypto::new(self)
314 }
315
316 /// # ⚠️ Hazardous Materials
317 ///
318 /// Get a [CryptoHazmat][crate::crypto::CryptoHazmat] for accessing the
319 /// cryptographic functions that are not generally recommended. Using them
320 /// incorrectly can introduce security vulnerabilities. Use [Crypto] if
321 /// possible.
322 #[cfg(any(test, feature = "hazmat"))]
323 #[cfg_attr(feature = "docs", doc(cfg(feature = "hazmat")))]
324 #[inline(always)]
325 pub fn crypto_hazmat(&self) -> crate::crypto::CryptoHazmat {
326 crate::crypto::CryptoHazmat::new(self)
327 }
328
329 /// Get a [Prng] for accessing the current functions which provide pseudo-randomness.
330 ///
331 /// # Warning
332 ///
333 /// **The pseudo-random generator returned is not suitable for
334 /// security-sensitive work.**
335 #[inline(always)]
336 pub fn prng(&self) -> Prng {
337 Prng::new(self)
338 }
339
340 /// Get the Address object corresponding to the current executing contract.
341 pub fn current_contract_address(&self) -> Address {
342 let address = internal::Env::get_current_contract_address(self).unwrap_infallible();
343 unsafe { Address::unchecked_new(self.clone(), address) }
344 }
345
346 #[doc(hidden)]
347 pub(crate) fn require_auth_for_args(&self, address: &Address, args: Vec<Val>) {
348 internal::Env::require_auth_for_args(self, address.to_object(), args.to_object())
349 .unwrap_infallible();
350 }
351
352 #[doc(hidden)]
353 pub(crate) fn require_auth(&self, address: &Address) {
354 internal::Env::require_auth(self, address.to_object()).unwrap_infallible();
355 }
356
357 /// Invokes a function of a contract that is registered in the [Env].
358 ///
359 /// # Panics
360 ///
361 /// Will panic if the `contract_id` does not match a registered contract,
362 /// `func` does not match a function of the referenced contract, or the
363 /// number of `args` do not match the argument count of the referenced
364 /// contract function.
365 ///
366 /// Will panic if the contract that is invoked fails or aborts in anyway.
367 ///
368 /// Will panic if the value returned from the contract cannot be converted
369 /// into the type `T`.
370 pub fn invoke_contract<T>(
371 &self,
372 contract_address: &Address,
373 func: &crate::Symbol,
374 args: Vec<Val>,
375 ) -> T
376 where
377 T: TryFromVal<Env, Val>,
378 {
379 let rv = internal::Env::call(
380 self,
381 contract_address.to_object(),
382 func.to_symbol_val(),
383 args.to_object(),
384 )
385 .unwrap_infallible();
386 T::try_from_val(self, &rv)
387 .map_err(|_| ConversionError)
388 .unwrap()
389 }
390
391 /// Invokes a function of a contract that is registered in the [Env],
392 /// returns an error if the invocation fails for any reason.
393 pub fn try_invoke_contract<T, E>(
394 &self,
395 contract_address: &Address,
396 func: &crate::Symbol,
397 args: Vec<Val>,
398 ) -> Result<Result<T, T::Error>, Result<E, InvokeError>>
399 where
400 T: TryFromVal<Env, Val>,
401 E: TryFrom<Error>,
402 E::Error: Into<InvokeError>,
403 {
404 let rv = internal::Env::try_call(
405 self,
406 contract_address.to_object(),
407 func.to_symbol_val(),
408 args.to_object(),
409 )
410 .unwrap_infallible();
411 match internal::Error::try_from_val(self, &rv) {
412 Ok(err) => Err(E::try_from(err).map_err(Into::into)),
413 Err(ConversionError) => Ok(T::try_from_val(self, &rv)),
414 }
415 }
416
417 /// Authorizes sub-contract calls on behalf of the current contract.
418 ///
419 /// All the direct calls that the current contract performs are always
420 /// considered to have been authorized. This is only needed to authorize
421 /// deeper calls that originate from the next contract call from the current
422 /// contract.
423 ///
424 /// For example, if the contract A calls contract B, contract
425 /// B calls contract C and contract C calls `A.require_auth()`, then an
426 /// entry corresponding to C call has to be passed in `auth_entries`. It
427 /// doesn't matter if contract B called `require_auth` or not. If contract A
428 /// calls contract B again, then `authorize_as_current_contract` has to be
429 /// called again with the respective entries.
430 ///
431 ///
432 pub fn authorize_as_current_contract(&self, auth_entries: Vec<InvokerContractAuthEntry>) {
433 internal::Env::authorize_as_curr_contract(self, auth_entries.to_object())
434 .unwrap_infallible();
435 }
436
437 /// Get the [Logs] for logging debug events.
438 #[inline(always)]
439 #[deprecated(note = "use [Env::logs]")]
440 #[doc(hidden)]
441 pub fn logger(&self) -> Logs {
442 self.logs()
443 }
444
445 /// Get the [Logs] for logging debug events.
446 #[inline(always)]
447 pub fn logs(&self) -> Logs {
448 Logs::new(self)
449 }
450}
451
452#[doc(hidden)]
453#[cfg(not(target_family = "wasm"))]
454impl Env {
455 pub(crate) fn is_same_env(&self, other: &Self) -> bool {
456 self.env_impl.check_same_env(&other.env_impl).is_ok()
457 }
458}
459
460#[cfg(any(test, feature = "testutils"))]
461use crate::testutils::cost_estimate::CostEstimate;
462#[cfg(any(test, feature = "testutils"))]
463use crate::{
464 auth,
465 testutils::{
466 budget::Budget, Address as _, AuthSnapshot, AuthorizedInvocation, ContractFunctionSet,
467 EventsSnapshot, Generators, Ledger as _, MockAuth, MockAuthContract, Register, Snapshot,
468 StellarAssetContract, StellarAssetIssuer,
469 },
470 Bytes, BytesN, ConstructorArgs,
471};
472#[cfg(any(test, feature = "testutils"))]
473use core::{cell::RefCell, cell::RefMut};
474#[cfg(any(test, feature = "testutils"))]
475use internal::ContractInvocationEvent;
476#[cfg(any(test, feature = "testutils"))]
477use soroban_ledger_snapshot::LedgerSnapshot;
478#[cfg(any(test, feature = "testutils"))]
479use std::{path::Path, rc::Rc};
480#[cfg(any(test, feature = "testutils"))]
481use xdr::{LedgerEntry, LedgerKey, LedgerKeyContractData, SorobanAuthorizationEntry};
482
483#[cfg(any(test, feature = "testutils"))]
484#[cfg_attr(feature = "docs", doc(cfg(feature = "testutils")))]
485impl Env {
486 #[doc(hidden)]
487 pub fn in_contract(&self) -> bool {
488 self.env_impl.has_frame().unwrap()
489 }
490
491 #[doc(hidden)]
492 pub fn host(&self) -> &internal::Host {
493 &self.env_impl
494 }
495
496 #[doc(hidden)]
497 pub(crate) fn with_generator<T>(&self, f: impl FnOnce(RefMut<'_, Generators>) -> T) -> T {
498 f((*self.test_state.generators).borrow_mut())
499 }
500
501 /// Create an Env with the test config.
502 pub fn new_with_config(config: EnvTestConfig) -> Env {
503 struct EmptySnapshotSource();
504
505 impl internal::storage::SnapshotSource for EmptySnapshotSource {
506 fn get(
507 &self,
508 _key: &Rc<xdr::LedgerKey>,
509 ) -> Result<Option<(Rc<xdr::LedgerEntry>, Option<u32>)>, soroban_env_host::HostError>
510 {
511 Ok(None)
512 }
513 }
514
515 let rf = Rc::new(EmptySnapshotSource());
516 let info = internal::LedgerInfo {
517 protocol_version: 22,
518 sequence_number: 0,
519 timestamp: 0,
520 network_id: [0; 32],
521 base_reserve: 0,
522 min_persistent_entry_ttl: 4096,
523 min_temp_entry_ttl: 16,
524 max_entry_ttl: 6_312_000,
525 };
526
527 Env::new_for_testutils(config, rf, None, info, None)
528 }
529
530 /// Change the test config of an Env.
531 pub fn set_config(&mut self, config: EnvTestConfig) {
532 self.test_state.config = config;
533 }
534
535 /// Used by multiple constructors to configure test environments consistently.
536 fn new_for_testutils(
537 config: EnvTestConfig,
538 recording_footprint: Rc<dyn internal::storage::SnapshotSource>,
539 generators: Option<Rc<RefCell<Generators>>>,
540 ledger_info: internal::LedgerInfo,
541 snapshot: Option<Rc<LedgerSnapshot>>,
542 ) -> Env {
543 let storage = internal::storage::Storage::with_recording_footprint(recording_footprint);
544 let budget = internal::budget::Budget::default();
545 let env_impl = internal::EnvImpl::with_storage_and_budget(storage, budget.clone());
546 env_impl
547 .set_source_account(xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(
548 xdr::Uint256([0; 32]),
549 )))
550 .unwrap();
551 env_impl
552 .set_diagnostic_level(internal::DiagnosticLevel::Debug)
553 .unwrap();
554 env_impl.set_base_prng_seed([0; 32]).unwrap();
555
556 let auth_snapshot = Rc::new(RefCell::new(AuthSnapshot::default()));
557 let auth_snapshot_in_hook = auth_snapshot.clone();
558 env_impl
559 .set_top_contract_invocation_hook(Some(Rc::new(move |host, event| {
560 match event {
561 ContractInvocationEvent::Start => {}
562 ContractInvocationEvent::Finish => {
563 let new_auths = host
564 .get_authenticated_authorizations()
565 // If an error occurs getting the authenticated authorizations
566 // it means that no auth has occurred.
567 .unwrap();
568 (*auth_snapshot_in_hook).borrow_mut().0.push(new_auths);
569 }
570 }
571 })))
572 .unwrap();
573 env_impl.enable_invocation_metering();
574
575 let env = Env {
576 env_impl,
577 test_state: EnvTestState {
578 config,
579 generators: generators.unwrap_or_default(),
580 snapshot,
581 auth_snapshot,
582 },
583 };
584
585 env.ledger().set(ledger_info);
586
587 env
588 }
589
590 /// Returns the resources metered during the last top level contract
591 /// invocation.
592 ///
593 /// In order to get non-`None` results, `enable_invocation_metering` has to
594 /// be called and at least one invocation has to happen after that.
595 ///
596 /// Take the return value with a grain of salt. The returned resources mostly
597 /// correspond only to the operations that have happened during the host
598 /// invocation, i.e. this won't try to simulate the work that happens in
599 /// production scenarios (e.g. certain XDR rountrips). This also doesn't try
600 /// to model resources related to the transaction size.
601 ///
602 /// The returned value is as useful as the preceding setup, e.g. if a test
603 /// contract is used instead of a Wasm contract, all the costs related to
604 /// VM instantiation and execution, as well as Wasm reads/rent bumps will be
605 /// missed.
606 ///
607 /// While the resource metering may be useful for contract optimization,
608 /// keep in mind that resource and fee estimation may be imprecise. Use
609 /// simulation with RPC in order to get the exact resources for submitting
610 /// the transactions to the network.
611 pub fn cost_estimate(&self) -> CostEstimate {
612 CostEstimate::new(self.clone())
613 }
614
615 /// Register a contract with the [Env] for testing.
616 ///
617 /// Pass the contract type when the contract is defined in the current crate
618 /// and is being registered natively. Pass the contract wasm bytes when the
619 /// contract has been loaded as wasm.
620 ///
621 /// Pass the arguments for the contract's constructor, or `()` if none. For
622 /// contracts with a constructor, use the contract's generated `Args` type
623 /// to construct the arguments with the appropropriate types for invoking
624 /// the constructor during registration.
625 ///
626 /// Returns the address of the registered contract that is the same as the
627 /// contract id passed in.
628 ///
629 /// If you need to specify the address the contract should be registered at,
630 /// use [`Env::register_at`].
631 ///
632 /// ### Examples
633 /// Register a contract defined in the current crate, by specifying the type
634 /// name:
635 /// ```
636 /// use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, BytesN, Env, Symbol};
637 ///
638 /// #[contract]
639 /// pub struct Contract;
640 ///
641 /// #[contractimpl]
642 /// impl Contract {
643 /// pub fn __constructor(_env: Env, _input: u32) {
644 /// }
645 /// }
646 ///
647 /// #[test]
648 /// fn test() {
649 /// # }
650 /// # fn main() {
651 /// let env = Env::default();
652 /// let contract_id = env.register(Contract, ContractArgs::__constructor(&123,));
653 /// }
654 /// ```
655 /// Register a contract wasm, by specifying the wasm bytes:
656 /// ```
657 /// use soroban_sdk::{testutils::Address as _, Address, BytesN, Env};
658 ///
659 /// const WASM: &[u8] = include_bytes!("../doctest_fixtures/contract.wasm");
660 ///
661 /// #[test]
662 /// fn test() {
663 /// # }
664 /// # fn main() {
665 /// let env = Env::default();
666 /// let contract_id = env.register(WASM, ());
667 /// }
668 /// ```
669 pub fn register<'a, C, A>(&self, contract: C, constructor_args: A) -> Address
670 where
671 C: Register,
672 A: ConstructorArgs,
673 {
674 contract.register(self, None, constructor_args)
675 }
676
677 /// Register a contract with the [Env] for testing.
678 ///
679 /// Passing a contract ID for the first arguments registers the contract
680 /// with that contract ID.
681 ///
682 /// Registering a contract that is already registered replaces it.
683 /// Use re-registration with caution as it does not exist in the real
684 /// (on-chain) environment. Specifically, the new contract's constructor
685 /// will be called again during re-registration. That behavior only exists
686 /// for this test utility and is not reproducible on-chain, where contract
687 /// Wasm updates don't cause constructor to be called.
688 ///
689 /// Pass the contract type when the contract is defined in the current crate
690 /// and is being registered natively. Pass the contract wasm bytes when the
691 /// contract has been loaded as wasm.
692 ///
693 /// Returns the address of the registered contract that is the same as the
694 /// contract id passed in.
695 ///
696 /// ### Examples
697 /// Register a contract defined in the current crate, by specifying the type
698 /// name:
699 /// ```
700 /// use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, BytesN, Env, Symbol};
701 ///
702 /// #[contract]
703 /// pub struct Contract;
704 ///
705 /// #[contractimpl]
706 /// impl Contract {
707 /// pub fn __constructor(_env: Env, _input: u32) {
708 /// }
709 /// }
710 ///
711 /// #[test]
712 /// fn test() {
713 /// # }
714 /// # fn main() {
715 /// let env = Env::default();
716 /// let contract_id = Address::generate(&env);
717 /// env.register_at(&contract_id, Contract, (123_u32,));
718 /// }
719 /// ```
720 /// Register a contract wasm, by specifying the wasm bytes:
721 /// ```
722 /// use soroban_sdk::{testutils::Address as _, Address, BytesN, Env};
723 ///
724 /// const WASM: &[u8] = include_bytes!("../doctest_fixtures/contract.wasm");
725 ///
726 /// #[test]
727 /// fn test() {
728 /// # }
729 /// # fn main() {
730 /// let env = Env::default();
731 /// let contract_id = Address::generate(&env);
732 /// env.register_at(&contract_id, WASM, ());
733 /// }
734 /// ```
735 pub fn register_at<C, A>(
736 &self,
737 contract_id: &Address,
738 contract: C,
739 constructor_args: A,
740 ) -> Address
741 where
742 C: Register,
743 A: ConstructorArgs,
744 {
745 contract.register(self, contract_id, constructor_args)
746 }
747
748 /// Register a contract with the [Env] for testing.
749 ///
750 /// Passing a contract ID for the first arguments registers the contract
751 /// with that contract ID. Providing `None` causes the Env to generate a new
752 /// contract ID that is assigned to the contract.
753 ///
754 /// If a contract has a constructor defined, then it will be called with
755 /// no arguments. If a constructor takes arguments, use `register`.
756 ///
757 /// Registering a contract that is already registered replaces it.
758 /// Use re-registration with caution as it does not exist in the real
759 /// (on-chain) environment. Specifically, the new contract's constructor
760 /// will be called again during re-registration. That behavior only exists
761 /// for this test utility and is not reproducible on-chain, where contract
762 /// Wasm updates don't cause constructor to be called.
763 ///
764 /// Returns the address of the registered contract.
765 ///
766 /// ### Examples
767 /// ```
768 /// use soroban_sdk::{contract, contractimpl, BytesN, Env, Symbol};
769 ///
770 /// #[contract]
771 /// pub struct HelloContract;
772 ///
773 /// #[contractimpl]
774 /// impl HelloContract {
775 /// pub fn hello(env: Env, recipient: Symbol) -> Symbol {
776 /// todo!()
777 /// }
778 /// }
779 ///
780 /// #[test]
781 /// fn test() {
782 /// # }
783 /// # fn main() {
784 /// let env = Env::default();
785 /// let contract_id = env.register_contract(None, HelloContract);
786 /// }
787 /// ```
788 #[deprecated(note = "use `register`")]
789 pub fn register_contract<'a, T: ContractFunctionSet + 'static>(
790 &self,
791 contract_id: impl Into<Option<&'a Address>>,
792 contract: T,
793 ) -> Address {
794 self.register_contract_with_constructor(contract_id, contract, ())
795 }
796
797 /// Register a contract with the [Env] for testing.
798 ///
799 /// This acts the in the same fashion as `register_contract`, but allows
800 /// passing arguments to the contract's constructor.
801 ///
802 /// Passing a contract ID for the first arguments registers the contract
803 /// with that contract ID. Providing `None` causes the Env to generate a new
804 /// contract ID that is assigned to the contract.
805 ///
806 /// Registering a contract that is already registered replaces it.
807 /// Use re-registration with caution as it does not exist in the real
808 /// (on-chain) environment. Specifically, the new contract's constructor
809 /// will be called again during re-registration. That behavior only exists
810 /// for this test utility and is not reproducible on-chain, where contract
811 /// Wasm updates don't cause constructor to be called.
812 ///
813 /// Returns the address of the registered contract.
814 pub(crate) fn register_contract_with_constructor<
815 'a,
816 T: ContractFunctionSet + 'static,
817 A: ConstructorArgs,
818 >(
819 &self,
820 contract_id: impl Into<Option<&'a Address>>,
821 contract: T,
822 constructor_args: A,
823 ) -> Address {
824 struct InternalContractFunctionSet<T: ContractFunctionSet>(pub(crate) T);
825 impl<T: ContractFunctionSet> internal::ContractFunctionSet for InternalContractFunctionSet<T> {
826 fn call(
827 &self,
828 func: &Symbol,
829 env_impl: &internal::EnvImpl,
830 args: &[Val],
831 ) -> Option<Val> {
832 let env = Env {
833 env_impl: env_impl.clone(),
834 test_state: Default::default(),
835 };
836 self.0.call(
837 crate::Symbol::try_from_val(&env, func)
838 .unwrap_infallible()
839 .to_string()
840 .as_str(),
841 env,
842 args,
843 )
844 }
845 }
846
847 let contract_id = if let Some(contract_id) = contract_id.into() {
848 contract_id.clone()
849 } else {
850 Address::generate(self)
851 };
852 self.env_impl
853 .register_test_contract_with_constructor(
854 contract_id.to_object(),
855 Rc::new(InternalContractFunctionSet(contract)),
856 constructor_args.into_val(self).to_object(),
857 )
858 .unwrap();
859 contract_id
860 }
861
862 /// Register a contract in a Wasm file with the [Env] for testing.
863 ///
864 /// Passing a contract ID for the first arguments registers the contract
865 /// with that contract ID. Providing `None` causes the Env to generate a new
866 /// contract ID that is assigned to the contract.
867 ///
868 /// Registering a contract that is already registered replaces it.
869 /// Use re-registration with caution as it does not exist in the real
870 /// (on-chain) environment. Specifically, the new contract's constructor
871 /// will be called again during re-registration. That behavior only exists
872 /// for this test utility and is not reproducible on-chain, where contract
873 /// Wasm updates don't cause constructor to be called.
874 ///
875 /// Returns the address of the registered contract.
876 ///
877 /// ### Examples
878 /// ```
879 /// use soroban_sdk::{BytesN, Env};
880 ///
881 /// const WASM: &[u8] = include_bytes!("../doctest_fixtures/contract.wasm");
882 ///
883 /// #[test]
884 /// fn test() {
885 /// # }
886 /// # fn main() {
887 /// let env = Env::default();
888 /// env.register_contract_wasm(None, WASM);
889 /// }
890 /// ```
891 #[deprecated(note = "use `register`")]
892 pub fn register_contract_wasm<'a>(
893 &self,
894 contract_id: impl Into<Option<&'a Address>>,
895 contract_wasm: impl IntoVal<Env, Bytes>,
896 ) -> Address {
897 let wasm_hash: BytesN<32> = self.deployer().upload_contract_wasm(contract_wasm);
898 self.register_contract_with_optional_contract_id_and_executable(
899 contract_id,
900 xdr::ContractExecutable::Wasm(xdr::Hash(wasm_hash.into())),
901 crate::vec![&self],
902 )
903 }
904
905 /// Register a contract in a Wasm file with the [Env] for testing.
906 ///
907 /// This acts the in the same fashion as `register_contract`, but allows
908 /// passing arguments to the contract's constructor.
909 ///
910 /// Passing a contract ID for the first arguments registers the contract
911 /// with that contract ID. Providing `None` causes the Env to generate a new
912 /// contract ID that is assigned to the contract.
913 ///
914 /// Registering a contract that is already registered replaces it.
915 /// Use re-registration with caution as it does not exist in the real
916 /// (on-chain) environment. Specifically, the new contract's constructor
917 /// will be called again during re-registration. That behavior only exists
918 /// for this test utility and is not reproducible on-chain, where contract
919 /// Wasm updates don't cause constructor to be called.
920 ///
921 /// Returns the address of the registered contract.
922 pub(crate) fn register_contract_wasm_with_constructor<'a>(
923 &self,
924 contract_id: impl Into<Option<&'a Address>>,
925 contract_wasm: impl IntoVal<Env, Bytes>,
926 constructor_args: impl ConstructorArgs,
927 ) -> Address {
928 let wasm_hash: BytesN<32> = self.deployer().upload_contract_wasm(contract_wasm);
929 self.register_contract_with_optional_contract_id_and_executable(
930 contract_id,
931 xdr::ContractExecutable::Wasm(xdr::Hash(wasm_hash.into())),
932 constructor_args.into_val(self),
933 )
934 }
935
936 /// Register the built-in Stellar Asset Contract with provided admin address.
937 ///
938 /// Returns a utility struct that contains the contract ID of the registered
939 /// token contract, as well as methods to read and update issuer flags.
940 ///
941 /// The contract will wrap a randomly-generated Stellar asset. This function
942 /// is useful for using in the tests when an arbitrary token contract
943 /// instance is needed.
944 pub fn register_stellar_asset_contract_v2(&self, admin: Address) -> StellarAssetContract {
945 let issuer_pk = self.with_generator(|mut g| g.address());
946 let issuer_id = xdr::AccountId(xdr::PublicKey::PublicKeyTypeEd25519(xdr::Uint256(
947 issuer_pk.clone(),
948 )));
949
950 self.host()
951 .with_mut_storage(|storage| {
952 let k = Rc::new(xdr::LedgerKey::Account(xdr::LedgerKeyAccount {
953 account_id: issuer_id.clone(),
954 }));
955
956 if !storage.has(
957 &k,
958 soroban_env_host::budget::AsBudget::as_budget(self.host()),
959 )? {
960 let v = Rc::new(xdr::LedgerEntry {
961 data: xdr::LedgerEntryData::Account(xdr::AccountEntry {
962 account_id: issuer_id.clone(),
963 balance: 0,
964 flags: 0,
965 home_domain: Default::default(),
966 inflation_dest: None,
967 num_sub_entries: 0,
968 seq_num: xdr::SequenceNumber(0),
969 thresholds: xdr::Thresholds([1; 4]),
970 signers: xdr::VecM::default(),
971 ext: xdr::AccountEntryExt::V0,
972 }),
973 last_modified_ledger_seq: 0,
974 ext: xdr::LedgerEntryExt::V0,
975 });
976 storage.put(
977 &k,
978 &v,
979 None,
980 soroban_env_host::budget::AsBudget::as_budget(self.host()),
981 )?
982 }
983 Ok(())
984 })
985 .unwrap();
986
987 let asset = xdr::Asset::CreditAlphanum4(xdr::AlphaNum4 {
988 asset_code: xdr::AssetCode4([b'a', b'a', b'a', 0]),
989 issuer: issuer_id.clone(),
990 });
991 let create = xdr::HostFunction::CreateContract(xdr::CreateContractArgs {
992 contract_id_preimage: xdr::ContractIdPreimage::Asset(asset),
993 executable: xdr::ContractExecutable::StellarAsset,
994 });
995
996 let token_id: Address = self
997 .env_impl
998 .invoke_function(create)
999 .unwrap()
1000 .try_into_val(self)
1001 .unwrap();
1002
1003 let prev_auth_manager = self.env_impl.snapshot_auth_manager().unwrap();
1004 self.env_impl
1005 .switch_to_recording_auth_inherited_from_snapshot(&prev_auth_manager)
1006 .unwrap();
1007 self.invoke_contract::<()>(
1008 &token_id,
1009 &soroban_sdk_macros::internal_symbol_short!("set_admin"),
1010 (admin,).try_into_val(self).unwrap(),
1011 );
1012 self.env_impl.set_auth_manager(prev_auth_manager).unwrap();
1013
1014 let issuer = StellarAssetIssuer::new(self.clone(), issuer_id);
1015
1016 StellarAssetContract::new(token_id, issuer)
1017 }
1018
1019 /// Register the built-in Stellar Asset Contract with provided admin address.
1020 ///
1021 /// Returns the contract ID of the registered token contract.
1022 ///
1023 /// The contract will wrap a randomly-generated Stellar asset. This function
1024 /// is useful for using in the tests when an arbitrary token contract
1025 /// instance is needed.
1026 #[deprecated(note = "use [Env::register_stellar_asset_contract_v2]")]
1027 pub fn register_stellar_asset_contract(&self, admin: Address) -> Address {
1028 self.register_stellar_asset_contract_v2(admin).address()
1029 }
1030
1031 fn register_contract_with_optional_contract_id_and_executable<'a>(
1032 &self,
1033 contract_id: impl Into<Option<&'a Address>>,
1034 executable: xdr::ContractExecutable,
1035 constructor_args: Vec<Val>,
1036 ) -> Address {
1037 if let Some(contract_id) = contract_id.into() {
1038 self.register_contract_with_contract_id_and_executable(
1039 contract_id,
1040 executable,
1041 constructor_args,
1042 );
1043 contract_id.clone()
1044 } else {
1045 self.register_contract_with_source(executable, constructor_args)
1046 }
1047 }
1048
1049 fn register_contract_with_source(
1050 &self,
1051 executable: xdr::ContractExecutable,
1052 constructor_args: Vec<Val>,
1053 ) -> Address {
1054 let prev_auth_manager = self.env_impl.snapshot_auth_manager().unwrap();
1055 self.env_impl
1056 .switch_to_recording_auth_inherited_from_snapshot(&prev_auth_manager)
1057 .unwrap();
1058 let args_vec: std::vec::Vec<xdr::ScVal> =
1059 constructor_args.iter().map(|v| v.into_val(self)).collect();
1060 let contract_id: Address = self
1061 .env_impl
1062 .invoke_function(xdr::HostFunction::CreateContractV2(
1063 xdr::CreateContractArgsV2 {
1064 contract_id_preimage: xdr::ContractIdPreimage::Address(
1065 xdr::ContractIdPreimageFromAddress {
1066 address: xdr::ScAddress::Contract(xdr::Hash(
1067 self.with_generator(|mut g| g.address()),
1068 )),
1069 salt: xdr::Uint256([0; 32]),
1070 },
1071 ),
1072 executable,
1073 constructor_args: args_vec.try_into().unwrap(),
1074 },
1075 ))
1076 .unwrap()
1077 .try_into_val(self)
1078 .unwrap();
1079
1080 self.env_impl.set_auth_manager(prev_auth_manager).unwrap();
1081
1082 contract_id
1083 }
1084
1085 /// Set authorizations and signatures in the environment which will be
1086 /// consumed by contracts when they invoke [`Address::require_auth`] or
1087 /// [`Address::require_auth_for_args`] functions.
1088 ///
1089 /// Requires valid signatures for the authorization to be successful.
1090 ///
1091 /// This function can also be called on contract clients.
1092 ///
1093 /// To mock auth for testing, without requiring valid signatures, use
1094 /// [`mock_all_auths`][Self::mock_all_auths] or
1095 /// [`mock_auths`][Self::mock_auths]. If mocking of auths is enabled,
1096 /// calling [`set_auths`][Self::set_auths] disables any mocking.
1097 pub fn set_auths(&self, auths: &[SorobanAuthorizationEntry]) {
1098 self.env_impl
1099 .set_authorization_entries(auths.to_vec())
1100 .unwrap();
1101 }
1102
1103 /// Mock authorizations in the environment which will cause matching invokes
1104 /// of [`Address::require_auth`] and [`Address::require_auth_for_args`] to
1105 /// pass.
1106 ///
1107 /// This function can also be called on contract clients.
1108 ///
1109 /// Authorizations not matching a mocked auth will fail.
1110 ///
1111 /// To mock all auths, use [`mock_all_auths`][Self::mock_all_auths].
1112 ///
1113 /// ### Examples
1114 /// ```
1115 /// use soroban_sdk::{contract, contractimpl, Env, Address, testutils::{Address as _, MockAuth, MockAuthInvoke}, IntoVal};
1116 ///
1117 /// #[contract]
1118 /// pub struct HelloContract;
1119 ///
1120 /// #[contractimpl]
1121 /// impl HelloContract {
1122 /// pub fn hello(env: Env, from: Address) {
1123 /// from.require_auth();
1124 /// // TODO
1125 /// }
1126 /// }
1127 ///
1128 /// #[test]
1129 /// fn test() {
1130 /// # }
1131 /// # fn main() {
1132 /// let env = Env::default();
1133 /// let contract_id = env.register(HelloContract, ());
1134 ///
1135 /// let client = HelloContractClient::new(&env, &contract_id);
1136 /// let addr = Address::generate(&env);
1137 /// client.mock_auths(&[
1138 /// MockAuth {
1139 /// address: &addr,
1140 /// invoke: &MockAuthInvoke {
1141 /// contract: &contract_id,
1142 /// fn_name: "hello",
1143 /// args: (&addr,).into_val(&env),
1144 /// sub_invokes: &[],
1145 /// },
1146 /// },
1147 /// ]).hello(&addr);
1148 /// }
1149 /// ```
1150 pub fn mock_auths(&self, auths: &[MockAuth]) {
1151 for a in auths {
1152 self.register_at(a.address, MockAuthContract, ());
1153 }
1154 let auths = auths
1155 .iter()
1156 .cloned()
1157 .map(Into::into)
1158 .collect::<std::vec::Vec<_>>();
1159 self.env_impl.set_authorization_entries(auths).unwrap();
1160 }
1161
1162 /// Mock all calls to the [`Address::require_auth`] and
1163 /// [`Address::require_auth_for_args`] functions in invoked contracts,
1164 /// having them succeed as if authorization was provided.
1165 ///
1166 /// When mocking is enabled, if the [`Address`] being authorized is the
1167 /// address of a contract, that contract's `__check_auth` function will not
1168 /// be called, and the contract does not need to exist or be registered in
1169 /// the test.
1170 ///
1171 /// When mocking is enabled, if the [`Address`] being authorized is the
1172 /// address of an account, the account does not need to exist.
1173 ///
1174 /// This function can also be called on contract clients.
1175 ///
1176 /// To disable mocking, see [`set_auths`][Self::set_auths].
1177 ///
1178 /// To access a list of auths that have occurred, see [`auths`][Self::auths].
1179 ///
1180 /// It is not currently possible to mock a subset of auths.
1181 ///
1182 /// ### Examples
1183 /// ```
1184 /// use soroban_sdk::{contract, contractimpl, Env, Address, testutils::Address as _};
1185 ///
1186 /// #[contract]
1187 /// pub struct HelloContract;
1188 ///
1189 /// #[contractimpl]
1190 /// impl HelloContract {
1191 /// pub fn hello(env: Env, from: Address) {
1192 /// from.require_auth();
1193 /// // TODO
1194 /// }
1195 /// }
1196 ///
1197 /// #[test]
1198 /// fn test() {
1199 /// # }
1200 /// # fn main() {
1201 /// let env = Env::default();
1202 /// let contract_id = env.register(HelloContract, ());
1203 ///
1204 /// env.mock_all_auths();
1205 ///
1206 /// let client = HelloContractClient::new(&env, &contract_id);
1207 /// let addr = Address::generate(&env);
1208 /// client.hello(&addr);
1209 /// }
1210 /// ```
1211 pub fn mock_all_auths(&self) {
1212 self.env_impl.switch_to_recording_auth(true).unwrap();
1213 }
1214
1215 /// A version of `mock_all_auths` that allows authorizations that are not
1216 /// present in the root invocation.
1217 ///
1218 /// Refer to `mock_all_auths` documentation for general information and
1219 /// prefer using `mock_all_auths` unless non-root authorization is required.
1220 ///
1221 /// The only difference from `mock_all_auths` is that this won't return an
1222 /// error when `require_auth` hasn't been called in the root invocation for
1223 /// any given address. This is useful to test contracts that bundle calls to
1224 /// another contract without atomicity requirements (i.e. any contract call
1225 /// can be frontrun).
1226 ///
1227 /// ### Examples
1228 /// ```
1229 /// use soroban_sdk::{contract, contractimpl, Env, Address, testutils::Address as _};
1230 ///
1231 /// #[contract]
1232 /// pub struct ContractA;
1233 ///
1234 /// #[contractimpl]
1235 /// impl ContractA {
1236 /// pub fn do_auth(env: Env, addr: Address) {
1237 /// addr.require_auth();
1238 /// }
1239 /// }
1240 /// #[contract]
1241 /// pub struct ContractB;
1242 ///
1243 /// #[contractimpl]
1244 /// impl ContractB {
1245 /// pub fn call_a(env: Env, contract_a: Address, addr: Address) {
1246 /// // Notice there is no `require_auth` call here.
1247 /// ContractAClient::new(&env, &contract_a).do_auth(&addr);
1248 /// }
1249 /// }
1250 /// #[test]
1251 /// fn test() {
1252 /// # }
1253 /// # fn main() {
1254 /// let env = Env::default();
1255 /// let contract_a = env.register(ContractA, ());
1256 /// let contract_b = env.register(ContractB, ());
1257 /// // The regular `env.mock_all_auths()` would result in the call
1258 /// // failure.
1259 /// env.mock_all_auths_allowing_non_root_auth();
1260 ///
1261 /// let client = ContractBClient::new(&env, &contract_b);
1262 /// let addr = Address::generate(&env);
1263 /// client.call_a(&contract_a, &addr);
1264 /// }
1265 /// ```
1266 pub fn mock_all_auths_allowing_non_root_auth(&self) {
1267 self.env_impl.switch_to_recording_auth(false).unwrap();
1268 }
1269
1270 /// Returns a list of authorization trees that were seen during the last
1271 /// contract or authorized host function invocation.
1272 ///
1273 /// Use this in tests to verify that the expected authorizations with the
1274 /// expected arguments are required.
1275 ///
1276 /// The return value is a vector of authorizations represented by tuples of
1277 /// `(address, AuthorizedInvocation)`. `AuthorizedInvocation` describes the
1278 /// tree of `require_auth_for_args(address, args)` from the contract
1279 /// functions (or `require_auth` with all the arguments of the function
1280 /// invocation). It also might contain the authorized host functions (
1281 /// currently CreateContract is the only such function) in case if
1282 /// corresponding host functions have been called.
1283 ///
1284 /// Refer to documentation for `AuthorizedInvocation` for detailed
1285 /// information on its contents.
1286 ///
1287 /// The order of the returned vector is defined by the order of
1288 /// [`Address::require_auth`] calls. Repeated calls to
1289 /// [`Address::require_auth`] with the same address and args in the same
1290 /// tree of contract invocations will appear only once in the vector.
1291 ///
1292 /// ### Examples
1293 /// ```
1294 /// use soroban_sdk::{contract, contractimpl, testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation}, symbol_short, Address, Symbol, Env, IntoVal};
1295 ///
1296 /// #[contract]
1297 /// pub struct Contract;
1298 ///
1299 /// #[contractimpl]
1300 /// impl Contract {
1301 /// pub fn transfer(env: Env, address: Address, amount: i128) {
1302 /// address.require_auth();
1303 /// }
1304 /// pub fn transfer2(env: Env, address: Address, amount: i128) {
1305 /// address.require_auth_for_args((amount / 2,).into_val(&env));
1306 /// }
1307 /// }
1308 ///
1309 /// #[test]
1310 /// fn test() {
1311 /// # }
1312 /// # #[cfg(feature = "testutils")]
1313 /// # fn main() {
1314 /// extern crate std;
1315 /// let env = Env::default();
1316 /// let contract_id = env.register(Contract, ());
1317 /// let client = ContractClient::new(&env, &contract_id);
1318 /// env.mock_all_auths();
1319 /// let address = Address::generate(&env);
1320 /// client.transfer(&address, &1000_i128);
1321 /// assert_eq!(
1322 /// env.auths(),
1323 /// std::vec![(
1324 /// address.clone(),
1325 /// AuthorizedInvocation {
1326 /// function: AuthorizedFunction::Contract((
1327 /// client.address.clone(),
1328 /// symbol_short!("transfer"),
1329 /// (&address, 1000_i128,).into_val(&env)
1330 /// )),
1331 /// sub_invocations: std::vec![]
1332 /// }
1333 /// )]
1334 /// );
1335 ///
1336 /// client.transfer2(&address, &1000_i128);
1337 /// assert_eq!(
1338 /// env.auths(),
1339 /// std::vec![(
1340 /// address.clone(),
1341 /// AuthorizedInvocation {
1342 /// function: AuthorizedFunction::Contract((
1343 /// client.address.clone(),
1344 /// symbol_short!("transfer2"),
1345 /// // `transfer2` requires auth for (amount / 2) == (1000 / 2) == 500.
1346 /// (500_i128,).into_val(&env)
1347 /// )),
1348 /// sub_invocations: std::vec![]
1349 /// }
1350 /// )]
1351 /// );
1352 /// }
1353 /// # #[cfg(not(feature = "testutils"))]
1354 /// # fn main() { }
1355 /// ```
1356 pub fn auths(&self) -> std::vec::Vec<(Address, AuthorizedInvocation)> {
1357 (*self.test_state.auth_snapshot)
1358 .borrow()
1359 .0
1360 .last()
1361 .cloned()
1362 .unwrap_or_default()
1363 .into_iter()
1364 .map(|(sc_addr, invocation)| {
1365 (
1366 xdr::ScVal::Address(sc_addr).try_into_val(self).unwrap(),
1367 AuthorizedInvocation::from_xdr(self, &invocation),
1368 )
1369 })
1370 .collect()
1371 }
1372
1373 /// Invokes the special `__check_auth` function of contracts that implement
1374 /// the custom account interface.
1375 ///
1376 /// `__check_auth` can't be called outside of the host-managed `require_auth`
1377 /// calls. This test utility allows testing custom account contracts without
1378 /// the need to setup complex contract call trees and enabling the enforcing
1379 /// auth on the host side.
1380 ///
1381 /// This function requires to provide the template argument for error. Use
1382 /// `soroban_sdk::Error` if `__check_auth` doesn't return a special
1383 /// contract error and use the error with `contracterror` attribute
1384 /// otherwise.
1385 ///
1386 /// ### Examples
1387 /// ```
1388 /// use soroban_sdk::{contract, contracterror, contractimpl, testutils::{Address as _, BytesN as _}, vec, auth::Context, BytesN, Env, Vec, Val};
1389 ///
1390 /// #[contracterror]
1391 /// #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
1392 /// #[repr(u32)]
1393 /// pub enum NoopAccountError {
1394 /// SomeError = 1,
1395 /// }
1396 /// #[contract]
1397 /// struct NoopAccountContract;
1398 /// #[contractimpl]
1399 /// impl NoopAccountContract {
1400 ///
1401 /// #[allow(non_snake_case)]
1402 /// pub fn __check_auth(
1403 /// _env: Env,
1404 /// _signature_payload: BytesN<32>,
1405 /// signature: Val,
1406 /// _auth_context: Vec<Context>,
1407 /// ) -> Result<(), NoopAccountError> {
1408 /// if signature.is_void() {
1409 /// Err(NoopAccountError::SomeError)
1410 /// } else {
1411 /// Ok(())
1412 /// }
1413 /// }
1414 /// }
1415 /// #[test]
1416 /// fn test() {
1417 /// # }
1418 /// # fn main() {
1419 /// let e: Env = Default::default();
1420 /// let account_contract = NoopAccountContractClient::new(&e, &e.register(NoopAccountContract, ()));
1421 /// // Non-succesful call of `__check_auth` with a `contracterror` error.
1422 /// assert_eq!(
1423 /// e.try_invoke_contract_check_auth::<NoopAccountError>(
1424 /// &account_contract.address,
1425 /// &BytesN::from_array(&e, &[0; 32]),
1426 /// ().into(),
1427 /// &vec![&e],
1428 /// ),
1429 /// // The inner `Result` is for conversion error and will be Ok
1430 /// // as long as a valid error type used.
1431 /// Err(Ok(NoopAccountError::SomeError))
1432 /// );
1433 /// // Successful call of `__check_auth` with a `soroban_sdk::InvokeError`
1434 /// // error - this should be compatible with any error type.
1435 /// assert_eq!(
1436 /// e.try_invoke_contract_check_auth::<soroban_sdk::InvokeError>(
1437 /// &account_contract.address,
1438 /// &BytesN::from_array(&e, &[0; 32]),
1439 /// 0_i32.into(),
1440 /// &vec![&e],
1441 /// ),
1442 /// Ok(())
1443 /// );
1444 /// }
1445 /// ```
1446 pub fn try_invoke_contract_check_auth<E>(
1447 &self,
1448 contract: &Address,
1449 signature_payload: &BytesN<32>,
1450 signature: Val,
1451 auth_context: &Vec<auth::Context>,
1452 ) -> Result<(), Result<E, InvokeError>>
1453 where
1454 E: TryFrom<Error>,
1455 E::Error: Into<InvokeError>,
1456 {
1457 let args = Vec::from_array(
1458 self,
1459 [signature_payload.to_val(), signature, auth_context.to_val()],
1460 );
1461 let res = self
1462 .host()
1463 .call_account_contract_check_auth(contract.to_object(), args.to_object());
1464 match res {
1465 Ok(rv) => Ok(rv.into_val(self)),
1466 Err(e) => Err(e.error.try_into().map_err(Into::into)),
1467 }
1468 }
1469
1470 fn register_contract_with_contract_id_and_executable(
1471 &self,
1472 contract_address: &Address,
1473 executable: xdr::ContractExecutable,
1474 constructor_args: Vec<Val>,
1475 ) {
1476 let contract_id = contract_address.contract_id();
1477 let data_key = xdr::ScVal::LedgerKeyContractInstance;
1478 let key = Rc::new(LedgerKey::ContractData(LedgerKeyContractData {
1479 contract: xdr::ScAddress::Contract(contract_id.clone()),
1480 key: data_key.clone(),
1481 durability: xdr::ContractDataDurability::Persistent,
1482 }));
1483
1484 let instance = xdr::ScContractInstance {
1485 executable,
1486 storage: Default::default(),
1487 };
1488
1489 let entry = Rc::new(LedgerEntry {
1490 ext: xdr::LedgerEntryExt::V0,
1491 last_modified_ledger_seq: 0,
1492 data: xdr::LedgerEntryData::ContractData(xdr::ContractDataEntry {
1493 contract: xdr::ScAddress::Contract(contract_id.clone()),
1494 key: data_key,
1495 val: xdr::ScVal::ContractInstance(instance),
1496 durability: xdr::ContractDataDurability::Persistent,
1497 ext: xdr::ExtensionPoint::V0,
1498 }),
1499 });
1500 let live_until_ledger = self.ledger().sequence() + 1;
1501 self.env_impl
1502 .with_mut_storage(|storage| {
1503 storage.put(
1504 &key,
1505 &entry,
1506 Some(live_until_ledger),
1507 soroban_env_host::budget::AsBudget::as_budget(self.host()),
1508 )
1509 })
1510 .unwrap();
1511 self.env_impl
1512 .call_constructor_for_stored_contract_unsafe(&contract_id, constructor_args.to_object())
1513 .unwrap();
1514 }
1515
1516 /// Run the function as if executed by the given contract ID.
1517 ///
1518 /// Used to write or read contract data, or take other actions in tests for
1519 /// setting up tests or asserting on internal state.
1520 pub fn as_contract<T>(&self, id: &Address, f: impl FnOnce() -> T) -> T {
1521 let id: [u8; 32] = id.contract_id().into();
1522 let func = Symbol::from_small_str("");
1523 let mut t: Option<T> = None;
1524 self.env_impl
1525 .with_test_contract_frame(id.into(), func, || {
1526 t = Some(f());
1527 Ok(().into())
1528 })
1529 .unwrap();
1530 t.unwrap()
1531 }
1532
1533 /// Creates a new Env loaded with the [`Snapshot`].
1534 ///
1535 /// The ledger info and state in the snapshot are loaded into the Env.
1536 ///
1537 /// Events, as an output source only, are not loaded into the Env.
1538 pub fn from_snapshot(s: Snapshot) -> Env {
1539 Env::new_for_testutils(
1540 EnvTestConfig::default(),
1541 Rc::new(s.ledger.clone()),
1542 Some(Rc::new(RefCell::new(s.generators))),
1543 s.ledger.ledger_info(),
1544 Some(Rc::new(s.ledger.clone())),
1545 )
1546 }
1547
1548 /// Creates a new Env loaded with the ledger snapshot loaded from the file.
1549 ///
1550 /// The ledger info and state in the snapshot are loaded into the Env.
1551 ///
1552 /// Events, as an output source only, are not loaded into the Env.
1553 ///
1554 /// ### Panics
1555 ///
1556 /// If there is any error reading the file.
1557 pub fn from_snapshot_file(p: impl AsRef<Path>) -> Env {
1558 Self::from_snapshot(Snapshot::read_file(p).unwrap())
1559 }
1560
1561 /// Create a snapshot from the Env's current state.
1562 pub fn to_snapshot(&self) -> Snapshot {
1563 Snapshot {
1564 generators: (*self.test_state.generators).borrow().clone(),
1565 auth: (*self.test_state.auth_snapshot).borrow().clone(),
1566 ledger: self.to_ledger_snapshot(),
1567 events: self.to_events_snapshot(),
1568 }
1569 }
1570
1571 /// Create a snapshot file from the Env's current state.
1572 ///
1573 /// ### Panics
1574 ///
1575 /// If there is any error writing the file.
1576 pub fn to_snapshot_file(&self, p: impl AsRef<Path>) {
1577 self.to_snapshot().write_file(p).unwrap();
1578 }
1579
1580 /// Creates a new Env loaded with the [`LedgerSnapshot`].
1581 ///
1582 /// The ledger info and state in the snapshot are loaded into the Env.
1583 pub fn from_ledger_snapshot(s: LedgerSnapshot) -> Env {
1584 Env::new_for_testutils(
1585 EnvTestConfig::default(), // TODO: Allow setting the config.
1586 Rc::new(s.clone()),
1587 None,
1588 s.ledger_info(),
1589 Some(Rc::new(s.clone())),
1590 )
1591 }
1592
1593 /// Creates a new Env loaded with the ledger snapshot loaded from the file.
1594 ///
1595 /// ### Panics
1596 ///
1597 /// If there is any error reading the file.
1598 pub fn from_ledger_snapshot_file(p: impl AsRef<Path>) -> Env {
1599 Self::from_ledger_snapshot(LedgerSnapshot::read_file(p).unwrap())
1600 }
1601
1602 /// Create a snapshot from the Env's current state.
1603 pub fn to_ledger_snapshot(&self) -> LedgerSnapshot {
1604 let snapshot = self.test_state.snapshot.clone().unwrap_or_default();
1605 let mut snapshot = (*snapshot).clone();
1606 snapshot.set_ledger_info(self.ledger().get());
1607 let budget = soroban_env_host::budget::AsBudget::as_budget(&self.env_impl);
1608 let storage = self
1609 .env_impl
1610 .with_mut_storage(|s| Ok(s.map.clone()))
1611 .unwrap();
1612 snapshot.update_entries(storage.iter(budget).unwrap());
1613 snapshot
1614 }
1615
1616 /// Create a snapshot file from the Env's current state.
1617 ///
1618 /// ### Panics
1619 ///
1620 /// If there is any error writing the file.
1621 pub fn to_ledger_snapshot_file(&self, p: impl AsRef<Path>) {
1622 self.to_ledger_snapshot().write_file(p).unwrap();
1623 }
1624
1625 /// Create an events snapshot from the Env's current state.
1626 pub(crate) fn to_events_snapshot(&self) -> EventsSnapshot {
1627 EventsSnapshot(
1628 self.host()
1629 .get_events()
1630 .unwrap()
1631 .0
1632 .into_iter()
1633 .filter(|e| match e.event.type_ {
1634 // Keep only system and contract events, because event
1635 // snapshots are used in test snapshots, and intended to be
1636 // stable over time because the goal is to record meaningful
1637 // observable behaviors. Diagnostic events are observable,
1638 // but events have no stability guarantees and are intended
1639 // to be used by developers when debugging, tracing, and
1640 // observing, not by systems that integrate.
1641 xdr::ContractEventType::System | xdr::ContractEventType::Contract => true,
1642 xdr::ContractEventType::Diagnostic => false,
1643 })
1644 .map(Into::into)
1645 .collect(),
1646 )
1647 }
1648
1649 /// Get the budget that tracks the resources consumed for the environment.
1650 #[deprecated(note = "use cost_estimate().budget()")]
1651 pub fn budget(&self) -> Budget {
1652 Budget::new(self.env_impl.budget_cloned())
1653 }
1654}
1655
1656#[cfg(any(test, feature = "testutils"))]
1657impl Drop for Env {
1658 fn drop(&mut self) {
1659 // If the env impl (Host) is finishable, that means this Env is the last
1660 // Env to hold a reference to the Host. The Env should only write a test
1661 // snapshot at that point when no other references to the host exist,
1662 // because it is only when there are no other references that the host
1663 // is being dropped.
1664 if self.env_impl.can_finish() && self.test_state.config.capture_snapshot_at_drop {
1665 self.to_test_snapshot_file();
1666 }
1667 }
1668}
1669
1670#[cfg(any(test, feature = "testutils"))]
1671#[derive(Default, Clone)]
1672struct LastTestSnapshot {
1673 name: String,
1674 number: usize,
1675}
1676
1677#[cfg(any(test, feature = "testutils"))]
1678thread_local! {
1679 static LAST_TEST_SNAPSHOT: RefCell<LastTestSnapshot> = RefCell::new(LastTestSnapshot::default());
1680}
1681
1682#[doc(hidden)]
1683#[cfg(any(test, feature = "testutils"))]
1684impl Env {
1685 /// Create a snapshot file for the currently executing test.
1686 ///
1687 /// Writes the file to the `test_snapshots/{test-name}.N.json` path where
1688 /// `N` is incremented for each unique `Env` in the test.
1689 ///
1690 /// Use to record the observable behavior of a test, and changes to that
1691 /// behavior over time. Commit the test snapshot file to version control and
1692 /// watch for changes in it on contract change, SDK upgrade, protocol
1693 /// upgrade, and other important events.
1694 ///
1695 /// No file will be created if the environment has no meaningful data such
1696 /// as stored entries or events.
1697 ///
1698 /// ### Panics
1699 ///
1700 /// If there is any error writing the file.
1701 pub(crate) fn to_test_snapshot_file(&self) {
1702 let snapshot = self.to_snapshot();
1703
1704 // Don't write a snapshot that has no data in it.
1705 if snapshot.ledger.entries().into_iter().count() == 0
1706 && snapshot.events.0.is_empty()
1707 && snapshot.auth.0.is_empty()
1708 {
1709 return;
1710 }
1711
1712 // Determine path to write test snapshots to.
1713 let thread = std::thread::current();
1714 let Some(test_name) = thread.name() else {
1715 // The stock unit test runner sets a thread name.
1716 // If there is no thread name, assume this is not running as
1717 // part of a unit test, and do nothing.
1718 return;
1719 };
1720 if test_name == "main" {
1721 // When doc tests are running they're all run with the thread name
1722 // main. There's no way to detect which doc test is being run and
1723 // there's little value in writing and overwriting a single file for
1724 // all doc tests.
1725 return;
1726 }
1727 let file_number = LAST_TEST_SNAPSHOT.with_borrow_mut(|l| {
1728 if test_name == l.name {
1729 *l = LastTestSnapshot::default();
1730 l.name = test_name.to_owned();
1731 }
1732 l.number += 1;
1733 l.number
1734 });
1735 // Break up the test name into directories, using :: as the separator.
1736 // The :: module separator cannot be written into the filename because
1737 // some operating systems (e.g. Windows) do not allow the : character in
1738 // filenames.
1739 let test_name_path = test_name
1740 .split("::")
1741 .map(|p| std::path::Path::new(p).to_path_buf())
1742 .reduce(|p0, p1| p0.join(p1))
1743 .expect("test name to not be empty");
1744 let dir = std::path::Path::new("test_snapshots");
1745 let p = dir
1746 .join(&test_name_path)
1747 .with_extension(format!("{file_number}.json"));
1748
1749 // Write test snapshots to file.
1750 eprintln!("Writing test snapshot file for test {test_name:?} to {p:?}.");
1751 snapshot.write_file(p).unwrap();
1752 }
1753}
1754
1755#[doc(hidden)]
1756impl internal::EnvBase for Env {
1757 type Error = Infallible;
1758
1759 // This exists to allow code in conversion paths to upgrade an Error to an
1760 // Env::Error with some control granted to the underlying Env (and panic
1761 // paths kept out of the host). We delegate this to our env_impl and then,
1762 // since our own Error type is Infallible, immediately throw it into either
1763 // the env_impl's Error escalation path (if testing), or just plain panic.
1764 #[cfg(not(target_family = "wasm"))]
1765 fn error_from_error_val(&self, e: crate::Error) -> Self::Error {
1766 let host_err = self.env_impl.error_from_error_val(e);
1767 #[cfg(any(test, feature = "testutils"))]
1768 self.env_impl.escalate_error_to_panic(host_err);
1769 #[cfg(not(any(test, feature = "testutils")))]
1770 panic!("{:?}", host_err);
1771 }
1772
1773 // When targeting wasm we don't even need to do that, just delegate to
1774 // the Guest's impl, which calls core::arch::wasm32::unreachable.
1775 #[cfg(target_family = "wasm")]
1776 fn error_from_error_val(&self, e: crate::Error) -> Self::Error {
1777 self.env_impl.error_from_error_val(e)
1778 }
1779
1780 fn check_protocol_version_lower_bound(&self, v: u32) -> Result<(), Self::Error> {
1781 Ok(self
1782 .env_impl
1783 .check_protocol_version_lower_bound(v)
1784 .unwrap_optimized())
1785 }
1786
1787 fn check_protocol_version_upper_bound(&self, v: u32) -> Result<(), Self::Error> {
1788 Ok(self
1789 .env_impl
1790 .check_protocol_version_upper_bound(v)
1791 .unwrap_optimized())
1792 }
1793
1794 // Note: the function `escalate_error_to_panic` only exists _on the `Env`
1795 // trait_ when the feature `soroban-env-common/testutils` is enabled. This
1796 // is because the host wants to never have this function even _compiled in_
1797 // when building for production, as it might be accidentally called (we have
1798 // mistakenly done so with conversion and comparison traits in the past).
1799 //
1800 // As a result, we only implement it here (fairly meaninglessly) when we're
1801 // in `cfg(test)` (which enables `soroban-env-host/testutils` thus
1802 // `soroban-env-common/testutils`) or when we've had our own `testutils`
1803 // feature enabled (which does the same).
1804 //
1805 // See the `internal::reject_err` functions above for more detail about what
1806 // it actually does (when implemented for real, on the host). In this
1807 // not-very-serious impl, since `Self::Error` is `Infallible`, this instance
1808 // can never actually be called and so its body is just a trivial
1809 // transformation from one empty type to another, for Type System Reasons.
1810 #[cfg(any(test, feature = "testutils"))]
1811 fn escalate_error_to_panic(&self, e: Self::Error) -> ! {
1812 match e {}
1813 }
1814
1815 fn check_same_env(&self, other: &Self) -> Result<(), Self::Error> {
1816 Ok(self
1817 .env_impl
1818 .check_same_env(&other.env_impl)
1819 .unwrap_optimized())
1820 }
1821
1822 fn bytes_copy_from_slice(
1823 &self,
1824 b: BytesObject,
1825 b_pos: U32Val,
1826 slice: &[u8],
1827 ) -> Result<BytesObject, Self::Error> {
1828 Ok(self
1829 .env_impl
1830 .bytes_copy_from_slice(b, b_pos, slice)
1831 .unwrap_optimized())
1832 }
1833
1834 fn bytes_copy_to_slice(
1835 &self,
1836 b: BytesObject,
1837 b_pos: U32Val,
1838 slice: &mut [u8],
1839 ) -> Result<(), Self::Error> {
1840 Ok(self
1841 .env_impl
1842 .bytes_copy_to_slice(b, b_pos, slice)
1843 .unwrap_optimized())
1844 }
1845
1846 fn bytes_new_from_slice(&self, slice: &[u8]) -> Result<BytesObject, Self::Error> {
1847 Ok(self.env_impl.bytes_new_from_slice(slice).unwrap_optimized())
1848 }
1849
1850 fn log_from_slice(&self, msg: &str, args: &[Val]) -> Result<Void, Self::Error> {
1851 Ok(self.env_impl.log_from_slice(msg, args).unwrap_optimized())
1852 }
1853
1854 fn string_copy_to_slice(
1855 &self,
1856 b: StringObject,
1857 b_pos: U32Val,
1858 slice: &mut [u8],
1859 ) -> Result<(), Self::Error> {
1860 Ok(self
1861 .env_impl
1862 .string_copy_to_slice(b, b_pos, slice)
1863 .unwrap_optimized())
1864 }
1865
1866 fn symbol_copy_to_slice(
1867 &self,
1868 b: SymbolObject,
1869 b_pos: U32Val,
1870 mem: &mut [u8],
1871 ) -> Result<(), Self::Error> {
1872 Ok(self
1873 .env_impl
1874 .symbol_copy_to_slice(b, b_pos, mem)
1875 .unwrap_optimized())
1876 }
1877
1878 fn string_new_from_slice(&self, slice: &[u8]) -> Result<StringObject, Self::Error> {
1879 Ok(self
1880 .env_impl
1881 .string_new_from_slice(slice)
1882 .unwrap_optimized())
1883 }
1884
1885 fn symbol_new_from_slice(&self, slice: &[u8]) -> Result<SymbolObject, Self::Error> {
1886 Ok(self
1887 .env_impl
1888 .symbol_new_from_slice(slice)
1889 .unwrap_optimized())
1890 }
1891
1892 fn map_new_from_slices(&self, keys: &[&str], vals: &[Val]) -> Result<MapObject, Self::Error> {
1893 Ok(self
1894 .env_impl
1895 .map_new_from_slices(keys, vals)
1896 .unwrap_optimized())
1897 }
1898
1899 fn map_unpack_to_slice(
1900 &self,
1901 map: MapObject,
1902 keys: &[&str],
1903 vals: &mut [Val],
1904 ) -> Result<Void, Self::Error> {
1905 Ok(self
1906 .env_impl
1907 .map_unpack_to_slice(map, keys, vals)
1908 .unwrap_optimized())
1909 }
1910
1911 fn vec_new_from_slice(&self, vals: &[Val]) -> Result<VecObject, Self::Error> {
1912 Ok(self.env_impl.vec_new_from_slice(vals).unwrap_optimized())
1913 }
1914
1915 fn vec_unpack_to_slice(&self, vec: VecObject, vals: &mut [Val]) -> Result<Void, Self::Error> {
1916 Ok(self
1917 .env_impl
1918 .vec_unpack_to_slice(vec, vals)
1919 .unwrap_optimized())
1920 }
1921
1922 fn symbol_index_in_strs(&self, key: Symbol, strs: &[&str]) -> Result<U32Val, Self::Error> {
1923 Ok(self
1924 .env_impl
1925 .symbol_index_in_strs(key, strs)
1926 .unwrap_optimized())
1927 }
1928}
1929
1930///////////////////////////////////////////////////////////////////////////////
1931/// X-macro use: impl Env for SDK's Env
1932///////////////////////////////////////////////////////////////////////////////
1933
1934// This is a helper macro used only by impl_env_for_sdk below. It consumes a
1935// token-tree of the form:
1936//
1937// {fn $fn_id:ident $args:tt -> $ret:ty}
1938//
1939// and produces the the corresponding method definition to be used in the
1940// SDK's Env implementation of the Env (calling through to the corresponding
1941// guest or host implementation).
1942macro_rules! sdk_function_helper {
1943 {$mod_id:ident, fn $fn_id:ident($($arg:ident:$type:ty),*) -> $ret:ty}
1944 =>
1945 {
1946 fn $fn_id(&self, $($arg:$type),*) -> Result<$ret, Self::Error> {
1947 internal::reject_err(&self.env_impl, self.env_impl.$fn_id($($arg),*))
1948 }
1949 };
1950}
1951
1952// This is a callback macro that pattern-matches the token-tree passed by the
1953// x-macro (call_macro_with_all_host_functions) and produces a suite of
1954// forwarding-method definitions, which it places in the body of the declaration
1955// of the implementation of Env for the SDK's Env.
1956macro_rules! impl_env_for_sdk {
1957 {
1958 $(
1959 // This outer pattern matches a single 'mod' block of the token-tree
1960 // passed from the x-macro to this macro. It is embedded in a `$()*`
1961 // pattern-repetition matcher so that it will match all provided
1962 // 'mod' blocks provided.
1963 $(#[$mod_attr:meta])*
1964 mod $mod_id:ident $mod_str:literal
1965 {
1966 $(
1967 // This inner pattern matches a single function description
1968 // inside a 'mod' block in the token-tree passed from the
1969 // x-macro to this macro. It is embedded in a `$()*`
1970 // pattern-repetition matcher so that it will match all such
1971 // descriptions.
1972 $(#[$fn_attr:meta])*
1973 { $fn_str:literal, $($min_proto:literal)?, $($max_proto:literal)?, fn $fn_id:ident $args:tt -> $ret:ty }
1974 )*
1975 }
1976 )*
1977 }
1978
1979 => // The part of the macro above this line is a matcher; below is its expansion.
1980
1981 {
1982 // This macro expands to a single item: the implementation of Env for
1983 // the SDK's Env struct used by client contract code running in a WASM VM.
1984 #[doc(hidden)]
1985 impl internal::Env for Env
1986 {
1987 $(
1988 $(
1989 // This invokes the guest_function_helper! macro above
1990 // passing only the relevant parts of the declaration
1991 // matched by the inner pattern above. It is embedded in two
1992 // nested `$()*` pattern-repetition expanders that
1993 // correspond to the pattern-repetition matchers in the
1994 // match section, but we ignore the structure of the 'mod'
1995 // block repetition-level from the outer pattern in the
1996 // expansion, flattening all functions from all 'mod' blocks
1997 // into the implementation of Env for Guest.
1998 sdk_function_helper!{$mod_id, fn $fn_id $args -> $ret}
1999 )*
2000 )*
2001 }
2002 };
2003}
2004
2005// Here we invoke the x-macro passing generate_env_trait as its callback macro.
2006internal::call_macro_with_all_host_functions! { impl_env_for_sdk }