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