1#![doc = include_str!("../README.md")]
19#![allow(rustdoc::private_intra_doc_links)]
20#![cfg_attr(not(feature = "std"), no_std)]
21#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")]
22
23extern crate alloc;
24mod address;
25mod benchmarking;
26mod exec;
27mod gas;
28mod limits;
29mod primitives;
30mod pure_precompiles;
31mod storage;
32mod transient_storage;
33mod wasm;
34
35#[cfg(test)]
36mod tests;
37
38pub mod chain_extension;
39pub mod evm;
40pub mod test_utils;
41pub mod tracing;
42pub mod weights;
43
44use crate::{
45 evm::{runtime::GAS_PRICE, CallTrace, GasEncoder, GenericTransaction, TracerConfig},
46 exec::{AccountIdOf, ExecError, Executable, Key, Stack as ExecStack},
47 gas::GasMeter,
48 storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
49 wasm::{CodeInfo, RuntimeCosts, WasmBlob},
50};
51use alloc::{boxed::Box, format, vec};
52use codec::{Codec, Decode, Encode};
53use environmental::*;
54use frame_support::{
55 dispatch::{
56 DispatchErrorWithPostInfo, DispatchInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays,
57 PostDispatchInfo, RawOrigin,
58 },
59 ensure,
60 pallet_prelude::DispatchClass,
61 traits::{
62 fungible::{Inspect, Mutate, MutateHold},
63 tokens::{Fortitude::Polite, Preservation::Preserve},
64 ConstU32, ConstU64, Contains, EnsureOrigin, Get, IsType, OriginTrait, Time,
65 },
66 weights::{Weight, WeightMeter},
67 BoundedVec, RuntimeDebugNoBound,
68};
69use frame_system::{
70 ensure_signed,
71 pallet_prelude::{BlockNumberFor, OriginFor},
72 Pallet as System,
73};
74use scale_info::TypeInfo;
75use sp_core::{H160, H256, U256};
76use sp_runtime::{
77 traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating, Zero},
78 AccountId32, DispatchError,
79};
80
81pub use crate::{
82 address::{create1, create2, AccountId32Mapper, AddressMapper},
83 exec::{MomentOf, Origin},
84 pallet::*,
85};
86pub use primitives::*;
87pub use weights::WeightInfo;
88
89#[cfg(doc)]
90pub use crate::wasm::SyscallDoc;
91
92type TrieId = BoundedVec<u8, ConstU32<128>>;
93type BalanceOf<T> =
94 <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
95type CodeVec = BoundedVec<u8, ConstU32<{ limits::code::BLOB_BYTES }>>;
96type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
97
98const SENTINEL: u32 = u32::MAX;
105
106const LOG_TARGET: &str = "runtime::revive";
112
113#[frame_support::pallet]
114pub mod pallet {
115 use super::*;
116 use frame_support::{pallet_prelude::*, traits::FindAuthor};
117 use frame_system::pallet_prelude::*;
118 use sp_core::U256;
119 use sp_runtime::Perbill;
120
121 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
123
124 #[pallet::pallet]
125 #[pallet::storage_version(STORAGE_VERSION)]
126 pub struct Pallet<T>(_);
127
128 #[pallet::config(with_default)]
129 pub trait Config: frame_system::Config {
130 type Time: Time;
132
133 #[pallet::no_default]
135 type Currency: Inspect<Self::AccountId>
136 + Mutate<Self::AccountId>
137 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
138
139 #[pallet::no_default_bounds]
141 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
142
143 #[pallet::no_default_bounds]
145 type RuntimeCall: Parameter
146 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
147 + GetDispatchInfo;
148
149 #[pallet::no_default_bounds]
151 type RuntimeHoldReason: From<HoldReason>;
152
153 #[pallet::no_default_bounds]
176 type CallFilter: Contains<<Self as frame_system::Config>::RuntimeCall>;
177
178 #[pallet::no_default_bounds]
181 type WeightPrice: Convert<Weight, BalanceOf<Self>>;
182
183 type WeightInfo: WeightInfo;
186
187 #[pallet::no_default_bounds]
189 type ChainExtension: chain_extension::ChainExtension<Self> + Default;
190
191 type FindAuthor: FindAuthor<Self::AccountId>;
193
194 #[pallet::constant]
200 #[pallet::no_default_bounds]
201 type DepositPerByte: Get<BalanceOf<Self>>;
202
203 #[pallet::constant]
209 #[pallet::no_default_bounds]
210 type DepositPerItem: Get<BalanceOf<Self>>;
211
212 #[pallet::constant]
216 type CodeHashLockupDepositPercent: Get<Perbill>;
217
218 #[pallet::no_default]
220 type AddressMapper: AddressMapper<Self>;
221
222 #[pallet::constant]
232 type UnsafeUnstableInterface: Get<bool>;
233
234 #[pallet::no_default_bounds]
239 type UploadOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
240
241 #[pallet::no_default_bounds]
252 type InstantiateOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
253
254 #[pallet::no_default_bounds]
257 type Xcm: xcm_builder::Controller<
258 OriginFor<Self>,
259 <Self as frame_system::Config>::RuntimeCall,
260 BlockNumberFor<Self>,
261 >;
262
263 type RuntimeMemory: Get<u32>;
268
269 type PVFMemory: Get<u32>;
277
278 #[pallet::constant]
283 type ChainId: Get<u64>;
284
285 #[pallet::constant]
287 type NativeToEthRatio: Get<u32>;
288
289 #[pallet::no_default_bounds]
292 type EthGasEncoder: GasEncoder<BalanceOf<Self>>;
293 }
294
295 pub mod config_preludes {
297 use super::*;
298 use frame_support::{
299 derive_impl,
300 traits::{ConstBool, ConstU32},
301 };
302 use frame_system::EnsureSigned;
303 use sp_core::parameter_types;
304
305 type AccountId = sp_runtime::AccountId32;
306 type Balance = u64;
307 const UNITS: Balance = 10_000_000_000;
308 const CENTS: Balance = UNITS / 100;
309
310 pub const fn deposit(items: u32, bytes: u32) -> Balance {
311 items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS
312 }
313
314 parameter_types! {
315 pub const DepositPerItem: Balance = deposit(1, 0);
316 pub const DepositPerByte: Balance = deposit(0, 1);
317 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
318 }
319
320 pub struct TestDefaultConfig;
322
323 impl Time for TestDefaultConfig {
324 type Moment = u64;
325 fn now() -> Self::Moment {
326 unimplemented!("No default `now` implementation in `TestDefaultConfig` provide a custom `T::Time` type.")
327 }
328 }
329
330 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
331 fn convert(w: Weight) -> T {
332 w.ref_time().into()
333 }
334 }
335
336 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
337 impl frame_system::DefaultConfig for TestDefaultConfig {}
338
339 #[frame_support::register_default_impl(TestDefaultConfig)]
340 impl DefaultConfig for TestDefaultConfig {
341 #[inject_runtime_type]
342 type RuntimeEvent = ();
343
344 #[inject_runtime_type]
345 type RuntimeHoldReason = ();
346
347 #[inject_runtime_type]
348 type RuntimeCall = ();
349 type CallFilter = ();
350 type ChainExtension = ();
351 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
352 type DepositPerByte = DepositPerByte;
353 type DepositPerItem = DepositPerItem;
354 type Time = Self;
355 type UnsafeUnstableInterface = ConstBool<true>;
356 type UploadOrigin = EnsureSigned<AccountId>;
357 type InstantiateOrigin = EnsureSigned<AccountId>;
358 type WeightInfo = ();
359 type WeightPrice = Self;
360 type Xcm = ();
361 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
362 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
363 type ChainId = ConstU64<0>;
364 type NativeToEthRatio = ConstU32<1>;
365 type EthGasEncoder = ();
366 type FindAuthor = ();
367 }
368 }
369
370 #[pallet::event]
371 pub enum Event<T: Config> {
372 ContractEmitted {
374 contract: H160,
376 data: Vec<u8>,
379 topics: Vec<H256>,
382 },
383 }
384
385 #[pallet::error]
386 pub enum Error<T> {
387 InvalidSchedule,
389 InvalidCallFlags,
391 OutOfGas,
393 TransferFailed,
396 MaxCallDepthReached,
399 ContractNotFound,
401 CodeNotFound,
403 CodeInfoNotFound,
405 OutOfBounds,
407 DecodingFailed,
409 ContractTrapped,
411 ValueTooLarge,
413 TerminatedWhileReentrant,
416 InputForwarded,
418 TooManyTopics,
420 NoChainExtension,
424 XCMDecodeFailed,
426 DuplicateContract,
428 TerminatedInConstructor,
432 ReentranceDenied,
434 ReenteredPallet,
436 StateChangeDenied,
438 StorageDepositNotEnoughFunds,
440 StorageDepositLimitExhausted,
442 CodeInUse,
444 ContractReverted,
449 CodeRejected,
454 BlobTooLarge,
456 StaticMemoryTooLarge,
459 BasicBlockTooLarge,
461 InvalidInstruction,
463 MaxDelegateDependenciesReached,
465 DelegateDependencyNotFound,
467 DelegateDependencyAlreadyExists,
469 CannotAddSelfAsDelegateDependency,
471 OutOfTransientStorage,
473 InvalidSyscall,
475 InvalidStorageFlags,
477 ExecutionFailed,
479 BalanceConversionFailed,
481 DecimalPrecisionLoss,
483 InvalidImmutableAccess,
486 AccountUnmapped,
490 AccountAlreadyMapped,
492 InvalidGenericTransaction,
494 RefcountOverOrUnderflow,
496 UnsupportedPrecompileAddress,
498 PrecompileFailure,
500 }
501
502 #[pallet::composite_enum]
504 pub enum HoldReason {
505 CodeUploadDepositReserve,
507 StorageDepositReserve,
509 AddressMapping,
511 }
512
513 #[pallet::storage]
515 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, CodeVec>;
516
517 #[pallet::storage]
519 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
520
521 #[pallet::storage]
523 pub(crate) type ContractInfoOf<T: Config> = StorageMap<_, Identity, H160, ContractInfo<T>>;
524
525 #[pallet::storage]
527 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
528
529 #[pallet::storage]
534 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
535
536 #[pallet::storage]
539 pub(crate) type DeletionQueueCounter<T: Config> =
540 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
541
542 #[pallet::storage]
549 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
550
551 #[pallet::hooks]
552 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
553 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
554 let mut meter = WeightMeter::with_limit(limit);
555 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
556 meter.consumed()
557 }
558
559 fn integrity_test() {
560 use limits::code::STATIC_MEMORY_BYTES;
561
562 let max_runtime_mem: u32 = T::RuntimeMemory::get();
564 let max_call_depth =
566 limits::CALL_STACK_DEPTH.checked_add(1).expect("CallStack size is too big");
567 let max_transient_storage_size = limits::TRANSIENT_STORAGE_BYTES
570 .checked_mul(2)
571 .expect("MaxTransientStorageSize is too large");
572
573 const TOTAL_MEMORY_DEVIDER: u32 = 2;
576
577 const MEMORY_ALLOCATOR_INEFFICENCY_DEVIDER: u32 = 4;
581
582 let static_memory_limit = max_runtime_mem
590 .saturating_div(TOTAL_MEMORY_DEVIDER)
591 .saturating_sub(max_transient_storage_size)
592 .saturating_div(max_call_depth)
593 .saturating_sub(STATIC_MEMORY_BYTES)
594 .saturating_div(MEMORY_ALLOCATOR_INEFFICENCY_DEVIDER);
595
596 assert!(
597 STATIC_MEMORY_BYTES < static_memory_limit,
598 "Given `CallStack` height {:?}, `STATIC_MEMORY_LIMIT` should be set less than {:?} \
599 (current value is {:?}), to avoid possible runtime oom issues.",
600 max_call_depth,
601 static_memory_limit,
602 STATIC_MEMORY_BYTES,
603 );
604
605 let max_block_ref_time = T::BlockWeights::get()
611 .get(DispatchClass::Normal)
612 .max_total
613 .unwrap_or_else(|| T::BlockWeights::get().max_block)
614 .ref_time();
615 let max_payload_size = limits::PAYLOAD_BYTES;
616 let max_key_size =
617 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
618 .expect("Key of maximal size shall be created")
619 .hash()
620 .len() as u32;
621
622 let max_immutable_key_size = T::AccountId::max_encoded_len() as u32;
623 let max_immutable_size: u32 = ((max_block_ref_time /
624 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetImmutableData(
625 limits::IMMUTABLE_BYTES,
626 ))
627 .ref_time()))
628 .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64))
629 .try_into()
630 .expect("Immutable data size too big");
631
632 let max_storage_size: u32 = ((max_block_ref_time /
635 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
636 new_bytes: max_payload_size,
637 old_bytes: 0,
638 })
639 .ref_time()))
640 .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64))
641 .saturating_add(max_immutable_size.into())
642 .try_into()
643 .expect("Storage size too big");
644
645 let max_pvf_mem: u32 = T::PVFMemory::get();
646 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
647
648 assert!(
649 max_storage_size < storage_size_limit,
650 "Maximal storage size {} exceeds the storage limit {}",
651 max_storage_size,
652 storage_size_limit
653 );
654
655 let max_events_size: u32 = ((max_block_ref_time /
659 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
660 num_topic: 0,
661 len: max_payload_size,
662 })
663 .saturating_add(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::HostFn))
664 .ref_time()))
665 .saturating_mul(max_payload_size as u64))
666 .try_into()
667 .expect("Events size too big");
668
669 assert!(
670 max_events_size < storage_size_limit,
671 "Maximal events size {} exceeds the events limit {}",
672 max_events_size,
673 storage_size_limit
674 );
675 }
676 }
677
678 #[pallet::call]
679 impl<T: Config> Pallet<T>
680 where
681 BalanceOf<T>: Into<U256> + TryFrom<U256>,
682 MomentOf<T>: Into<U256>,
683 T::Hash: frame_support::traits::IsType<H256>,
684 {
685 #[allow(unused_variables)]
701 #[pallet::call_index(0)]
702 #[pallet::weight(Weight::MAX)]
703 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
704 Err(frame_system::Error::CallFiltered::<T>.into())
705 }
706
707 #[pallet::call_index(1)]
724 #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))]
725 pub fn call(
726 origin: OriginFor<T>,
727 dest: H160,
728 #[pallet::compact] value: BalanceOf<T>,
729 gas_limit: Weight,
730 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
731 data: Vec<u8>,
732 ) -> DispatchResultWithPostInfo {
733 let mut output = Self::bare_call(
734 origin,
735 dest,
736 value,
737 gas_limit,
738 DepositLimit::Balance(storage_deposit_limit),
739 data,
740 );
741
742 if let Ok(return_value) = &output.result {
743 if return_value.did_revert() {
744 output.result = Err(<Error<T>>::ContractReverted.into());
745 }
746 }
747 dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call())
748 }
749
750 #[pallet::call_index(2)]
756 #[pallet::weight(
757 T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
758 )]
759 pub fn instantiate(
760 origin: OriginFor<T>,
761 #[pallet::compact] value: BalanceOf<T>,
762 gas_limit: Weight,
763 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
764 code_hash: sp_core::H256,
765 data: Vec<u8>,
766 salt: Option<[u8; 32]>,
767 ) -> DispatchResultWithPostInfo {
768 let data_len = data.len() as u32;
769 let mut output = Self::bare_instantiate(
770 origin,
771 value,
772 gas_limit,
773 DepositLimit::Balance(storage_deposit_limit),
774 Code::Existing(code_hash),
775 data,
776 salt,
777 );
778 if let Ok(retval) = &output.result {
779 if retval.result.did_revert() {
780 output.result = Err(<Error<T>>::ContractReverted.into());
781 }
782 }
783 dispatch_result(
784 output.result.map(|result| result.result),
785 output.gas_consumed,
786 T::WeightInfo::instantiate(data_len),
787 )
788 }
789
790 #[pallet::call_index(3)]
818 #[pallet::weight(
819 T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
820 .saturating_add(*gas_limit)
821 )]
822 pub fn instantiate_with_code(
823 origin: OriginFor<T>,
824 #[pallet::compact] value: BalanceOf<T>,
825 gas_limit: Weight,
826 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
827 code: Vec<u8>,
828 data: Vec<u8>,
829 salt: Option<[u8; 32]>,
830 ) -> DispatchResultWithPostInfo {
831 let code_len = code.len() as u32;
832 let data_len = data.len() as u32;
833 let mut output = Self::bare_instantiate(
834 origin,
835 value,
836 gas_limit,
837 DepositLimit::Balance(storage_deposit_limit),
838 Code::Upload(code),
839 data,
840 salt,
841 );
842 if let Ok(retval) = &output.result {
843 if retval.result.did_revert() {
844 output.result = Err(<Error<T>>::ContractReverted.into());
845 }
846 }
847 dispatch_result(
848 output.result.map(|result| result.result),
849 output.gas_consumed,
850 T::WeightInfo::instantiate_with_code(code_len, data_len),
851 )
852 }
853
854 #[pallet::call_index(4)]
867 #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))]
868 pub fn upload_code(
869 origin: OriginFor<T>,
870 code: Vec<u8>,
871 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
872 ) -> DispatchResult {
873 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
874 }
875
876 #[pallet::call_index(5)]
881 #[pallet::weight(T::WeightInfo::remove_code())]
882 pub fn remove_code(
883 origin: OriginFor<T>,
884 code_hash: sp_core::H256,
885 ) -> DispatchResultWithPostInfo {
886 let origin = ensure_signed(origin)?;
887 <WasmBlob<T>>::remove(&origin, code_hash)?;
888 Ok(Pays::No.into())
890 }
891
892 #[pallet::call_index(6)]
903 #[pallet::weight(T::WeightInfo::set_code())]
904 pub fn set_code(
905 origin: OriginFor<T>,
906 dest: H160,
907 code_hash: sp_core::H256,
908 ) -> DispatchResult {
909 ensure_root(origin)?;
910 <ContractInfoOf<T>>::try_mutate(&dest, |contract| {
911 let contract = if let Some(contract) = contract {
912 contract
913 } else {
914 return Err(<Error<T>>::ContractNotFound.into());
915 };
916 <CodeInfo<T>>::increment_refcount(code_hash)?;
917 <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
918 contract.code_hash = code_hash;
919 Ok(())
920 })
921 }
922
923 #[pallet::call_index(7)]
928 #[pallet::weight(T::WeightInfo::map_account())]
929 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
930 let origin = ensure_signed(origin)?;
931 T::AddressMapper::map(&origin)
932 }
933
934 #[pallet::call_index(8)]
939 #[pallet::weight(T::WeightInfo::unmap_account())]
940 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
941 let origin = ensure_signed(origin)?;
942 T::AddressMapper::unmap(&origin)
943 }
944
945 #[pallet::call_index(9)]
951 #[pallet::weight({
952 let dispatch_info = call.get_dispatch_info();
953 (
954 T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
955 dispatch_info.class
956 )
957 })]
958 pub fn dispatch_as_fallback_account(
959 origin: OriginFor<T>,
960 call: Box<<T as Config>::RuntimeCall>,
961 ) -> DispatchResultWithPostInfo {
962 let origin = ensure_signed(origin)?;
963 let unmapped_account =
964 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
965 call.dispatch(RawOrigin::Signed(unmapped_account).into())
966 }
967 }
968}
969
970fn dispatch_result<R>(
972 result: Result<R, DispatchError>,
973 gas_consumed: Weight,
974 base_weight: Weight,
975) -> DispatchResultWithPostInfo {
976 let post_info = PostDispatchInfo {
977 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
978 pays_fee: Default::default(),
979 };
980
981 result
982 .map(|_| post_info)
983 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
984}
985
986impl<T: Config> Pallet<T>
987where
988 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
989 MomentOf<T>: Into<U256>,
990 T::Hash: frame_support::traits::IsType<H256>,
991{
992 pub fn bare_call(
999 origin: OriginFor<T>,
1000 dest: H160,
1001 value: BalanceOf<T>,
1002 gas_limit: Weight,
1003 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1004 data: Vec<u8>,
1005 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1006 let mut gas_meter = GasMeter::new(gas_limit);
1007 let mut storage_deposit = Default::default();
1008
1009 let try_call = || {
1010 let origin = Origin::from_runtime_origin(origin)?;
1011 let mut storage_meter = match storage_deposit_limit {
1012 DepositLimit::Balance(limit) => StorageMeter::new(&origin, limit, value)?,
1013 DepositLimit::Unchecked => StorageMeter::new_unchecked(BalanceOf::<T>::max_value()),
1014 };
1015 let result = ExecStack::<T, WasmBlob<T>>::run_call(
1016 origin.clone(),
1017 dest,
1018 &mut gas_meter,
1019 &mut storage_meter,
1020 Self::convert_native_to_evm(value),
1021 data,
1022 storage_deposit_limit.is_unchecked(),
1023 )?;
1024 storage_deposit = storage_meter
1025 .try_into_deposit(&origin, storage_deposit_limit.is_unchecked())
1026 .inspect_err(|err| {
1027 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1028 })?;
1029 Ok(result)
1030 };
1031 let result = Self::run_guarded(try_call);
1032 ContractResult {
1033 result: result.map_err(|r| r.error),
1034 gas_consumed: gas_meter.gas_consumed(),
1035 gas_required: gas_meter.gas_required(),
1036 storage_deposit,
1037 }
1038 }
1039
1040 pub fn bare_instantiate(
1046 origin: OriginFor<T>,
1047 value: BalanceOf<T>,
1048 gas_limit: Weight,
1049 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1050 code: Code,
1051 data: Vec<u8>,
1052 salt: Option<[u8; 32]>,
1053 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1054 let mut gas_meter = GasMeter::new(gas_limit);
1055 let mut storage_deposit = Default::default();
1056 let unchecked_deposit_limit = storage_deposit_limit.is_unchecked();
1057 let mut storage_deposit_limit = match storage_deposit_limit {
1058 DepositLimit::Balance(limit) => limit,
1059 DepositLimit::Unchecked => BalanceOf::<T>::max_value(),
1060 };
1061
1062 let try_instantiate = || {
1063 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1064 let (executable, upload_deposit) = match code {
1065 Code::Upload(code) => {
1066 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1067 let (executable, upload_deposit) = Self::try_upload_code(
1068 upload_account,
1069 code,
1070 storage_deposit_limit,
1071 unchecked_deposit_limit,
1072 )?;
1073 storage_deposit_limit.saturating_reduce(upload_deposit);
1074 (executable, upload_deposit)
1075 },
1076 Code::Existing(code_hash) =>
1077 (WasmBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()),
1078 };
1079 let instantiate_origin = Origin::from_account_id(instantiate_account.clone());
1080 let mut storage_meter = if unchecked_deposit_limit {
1081 StorageMeter::new_unchecked(storage_deposit_limit)
1082 } else {
1083 StorageMeter::new(&instantiate_origin, storage_deposit_limit, value)?
1084 };
1085
1086 let result = ExecStack::<T, WasmBlob<T>>::run_instantiate(
1087 instantiate_account,
1088 executable,
1089 &mut gas_meter,
1090 &mut storage_meter,
1091 Self::convert_native_to_evm(value),
1092 data,
1093 salt.as_ref(),
1094 unchecked_deposit_limit,
1095 );
1096 storage_deposit = storage_meter
1097 .try_into_deposit(&instantiate_origin, unchecked_deposit_limit)?
1098 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1099 result
1100 };
1101 let output = Self::run_guarded(try_instantiate);
1102 ContractResult {
1103 result: output
1104 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1105 .map_err(|e| e.error),
1106 gas_consumed: gas_meter.gas_consumed(),
1107 gas_required: gas_meter.gas_required(),
1108 storage_deposit,
1109 }
1110 }
1111
1112 pub fn bare_eth_transact(
1120 mut tx: GenericTransaction,
1121 gas_limit: Weight,
1122 tx_fee: impl Fn(Call<T>, DispatchInfo) -> BalanceOf<T>,
1123 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1124 where
1125 <T as frame_system::Config>::RuntimeCall:
1126 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1127 <T as Config>::RuntimeCall: From<crate::Call<T>>,
1128 <T as Config>::RuntimeCall: Encode,
1129 T::Nonce: Into<U256>,
1130 T::Hash: frame_support::traits::IsType<H256>,
1131 {
1132 log::trace!(target: LOG_TARGET, "bare_eth_transact: tx: {tx:?} gas_limit: {gas_limit:?}");
1133
1134 let from = tx.from.unwrap_or_default();
1135 let origin = T::AddressMapper::to_account_id(&from);
1136
1137 let storage_deposit_limit = if tx.gas.is_some() {
1138 DepositLimit::Balance(BalanceOf::<T>::max_value())
1139 } else {
1140 DepositLimit::Unchecked
1141 };
1142
1143 if tx.nonce.is_none() {
1144 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1145 }
1146 if tx.chain_id.is_none() {
1147 tx.chain_id = Some(T::ChainId::get().into());
1148 }
1149 if tx.gas_price.is_none() {
1150 tx.gas_price = Some(GAS_PRICE.into());
1151 }
1152 if tx.gas.is_none() {
1153 tx.gas = Some(Self::evm_block_gas_limit());
1154 }
1155
1156 let evm_value = tx.value.unwrap_or_default();
1158 let native_value = match Self::convert_evm_to_native(evm_value, ConversionPrecision::Exact)
1159 {
1160 Ok(v) => v,
1161 Err(_) => return Err(EthTransactError::Message("Failed to convert value".into())),
1162 };
1163
1164 let input = tx.input.clone().to_vec();
1165
1166 let extract_error = |err| {
1167 if err == Error::<T>::TransferFailed.into() ||
1168 err == Error::<T>::StorageDepositNotEnoughFunds.into() ||
1169 err == Error::<T>::StorageDepositLimitExhausted.into()
1170 {
1171 let balance = Self::evm_balance(&from);
1172 return Err(EthTransactError::Message(
1173 format!("insufficient funds for gas * price + value: address {from:?} have {balance} (supplied gas {})",
1174 tx.gas.unwrap_or_default()))
1175 );
1176 }
1177
1178 return Err(EthTransactError::Message(format!(
1179 "Failed to instantiate contract: {err:?}"
1180 )));
1181 };
1182
1183 let (mut result, dispatch_info) = match tx.to {
1185 Some(dest) => {
1187 let result = crate::Pallet::<T>::bare_call(
1189 T::RuntimeOrigin::signed(origin),
1190 dest,
1191 native_value,
1192 gas_limit,
1193 storage_deposit_limit,
1194 input.clone(),
1195 );
1196
1197 let data = match result.result {
1198 Ok(return_value) => {
1199 if return_value.did_revert() {
1200 return Err(EthTransactError::Data(return_value.data));
1201 }
1202 return_value.data
1203 },
1204 Err(err) => {
1205 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1206 return extract_error(err)
1207 },
1208 };
1209
1210 let result = EthTransactInfo {
1211 gas_required: result.gas_required,
1212 storage_deposit: result.storage_deposit.charge_or_zero(),
1213 data,
1214 eth_gas: Default::default(),
1215 };
1216
1217 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1218 result.gas_required,
1219 result.storage_deposit,
1220 );
1221 let dispatch_call: <T as Config>::RuntimeCall = crate::Call::<T>::call {
1222 dest,
1223 value: native_value,
1224 gas_limit,
1225 storage_deposit_limit,
1226 data: input.clone(),
1227 }
1228 .into();
1229 (result, dispatch_call.get_dispatch_info())
1230 },
1231 None => {
1233 let (code, data) = match polkavm::ProgramBlob::blob_length(&input) {
1235 Some(blob_len) => blob_len
1236 .try_into()
1237 .ok()
1238 .and_then(|blob_len| (input.split_at_checked(blob_len)))
1239 .unwrap_or_else(|| (&input[..], &[][..])),
1240 _ => {
1241 log::debug!(target: LOG_TARGET, "Failed to extract polkavm blob length");
1242 (&input[..], &[][..])
1243 },
1244 };
1245
1246 let result = crate::Pallet::<T>::bare_instantiate(
1248 T::RuntimeOrigin::signed(origin),
1249 native_value,
1250 gas_limit,
1251 storage_deposit_limit,
1252 Code::Upload(code.to_vec()),
1253 data.to_vec(),
1254 None,
1255 );
1256
1257 let returned_data = match result.result {
1258 Ok(return_value) => {
1259 if return_value.result.did_revert() {
1260 return Err(EthTransactError::Data(return_value.result.data));
1261 }
1262 return_value.result.data
1263 },
1264 Err(err) => {
1265 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1266 return extract_error(err)
1267 },
1268 };
1269
1270 let result = EthTransactInfo {
1271 gas_required: result.gas_required,
1272 storage_deposit: result.storage_deposit.charge_or_zero(),
1273 data: returned_data,
1274 eth_gas: Default::default(),
1275 };
1276
1277 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1279 result.gas_required,
1280 result.storage_deposit,
1281 );
1282 let dispatch_call: <T as Config>::RuntimeCall =
1283 crate::Call::<T>::instantiate_with_code {
1284 value: native_value,
1285 gas_limit,
1286 storage_deposit_limit,
1287 code: code.to_vec(),
1288 data: data.to_vec(),
1289 salt: None,
1290 }
1291 .into();
1292 (result, dispatch_call.get_dispatch_info())
1293 },
1294 };
1295
1296 let Ok(unsigned_tx) = tx.clone().try_into_unsigned() else {
1297 return Err(EthTransactError::Message("Invalid transaction".into()));
1298 };
1299
1300 let eth_dispatch_call =
1301 crate::Call::<T>::eth_transact { payload: unsigned_tx.dummy_signed_payload() };
1302 let fee = tx_fee(eth_dispatch_call, dispatch_info);
1303 let raw_gas = Self::evm_fee_to_gas(fee);
1304 let eth_gas =
1305 T::EthGasEncoder::encode(raw_gas, result.gas_required, result.storage_deposit);
1306
1307 log::trace!(target: LOG_TARGET, "bare_eth_call: raw_gas: {raw_gas:?} eth_gas: {eth_gas:?}");
1308 result.eth_gas = eth_gas;
1309 Ok(result)
1310 }
1311
1312 pub fn evm_balance(address: &H160) -> U256 {
1314 let account = T::AddressMapper::to_account_id(&address);
1315 Self::convert_native_to_evm(T::Currency::reducible_balance(&account, Preserve, Polite))
1316 }
1317
1318 pub fn evm_fee_to_gas(fee: BalanceOf<T>) -> U256 {
1321 let fee = Self::convert_native_to_evm(fee);
1322 let gas_price = GAS_PRICE.into();
1323 let (quotient, remainder) = fee.div_mod(gas_price);
1324 if remainder.is_zero() {
1325 quotient
1326 } else {
1327 quotient + U256::one()
1328 }
1329 }
1330
1331 fn evm_gas_to_fee(gas: U256, gas_price: U256) -> Result<BalanceOf<T>, Error<T>> {
1333 let fee = gas.saturating_mul(gas_price);
1334 Self::convert_evm_to_native(fee, ConversionPrecision::RoundUp)
1335 }
1336
1337 pub fn evm_gas_from_weight(weight: Weight) -> U256 {
1339 let fee = T::WeightPrice::convert(weight);
1340 Self::evm_fee_to_gas(fee)
1341 }
1342
1343 pub fn evm_block_gas_limit() -> U256 {
1345 let max_block_weight = T::BlockWeights::get()
1346 .get(DispatchClass::Normal)
1347 .max_total
1348 .unwrap_or_else(|| T::BlockWeights::get().max_block);
1349
1350 Self::evm_gas_from_weight(max_block_weight)
1351 }
1352
1353 pub fn evm_gas_price() -> U256 {
1355 GAS_PRICE.into()
1356 }
1357
1358 pub fn bare_upload_code(
1362 origin: OriginFor<T>,
1363 code: Vec<u8>,
1364 storage_deposit_limit: BalanceOf<T>,
1365 ) -> CodeUploadResult<BalanceOf<T>> {
1366 let origin = T::UploadOrigin::ensure_origin(origin)?;
1367 let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit, false)?;
1368 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
1369 }
1370
1371 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
1373 let contract_info =
1374 ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;
1375
1376 let maybe_value = contract_info.read(&Key::from_fixed(key));
1377 Ok(maybe_value)
1378 }
1379
1380 fn try_upload_code(
1382 origin: T::AccountId,
1383 code: Vec<u8>,
1384 storage_deposit_limit: BalanceOf<T>,
1385 skip_transfer: bool,
1386 ) -> Result<(WasmBlob<T>, BalanceOf<T>), DispatchError> {
1387 let mut module = WasmBlob::from_code(code, origin)?;
1388 let deposit = module.store_code(skip_transfer)?;
1389 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
1390 Ok((module, deposit))
1391 }
1392
1393 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
1395 executing_contract::using_once(&mut false, || {
1396 executing_contract::with(|f| {
1397 if *f {
1399 return Err(())
1400 }
1401 *f = true;
1403 Ok(())
1404 })
1405 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
1406 .map_err(|_| <Error<T>>::ReenteredPallet.into())
1407 .map(|_| f())
1408 .and_then(|r| r)
1409 })
1410 }
1411
1412 fn convert_native_to_evm(value: BalanceOf<T>) -> U256 {
1414 value.into().saturating_mul(T::NativeToEthRatio::get().into())
1415 }
1416
1417 fn convert_evm_to_native(
1419 value: U256,
1420 precision: ConversionPrecision,
1421 ) -> Result<BalanceOf<T>, Error<T>> {
1422 if value.is_zero() {
1423 return Ok(Zero::zero())
1424 }
1425
1426 let (quotient, remainder) = value.div_mod(T::NativeToEthRatio::get().into());
1427 match (precision, remainder.is_zero()) {
1428 (ConversionPrecision::Exact, false) => Err(Error::<T>::DecimalPrecisionLoss),
1429 (_, true) => quotient.try_into().map_err(|_| Error::<T>::BalanceConversionFailed),
1430 (_, false) => quotient
1431 .saturating_add(U256::one())
1432 .try_into()
1433 .map_err(|_| Error::<T>::BalanceConversionFailed),
1434 }
1435 }
1436}
1437
1438impl<T: Config> Pallet<T> {
1439 fn min_balance() -> BalanceOf<T> {
1441 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
1442 }
1443
1444 fn deposit_event(event: Event<T>) {
1446 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
1447 }
1448}
1449
1450environmental!(executing_contract: bool);
1452
1453sp_api::decl_runtime_apis! {
1454 #[api_version(1)]
1456 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber> where
1457 AccountId: Codec,
1458 Balance: Codec,
1459 Nonce: Codec,
1460 BlockNumber: Codec,
1461 {
1462 fn block_gas_limit() -> U256;
1464
1465 fn balance(address: H160) -> U256;
1467
1468 fn gas_price() -> U256;
1470
1471 fn nonce(address: H160) -> Nonce;
1473
1474 fn call(
1478 origin: AccountId,
1479 dest: H160,
1480 value: Balance,
1481 gas_limit: Option<Weight>,
1482 storage_deposit_limit: Option<Balance>,
1483 input_data: Vec<u8>,
1484 ) -> ContractResult<ExecReturnValue, Balance>;
1485
1486 fn instantiate(
1490 origin: AccountId,
1491 value: Balance,
1492 gas_limit: Option<Weight>,
1493 storage_deposit_limit: Option<Balance>,
1494 code: Code,
1495 data: Vec<u8>,
1496 salt: Option<[u8; 32]>,
1497 ) -> ContractResult<InstantiateReturnValue, Balance>;
1498
1499
1500 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
1504
1505 fn upload_code(
1509 origin: AccountId,
1510 code: Vec<u8>,
1511 storage_deposit_limit: Option<Balance>,
1512 ) -> CodeUploadResult<Balance>;
1513
1514 fn get_storage(
1520 address: H160,
1521 key: [u8; 32],
1522 ) -> GetStorageResult;
1523
1524
1525 fn trace_block(
1532 block: Block,
1533 config: TracerConfig
1534 ) -> Vec<(u32, CallTrace)>;
1535
1536 fn trace_tx(
1543 block: Block,
1544 tx_index: u32,
1545 config: TracerConfig
1546 ) -> Option<CallTrace>;
1547
1548 fn trace_call(tx: GenericTransaction, config: TracerConfig) -> Result<CallTrace, EthTransactError>;
1552
1553 }
1554}