1use cfg_if::cfg_if;
16use ink_primitives::ConstructorResult;
17use pallet_revive_uapi::ReturnErrorCode;
18
19use crate::{
20 Error,
21 Result as EnvResult,
22 backend::{
23 EnvBackend,
24 TypedEnvBackend,
25 },
26 call::{
27 ConstructorReturnType,
28 FromAddr,
29 utils::{
30 ConstructorError,
31 DecodeConstructorError,
32 },
33 },
34};
35
36macro_rules! array_mut_ref {
45 ($arr:expr, $offset:expr, $len:expr) => {{
46 {
47 fn as_array<T>(slice: &mut [T]) -> &mut [T; $len] {
48 slice.try_into().unwrap()
49 }
50 let offset: usize = $offset;
51 let slice = &mut $arr[offset..offset.checked_add($len).unwrap()];
52 as_array(slice)
53 }
54 }};
55}
56
57pub trait OnInstance: EnvBackend + TypedEnvBackend {
58 fn on_instance<F, R>(f: F) -> R
59 where
60 F: FnOnce(&mut Self) -> R;
61}
62
63cfg_if! {
64 if #[cfg(not(feature = "std"))] {
65 mod on_chain;
66 pub use self::on_chain::EnvInstance;
67 } else {
68 pub mod off_chain;
69 pub use self::off_chain::EnvInstance;
70 }
71}
72
73#[cfg_attr(all(feature = "std", not(test)), allow(dead_code))]
75pub(crate) fn decode_instantiate_result<I, ContractRef, R, Abi>(
76 instantiate_result: EnvResult<()>,
77 out_address: &mut I,
78 out_return_value: &[u8],
79) -> EnvResult<ConstructorResult<<R as ConstructorReturnType<ContractRef, Abi>>::Output>>
80where
81 I: scale::Input,
82 ContractRef: FromAddr,
83 R: ConstructorReturnType<ContractRef, Abi>,
84{
85 match instantiate_result {
86 Ok(()) => {
87 let addr = scale::Decode::decode(out_address)?;
88 let contract_ref = <ContractRef as FromAddr>::from_addr(addr);
89 let output = <R as ConstructorReturnType<ContractRef, Abi>>::ok(contract_ref);
90 Ok(Ok(output))
91 }
92 Err(Error::ReturnError(ReturnErrorCode::CalleeReverted)) => {
93 decode_instantiate_err::<ContractRef, R, Abi>(out_return_value)
94 }
95 Err(actual_error) => Err(actual_error),
96 }
97}
98
99#[cfg_attr(all(feature = "std", not(test)), allow(dead_code))]
100fn decode_instantiate_err<ContractRef, R, Abi>(
101 out_return_value: &[u8],
102) -> EnvResult<ConstructorResult<<R as ConstructorReturnType<ContractRef, Abi>>::Output>>
103where
104 ContractRef: FromAddr,
105 R: ConstructorReturnType<ContractRef, Abi>,
106{
107 let decoded_error =
108 <<R as ConstructorReturnType<ContractRef, Abi>>::Error as DecodeConstructorError<
109 Abi,
110 >>::decode_error_output(out_return_value);
111 match decoded_error {
112 ConstructorError::Contract(contract_err) => {
113 let error_output =
114 <R as ConstructorReturnType<ContractRef, Abi>>::err(contract_err);
115 match error_output {
116 Some(output) => Ok(Ok(output)),
117 None => {
118 Err(Error::ReturnError(ReturnErrorCode::CalleeReverted))
122 }
123 }
124 }
125 ConstructorError::Lang(lang_err) => Ok(Err(lang_err)),
126 ConstructorError::Env(env_err) => Err(env_err),
127 }
128}
129
130#[cfg(test)]
131mod decode_instantiate_result_tests {
132 use super::*;
133 use crate::DefaultEnvironment;
134 use ink_primitives::Address;
135 use scale::Encode;
136
137 type ContractResult<T, E> = Result<T, E>;
139
140 #[derive(Debug, PartialEq, scale::Encode, scale::Decode)]
141 struct ContractError(String);
142
143 #[allow(dead_code)]
145 #[derive(Debug, PartialEq)]
146 struct TestContractRef(Address);
147
148 impl crate::ContractEnv for TestContractRef {
149 type Env = DefaultEnvironment;
150 }
151
152 impl FromAddr for TestContractRef {
153 fn from_addr(addr: Address) -> Self {
154 Self(addr)
155 }
156 }
157
158 fn encode_and_decode_return_value(
159 return_value: ConstructorResult<Result<(), ContractError>>,
160 ) -> EnvResult<ConstructorResult<Result<TestContractRef, ContractError>>> {
161 let out_address = Vec::new();
162 let encoded_return_value = return_value.encode();
163 decode_return_value_fallible(&mut &out_address[..], &encoded_return_value[..])
164 }
165
166 fn decode_return_value_fallible<I: scale::Input>(
167 out_address: &mut I,
168 out_return_value: &[u8],
169 ) -> EnvResult<ConstructorResult<Result<TestContractRef, ContractError>>> {
170 decode_instantiate_result::<
171 I,
172 TestContractRef,
173 Result<TestContractRef, ContractError>,
174 ink_primitives::abi::Ink,
175 >(
176 Err(ReturnErrorCode::CalleeReverted.into()),
177 out_address,
178 out_return_value,
179 )
180 }
181
182 #[test]
183 fn revert_branch_rejects_valid_output_buffer_with_success_case() {
184 let return_value = ConstructorResult::Ok(ContractResult::Ok(()));
185
186 let decoded_result = encode_and_decode_return_value(return_value);
187
188 let expected_error: EnvResult<
189 ConstructorResult<Result<TestContractRef, ContractError>>,
190 > = EnvResult::Err(crate::Error::Decode(
191 "The callee reverted, but did not encode an error in the output buffer."
192 .into(),
193 ));
194 assert_eq!(decoded_result, expected_error);
195 }
196
197 #[test]
198 fn successful_dispatch_with_error_from_contract_constructor() {
199 let return_value = ConstructorResult::Ok(ContractResult::Err(ContractError(
200 "Contract's constructor failed.".to_owned(),
201 )));
202
203 let decoded_result = encode_and_decode_return_value(return_value);
204
205 assert!(matches!(
206 decoded_result,
207 EnvResult::Ok(ConstructorResult::Ok(ContractResult::Err(ContractError(_))))
208 ))
209 }
210
211 #[test]
212 fn dispatch_error_gets_decoded_correctly() {
213 let return_value =
214 ConstructorResult::Err(ink_primitives::LangError::CouldNotReadInput);
215
216 let decoded_result = encode_and_decode_return_value(return_value);
217
218 assert!(matches!(
219 decoded_result,
220 EnvResult::Ok(ConstructorResult::Err(
221 ink_primitives::LangError::CouldNotReadInput
222 ))
223 ))
224 }
225
226 #[test]
227 fn invalid_bytes_in_output_buffer_fail_decoding() {
228 let out_address = Vec::new();
229 let invalid_encoded_return_value = [69];
230
231 let decoded_result = decode_return_value_fallible(
232 &mut &out_address[..],
233 &invalid_encoded_return_value[..],
234 );
235
236 assert!(matches!(decoded_result, Err(crate::Error::Decode(_))))
237 }
238}