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