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