Skip to content

Commit

Permalink
Add support for language level errors (LangError) (#1450)
Browse files Browse the repository at this point in the history
* Add example cross-chain contract

* Update `CallBuilder`'s return type to a `Result`

We're gonna try and handle an ink! error later

* Write to output buffer on dispatch error

* Whoops, reduce value of `u8`

* Add some `cargo-contract` automation helpers

* Set default if no input is given

* Return type expected by call builder after succesful dispatch

* Fix typo

* Return an actual `DispatchError` back to the caller

* Temporarily stop generating contract ref code

* Manually change message return type to Result

* Transform message return type in the IR

* Change message return type when generating contract reference

* Return message output directly in the codegen

* Add error type for handling language errors

* Clean up some things

* Remove some unused code

* Add DispatchError type metadata

* Use language error over dispatch error

* Change message body's visibility based on message signature

* Fix unreachable code warning

* Appease Clippy

* Rustfmt cross-contract example

* Fix formatting

* Write `lang_error` into metadata using spec builder

* Use `parse_quote` macro over the `syn::parse` function

* Use correct span in call builder

* Always return `Type` instead of `Option` from `wrapped_output`

* Temporarily appease Clippy

* Another temporary Clippy fix

* Generate checked and unchecked versions of contract messages

We're now able to specify if we want to use an unchecked or checked
version of the message functions in our code. This means that existing
code, such as in tests, doesn't need to worry about the new wrapped
`Result` type.

At the same time, as far as external callers are concerned they are
always calling the checked version of the messages.

* Use non-checked message as checked message implementation

* Remove error handling from test

* Add method for generating checked message idents

* Fix UI tests related to `no_implicit_prelude`

* Remove unneccessary import

* Add `Default` implementation for `cross_chain_test`

* Bless some UI tests

* Update `Output` type for doc test

* Update `cross_chain_test` to appease Clippy

* Generate `*_checked` functions for `ContractRef` calls

* Implement `Debug` for Multisig Wasm builds

* Add another `Debug` impl

* Add `lang_error` to metadata tests

* Add example which uses ContractRefs

* Handle dispatch error in example

* Add UI test for checking that messages now return `Result`s

* Add test for message hygiene (or lack thereof)

* Add test to check that `*_checked` messages are generated

* Remove debug logging from macro code

* Appease Clippy

* Remove `--skip-linting` from scripts

This is because I've updated my local version of `cargo-contract`

* Fix fallible constructor revert, make `Ok` explicit

* Hack together E2E test example for `LangError`

* Add a couple of type aliases to E2E test

* Add E2E test which demonstrates write-on-err bug

Currently using a single test for all E2E tests since there seems to be
some problem with the nonce management that isn't letting me split the
tests out into seperate functions.

* Split out E2E tests into seperate functions

Had to use make calls using different accounts

* Tweak E2E tests to make them pass given Andrew's fixes

* Remove outdated UI test

* Fix a couple more tests

* Unify output type and arrow in codegen

* Allow `result_unit_err` Clippy lint

* Move `Flipper` functionality into integration example

* Rename `cross_chain_test` to indicate that its an integration test

* Remove test helper scripts

* Remove `ContractRef` example/compile test

It would be nice to have this as a proper compile test at some point,
but right now the set of delegator contracts can be used as an
indication for breakages.

* Appease Clippy

* Add `MessageResult` type alias

* Remove `Ref` re-export from Flipper

* Remove TODOs related to E2E issues

These have now been logged in the issue tracker.

* Work around `additional_contracts` bug by moving tests

* Clean up `lang_err_integration_tests` a bit

* Fix spellcheck lint

* Place E2E tests behind feature flag

* Apply suggestions from code review

Co-authored-by: Andrew Jones <ascjones@gmail.com>

* Unwrap message result in delegator E2E tests

* Revert "Remove `ContractRef` example/compile test"

This reverts commit 6c24543.

* Move `LangError` integration tests into single workspace

* Move flip related E2E tests to `integration_flipper`

* Move `integration_flipper` into its own folder

This way we can refer to it from the `additional_contracts` argument of
the `ink_e2e::test` macro.

* Update `call_builder` test to use `integration_flipper`

* Get `contract_ref` E2E test working again

* Fill out author and version metadata

* Add integration tests to CI

* Remove leftover .gitignore

* Skip default checks if it's a lang error integration test

* Fix UI test

* Skip a few CI stages

* Add back Clippy stage

* Revert "Skip a few CI stages"

This reverts commit 1a74375.

* Run `LangError` E2E tests in CI

* Rename tests using kabob-case instead of snake_case

* Try running with `staging` image

* Change image back to `production`

Didn't help either way

* Try cleaning some things

* Guess there was no lockfile

* Clean up the cleanup

* Clean em up again

* Use `MessageResult` typedef in one more place

* Poke CI

* Move comment location

* Update contract name

Co-authored-by: Andrew Jones <ascjones@gmail.com>
Co-authored-by: xermicus <cyrill@parity.io>
  • Loading branch information
3 people committed Nov 16, 2022
1 parent 6c1a5e8 commit 6c6e51a
Show file tree
Hide file tree
Showing 26 changed files with 811 additions and 69 deletions.
28 changes: 28 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ variables:
ALL_CRATES: "${PURELY_STD_CRATES} ${ALSO_WASM_CRATES}"
DELEGATOR_SUBCONTRACTS: "accumulator adder subber"
UPGRADEABLE_CONTRACTS: "forward-calls set-code-hash"
LANG_ERR_INTEGRATION_CONTRACTS: "integration-flipper call-builder contract-ref"
# TODO `cargo clippy --verbose --all-targets --all-features` for this crate
# currently fails on `stable`, but succeeds on `nightly`. This is due to
# this fix not yet in stable: https://github.com/rust-lang/rust-clippy/issues/8895.
Expand Down Expand Up @@ -122,6 +123,7 @@ examples-fmt:
# Note that we disable the license header check for the examples, since they are unlicensed.
- for example in examples/*/; do
if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi;
if [ "$example" = "examples/lang-err-integration-tests/" ]; then continue; fi;
cargo +nightly fmt --verbose --manifest-path ${example}/Cargo.toml -- --check;
done
- for contract in ${DELEGATOR_SUBCONTRACTS}; do
Expand All @@ -130,6 +132,9 @@ examples-fmt:
- for contract in ${UPGRADEABLE_CONTRACTS}; do
cargo +nightly fmt --verbose --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml -- --check;
done
- for contract in ${LANG_ERR_INTEGRATION_CONTRACTS}; do
cargo +nightly fmt --verbose --manifest-path ./examples/lang-err-integration-tests/${contract}/Cargo.toml -- --check;
done
- cargo +nightly fmt --verbose --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml -- --check
# This file is not a part of the cargo project, so it wouldn't be formatted the usual way
- rustfmt +nightly --verbose --check ./examples/psp22-extension/runtime/psp22-extension-example.rs
Expand Down Expand Up @@ -161,6 +166,7 @@ examples-clippy-std:
script:
- for example in examples/*/; do
if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi;
if [ "$example" = "examples/lang-err-integration-tests/" ]; then continue; fi;
cargo clippy --verbose --all-targets --manifest-path ${example}/Cargo.toml -- -D warnings -A $CLIPPY_ALLOWED;
done
- for contract in ${DELEGATOR_SUBCONTRACTS}; do
Expand All @@ -169,6 +175,9 @@ examples-clippy-std:
- for contract in ${UPGRADEABLE_CONTRACTS}; do
cargo clippy --verbose --all-targets --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml -- -D warnings -A $CLIPPY_ALLOWED;
done
- for contract in ${LANG_ERR_INTEGRATION_CONTRACTS}; do
cargo clippy --verbose --all-targets --manifest-path ./examples/lang-err-integration-tests/${contract}/Cargo.toml -- -D warnings -A $CLIPPY_ALLOWED;
done
- cargo clippy --verbose --all-targets --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml -- -D warnings -A $CLIPPY_ALLOWED;
allow_failure: true

Expand All @@ -179,6 +188,7 @@ examples-clippy-wasm:
script:
- for example in examples/*/; do
if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi;
if [ "$example" = "examples/lang-err-integration-tests/" ]; then continue; fi;
cargo clippy --verbose --manifest-path ${example}/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings -A $CLIPPY_ALLOWED;
done
- for contract in ${DELEGATOR_SUBCONTRACTS}; do
Expand All @@ -187,6 +197,9 @@ examples-clippy-wasm:
- for contract in ${UPGRADEABLE_CONTRACTS}; do
cargo clippy --verbose --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings -A $CLIPPY_ALLOWED;
done
- for contract in ${LANG_ERR_INTEGRATION_CONTRACTS}; do
cargo clippy --verbose --manifest-path ./examples/lang-err-integration-tests/${contract}/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings -A $CLIPPY_ALLOWED;
done
- cargo clippy --verbose --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings -A $CLIPPY_ALLOWED;
allow_failure: true

Expand Down Expand Up @@ -363,6 +376,7 @@ examples-test:
- *start-substrate-contracts-node
- for example in examples/*/; do
if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi;
if [ "$example" = "examples/lang-err-integration-tests/" ]; then continue; fi;
if grep -q "e2e-tests = \[\]" "${example}/Cargo.toml"; then
cargo test --verbose --manifest-path ${example}/Cargo.toml --features e2e-tests;
else
Expand All @@ -375,6 +389,12 @@ examples-test:
- for contract in ${UPGRADEABLE_CONTRACTS}; do
cargo test --verbose --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml;
done
# TODO (#1502): We need to clean before running, otherwise the CI fails with a
# linking error.
- for contract in ${LANG_ERR_INTEGRATION_CONTRACTS}; do
cargo clean --verbose --manifest-path ./examples/lang-err-integration-tests/${contract}/Cargo.toml;
cargo test --verbose --manifest-path ./examples/lang-err-integration-tests/${contract}/Cargo.toml --features e2e-tests;
done
- cargo test --verbose --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml;

examples-contract-build:
Expand All @@ -386,6 +406,7 @@ examples-contract-build:
- cargo contract -V
- for example in examples/*/; do
if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi;
if [ "$example" = "examples/lang-err-integration-tests/" ]; then continue; fi;
pushd $example &&
cargo +stable contract build &&
popd;
Expand All @@ -394,6 +415,9 @@ examples-contract-build:
- for contract in ${UPGRADEABLE_CONTRACTS}; do
cargo +stable contract build --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml;
done
- for contract in ${LANG_ERR_INTEGRATION_CONTRACTS}; do
cargo +stable contract build --manifest-path ./examples/lang-err-integration-tests/${contract}/Cargo.toml;
done
- cargo +stable contract build --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml

examples-docs:
Expand All @@ -409,6 +433,7 @@ examples-docs:
# of this flag.
- for example in examples/*/; do
if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi;
if [ "$example" = "examples/lang-err-integration-tests/" ]; then continue; fi;
cargo doc --manifest-path ${example}/Cargo.toml --document-private-items --verbose --no-deps;
done
- for contract in ${DELEGATOR_SUBCONTRACTS}; do
Expand All @@ -417,6 +442,9 @@ examples-docs:
- for contract in ${UPGRADEABLE_CONTRACTS}; do
cargo doc --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml --document-private-items --verbose --no-deps;
done
- for contract in ${LANG_ERR_INTEGRATION_CONTRACTS}; do
cargo doc --manifest-path ./examples/lang-err-integration-tests/${contract}/Cargo.toml --document-private-items --verbose --no-deps;
done
- cargo doc --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml --document-private-items --verbose --no-deps


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,10 +369,8 @@ 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 output = message.output();
let return_type =
output.map_or_else(|| quote! { () }, |output| quote! { #output });
let output_span = output.span();
let return_type = message.wrapped_output();
let output_span = return_type.span();
let output_type = quote_spanned!(output_span=>
::ink::env::call::CallBuilder<
Environment,
Expand Down
17 changes: 17 additions & 0 deletions crates/ink/codegen/src/generator/as_dependency/contract_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ impl ContractRef<'_> {
.filter_attr(message.attrs().to_vec());
let storage_ident = self.contract.module().storage().ident();
let message_ident = message.ident();
let checked_message_ident = message.checked_ident();
let call_operator = match message.receiver() {
ir::Receiver::Ref => quote! { call },
ir::Receiver::RefMut => quote! { call_mut },
Expand All @@ -344,13 +345,29 @@ impl ContractRef<'_> {
let input_bindings = message.inputs().map(|input| &input.pat).collect::<Vec<_>>();
let input_types = message.inputs().map(|input| &input.ty).collect::<Vec<_>>();
let output_type = message.output().map(|ty| quote! { -> #ty });
let wrapped_output_type = message.wrapped_output();
quote_spanned!(span=>
#( #attrs )*
#[inline]
pub fn #message_ident(
& #mut_token self
#( , #input_bindings : #input_types )*
) #output_type {
self.#checked_message_ident( #( #input_bindings, )* )
.unwrap_or_else(|error| ::core::panic!(
"encountered error while calling {}::{}: {:?}",
::core::stringify!(#storage_ident),
::core::stringify!(#message_ident),
error,
))
}

#( #attrs )*
#[inline]
pub fn #checked_message_ident(
& #mut_token self
#( , #input_bindings : #input_types )*
) -> #wrapped_output_type {
<Self as ::ink::codegen::TraitCallBuilder>::#call_operator(self)
.#message_ident( #( #input_bindings ),* )
.fire()
Expand Down
57 changes: 37 additions & 20 deletions crates/ink/codegen/src/generator/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,16 +435,34 @@ impl Dispatch<'_> {
.unwrap_or_else(|error| ::core::panic!("{}", error))
}

::ink::env::decode_input::<
<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type>()
.map_err(|_| ::ink::reflect::DispatchError::CouldNotReadInput)
.and_then(|decoder| {
<<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type
as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(decoder)
})
.unwrap_or_else(|error| {
::core::panic!("dispatching ink! message failed: {}", error)
})
let dispatchable = match ::ink::env::decode_input::<
<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type,
>() {
::core::result::Result::Ok(decoded_dispatchable) => {
decoded_dispatchable
}
::core::result::Result::Err(_decoding_error) => {
use ::core::default::Default;
let error = ::core::result::Result::Err(::ink::LangError::CouldNotReadInput);

// At this point we're unable to set the `Ok` variant to be the any "real"
// message output since we were unable to figure out what the caller wanted
// to dispatch in the first place, so we set it to `()`.
//
// This is okay since we're going to only be encoding the `Err` variant
// into the output buffer anyways.
::ink::env::return_value::<::ink::MessageResult<()>>(
::ink::env::ReturnFlags::default().set_reverted(true),
&error,
);
}
};

<<#storage_ident as ::ink::reflect::ContractMessageDecoder>::Type
as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
.unwrap_or_else(|error| {
::core::panic!("dispatching ink! message failed: {}", error)
})
}
)
}
Expand Down Expand Up @@ -761,23 +779,24 @@ impl Dispatch<'_> {
let failure = ::ink::is_result_type!(#message_output)
&& ::ink::is_result_err!(result);

// Currently no `LangError`s are raised at this level of the dispatch logic
// so `Ok` is always returned to the caller.
let return_value = ::core::result::Result::Ok(result);

if failure {
// We return early here since there is no need to push back the
// intermediate results of the contract - the transaction is going to be
// reverted anyways.
::ink::env::return_value::<#message_output>(
::ink::env::ReturnFlags::default().set_reverted(true), &result
::ink::env::return_value::<::ink::MessageResult::<#message_output>>(
::ink::env::ReturnFlags::default().set_reverted(true), &return_value
)
}

push_contract(contract, #mutates_storage);

if ::core::any::TypeId::of::<#message_output>() != ::core::any::TypeId::of::<()>() {
// In case the return type is `()` we do not return a value.
::ink::env::return_value::<#message_output>(
::ink::env::ReturnFlags::default(), &result
)
}
::ink::env::return_value::<::ink::MessageResult::<#message_output>>(
::ink::env::ReturnFlags::default(), &return_value
)
}
)
});
Expand Down Expand Up @@ -848,8 +867,6 @@ impl Dispatch<'_> {
match self {
#( #message_execute ),*
};

