diff --git a/CHANGELOG.md b/CHANGELOG.md index 754fada5fe1..e447c852d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Add E2E testing framework MVP ‒ [#1395](https://github.com/paritytech/ink/pull/1395) - Add E2E tests for `Mapping` functions - [#1492](https://github.com/paritytech/ink/pull/1492) +- Make CallBuilder and CreateBuilder error handling optional - [#1602](https://github.com/paritytech/ink/pull/1602) + +### Breaking Changes +With the addition of [#1602](https://github.com/paritytech/ink/pull/1602), +the `CallBuilder::fire()`, `CallParams::invoke()`, and `CreateBuilder::instantiate()` +methods now unwrap the `Result` from `pallet-contracts` under the hood. + +If you wish to handle the error use the new `try_` variants of those methods instead. ## Version 4.0.0-beta diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..7a6cbe7ecc9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Reporting a vulnerability + +If you find something that can be treated as a security vulnerability, please do not use the issue tracker or discuss it in the public forum/channels, as it can cause more damage rather than giving real help to the ecosystem. + +Security vulnerabilities should be reported using [this contact form](https://security-submission.parity.io/). + +If you think that your report might be eligible for the Bug Bounty Program, please mark this during the submission. Please check up-to-date [Parity Bug Bounty Program rules](https://www.parity.io/bug-bounty) for more information about our Bug Bounty Program. + +**Warning:** This is an unified `SECURITY.md` file for the Paritytech GitHub Organization. The presence of this file does not mean that this repository is covered by the Bug Bounty program. Please always check the Bug Bounty Program scope for the information. diff --git a/crates/e2e/src/client.rs b/crates/e2e/src/client.rs index 3f9981d2cc5..4860bbf75cb 100644 --- a/crates/e2e/src/client.rs +++ b/crates/e2e/src/client.rs @@ -33,6 +33,7 @@ use super::{ }; use contract_metadata::ContractMetadata; use ink_env::Environment; +use ink_primitives::MessageResult; use sp_runtime::traits::{ IdentifyAccount, @@ -124,7 +125,7 @@ pub struct CallResult { pub events: ExtrinsicEvents, /// Contains the result of decoding the return value of the called /// function. - pub value: Result, + pub value: Result, scale::Error>, /// Returns the bytes of the encoded dry-run return value. pub data: Vec, } @@ -139,12 +140,19 @@ where /// Panics if the value could not be decoded. The raw bytes can be accessed /// via [`return_data`]. pub fn return_value(self) -> V { - self.value.unwrap_or_else(|err| { - panic!( - "decoding dry run result to ink! message return type failed: {}", - err - ) - }) + self.value + .unwrap_or_else(|env_err| { + panic!( + "Decoding dry run result to ink! message return type failed: {}", + env_err + ) + }) + .unwrap_or_else(|lang_err| { + panic!( + "Encountered a `LangError` while decoding dry run result to ink! message: {:?}", + lang_err + ) + }) } /// Returns true if the specified event was triggered by the call. @@ -655,7 +663,7 @@ where } let bytes = &dry_run.result.as_ref().unwrap().data; - let value: Result = + let value: Result, scale::Error> = scale::Decode::decode(&mut bytes.as_ref()); Ok(CallResult { diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 65abb542446..90c1294da19 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -268,7 +268,9 @@ where /// - If the called contract execution has trapped. /// - If the called contract ran out of gas upon execution. /// - If the returned value failed to decode properly. -pub fn invoke_contract(params: &CallParams, Args, R>) -> Result +pub fn invoke_contract( + params: &CallParams, Args, R>, +) -> Result> where E: Environment, Args: scale::Encode, diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index b186f45b35e..99b2024f39d 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -414,7 +414,7 @@ pub trait TypedEnvBackend: EnvBackend { fn invoke_contract( &mut self, call_data: &CallParams, Args, R>, - ) -> Result + ) -> Result> where E: Environment, Args: scale::Encode, diff --git a/crates/env/src/call/call_builder.rs b/crates/env/src/call/call_builder.rs index 9a45106ca7d..e88ec37df1a 100644 --- a/crates/env/src/call/call_builder.rs +++ b/crates/env/src/call/call_builder.rs @@ -109,7 +109,31 @@ where /// Invokes the contract with the given built-up call parameters. /// /// Returns the result of the contract execution. - pub fn invoke(&self) -> Result { + /// + /// # Panics + /// + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an + /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle those + /// use the [`try_invoke`][`CallParams::try_invoke`] method instead. + pub fn invoke(&self) -> R { + crate::invoke_contract(self) + .unwrap_or_else(|env_error| { + panic!("Cross-contract call failed with {:?}", env_error) + }) + .unwrap_or_else(|lang_error| { + panic!("Cross-contract call failed with {:?}", lang_error) + }) + } + + /// Invokes the contract with the given built-up call parameters. + /// + /// Returns the result of the contract execution. + /// + /// # Note + /// + /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner + /// [`ink_primitives::LangError`], both of which can be handled by the caller. + pub fn try_invoke(&self) -> Result, crate::Error> { crate::invoke_contract(self) } } @@ -120,11 +144,29 @@ where Args: scale::Encode, R: scale::Decode, { - /// Invokes the contract via delegated call with the given - /// built-up call parameters. + /// Invoke the contract using Delegate Call semantics with the given built-up call parameters. /// /// Returns the result of the contract execution. - pub fn invoke(&self) -> Result { + /// + /// # Panics + /// + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] If you want to + /// handle those use the [`try_invoke`][`CallParams::try_invoke`] method instead. + pub fn invoke(&self) -> R { + crate::invoke_contract_delegate(self).unwrap_or_else(|env_error| { + panic!("Cross-contract call failed with {:?}", env_error) + }) + } + + /// Invoke the contract using Delegate Call semantics with the given built-up call parameters. + /// + /// Returns the result of the contract execution. + /// + /// # Note + /// + /// On failure this returns an [`ink::env::Error`][`crate::Error`] which can be handled by the + /// caller. + pub fn try_invoke(&self) -> Result { crate::invoke_contract_delegate(self) } } @@ -172,8 +214,7 @@ where /// .push_arg(&[0x10u8; 32]) /// ) /// .returns::<()>() -/// .fire() -/// .unwrap(); +/// .fire(); /// ``` /// /// ## Example 2: With Return Value @@ -208,8 +249,7 @@ where /// .push_arg(&[0x10u8; 32]) /// ) /// .returns::() -/// .fire() -/// .unwrap(); +/// .fire(); /// ``` /// /// ## Example 3: Delegate call @@ -236,8 +276,47 @@ where /// .push_arg(&[0x10u8; 32]) /// ) /// .returns::() -/// .fire() -/// .unwrap(); +/// .fire(); +/// ``` +/// +/// # Handling `LangError`s +/// +/// It is also important to note that there are certain types of errors which can happen during +/// cross-contract calls which can be handled know as [`LangError`][`ink_primitives::LangError`]. +/// +/// If you want to handle these errors use the [`CallBuilder::try_fire`] methods instead of the +/// [`CallBuilder::fire`] ones. +/// +/// **Note:** The shown examples panic because there is currently no cross-calling +/// support in the off-chain testing environment. However, this code +/// should work fine in on-chain environments. +/// +/// ## Example: Handling a `LangError` +/// +/// ```should_panic +/// # use ::ink_env::{ +/// # Environment, +/// # DefaultEnvironment, +/// # call::{build_call, Selector, ExecutionInput} +/// # }; +/// # use ink_env::call::Call; +/// # type AccountId = ::AccountId; +/// # type Balance = ::Balance; +/// let call_result = build_call::() +/// .call_type( +/// Call::new() +/// .callee(AccountId::from([0x42; 32])) +/// .gas_limit(5000) +/// .transferred_value(10), +/// ) +/// .try_fire() +/// .expect("Got an error from the Contract's pallet."); +/// +/// match call_result { +/// Ok(_) => unimplemented!(), +/// Err(e @ ink_primitives::LangError::CouldNotReadInput) => unimplemented!(), +/// Err(_) => unimplemented!(), +/// } /// ``` #[allow(clippy::type_complexity)] pub fn build_call() -> CallBuilder< @@ -597,9 +676,25 @@ where E: Environment, { /// Invokes the cross-chain function call. - pub fn fire(self) -> Result<(), Error> { + /// + /// # Panics + /// + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an + /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle those + /// use the [`try_fire`][`CallBuilder::try_fire`] method instead. + pub fn fire(self) { self.params().invoke() } + + /// Invokes the cross-chain function call. + /// + /// # Note + /// + /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner + /// [`ink_primitives::LangError`], both of which can be handled by the caller. + pub fn try_fire(self) -> Result, Error> { + self.params().try_invoke() + } } impl @@ -612,10 +707,24 @@ impl where E: Environment, { - /// Invokes the cross-chain function call. - pub fn fire(self) -> Result<(), Error> { + /// Invokes the cross-chain function call using Delegate Call semantics. + /// + /// # Panics + /// + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] + /// If you want to handle those use the [`try_fire`][`CallBuilder::try_fire`] method instead. + pub fn fire(self) { self.params().invoke() } + + /// Invokes the cross-chain function call using Delegate Call semantics. + /// + /// # Note + /// + /// On failure this an [`ink::env::Error`][`crate::Error`] which can be handled by the caller. + pub fn try_fire(self) -> Result<(), Error> { + self.params().try_invoke() + } } impl @@ -626,9 +735,25 @@ where R: scale::Decode, { /// Invokes the cross-chain function call and returns the result. - pub fn fire(self) -> Result { + /// + /// # Panics + /// + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] or an + /// [`ink::primitives::LangError`][`ink_primitives::LangError`]. If you want to handle those + /// use the [`try_fire`][`CallBuilder::try_fire`] method instead. + pub fn fire(self) -> R { self.params().invoke() } + + /// Invokes the cross-chain function call and returns the result. + /// + /// # Note + /// + /// On failure this returns an outer [`ink::env::Error`][`crate::Error`] or inner + /// [`ink_primitives::LangError`], both of which can be handled by the caller. + pub fn try_fire(self) -> Result, Error> { + self.params().try_invoke() + } } impl @@ -638,8 +763,22 @@ where Args: scale::Encode, R: scale::Decode, { - /// Invokes the cross-chain function call and returns the result. - pub fn fire(self) -> Result { + /// Invokes the cross-chain function call using Delegate Call semantics and returns the result. + /// + /// # Panics + /// + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`] + /// If you want to handle those use the [`try_fire`][`CallBuilder::try_fire`] method instead. + pub fn fire(self) -> R { self.params().invoke() } + + /// Invokes the cross-chain function call using Delegate Call semantics and returns the result. + /// + /// # Note + /// + /// On failure this an [`ink::env::Error`][`crate::Error`] which can be handled by the caller. + pub fn try_fire(self) -> Result { + self.params().try_invoke() + } } diff --git a/crates/env/src/call/create_builder.rs b/crates/env/src/call/create_builder.rs index 3baee0a32ce..b7f00a3d748 100644 --- a/crates/env/src/call/create_builder.rs +++ b/crates/env/src/call/create_builder.rs @@ -122,74 +122,26 @@ where /// /// # Panics /// - /// This method panics if it encounters an [`ink_primitives::LangError`]. If you want to handle - /// those use the [`try_instantiate`][`CreateParams::try_instantiate`] method instead. + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`]. If you want to + /// handle those use the [`try_instantiate`][`CreateParams::try_instantiate`] method instead. #[inline] - pub fn instantiate(&self) -> Result { + pub fn instantiate(&self) -> R { crate::instantiate_contract(self) - .map(|inner| { - inner.unwrap_or_else(|error| { - panic!("Received a `LangError` while instantiating: {:?}", error) - }) - }) .map(FromAccountId::from_account_id) + .unwrap_or_else(|env_error| { + panic!("Cross-contract instantiation failed with {:?}", env_error) + }) } /// Instantiates the contract and returns its account ID back to the caller. /// /// # Note /// - /// On failure this returns an [`ink_primitives::LangError`] which can be handled by the caller. + /// On failure this returns an [`ink::env::Error`][`crate::Error`] which can be handled by the + /// caller. #[inline] - pub fn try_instantiate( - &self, - ) -> Result, crate::Error> { - crate::instantiate_contract(self) - .map(|inner| inner.map(FromAccountId::from_account_id)) - } -} - -impl - CreateParams> -where - E: Environment, - Args: scale::Encode, - Salt: AsRef<[u8]>, - R: FromAccountId, - ContractError: scale::Decode, -{ - /// Attempts to instantiate the contract, returning the execution result back to the caller. - /// - /// # Panics - /// - /// This method panics if it encounters an [`ink_primitives::LangError`]. If you want to handle - /// those use the [`try_instantiate_fallible`][`CreateParams::try_instantiate_fallible`] method instead. - #[inline] - pub fn instantiate_fallible(&self) -> Result, crate::Error> { - crate::instantiate_fallible_contract(self).map(|constructor_result| { - constructor_result - .unwrap_or_else(|error| { - panic!("Received a `LangError` while instantiating: {:?}", error) - }) - .map(FromAccountId::from_account_id) - }) - } - - /// Attempts to instantiate the contract, returning the execution result back to the caller. - /// - /// # Note - /// - /// On failure this returns an [`ink_primitives::LangError`] which can be handled by the caller. - #[inline] - pub fn try_instantiate_fallible( - &self, - ) -> Result>, crate::Error> - { - crate::instantiate_fallible_contract(self).map(|constructor_result| { - constructor_result.map(|contract_result| { - contract_result.map(FromAccountId::from_account_id) - }) - }) + pub fn try_instantiate(&self) -> Result { + crate::instantiate_contract(self).map(FromAccountId::from_account_id) } } @@ -255,8 +207,7 @@ where /// .salt_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]) /// .returns::() /// .params() -/// .instantiate() -/// .unwrap(); +/// .instantiate(); /// ``` /// /// ## Example 2: Handles Result from Fallible Constructor @@ -530,69 +481,25 @@ where Salt: AsRef<[u8]>, RetType: FromAccountId, { - /// Instantiates the contract using the given instantiation parameters. + /// Instantiates the contract and returns its account ID back to the caller. /// /// # Panics /// - /// This method panics if it encounters an [`ink_primitives::LangError`]. If you want to handle - /// those use the [`try_instantiate`][`CreateBuilder::try_instantiate`] method instead. + /// This method panics if it encounters an [`ink::env::Error`][`crate::Error`]. If you want to + /// handle those use the [`try_instantiate`][`CreateBuilder::try_instantiate`] method instead. #[inline] - pub fn instantiate(self) -> Result { + pub fn instantiate(self) -> R { self.params().instantiate() } - /// Instantiates the contract using the given instantiation parameters. + /// Instantiates the contract and returns its account ID back to the caller. /// /// # Note /// - /// On failure this returns an [`ink_primitives::LangError`] which can be handled by the caller. + /// On failure this returns an [`ink::env::Error`][`crate::Error`] which can be handled by the + /// caller. #[inline] - pub fn try_instantiate( - self, - ) -> Result, Error> { + pub fn try_instantiate(self) -> Result { self.params().try_instantiate() } } - -impl - CreateBuilder< - E, - Set, - GasLimit, - Set, - Set>, - Set, - Set>>, - > -where - E: Environment, - GasLimit: Unwrap, - Args: scale::Encode, - Salt: AsRef<[u8]>, - RetType: FromAccountId, - ContractError: scale::Decode, -{ - /// Attempts to instantiate the contract, returning the execution result back to the caller. - /// - /// # Panics - /// - /// This method panics if it encounters an [`ink_primitives::LangError`]. If you want to handle - /// those use the [`try_instantiate_fallible`][`CreateParams::try_instantiate_fallible`] method instead. - #[inline] - pub fn instantiate_fallible(self) -> Result, Error> { - self.params().instantiate_fallible() - } - - /// Attempts to instantiate the contract, returning the execution result back to the caller. - /// - /// # Note - /// - /// On failure this returns an [`ink_primitives::LangError`] which can be handled by the caller. - #[inline] - pub fn try_instantiate_fallible( - self, - ) -> Result>, Error> - { - self.params().try_instantiate_fallible() - } -} diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 363a9c77454..771d8608054 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -440,7 +440,7 @@ impl TypedEnvBackend for EnvInstance { fn invoke_contract( &mut self, params: &CallParams, Args, R>, - ) -> Result + ) -> Result> where E: Environment, Args: scale::Encode, diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 0451528d918..3b98fa02ec9 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -415,7 +415,7 @@ impl TypedEnvBackend for EnvInstance { fn invoke_contract( &mut self, params: &CallParams, Args, R>, - ) -> Result + ) -> Result> where E: Environment, Args: scale::Encode, diff --git a/crates/ink/codegen/src/generator/as_dependency/call_builder.rs b/crates/ink/codegen/src/generator/as_dependency/call_builder.rs index 9d3232133f8..e85e52b0ee3 100644 --- a/crates/ink/codegen/src/generator/as_dependency/call_builder.rs +++ b/crates/ink/codegen/src/generator/as_dependency/call_builder.rs @@ -369,7 +369,10 @@ impl CallBuilder<'_> { let input_types = generator::input_types(message.inputs()); let arg_list = generator::generate_argument_list(input_types.iter().cloned()); let mut_tok = callable.receiver().is_ref_mut().then(|| quote! { mut }); - let return_type = message.wrapped_output(); + let return_type = message + .output() + .map(quote::ToTokens::to_token_stream) + .unwrap_or_else(|| quote::quote! { () }); let output_span = return_type.span(); let output_type = quote_spanned!(output_span=> ::ink::env::call::CallBuilder< diff --git a/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs b/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs index 756dae0ed71..b5e1bfe17be 100644 --- a/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs +++ b/crates/ink/codegen/src/generator/as_dependency/contract_ref.rs @@ -370,7 +370,7 @@ impl ContractRef<'_> { ) -> #wrapped_output_type { ::#call_operator(self) .#message_ident( #( #input_bindings ),* ) - .fire() + .try_fire() .unwrap_or_else(|error| ::core::panic!( "encountered error while calling {}::{}: {:?}", ::core::stringify!(#storage_ident), diff --git a/crates/ink/codegen/src/generator/trait_def/call_forwarder.rs b/crates/ink/codegen/src/generator/trait_def/call_forwarder.rs index d2828712aa7..46017c5dff2 100644 --- a/crates/ink/codegen/src/generator/trait_def/call_forwarder.rs +++ b/crates/ink/codegen/src/generator/trait_def/call_forwarder.rs @@ -355,8 +355,9 @@ impl CallForwarder<'_> { , #input_bindings )* ) - .fire() - .unwrap_or_else(|err| ::core::panic!("{}: {:?}", #panic_str, err)) + .try_fire() + .unwrap_or_else(|env_err| ::core::panic!("{}: {:?}", #panic_str, env_err)) + .unwrap_or_else(|lang_err| ::core::panic!("{}: {:?}", #panic_str, lang_err)) } ) } diff --git a/crates/ink/src/env_access.rs b/crates/ink/src/env_access.rs index 4c929a9a515..59b3ad376b8 100644 --- a/crates/ink/src/env_access.rs +++ b/crates/ink/src/env_access.rs @@ -621,7 +621,10 @@ where /// ) /// .returns::() /// .params(); - /// self.env().invoke_contract(&call_params).unwrap_or_else(|err| panic!("call invocation must succeed: {:?}", err)) + /// + /// self.env().invoke_contract(&call_params) + /// .unwrap_or_else(|env_err| panic!("Received an error from the Environment: {:?}", env_err)) + /// .unwrap_or_else(|lang_err| panic!("Received a `LangError`: {:?}", lang_err)) /// } /// # /// # } @@ -634,7 +637,7 @@ where pub fn invoke_contract( self, params: &CallParams, Args, R>, - ) -> Result + ) -> Result> where Args: scale::Encode, R: scale::Decode, diff --git a/crates/ink/tests/ui/contract/fail/message-input-non-codec.stderr b/crates/ink/tests/ui/contract/fail/message-input-non-codec.stderr index e6c2260060a..a083a628f30 100644 --- a/crates/ink/tests/ui/contract/fail/message-input-non-codec.stderr +++ b/crates/ink/tests/ui/contract/fail/message-input-non-codec.stderr @@ -53,11 +53,11 @@ note: required by a bound in `ExecutionInput::>::push_arg` -error[E0599]: the method `fire` exists for struct `ink::ink_env::call::CallBuilder>, Set, ArgumentList>>>, Set>>>`, but its trait bounds were not satisfied +error[E0599]: the method `try_fire` exists for struct `ink::ink_env::call::CallBuilder>, Set, ArgumentList>>>, Set>>`, but its trait bounds were not satisfied --> tests/ui/contract/fail/message-input-non-codec.rs:16:9 | 16 | pub fn message(&self, _input: NonCodecType) {} - | ^^^ method cannot be called on `ink::ink_env::call::CallBuilder>, Set, ArgumentList>>>, Set>>>` due to unsatisfied trait bounds + | ^^^ method cannot be called on `ink::ink_env::call::CallBuilder>, Set, ArgumentList>>>, Set>>` due to unsatisfied trait bounds | ::: $WORKSPACE/crates/env/src/call/execution_input.rs | diff --git a/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr b/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr index b5565368175..1bdb9e94006 100644 --- a/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr +++ b/crates/ink/tests/ui/contract/fail/message-returns-non-codec.stderr @@ -34,16 +34,19 @@ note: required by a bound in `return_value` | R: scale::Encode, | ^^^^^^^^^^^^^ required by this bound in `return_value` -error[E0599]: the method `fire` exists for struct `ink::ink_env::call::CallBuilder>, Set>>, Set>>>`, but its trait bounds were not satisfied +error[E0599]: the method `try_fire` exists for struct `ink::ink_env::call::CallBuilder>, Set>>, Set>>`, but its trait bounds were not satisfied --> tests/ui/contract/fail/message-returns-non-codec.rs:16:9 | +4 | pub struct NonCodecType; + | ----------------------- doesn't satisfy `NonCodecType: parity_scale_codec::Decode` +... 16 | pub fn message(&self) -> NonCodecType { - | ^^^ method cannot be called on `ink::ink_env::call::CallBuilder>, Set>>, Set>>>` due to unsatisfied trait bounds - | - ::: $RUST/core/src/result.rs - | - | pub enum Result { - | --------------------- doesn't satisfy `_: parity_scale_codec::Decode` + | ^^^ method cannot be called on `ink::ink_env::call::CallBuilder>, Set>>, Set>>` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: - `Result: parity_scale_codec::Decode` + `NonCodecType: parity_scale_codec::Decode` +note: the following trait must be implemented + --> $CARGO/parity-scale-codec-3.2.2/src/codec.rs + | + | pub trait Decode: Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/ink/tests/ui/trait_def/fail/message_input_non_codec.stderr b/crates/ink/tests/ui/trait_def/fail/message_input_non_codec.stderr index 63b1a30a63a..a4a79052510 100644 --- a/crates/ink/tests/ui/trait_def/fail/message_input_non_codec.stderr +++ b/crates/ink/tests/ui/trait_def/fail/message_input_non_codec.stderr @@ -1,56 +1,56 @@ error[E0277]: the trait bound `NonCodec: WrapperTypeDecode` is not satisfied - --> tests/ui/trait_def/fail/message_input_non_codec.rs:6:23 - | -6 | fn message(&self, input: NonCodec); - | ^^^^^ the trait `WrapperTypeDecode` is not implemented for `NonCodec` - | - = help: the following other types implement trait `WrapperTypeDecode`: - Arc - Box - Rc - = note: required for `NonCodec` to implement `parity_scale_codec::Decode` + --> tests/ui/trait_def/fail/message_input_non_codec.rs:6:23 + | +6 | fn message(&self, input: NonCodec); + | ^^^^^ the trait `WrapperTypeDecode` is not implemented for `NonCodec` + | + = help: the following other types implement trait `WrapperTypeDecode`: + Arc + Box + Rc + = note: required for `NonCodec` to implement `parity_scale_codec::Decode` note: required by a bound in `DispatchInput` - --> src/codegen/dispatch/type_check.rs - | - | T: scale::Decode + 'static; - | ^^^^^^^^^^^^^ required by this bound in `DispatchInput` + --> src/codegen/dispatch/type_check.rs + | + | T: scale::Decode + 'static; + | ^^^^^^^^^^^^^ required by this bound in `DispatchInput` error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied - --> tests/ui/trait_def/fail/message_input_non_codec.rs:3:1 - | -3 | #[ink::trait_definition] - | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` -4 | pub trait TraitDefinition { -5 | #[ink(message)] - | - required by a bound introduced by this call - | - = help: the following other types implement trait `WrapperTypeEncode`: - &T - &mut T - Arc - Box - Cow<'a, T> - Rc - String - Vec - parity_scale_codec::Ref<'a, T, U> - = note: required for `NonCodec` to implement `Encode` + --> tests/ui/trait_def/fail/message_input_non_codec.rs:3:1 + | +3 | #[ink::trait_definition] + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` +4 | pub trait TraitDefinition { +5 | #[ink(message)] + | - required by a bound introduced by this call + | + = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc + Box + Cow<'a, T> + Rc + String + Vec + parity_scale_codec::Ref<'a, T, U> + = note: required for `NonCodec` to implement `Encode` note: required by a bound in `ExecutionInput::>::push_arg` - --> $WORKSPACE/crates/env/src/call/execution_input.rs - | - | T: scale::Encode, - | ^^^^^^^^^^^^^ required by this bound in `ExecutionInput::>::push_arg` + --> $WORKSPACE/crates/env/src/call/execution_input.rs + | + | T: scale::Encode, + | ^^^^^^^^^^^^^ required by this bound in `ExecutionInput::>::push_arg` -error[E0599]: the method `fire` exists for struct `CallBuilder>, Set, ArgumentList>>>, Set>>`, but its trait bounds were not satisfied - --> tests/ui/trait_def/fail/message_input_non_codec.rs:5:5 - | -5 | #[ink(message)] - | ^ method cannot be called on `CallBuilder>, Set, ArgumentList>>>, Set>>` due to unsatisfied trait bounds - | - ::: $WORKSPACE/crates/env/src/call/execution_input.rs - | - | pub struct ArgumentList { - | ----------------------------------- doesn't satisfy `_: Encode` - | - = note: the following trait bounds were not satisfied: - `ArgumentList, ArgumentList>: Encode` +error[E0599]: the method `try_fire` exists for struct `CallBuilder>, Set, ArgumentList>>>, Set>>`, but its trait bounds were not satisfied + --> tests/ui/trait_def/fail/message_input_non_codec.rs:5:5 + | +5 | #[ink(message)] + | ^ method cannot be called on `CallBuilder>, Set, ArgumentList>>>, Set>>` due to unsatisfied trait bounds + | + ::: $WORKSPACE/crates/env/src/call/execution_input.rs + | + | pub struct ArgumentList { + | ----------------------------------- doesn't satisfy `_: Encode` + | + = note: the following trait bounds were not satisfied: + `ArgumentList, ArgumentList>: Encode` diff --git a/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr b/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr index d9b358b613f..2cbf7883085 100644 --- a/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr +++ b/crates/ink/tests/ui/trait_def/fail/message_output_non_codec.stderr @@ -1,39 +1,39 @@ error[E0277]: the trait bound `NonCodec: WrapperTypeEncode` is not satisfied - --> tests/ui/trait_def/fail/message_output_non_codec.rs:6:26 - | -6 | fn message(&self) -> NonCodec; - | ^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` - | - = help: the following other types implement trait `WrapperTypeEncode`: - &T - &mut T - Arc - Box - Cow<'a, T> - Rc - String - Vec - parity_scale_codec::Ref<'a, T, U> - = note: required for `NonCodec` to implement `Encode` + --> tests/ui/trait_def/fail/message_output_non_codec.rs:6:26 + | +6 | fn message(&self) -> NonCodec; + | ^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonCodec` + | + = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc + Box + Cow<'a, T> + Rc + String + Vec + parity_scale_codec::Ref<'a, T, U> + = note: required for `NonCodec` to implement `Encode` note: required by a bound in `DispatchOutput` - --> src/codegen/dispatch/type_check.rs - | - | T: scale::Encode + 'static; - | ^^^^^^^^^^^^^ required by this bound in `DispatchOutput` + --> src/codegen/dispatch/type_check.rs + | + | T: scale::Encode + 'static; + | ^^^^^^^^^^^^^ required by this bound in `DispatchOutput` -error[E0599]: the method `fire` exists for struct `CallBuilder>, Set>>, Set>>`, but its trait bounds were not satisfied - --> tests/ui/trait_def/fail/message_output_non_codec.rs:5:5 - | -1 | pub struct NonCodec; - | ------------------- doesn't satisfy `NonCodec: parity_scale_codec::Decode` +error[E0599]: the method `try_fire` exists for struct `CallBuilder>, Set>>, Set>>`, but its trait bounds were not satisfied + --> tests/ui/trait_def/fail/message_output_non_codec.rs:5:5 + | +1 | pub struct NonCodec; + | ------------------- doesn't satisfy `NonCodec: parity_scale_codec::Decode` ... -5 | #[ink(message)] - | ^ method cannot be called on `CallBuilder>, Set>>, Set>>` due to unsatisfied trait bounds - | - = note: the following trait bounds were not satisfied: - `NonCodec: parity_scale_codec::Decode` +5 | #[ink(message)] + | ^ method cannot be called on `CallBuilder>, Set>>, Set>>` due to unsatisfied trait bounds + | + = note: the following trait bounds were not satisfied: + `NonCodec: parity_scale_codec::Decode` note: the following trait must be implemented - --> $CARGO/parity-scale-codec-3.2.1/src/codec.rs - | - | pub trait Decode: Sized { - | ^^^^^^^^^^^^^^^^^^^^^^^ + --> $CARGO/parity-scale-codec-3.2.2/src/codec.rs + | + | pub trait Decode: Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/examples/delegator/lib.rs b/examples/delegator/lib.rs index ec45e70c0d3..69f5c5ab354 100644 --- a/examples/delegator/lib.rs +++ b/examples/delegator/lib.rs @@ -63,29 +63,17 @@ mod delegator { .endowment(total_balance / 4) .code_hash(accumulator_code_hash) .salt_bytes(salt) - .instantiate() - .unwrap_or_else(|error| { - panic!( - "failed at instantiating the Accumulator contract: {:?}", - error - ) - }); + .instantiate(); let adder = AdderRef::new(accumulator.clone()) .endowment(total_balance / 4) .code_hash(adder_code_hash) .salt_bytes(salt) - .instantiate() - .unwrap_or_else(|error| { - panic!("failed at instantiating the Adder contract: {:?}", error) - }); + .instantiate(); let subber = SubberRef::new(accumulator.clone()) .endowment(total_balance / 4) .code_hash(subber_code_hash) .salt_bytes(salt) - .instantiate() - .unwrap_or_else(|error| { - panic!("failed at instantiating the Subber contract: {:?}", error) - }); + .instantiate(); Self { which: Which::Adder, accumulator, @@ -174,8 +162,7 @@ mod delegator { .call(&ink_e2e::bob(), get, 0, None) .await .expect("calling `get` failed") - .return_value() - .expect("calling `get` returned a `LangError`"); + .return_value(); assert_eq!(value, 1234); let change = build_message::(delegator_acc_id.clone()) .call(|contract| contract.change(6)); @@ -191,8 +178,7 @@ mod delegator { .call(&ink_e2e::bob(), get, 0, None) .await .expect("calling `get` failed") - .return_value() - .expect("calling `get` returned a `LangError`"); + .return_value(); assert_eq!(value, 1234 + 6); // when @@ -216,8 +202,7 @@ mod delegator { .call(&ink_e2e::bob(), get, 0, None) .await .expect("calling `get` failed") - .return_value() - .expect("calling `get` returned a `LangError`"); + .return_value(); assert_eq!(value, 1234 + 6 - 3); Ok(()) diff --git a/examples/erc1155/lib.rs b/examples/erc1155/lib.rs index 9140fa21f80..0f86d98d48b 100644 --- a/examples/erc1155/lib.rs +++ b/examples/erc1155/lib.rs @@ -365,7 +365,7 @@ mod erc1155 { // If our recipient is a smart contract we need to see if they accept or // reject this transfer. If they reject it we need to revert the call. - let params = build_call::() + let result = build_call::() .call_type(Call::new().callee(to).gas_limit(5000)) .exec_input( ExecutionInput::new(Selector::new(ON_ERC_1155_RECEIVED_SELECTOR)) @@ -376,17 +376,20 @@ mod erc1155 { .push_arg(data), ) .returns::>() - .params(); + .params() + .try_invoke(); - match ink::env::invoke_contract(¶ms) { + match result { Ok(v) => { ink::env::debug_println!( "Received return value \"{:?}\" from contract {:?}", - v, + v.clone().expect( + "Call should be valid, don't expect a `LangError`." + ), from ); assert_eq!( - v, + v.clone().expect("Call should be valid, don't expect a `LangError`."), &ON_ERC_1155_RECEIVED_SELECTOR[..], "The recipient contract at {:?} does not accept token transfers.\n Expected: {:?}, Got {:?}", to, ON_ERC_1155_RECEIVED_SELECTOR, v diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs index 4608550f0aa..65e14a15f29 100644 --- a/examples/erc20/lib.rs +++ b/examples/erc20/lib.rs @@ -577,14 +577,10 @@ mod erc20 { // then assert_eq!( total_supply, - total_supply_res.return_value().unwrap(), + total_supply_res.return_value(), "total_supply" ); - assert_eq!( - transfer_to_bob, - balance_of_res.return_value().unwrap(), - "balance_of" - ); + assert_eq!(transfer_to_bob, balance_of_res.return_value(), "balance_of"); Ok(()) } @@ -671,7 +667,7 @@ mod erc20 { assert_eq!( total_supply - approved_value, - balance_of_res.return_value().unwrap(), + balance_of_res.return_value(), "balance_of" ); diff --git a/examples/flipper/lib.rs b/examples/flipper/lib.rs index d4d0c82b2ac..3525b8b633d 100644 --- a/examples/flipper/lib.rs +++ b/examples/flipper/lib.rs @@ -75,7 +75,7 @@ pub mod flipper { .call(&ink_e2e::bob(), get, 0, None) .await .expect("get failed"); - assert!(matches!(get_res.return_value(), Ok(false))); + assert!(matches!(get_res.return_value(), false)); // when let flip = build_message::(contract_acc_id.clone()) @@ -92,7 +92,7 @@ pub mod flipper { .call(&ink_e2e::bob(), get, 0, None) .await .expect("get failed"); - assert!(matches!(get_res.return_value(), Ok(true))); + assert!(matches!(get_res.return_value(), true)); Ok(()) } @@ -116,7 +116,7 @@ pub mod flipper { .call(&ink_e2e::bob(), get, 0, None) .await .expect("get failed"); - assert!(matches!(get_res.return_value(), Ok(false))); + assert!(matches!(get_res.return_value(), false)); Ok(()) } diff --git a/examples/lang-err-integration-tests/call-builder/lib.rs b/examples/lang-err-integration-tests/call-builder/lib.rs index 252353046b2..992bc01bc09 100755 --- a/examples/lang-err-integration-tests/call-builder/lib.rs +++ b/examples/lang-err-integration-tests/call-builder/lib.rs @@ -54,8 +54,8 @@ mod call_builder { let result = build_call::() .call_type(Call::new().callee(address)) .exec_input(ExecutionInput::new(Selector::new(selector))) - .returns::>() - .fire() + .returns::<()>() + .try_fire() .expect("Error from the Contracts pallet."); match result { @@ -67,6 +67,24 @@ mod call_builder { } } + /// Call a contract using the `CallBuilder`. + /// + /// Since we can't use the `CallBuilder` in a test environment directly we need this + /// wrapper to test things like crafting calls with invalid selectors. + /// + /// This message does not allow the caller to handle any `LangErrors`, for that use the + /// `call` message instead. + #[ink(message)] + pub fn fire(&mut self, address: AccountId, selector: [u8; 4]) { + use ink::env::call::build_call; + + build_call::() + .call_type(Call::new().callee(address)) + .exec_input(ExecutionInput::new(Selector::new(selector))) + .returns::<()>() + .fire() + } + /// Instantiate a contract using the `CreateBuilder`. /// /// Since we can't use the `CreateBuilder` in a test environment directly we need this @@ -94,13 +112,21 @@ mod call_builder { .try_instantiate() .expect("Error from the Contracts pallet."); - match result { - Ok(_) => None, - Err(e @ ink::LangError::CouldNotReadInput) => Some(e), - Err(_) => { - unimplemented!("No other `LangError` variants exist at the moment.") - } - } + let result = build_create::< + DefaultEnvironment, + constructors_return_value::ConstructorsReturnValueRef, + >() + .code_hash(code_hash) + .gas_limit(0) + .endowment(0) + .exec_input(ExecutionInput::new(Selector::new(selector)).push_arg(init_value)) + .salt_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]) + .params() + .try_instantiate(); + + // NOTE: Right now we can't handle any `LangError` from `instantiate`, we can only tell + // that our contract reverted (i.e we see error from the Contracts pallet). + result.ok().map(|id| ink::ToAccountId::to_account_id(&id)) } /// Attempt to instantiate a contract using the `CreateBuilder`. @@ -184,9 +210,7 @@ mod call_builder { .call(&ink_e2e::alice(), flipper_get, 0, None) .await .expect("Calling `flipper::get` failed"); - let initial_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let initial_value = get_call_result.return_value(); let selector = ink::selector_bytes!("invalid_selector"); let call = build_message::(contract_acc_id) @@ -196,9 +220,7 @@ mod call_builder { .await .expect("Calling `call_builder::call` failed"); - let flipper_result = call_result - .return_value() - .expect("Call to `call_builder::call` failed"); + let flipper_result = call_result.return_value(); assert!(matches!( flipper_result, @@ -211,14 +233,56 @@ mod call_builder { .call(&ink_e2e::alice(), flipper_get, 0, None) .await .expect("Calling `flipper::get` failed"); - let flipped_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let flipped_value = get_call_result.return_value(); assert!(flipped_value == initial_value); Ok(()) } + #[ink_e2e::test(additional_contracts = "../integration-flipper/Cargo.toml")] + async fn e2e_invalid_message_selector_panics_on_fire( + mut client: ink_e2e::Client, + ) -> E2EResult<()> { + let constructor = CallBuilderTestRef::new(); + let contract_acc_id = client + .instantiate("call_builder", &ink_e2e::ferdie(), constructor, 0, None) + .await + .expect("instantiate failed") + .account_id; + + let flipper_constructor = FlipperRef::new_default(); + let flipper_acc_id = client + .instantiate( + "integration_flipper", + &ink_e2e::ferdie(), + flipper_constructor, + 0, + None, + ) + .await + .expect("instantiate `flipper` failed") + .account_id; + + // Since `LangError`s can't be handled by the `CallBuilder::fire()` method we expect + // this to panic. + let invalid_selector = [0x00, 0x00, 0x00, 0x00]; + let call = build_message::(contract_acc_id) + .call(|contract| contract.fire(flipper_acc_id, invalid_selector)); + let call_result = client.call(&ink_e2e::ferdie(), call, 0, None).await; + + assert!(call_result.is_err()); + let contains_err_msg = match call_result.unwrap_err() { + ink_e2e::Error::CallDryRun(dry_run) => { + String::from_utf8_lossy(&dry_run.debug_message) + .contains("Cross-contract call failed with CouldNotReadInput") + } + _ => false, + }; + assert!(contains_err_msg); + + Ok(()) + } + #[ink_e2e::test(additional_contracts = "../constructors-return-value/Cargo.toml")] async fn e2e_create_builder_works_with_valid_selector( mut client: ink_e2e::Client, @@ -246,8 +310,7 @@ mod call_builder { .call(&ink_e2e::bob(), call, 0, None) .await .expect("Client failed to call `call_builder::call_instantiate`.") - .return_value() - .expect("Dispatching `call_builder::call_instantiate` failed."); + .return_value(); assert!( call_result.is_none(), @@ -284,8 +347,7 @@ mod call_builder { .call(&ink_e2e::charlie(), call, 0, None) .await .expect("Client failed to call `call_builder::call_instantiate`.") - .return_value() - .expect("Dispatching `call_builder::call_instantiate` failed."); + .return_value(); assert!( matches!(call_result, Some(ink::LangError::CouldNotReadInput)), diff --git a/examples/lang-err-integration-tests/constructors-return-value/lib.rs b/examples/lang-err-integration-tests/constructors-return-value/lib.rs index 339cc95b508..018df58ddf9 100644 --- a/examples/lang-err-integration-tests/constructors-return-value/lib.rs +++ b/examples/lang-err-integration-tests/constructors-return-value/lib.rs @@ -216,8 +216,7 @@ pub mod constructors_return_value { .call(&ink_e2e::bob(), get, 0, None) .await .expect("Calling `get_value` failed") - .return_value() - .expect("Input is valid, call must not fail."); + .return_value(); assert_eq!( true, value, diff --git a/examples/lang-err-integration-tests/contract-ref/lib.rs b/examples/lang-err-integration-tests/contract-ref/lib.rs index 03ed299bce5..c402f447e1f 100755 --- a/examples/lang-err-integration-tests/contract-ref/lib.rs +++ b/examples/lang-err-integration-tests/contract-ref/lib.rs @@ -17,10 +17,7 @@ mod contract_ref { .endowment(0) .code_hash(flipper_code_hash) .salt_bytes(salt) - .instantiate() - .unwrap_or_else(|error| { - panic!("Received an error from the Contracts pallet while instantiating Flipper {:?}", error) - }); + .instantiate(); Self { flipper } } @@ -98,9 +95,7 @@ mod contract_ref { .call(&ink_e2e::alice(), get_check, 0, None) .await .expect("Calling `get_check` failed"); - let initial_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let initial_value = get_call_result.return_value(); let flip_check = build_message::(contract_acc_id.clone()) .call(|contract| contract.flip_check()); @@ -119,9 +114,7 @@ mod contract_ref { .call(&ink_e2e::alice(), get_check, 0, None) .await .expect("Calling `get_check` failed"); - let flipped_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let flipped_value = get_call_result.return_value(); assert!(flipped_value != initial_value); Ok(()) diff --git a/examples/lang-err-integration-tests/integration-flipper/lib.rs b/examples/lang-err-integration-tests/integration-flipper/lib.rs index a2d56ccc308..1a9eeaf70f5 100644 --- a/examples/lang-err-integration-tests/integration-flipper/lib.rs +++ b/examples/lang-err-integration-tests/integration-flipper/lib.rs @@ -92,9 +92,7 @@ pub mod integration_flipper { .call(&ink_e2e::alice(), get, 0, None) .await .expect("Calling `get` failed"); - let initial_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let initial_value = get_call_result.return_value(); let flip = build_message::(contract_acc_id) .call(|contract| contract.flip()); @@ -113,9 +111,7 @@ pub mod integration_flipper { .call(&ink_e2e::alice(), get, 0, None) .await .expect("Calling `get` failed"); - let flipped_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let flipped_value = get_call_result.return_value(); assert!(flipped_value != initial_value); Ok(()) @@ -138,9 +134,7 @@ pub mod integration_flipper { .call(&ink_e2e::bob(), get, 0, None) .await .expect("Calling `get` failed"); - let initial_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let initial_value = get_call_result.return_value(); let err_flip = build_message::(contract_acc_id) .call(|contract| contract.err_flip()); @@ -158,9 +152,7 @@ pub mod integration_flipper { .call(&ink_e2e::bob(), get, 0, None) .await .expect("Calling `get` failed"); - let flipped_value = get_call_result - .return_value() - .expect("Input is valid, call must not fail."); + let flipped_value = get_call_result.return_value(); assert!(flipped_value == initial_value); Ok(()) diff --git a/examples/multisig/lib.rs b/examples/multisig/lib.rs index 16a35430528..42fa16593df 100755 --- a/examples/multisig/lib.rs +++ b/examples/multisig/lib.rs @@ -348,8 +348,7 @@ mod multisig { /// .push_arg(&transaction_candidate) /// ) /// .returns::<(u32, ConfirmationStatus)>() - /// .fire() - /// .expect("submit_transaction won't panic"); + /// .fire(); /// /// // Wait until all required owners have confirmed and then execute the transaction /// // @@ -362,8 +361,7 @@ mod multisig { /// .push_arg(&id) /// ) /// .returns::<()>() - /// .fire() - /// .expect("invoke_transaction won't panic"); + /// .fire(); /// ``` #[ink(message)] pub fn add_owner(&mut self, new_owner: AccountId) { @@ -549,8 +547,13 @@ mod multisig { ExecutionInput::new(t.selector.into()).push_arg(CallInput(&t.input)), ) .returns::<()>() - .fire() - .map_err(|_| Error::TransactionFailed); + .try_fire(); + + let result = match result { + Ok(Ok(_)) => Ok(()), + _ => Err(Error::TransactionFailed), + }; + self.env().emit_event(Execution { transaction: trans_id, result: result.map(|_| None), @@ -582,8 +585,13 @@ mod multisig { ExecutionInput::new(t.selector.into()).push_arg(CallInput(&t.input)), ) .returns::>() - .fire() - .map_err(|_| Error::TransactionFailed); + .try_fire(); + + let result = match result { + Ok(Ok(v)) => Ok(v), + _ => Err(Error::TransactionFailed), + }; + self.env().emit_event(Execution { transaction: trans_id, result: result.clone().map(Some), diff --git a/examples/upgradeable-contracts/forward-calls/lib.rs b/examples/upgradeable-contracts/forward-calls/lib.rs index 09a47cd3996..b4b073a512b 100644 --- a/examples/upgradeable-contracts/forward-calls/lib.rs +++ b/examples/upgradeable-contracts/forward-calls/lib.rs @@ -81,11 +81,17 @@ pub mod proxy { .set_forward_input(true) .set_tail_call(true), ) - .fire() - .unwrap_or_else(|err| { + .try_fire() + .unwrap_or_else(|env_err| { panic!( "cross-contract call to {:?} failed due to {:?}", - self.forward_to, err + self.forward_to, env_err + ) + }) + .unwrap_or_else(|lang_err| { + panic!( + "cross-contract call to {:?} failed due to {:?}", + self.forward_to, lang_err ) }); unreachable!(