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