::core::result::Result::Ok(())
}
}

Expand Down
10 changes: 9 additions & 1 deletion crates/ink/codegen/src/generator/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ impl Metadata<'_> {
.attrs()
.iter()
.filter_map(|attr| attr.extract_docs());
let error_ty = syn::parse_quote! {
::ink::LangError
};
let error = Self::generate_type_spec(&error_ty);
quote! {
::ink::metadata::ContractSpec::new()
.constructors([
Expand All @@ -107,6 +111,9 @@ impl Metadata<'_> {
.docs([
#( #docs ),*
])
.lang_error(
#error
)
.done()
}
}
Expand Down Expand Up @@ -172,6 +179,7 @@ impl Metadata<'_> {
fn without_display_name(ty: &syn::Type) -> TokenStream2 {
quote! { ::ink::metadata::TypeSpec::of_type::<#ty>() }
}

if let syn::Type::Path(type_path) = ty {
if type_path.qself.is_some() {
return without_display_name(ty)
Expand Down Expand Up @@ -256,7 +264,7 @@ impl Metadata<'_> {
let mutates = message.receiver().is_ref_mut();
let ident = message.ident();
let args = message.inputs().map(Self::generate_dispatch_argument);
let ret_ty = Self::generate_return_type(message.output());
let ret_ty = Self::generate_return_type(Some(&message.wrapped_output()));
quote_spanned!(span =>
::ink::metadata::MessageSpec::from_label(::core::stringify!(#ident))
.selector([
Expand Down
20 changes: 20 additions & 0 deletions crates/ink/ir/src/ir/item_impl/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,21 @@ impl Message {
}
}

/// Returns the return type of the message, but wrapped within a `Result`.
///
/// This is used to to allow callers to handle certain types of errors which are not exposed
/// by messages.
pub fn wrapped_output(&self) -> syn::Type {
let return_type = self
.output()
.map(quote::ToTokens::to_token_stream)
.unwrap_or_else(|| quote::quote! { () });

syn::parse_quote! {
::ink::MessageResult<#return_type>
}
}

/// Returns a local ID unique to the ink! message with respect to its implementation block.
///
/// # Note
Expand All @@ -301,6 +316,11 @@ impl Message {
pub fn local_id(&self) -> u32 {
utils::local_message_id(self.ident())
}

/// Returns the identifier of the message with an additional `_checked` suffix attached.
pub fn checked_ident(&self) -> Ident {
quote::format_ident!("{}_checked", self.ident())
}
}

#[cfg(test)]
Expand Down
4 changes: 4 additions & 0 deletions crates/ink/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,7 @@ pub use ink_macro::{
test,
trait_definition,
};
pub use ink_primitives::{
LangError,
MessageResult,
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ error[E0277]: the trait bound `contract::Error: TypeInfo` is not satisfied
(A, B, C, D)
(A, B, C, D, E)
(A, B, C, D, E, F)
and 64 others
and $N others
= note: required for `Result<(), contract::Error>` to implement `ConstructorReturnSpec`
= note: this error originates in the attribute macro `ink::contract` (in Nightly builds, run with -Z macro-backtrace for more info)
20 changes: 20 additions & 0 deletions crates/ink/tests/ui/contract/fail/message-hygiene-checked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[ink::contract]
mod contract {
#[ink(storage)]
pub struct Contract {}

impl Contract {
#[ink(constructor)]
pub fn constructor() -> Self {
Self {}
}

#[ink(message)]
pub fn message(&self) {}

#[ink(message)]
pub fn message_checked(&self) {}
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error[E0201]: duplicate definitions with name `message_checked`:
--> tests/ui/contract/fail/message-hygiene-checked.rs:16:9
|
1 | #[ink::contract]
| ---------------- previous definition of `message_checked` here
...
16 | pub fn message_checked(&self) {}
| ^^^ duplicate definition
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ note: required by a bound in `ExecutionInput::<ArgumentList<ArgumentListEnd, Arg
| T: scale::Encode,
| ^^^^^^^^^^^^^ required by this bound in `ExecutionInput::<ArgumentList<ArgumentListEnd, ArgumentListEnd>>::push_arg`

error[E0599]: the method `fire` exists for struct `ink::ink_env::call::CallBuilder<DefaultEnvironment, Set<Call<DefaultEnvironment>>, Set<ExecutionInput<ArgumentList<ink::ink_env::call::utils::Argument<NonCodecType>, ArgumentList<ArgumentListEnd, ArgumentListEnd>>>>, Set<ReturnType<()>>>`, but its trait bounds were not satisfied
error[E0599]: the method `fire` exists for struct `ink::ink_env::call::CallBuilder<DefaultEnvironment, Set<Call<DefaultEnvironment>>, Set<ExecutionInput<ArgumentList<ink::ink_env::call::utils::Argument<NonCodecType>, ArgumentList<ArgumentListEnd, ArgumentListEnd>>>>, Set<ReturnType<Result<(), LangError>>>>`, 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<DefaultEnvironment, Set<Call<DefaultEnvironment>>, Set<ExecutionInput<ArgumentList<ink::ink_env::call::utils::Argument<NonCodecType>, ArgumentList<ArgumentListEnd, ArgumentListEnd>>>>, Set<ReturnType<()>>>` due to unsatisfied trait bounds
| ^^^ method cannot be called on `ink::ink_env::call::CallBuilder<DefaultEnvironment, Set<Call<DefaultEnvironment>>, Set<ExecutionInput<ArgumentList<ink::ink_env::call::utils::Argument<NonCodecType>, ArgumentList<ArgumentListEnd, ArgumentListEnd>>>>, Set<ReturnType<Result<(), LangError>>>>` due to unsatisfied trait bounds
|
::: $WORKSPACE/crates/env/src/call/execution_input.rs
|
Expand Down
Loading

0 comments on commit 6c6e51a

Please sign in to comment.