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