Skip to main content

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