ink_env/engine/off_chain/
impls.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use ink_engine::ext::Engine;
16use ink_primitives::{
17    Address,
18    CodeHashErr,
19    H256,
20    U256,
21    abi::{
22        AbiEncodeWith,
23        Ink,
24        Sol,
25    },
26    sol::SolResultEncode,
27    types::{
28        AccountIdMapper,
29        Environment,
30    },
31};
32use ink_storage_traits::{
33    Storable,
34    decode_all,
35};
36use pallet_revive_uapi::{
37    ReturnErrorCode,
38    ReturnFlags,
39};
40#[cfg(feature = "unstable-hostfn")]
41use schnorrkel::{
42    PublicKey,
43    Signature,
44};
45
46use super::EnvInstance;
47use crate::{
48    DecodeDispatch,
49    DispatchError,
50    EnvBackend,
51    Result,
52    TypedEnvBackend,
53    call::{
54        Call,
55        CallParams,
56        ConstructorReturnType,
57        CreateParams,
58        DelegateCall,
59        FromAddr,
60        LimitParamsV2,
61        utils::{
62            DecodeMessageResult,
63            EncodeArgsWith,
64        },
65    },
66    event::{
67        Event,
68        TopicEncoder,
69        TopicsBuilderBackend,
70    },
71    hash::{
72        Blake2x128,
73        Blake2x256,
74        CryptoHash,
75        HashOutput,
76        Keccak256,
77        Sha2x256,
78    },
79    test::callee,
80};
81
82/// The capacity of the static buffer.
83/// This is the same size as the ink! on-chain environment. We chose to use the same size
84/// to be as close to the on-chain behavior as possible.
85const BUFFER_SIZE: usize = crate::BUFFER_SIZE;
86
87/// Proxy function used to simulate code hash and to invoke contract methods.
88fn execute_contract_call<ContractRef>(input: Vec<u8>) -> Vec<u8>
89where
90    ContractRef: crate::ContractReverseReference,
91    <ContractRef as crate::ContractReverseReference>::Type:
92        crate::reflect::ContractMessageDecoder,
93{
94    let dispatch = <
95        <
96            <
97                ContractRef
98                as crate::ContractReverseReference
99            >::Type
100            as crate::reflect::ContractMessageDecoder
101        >::Type
102        as DecodeDispatch
103    >::decode_dispatch(&mut &input[..])
104        .unwrap_or_else(|e| panic!("Failed to decode constructor call: {e:?}"));
105
106    crate::reflect::ExecuteDispatchable::execute_dispatchable(dispatch)
107        .unwrap_or_else(|e| panic!("Message call failed: {e:?}"));
108
109    crate::test::get_return_value()
110}
111
112fn invoke_contract_impl<R, Abi>(
113    env: &mut EnvInstance,
114    _gas_limit: Option<u64>,
115    _call_flags: u32,
116    _transferred_value: Option<&U256>,
117    callee_account: Address,
118    input: Vec<u8>,
119) -> Result<ink_primitives::MessageResult<R>>
120where
121    R: DecodeMessageResult<Abi>,
122{
123    let callee_code_hash = env.code_hash(&callee_account).unwrap_or_else(|err| {
124        panic!("failed getting code hash for {callee_account:?}: {err:?}")
125    });
126
127    let handler = env
128        .engine
129        .database
130        .get_contract_message_handler(&callee_code_hash);
131    let old_callee = env.engine.get_callee();
132    env.engine.set_callee(callee_account);
133
134    let result = handler(input);
135
136    env.engine.set_callee(old_callee);
137
138    // TODO: (@davidsemakula) Track return flag and set `did_revert` as appropriate.
139    R::decode_output(&result, false)
140}
141
142fn invoke_contract_impl_delegate<R, Abi>(
143    env: &mut EnvInstance,
144    _gas_limit: Option<u64>,
145    _call_flags: u32,
146    _transferred_value: Option<&U256>,
147    callee_account: Address,
148    input: Vec<u8>,
149) -> Result<ink_primitives::MessageResult<R>>
150where
151    R: DecodeMessageResult<Abi>,
152{
153    let callee_code_hash = env.code_hash(&callee_account).unwrap_or_else(|err| {
154        panic!("failed getting code hash for {callee_account:?}: {err:?}")
155    });
156
157    let handler = env
158        .engine
159        .database
160        .get_contract_message_handler(&callee_code_hash);
161    let result = handler(input);
162
163    // TODO: (@davidsemakula) Track return flag and set `did_revert` as appropriate.
164    R::decode_output(&result, false)
165}
166
167impl CryptoHash for Blake2x128 {
168    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
169        type OutputType = [u8; 16];
170        static_assertions::assert_type_eq_all!(
171            <Blake2x128 as HashOutput>::Type,
172            OutputType
173        );
174        let output: &mut OutputType = array_mut_ref!(output, 0, 16);
175        Engine::hash_blake2_128(input, output);
176    }
177
178    fn hash_with_buffer(
179        _input: &[u8],
180        _buffer: &mut [u8],
181        _output: &mut <Self as HashOutput>::Type,
182    ) {
183        unreachable!("not required, `hash` has been implemented.");
184    }
185}
186
187impl CryptoHash for Blake2x256 {
188    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
189        // For the engine (simulation of `pallet-revive` in `std`) we skip on the buffer
190        // in the implementation for simplicity.
191        type OutputType = [u8; 32];
192        static_assertions::assert_type_eq_all!(
193            <Blake2x256 as HashOutput>::Type,
194            OutputType
195        );
196        let output: &mut OutputType = array_mut_ref!(output, 0, 32);
197        Engine::hash_blake2_256(input, output);
198    }
199
200    fn hash_with_buffer(
201        _input: &[u8],
202        _buffer: &mut [u8],
203        _output: &mut <Self as HashOutput>::Type,
204    ) {
205        unreachable!("not required, `hash` has been implemented.");
206    }
207}
208
209impl CryptoHash for Sha2x256 {
210    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
211        // For the engine (simulation of `pallet-revive` in `std`) we skip on the buffer
212        // in the implementation for simplicity.
213        type OutputType = [u8; 32];
214        static_assertions::assert_type_eq_all!(
215            <Sha2x256 as HashOutput>::Type,
216            OutputType
217        );
218        let output: &mut OutputType = array_mut_ref!(output, 0, 32);
219        Engine::hash_sha2_256(input, output);
220    }
221
222    fn hash_with_buffer(
223        _input: &[u8],
224        _buffer: &mut [u8],
225        _output: &mut <Self as HashOutput>::Type,
226    ) {
227        unreachable!("not required, `hash` has been implemented.");
228    }
229}
230
231impl CryptoHash for Keccak256 {
232    fn hash(input: &[u8], output: &mut <Self as HashOutput>::Type) {
233        type OutputType = [u8; 32];
234        static_assertions::assert_type_eq_all!(
235            <Keccak256 as HashOutput>::Type,
236            OutputType
237        );
238        let output: &mut OutputType = array_mut_ref!(output, 0, 32);
239        Engine::hash_keccak_256(input, output);
240    }
241
242    fn hash_with_buffer(
243        _input: &[u8],
244        _buffer: &mut [u8],
245        _output: &mut <Self as HashOutput>::Type,
246    ) {
247        unreachable!("not required, `hash` has been implemented.");
248    }
249}
250
251#[derive(Default)]
252pub struct TopicsBuilder {
253    pub topics: Vec<[u8; 32]>,
254}
255
256impl<Abi> TopicsBuilderBackend<Abi> for TopicsBuilder
257where
258    Abi: TopicEncoder,
259{
260    type Output = Vec<[u8; 32]>;
261
262    fn push_topic<T>(&mut self, topic_value: &T)
263    where
264        T: AbiEncodeWith<Abi>,
265    {
266        let output = <Abi as TopicEncoder>::encode_topic(topic_value);
267        self.topics.push(output);
268    }
269
270    fn output(self) -> Self::Output {
271        self.topics
272    }
273}
274
275impl TopicEncoder for Ink {
276    const REQUIRES_BUFFER: bool = false;
277
278    fn encode_topic<T>(value: &T) -> [u8; 32]
279    where
280        T: AbiEncodeWith<Self>,
281    {
282        value.encode_topic(<Blake2x256 as CryptoHash>::hash)
283    }
284
285    fn encode_topic_with_hash_buffer<T>(
286        _value: &T,
287        _output: &mut [u8; 32],
288        _buffer: &mut [u8],
289    ) where
290        T: AbiEncodeWith<Self>,
291    {
292        unreachable!("not required, `encode_topic` has been implemented.");
293    }
294}
295
296impl TopicEncoder for Sol {
297    const REQUIRES_BUFFER: bool = false;
298
299    fn encode_topic<T>(value: &T) -> [u8; 32]
300    where
301        T: AbiEncodeWith<Self>,
302    {
303        value.encode_topic(<Keccak256 as CryptoHash>::hash)
304    }
305
306    fn encode_topic_with_hash_buffer<T>(
307        _value: &T,
308        _output: &mut [u8; 32],
309        _buffer: &mut [u8],
310    ) where
311        T: AbiEncodeWith<Self>,
312    {
313        unreachable!("not required, `encode_topic` has been implemented.");
314    }
315}
316
317impl EnvInstance {
318    /// Returns the contract property value.
319    fn get_property<T>(
320        &mut self,
321        ext_fn: fn(engine: &Engine, output: &mut &mut [u8]),
322    ) -> Result<T>
323    where
324        T: scale::Decode,
325    {
326        let mut full_scope: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
327        let full_scope = &mut &mut full_scope[..];
328        ext_fn(&self.engine, full_scope);
329        scale::Decode::decode(&mut &full_scope[..]).map_err(Into::into)
330    }
331
332    pub fn get_return_value(&mut self) -> Vec<u8> {
333        self.engine.get_storage(&[255_u8; 32]).unwrap().to_vec()
334    }
335
336    pub fn upload_code<ContractRef>(&mut self) -> H256
337    where
338        ContractRef: crate::ContractReverseReference,
339        <ContractRef as crate::ContractReverseReference>::Type:
340            crate::reflect::ContractMessageDecoder,
341    {
342        H256::from(
343            self.engine
344                .database
345                .set_contract_message_handler(execute_contract_call::<ContractRef>),
346        )
347    }
348}
349
350impl EnvBackend for EnvInstance {
351    fn set_contract_storage<K, V>(&mut self, key: &K, value: &V) -> Option<u32>
352    where
353        K: scale::Encode,
354        V: Storable,
355    {
356        let mut v = vec![];
357        Storable::encode(value, &mut v);
358        self.engine.set_storage(&key.encode(), &v[..])
359    }
360
361    fn get_contract_storage<K, R>(&mut self, key: &K) -> Result<Option<R>>
362    where
363        K: scale::Encode,
364        R: Storable,
365    {
366        match self.engine.get_storage(&key.encode()) {
367            Ok(res) => {
368                let decoded = decode_all(&mut &res[..])?;
369                Ok(Some(decoded))
370            }
371            Err(ReturnErrorCode::KeyNotFound) => Ok(None),
372            Err(_) => panic!("encountered unexpected error"),
373        }
374    }
375
376    fn take_contract_storage<K, R>(&mut self, key: &K) -> Result<Option<R>>
377    where
378        K: scale::Encode,
379        R: Storable,
380    {
381        match self.engine.take_storage(&key.encode()) {
382            Ok(output) => {
383                let decoded = decode_all(&mut &output[..])?;
384                Ok(Some(decoded))
385            }
386            Err(ReturnErrorCode::KeyNotFound) => Ok(None),
387            Err(_) => panic!("encountered unexpected error"),
388        }
389    }
390
391    fn remaining_buffer(&mut self) -> usize {
392        BUFFER_SIZE
393    }
394
395    fn contains_contract_storage<K>(&mut self, key: &K) -> Option<u32>
396    where
397        K: scale::Encode,
398    {
399        self.engine.contains_storage(&key.encode())
400    }
401
402    fn clear_contract_storage<K>(&mut self, key: &K) -> Option<u32>
403    where
404        K: scale::Encode,
405    {
406        self.engine.clear_storage(&key.encode())
407    }
408
409    fn decode_input<T>(&mut self) -> core::result::Result<T, DispatchError>
410    where
411        T: DecodeDispatch,
412    {
413        unimplemented!("the off-chain env does not implement `input`")
414    }
415
416    #[cfg(not(feature = "std"))]
417    fn return_value<R>(&mut self, _flags: ReturnFlags, _return_value: &R) -> !
418    where
419        R: scale::Encode,
420    {
421        panic!("enable feature `std` to use `return_value()`")
422    }
423
424    #[cfg(feature = "std")]
425    fn return_value<R>(&mut self, _flags: ReturnFlags, return_value: &R)
426    where
427        R: scale::Encode,
428    {
429        let mut v = vec![];
430        return_value.encode_to(&mut v);
431        self.engine.set_storage(&[255_u8; 32], &v[..]);
432    }
433
434    fn return_value_solidity<R>(&mut self, _flags: ReturnFlags, _return_value: &R) -> !
435    where
436        R: for<'a> SolResultEncode<'a>,
437    {
438        unimplemented!("the off-chain env does not implement `return_value_solidity`")
439    }
440
441    fn hash_bytes<H>(&mut self, input: &[u8], output: &mut <H as HashOutput>::Type)
442    where
443        H: CryptoHash,
444    {
445        <H as CryptoHash>::hash(input, output)
446    }
447
448    fn hash_encoded<H, T>(&mut self, input: &T, output: &mut <H as HashOutput>::Type)
449    where
450        H: CryptoHash,
451        T: scale::Encode,
452    {
453        let enc_input = &scale::Encode::encode(input)[..];
454        <H as CryptoHash>::hash(enc_input, output)
455    }
456
457    #[allow(clippy::arithmetic_side_effects)] // todo
458    fn ecdsa_recover(
459        &mut self,
460        signature: &[u8; 65],
461        message_hash: &[u8; 32],
462        output: &mut [u8; 33],
463    ) -> Result<()> {
464        use secp256k1::{
465            Message,
466            SECP256K1,
467            ecdsa::{
468                RecoverableSignature,
469                RecoveryId,
470            },
471        };
472
473        // In most implementations, the v is just 0 or 1 internally, but 27 was added
474        // as an arbitrary number for signing Bitcoin messages and Ethereum adopted that
475        // as well.
476        let recovery_byte = if signature[64] > 26 {
477            signature[64] - 27
478        } else {
479            signature[64]
480        };
481        let recovery_id = RecoveryId::try_from(recovery_byte as i32)
482            .unwrap_or_else(|error| panic!("Unable to parse the recovery id: {error}"));
483        let message = Message::from_digest_slice(message_hash).unwrap_or_else(|error| {
484            panic!("Unable to create the message from hash: {error}")
485        });
486        let signature =
487            RecoverableSignature::from_compact(&signature[0..64], recovery_id)
488                .unwrap_or_else(|error| panic!("Unable to parse the signature: {error}"));
489
490        let pub_key = SECP256K1.recover_ecdsa(&message, &signature);
491        match pub_key {
492            Ok(pub_key) => {
493                *output = pub_key.serialize();
494                Ok(())
495            }
496            Err(_) => Err(ReturnErrorCode::EcdsaRecoveryFailed.into()),
497        }
498    }
499
500    #[cfg(feature = "unstable-hostfn")]
501    fn ecdsa_to_eth_address(
502        &mut self,
503        pubkey: &[u8; 33],
504        output: &mut [u8; 20],
505    ) -> Result<()> {
506        let pk = secp256k1::PublicKey::from_slice(pubkey)
507            .map_err(|_| ReturnErrorCode::EcdsaRecoveryFailed)?;
508        let uncompressed = pk.serialize_uncompressed();
509        let mut hash = <Keccak256 as HashOutput>::Type::default();
510        <Keccak256>::hash(&uncompressed[1..], &mut hash);
511        output.as_mut().copy_from_slice(&hash[12..]);
512        Ok(())
513    }
514
515    #[cfg(feature = "unstable-hostfn")]
516    fn sr25519_verify(
517        &mut self,
518        signature: &[u8; 64],
519        message: &[u8],
520        pub_key: &[u8; 32],
521    ) -> Result<()> {
522        // the context associated with the signing (specific to the sr25519 algorithm)
523        // defaults to "substrate" in substrate, but could be different elsewhere
524        // https://github.com/paritytech/substrate/blob/c32f5ed2ae6746d6f791f08cecbfc22fa188f5f9/primitives/core/src/sr25519.rs#L60
525        let context = b"substrate";
526        // attempt to parse a signature from bytes
527        let signature: Signature = Signature::from_bytes(signature)
528            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
529        // attempt to parse a public key from bytes
530        let public_key: PublicKey = PublicKey::from_bytes(pub_key)
531            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed)?;
532        // verify the signature
533        public_key
534            .verify_simple(context, message, &signature)
535            .map_err(|_| ReturnErrorCode::Sr25519VerifyFailed.into())
536    }
537
538    #[cfg(feature = "unstable-hostfn")]
539    fn set_code_hash(&mut self, code_hash: &H256) -> Result<()> {
540        self.engine
541            .database
542            .set_code_hash(&self.engine.get_callee(), code_hash);
543        Ok(())
544    }
545}
546
547impl TypedEnvBackend for EnvInstance {
548    fn caller(&mut self) -> Address {
549        self.get_property::<Address>(Engine::caller)
550            .unwrap_or_else(|error| panic!("could not read `caller` property: {error:?}"))
551    }
552
553    fn transferred_value(&mut self) -> U256 {
554        self.get_property(Engine::value_transferred)
555            .unwrap_or_else(|error| {
556                panic!("could not read `transferred_value` property: {error:?}")
557            })
558    }
559
560    fn block_timestamp<E: Environment>(&mut self) -> E::Timestamp {
561        self.get_property::<E::Timestamp>(Engine::block_timestamp)
562            .unwrap_or_else(|error| {
563                panic!("could not read `block_timestamp` property: {error:?}")
564            })
565    }
566
567    fn to_account_id<E: Environment>(&mut self, addr: Address) -> E::AccountId {
568        let mut full_scope: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
569        let full_scope = &mut &mut full_scope[..];
570        Engine::to_account_id(&self.engine, addr.as_bytes(), full_scope);
571        scale::Decode::decode(&mut &full_scope[..]).unwrap()
572    }
573
574    fn account_id<E: Environment>(&mut self) -> E::AccountId {
575        self.get_property::<E::AccountId>(Engine::account_id)
576            .unwrap_or_else(|error| {
577                panic!("could not read `account_id` property: {error:?}")
578            })
579    }
580
581    fn address(&mut self) -> Address {
582        self.get_property::<Address>(Engine::address)
583            .unwrap_or_else(|error| {
584                panic!("could not read `account_id` property: {error:?}")
585            })
586    }
587
588    fn balance(&mut self) -> U256 {
589        self.get_property::<U256>(Engine::balance)
590            .unwrap_or_else(|error| {
591                panic!("could not read `balance` property: {error:?}")
592            })
593    }
594
595    fn block_number<E: Environment>(&mut self) -> E::BlockNumber {
596        self.get_property::<E::BlockNumber>(Engine::block_number)
597            .unwrap_or_else(|error| {
598                panic!("could not read `block_number` property: {error:?}")
599            })
600    }
601
602    fn minimum_balance(&mut self) -> U256 {
603        self.get_property::<U256>(Engine::minimum_balance)
604            .unwrap_or_else(|error| {
605                panic!("could not read `minimum_balance` property: {error:?}")
606            })
607    }
608
609    fn emit_event<Evt, Abi>(&mut self, event: &Evt)
610    where
611        Evt: Event<Abi>,
612        Abi: TopicEncoder,
613    {
614        let builder = TopicsBuilder::default();
615        let enc_topics = event.topics(builder.into());
616        let enc_data = event.encode_data();
617        self.engine
618            .deposit_event(&enc_topics[..], enc_data.as_slice());
619    }
620
621    fn invoke_contract<E, Args, R, Abi>(
622        &mut self,
623        params: &CallParams<E, Call, Args, R, Abi>,
624    ) -> Result<ink_primitives::MessageResult<R>>
625    where
626        E: Environment,
627        Args: EncodeArgsWith<Abi>,
628        R: DecodeMessageResult<Abi>,
629    {
630        let call_flags = params.call_flags().bits();
631        let transferred_value = params.transferred_value();
632        let input = params.exec_input().encode();
633        let callee_account = params.callee();
634
635        invoke_contract_impl::<R, Abi>(
636            self,
637            None,
638            call_flags,
639            Some(transferred_value),
640            *callee_account, // todo possibly return no reference from callee()
641            input,
642        )
643    }
644
645    fn invoke_contract_delegate<E, Args, R, Abi>(
646        &mut self,
647        params: &CallParams<E, DelegateCall, Args, R, Abi>,
648    ) -> Result<ink_primitives::MessageResult<R>>
649    where
650        E: Environment,
651        Args: EncodeArgsWith<Abi>,
652        R: DecodeMessageResult<Abi>,
653    {
654        let _addr = params.address(); // todo remove
655        let call_flags = params.call_flags().bits();
656        let input = params.exec_input().encode();
657
658        invoke_contract_impl_delegate::<R, Abi>(
659            self,
660            None,
661            call_flags,
662            None,
663            *params.address(),
664            input,
665        )
666    }
667
668    fn instantiate_contract<E, ContractRef, Args, R, Abi>(
669        &mut self,
670        params: &CreateParams<E, ContractRef, LimitParamsV2, Args, R, Abi>,
671    ) -> Result<
672        ink_primitives::ConstructorResult<
673            <R as ConstructorReturnType<ContractRef, Abi>>::Output,
674        >,
675    >
676    where
677        E: Environment,
678        ContractRef: FromAddr + crate::ContractReverseReference,
679        <ContractRef as crate::ContractReverseReference>::Type:
680            crate::reflect::ContractConstructorDecoder,
681        Args: EncodeArgsWith<Abi>,
682        R: ConstructorReturnType<ContractRef, Abi>,
683    {
684        let endowment = params.endowment();
685        let salt_bytes = params.salt_bytes();
686        let code_hash = params.code_hash();
687
688        let input = params.exec_input().encode();
689
690        // Compute address for instantiated contract.
691        let account_id_vec = {
692            let mut account_input = Vec::<u8>::new();
693            account_input.extend(&b"contract_addr".to_vec());
694            scale::Encode::encode_to(
695                &self.engine.exec_context.caller.as_bytes(),
696                &mut account_input,
697            );
698            account_input.extend(&code_hash.0);
699            account_input.extend(&input);
700            if let Some(salt) = salt_bytes {
701                account_input.extend(salt);
702            }
703            let mut account_id = [0_u8; 32];
704            ink_engine::hashing::blake2b_256(&account_input[..], &mut account_id);
705            account_id.to_vec()
706        };
707        // todo don't convert to vec and back, simplify type
708        let contract_addr = AccountIdMapper::to_address(&account_id_vec[..]);
709
710        let old_callee = self.engine.get_callee();
711        self.engine.set_callee(contract_addr);
712
713        let dispatch = <
714            <
715                <
716                    ContractRef
717                    as crate::ContractReverseReference
718                >::Type
719                as crate::reflect::ContractConstructorDecoder
720            >::Type
721            as DecodeDispatch
722        >::decode_dispatch(&mut &input[..])
723            .unwrap_or_else(|e| panic!("Failed to decode constructor call: {e:?}"));
724        crate::reflect::ExecuteDispatchable::execute_dispatchable(dispatch)
725            .unwrap_or_else(|e| panic!("Constructor call failed: {e:?}"));
726
727        self.engine
728            .database
729            .set_code_hash(&self.engine.get_callee(), code_hash);
730        self.engine.set_contract(callee());
731        self.engine
732            .database
733            // todo passing the types instead of refs would be better
734            .set_balance(&callee(), *endowment);
735
736        // todo why?
737        self.engine.set_callee(old_callee);
738
739        Ok(Ok(R::ok(<ContractRef as FromAddr>::from_addr(
740            contract_addr,
741        ))))
742    }
743
744    #[cfg(feature = "unstable-hostfn")]
745    fn terminate_contract(&mut self, beneficiary: Address) -> ! {
746        self.engine.terminate(beneficiary)
747    }
748
749    fn transfer<E>(&mut self, destination: Address, value: U256) -> Result<()>
750    where
751        E: Environment,
752    {
753        let enc_value = &scale::Encode::encode(&value)[..];
754        self.engine
755            .transfer(destination, enc_value)
756            .map_err(Into::into)
757    }
758
759    fn weight_to_fee(&mut self, gas: u64) -> U256 {
760        let mut output: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
761        self.engine.weight_to_fee(gas, &mut &mut output[..]);
762        scale::Decode::decode(&mut &output[..]).unwrap_or_else(|error| {
763            panic!("could not read `weight_to_fee` property: {error:?}")
764        })
765    }
766
767    fn is_contract(&mut self, account: &Address) -> bool {
768        self.engine.is_contract(account)
769    }
770
771    fn caller_is_origin(&mut self) -> bool {
772        unimplemented!("off-chain environment does not support cross-contract calls")
773    }
774
775    fn caller_is_root(&mut self) -> bool {
776        unimplemented!("off-chain environment does not support `caller_is_root`")
777    }
778
779    fn code_hash(&mut self, addr: &Address) -> core::result::Result<H256, CodeHashErr> {
780        let code_hash = self.engine.database.get_code_hash(addr);
781        if let Some(code_hash) = code_hash {
782            // todo
783            let code_hash = H256::decode(&mut &code_hash[..]).unwrap();
784            Ok(code_hash)
785        } else {
786            Err(CodeHashErr::AddressNotFound)
787        }
788    }
789
790    fn own_code_hash(&mut self) -> H256 {
791        let callee = &self.engine.get_callee();
792        self.engine
793            .database
794            .get_code_hash(callee)
795            .expect("own code hash not found")
796    }
797
798    #[cfg(all(feature = "xcm", feature = "unstable-hostfn"))]
799    fn xcm_execute<E, Call>(&mut self, _msg: &xcm::VersionedXcm<Call>) -> Result<()>
800    where
801        E: Environment,
802    {
803        unimplemented!("off-chain environment does not support `xcm_execute`")
804    }
805
806    #[cfg(all(feature = "xcm", feature = "unstable-hostfn"))]
807    fn xcm_send<E, Call>(
808        &mut self,
809        _dest: &xcm::VersionedLocation,
810        _msg: &xcm::VersionedXcm<Call>,
811    ) -> Result<xcm::v4::XcmHash>
812    where
813        E: Environment,
814    {
815        unimplemented!("off-chain environment does not support `xcm_send`")
816    }
817}