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 storage;
31mod transient_storage;
32mod wasm;
33
34#[cfg(test)]
35mod tests;
36
37pub mod chain_extension;
38pub mod debug;
39pub mod evm;
40pub mod test_utils;
41pub mod weights;
42
43use crate::{
44 evm::{runtime::GAS_PRICE, TransactionLegacyUnsigned},
45 exec::{AccountIdOf, ExecError, Executable, Ext, Key, Origin, Stack as ExecStack},
46 gas::GasMeter,
47 storage::{meter::Meter as StorageMeter, ContractInfo, DeletionQueueManager},
48 wasm::{CodeInfo, RuntimeCosts, WasmBlob},
49};
50use alloc::boxed::Box;
51use codec::{Codec, Decode, Encode};
52use environmental::*;
53use frame_support::{
54 dispatch::{
55 DispatchErrorWithPostInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays,
56 PostDispatchInfo, RawOrigin,
57 },
58 ensure,
59 pallet_prelude::DispatchClass,
60 traits::{
61 fungible::{Inspect, Mutate, MutateHold},
62 ConstU32, ConstU64, Contains, EnsureOrigin, Get, IsType, OriginTrait, Time,
63 },
64 weights::{Weight, WeightMeter},
65 BoundedVec, RuntimeDebugNoBound,
66};
67use frame_system::{
68 ensure_signed,
69 pallet_prelude::{BlockNumberFor, OriginFor},
70 EventRecord, Pallet as System,
71};
72use pallet_transaction_payment::OnChargeTransaction;
73use scale_info::TypeInfo;
74use sp_core::{H160, H256, U256};
75use sp_runtime::{
76 traits::{BadOrigin, Convert, Dispatchable, Saturating},
77 DispatchError,
78};
79
80pub use crate::{
81 address::{create1, create2, AccountId32Mapper, AddressMapper},
82 debug::Tracing,
83 exec::MomentOf,
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 OnChargeTransactionBalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;
96type CodeVec = BoundedVec<u8, ConstU32<{ limits::code::BLOB_BYTES }>>;
97type EventRecordOf<T> =
98 EventRecord<<T as frame_system::Config>::RuntimeEvent, <T as frame_system::Config>::Hash>;
99type DebugBuffer = BoundedVec<u8, ConstU32<{ limits::DEBUG_BUFFER_BYTES }>>;
100type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
101
102const SENTINEL: u32 = u32::MAX;
109
110const LOG_TARGET: &str = "runtime::revive";
116
117const API_VERSION: u16 = 0;
121
122#[test]
123fn api_version_up_to_date() {
124 assert!(
125 API_VERSION == crate::wasm::HIGHEST_API_VERSION,
126 "A new versioned API has been added. The `API_VERSION` needs to be bumped."
127 );
128}
129
130#[frame_support::pallet]
131pub mod pallet {
132 use super::*;
133 use crate::debug::Debugger;
134 use frame_support::pallet_prelude::*;
135 use frame_system::pallet_prelude::*;
136 use sp_core::U256;
137 use sp_runtime::Perbill;
138
139 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
141
142 #[pallet::pallet]
143 #[pallet::storage_version(STORAGE_VERSION)]
144 pub struct Pallet<T>(_);
145
146 #[pallet::config(with_default)]
147 pub trait Config: frame_system::Config {
148 type Time: Time;
150
151 #[pallet::no_default]
153 type Currency: Inspect<Self::AccountId>
154 + Mutate<Self::AccountId>
155 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
156
157 #[pallet::no_default_bounds]
159 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
160
161 #[pallet::no_default_bounds]
163 type RuntimeCall: Parameter
164 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
165 + GetDispatchInfo;
166
167 #[pallet::no_default_bounds]
169 type RuntimeHoldReason: From<HoldReason>;
170
171 #[pallet::no_default_bounds]
194 type CallFilter: Contains<<Self as frame_system::Config>::RuntimeCall>;
195
196 #[pallet::no_default_bounds]
199 type WeightPrice: Convert<Weight, BalanceOf<Self>>;
200
201 type WeightInfo: WeightInfo;
204
205 #[pallet::no_default_bounds]
207 type ChainExtension: chain_extension::ChainExtension<Self> + Default;
208
209 #[pallet::constant]
215 #[pallet::no_default_bounds]
216 type DepositPerByte: Get<BalanceOf<Self>>;
217
218 #[pallet::constant]
224 #[pallet::no_default_bounds]
225 type DepositPerItem: Get<BalanceOf<Self>>;
226
227 #[pallet::constant]
232 type CodeHashLockupDepositPercent: Get<Perbill>;
233
234 #[pallet::no_default]
236 type AddressMapper: AddressMapper<Self>;
237
238 #[pallet::constant]
248 type UnsafeUnstableInterface: Get<bool>;
249
250 #[pallet::no_default_bounds]
255 type UploadOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
256
257 #[pallet::no_default_bounds]
268 type InstantiateOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
269
270 #[pallet::no_default_bounds]
274 type Debug: Debugger<Self>;
275
276 #[pallet::no_default_bounds]
279 type Xcm: xcm_builder::Controller<
280 OriginFor<Self>,
281 <Self as frame_system::Config>::RuntimeCall,
282 BlockNumberFor<Self>,
283 >;
284
285 type RuntimeMemory: Get<u32>;
290
291 type PVFMemory: Get<u32>;
299
300 #[pallet::constant]
305 type ChainId: Get<u64>;
306
307 #[pallet::constant]
309 type NativeToEthRatio: Get<u32>;
310 }
311
312 pub mod config_preludes {
314 use super::*;
315 use frame_support::{
316 derive_impl,
317 traits::{ConstBool, ConstU32},
318 };
319 use frame_system::EnsureSigned;
320 use sp_core::parameter_types;
321
322 type AccountId = sp_runtime::AccountId32;
323 type Balance = u64;
324 const UNITS: Balance = 10_000_000_000;
325 const CENTS: Balance = UNITS / 100;
326
327 pub const fn deposit(items: u32, bytes: u32) -> Balance {
328 items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS
329 }
330
331 parameter_types! {
332 pub const DepositPerItem: Balance = deposit(1, 0);
333 pub const DepositPerByte: Balance = deposit(0, 1);
334 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
335 }
336
337 pub struct TestDefaultConfig;
339
340 impl Time for TestDefaultConfig {
341 type Moment = u64;
342 fn now() -> Self::Moment {
343 unimplemented!("No default `now` implementation in `TestDefaultConfig` provide a custom `T::Time` type.")
344 }
345 }
346
347 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
348 fn convert(w: Weight) -> T {
349 w.ref_time().into()
350 }
351 }
352
353 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
354 impl frame_system::DefaultConfig for TestDefaultConfig {}
355
356 #[frame_support::register_default_impl(TestDefaultConfig)]
357 impl DefaultConfig for TestDefaultConfig {
358 #[inject_runtime_type]
359 type RuntimeEvent = ();
360
361 #[inject_runtime_type]
362 type RuntimeHoldReason = ();
363
364 #[inject_runtime_type]
365 type RuntimeCall = ();
366 type CallFilter = ();
367 type ChainExtension = ();
368 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
369 type DepositPerByte = DepositPerByte;
370 type DepositPerItem = DepositPerItem;
371 type Time = Self;
372 type UnsafeUnstableInterface = ConstBool<true>;
373 type UploadOrigin = EnsureSigned<AccountId>;
374 type InstantiateOrigin = EnsureSigned<AccountId>;
375 type WeightInfo = ();
376 type WeightPrice = Self;
377 type Debug = ();
378 type Xcm = ();
379 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
380 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
381 type ChainId = ConstU64<0>;
382 type NativeToEthRatio = ConstU32<1_000_000>;
383 }
384 }
385
386 #[pallet::event]
387 pub enum Event<T: Config> {
388 Instantiated { deployer: H160, contract: H160 },
390
391 Terminated {
398 contract: H160,
400 beneficiary: H160,
402 },
403
404 CodeStored { code_hash: H256, deposit_held: BalanceOf<T>, uploader: H160 },
406
407 ContractEmitted {
409 contract: H160,
411 data: Vec<u8>,
414 topics: Vec<H256>,
417 },
418
419 CodeRemoved { code_hash: H256, deposit_released: BalanceOf<T>, remover: H160 },
421
422 ContractCodeUpdated {
424 contract: H160,
426 new_code_hash: H256,
428 old_code_hash: H256,
430 },
431
432 Called {
440 caller: Origin<T>,
442 contract: H160,
444 },
445
446 DelegateCalled {
454 contract: H160,
457 code_hash: H256,
459 },
460
461 StorageDepositTransferredAndHeld { from: H160, to: H160, amount: BalanceOf<T> },
463
464 StorageDepositTransferredAndReleased { from: H160, to: H160, amount: BalanceOf<T> },
466 }
467
468 #[pallet::error]
469 pub enum Error<T> {
470 InvalidSchedule,
472 InvalidCallFlags,
474 OutOfGas,
476 TransferFailed,
479 MaxCallDepthReached,
482 ContractNotFound,
484 CodeNotFound,
486 CodeInfoNotFound,
488 OutOfBounds,
490 DecodingFailed,
492 ContractTrapped,
494 ValueTooLarge,
496 TerminatedWhileReentrant,
499 InputForwarded,
501 TooManyTopics,
503 NoChainExtension,
507 XCMDecodeFailed,
509 DuplicateContract,
511 TerminatedInConstructor,
515 ReentranceDenied,
517 ReenteredPallet,
519 StateChangeDenied,
521 StorageDepositNotEnoughFunds,
523 StorageDepositLimitExhausted,
525 CodeInUse,
527 ContractReverted,
532 CodeRejected,
537 BlobTooLarge,
539 StaticMemoryTooLarge,
542 BasicBlockTooLarge,
544 InvalidInstruction,
546 MaxDelegateDependenciesReached,
548 DelegateDependencyNotFound,
550 DelegateDependencyAlreadyExists,
552 CannotAddSelfAsDelegateDependency,
554 OutOfTransientStorage,
556 InvalidSyscall,
558 InvalidStorageFlags,
560 ExecutionFailed,
562 BalanceConversionFailed,
564 InvalidImmutableAccess,
567 AccountUnmapped,
571 AccountAlreadyMapped,
573 }
574
575 #[pallet::composite_enum]
577 pub enum HoldReason {
578 CodeUploadDepositReserve,
580 StorageDepositReserve,
582 AddressMapping,
584 }
585
586 #[pallet::storage]
588 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, CodeVec>;
589
590 #[pallet::storage]
592 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
593
594 #[pallet::storage]
596 pub(crate) type ContractInfoOf<T: Config> = StorageMap<_, Identity, H160, ContractInfo<T>>;
597
598 #[pallet::storage]
600 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
601
602 #[pallet::storage]
607 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
608
609 #[pallet::storage]
612 pub(crate) type DeletionQueueCounter<T: Config> =
613 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
614
615 #[pallet::storage]
621 pub(crate) type AddressSuffix<T: Config> = StorageMap<_, Identity, H160, [u8; 12]>;
622
623 #[pallet::extra_constants]
624 impl<T: Config> Pallet<T> {
625 #[pallet::constant_name(ApiVersion)]
626 fn api_version() -> u16 {
627 API_VERSION
628 }
629 }
630
631 #[pallet::hooks]
632 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
633 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
634 let mut meter = WeightMeter::with_limit(limit);
635 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
636 meter.consumed()
637 }
638
639 fn integrity_test() {
640 use limits::code::STATIC_MEMORY_BYTES;
641
642 let max_runtime_mem: u32 = T::RuntimeMemory::get();
644 let max_call_depth =
646 limits::CALL_STACK_DEPTH.checked_add(1).expect("CallStack size is too big");
647 let max_transient_storage_size = limits::TRANSIENT_STORAGE_BYTES
650 .checked_mul(2)
651 .expect("MaxTransientStorageSize is too large");
652
653 const TOTAL_MEMORY_DEVIDER: u32 = 2;
656
657 const MEMORY_ALLOCATOR_INEFFICENCY_DEVIDER: u32 = 4;
661
662 let static_memory_limit = max_runtime_mem
670 .saturating_div(TOTAL_MEMORY_DEVIDER)
671 .saturating_sub(max_transient_storage_size)
672 .saturating_div(max_call_depth)
673 .saturating_sub(STATIC_MEMORY_BYTES)
674 .saturating_div(MEMORY_ALLOCATOR_INEFFICENCY_DEVIDER);
675
676 assert!(
677 STATIC_MEMORY_BYTES < static_memory_limit,
678 "Given `CallStack` height {:?}, `STATIC_MEMORY_LIMIT` should be set less than {:?} \
679 (current value is {:?}), to avoid possible runtime oom issues.",
680 max_call_depth,
681 static_memory_limit,
682 STATIC_MEMORY_BYTES,
683 );
684
685 let max_block_ref_time = T::BlockWeights::get()
691 .get(DispatchClass::Normal)
692 .max_total
693 .unwrap_or_else(|| T::BlockWeights::get().max_block)
694 .ref_time();
695 let max_payload_size = limits::PAYLOAD_BYTES;
696 let max_key_size =
697 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
698 .expect("Key of maximal size shall be created")
699 .hash()
700 .len() as u32;
701
702 let max_immutable_key_size = T::AccountId::max_encoded_len() as u32;
703 let max_immutable_size: u32 = ((max_block_ref_time /
704 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetImmutableData(
705 limits::IMMUTABLE_BYTES,
706 ))
707 .ref_time()))
708 .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64))
709 .try_into()
710 .expect("Immutable data size too big");
711
712 let max_storage_size: u32 = ((max_block_ref_time /
715 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
716 new_bytes: max_payload_size,
717 old_bytes: 0,
718 })
719 .ref_time()))
720 .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64))
721 .saturating_add(max_immutable_size.into())
722 .try_into()
723 .expect("Storage size too big");
724
725 let max_pvf_mem: u32 = T::PVFMemory::get();
726 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
727
728 assert!(
729 max_storage_size < storage_size_limit,
730 "Maximal storage size {} exceeds the storage limit {}",
731 max_storage_size,
732 storage_size_limit
733 );
734
735 let max_events_size: u32 = ((max_block_ref_time /
739 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
740 num_topic: 0,
741 len: max_payload_size,
742 })
743 .ref_time()))
744 .saturating_mul(max_payload_size as u64))
745 .try_into()
746 .expect("Events size too big");
747
748 assert!(
749 max_events_size < storage_size_limit,
750 "Maximal events size {} exceeds the events limit {}",
751 max_events_size,
752 storage_size_limit
753 );
754 }
755 }
756
757 #[pallet::call]
758 impl<T: Config> Pallet<T>
759 where
760 BalanceOf<T>: Into<U256> + TryFrom<U256>,
761 MomentOf<T>: Into<U256>,
762 T::Hash: frame_support::traits::IsType<H256>,
763 {
764 #[allow(unused_variables)]
780 #[pallet::call_index(0)]
781 #[pallet::weight(Weight::MAX)]
782 pub fn eth_transact(
783 origin: OriginFor<T>,
784 payload: Vec<u8>,
785 gas_limit: Weight,
786 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
787 ) -> DispatchResultWithPostInfo {
788 Err(frame_system::Error::CallFiltered::<T>.into())
789 }
790
791 #[pallet::call_index(1)]
808 #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))]
809 pub fn call(
810 origin: OriginFor<T>,
811 dest: H160,
812 #[pallet::compact] value: BalanceOf<T>,
813 gas_limit: Weight,
814 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
815 data: Vec<u8>,
816 ) -> DispatchResultWithPostInfo {
817 log::info!(target: LOG_TARGET, "Call: {:?} {:?} {:?}", dest, value, data);
818 let mut output = Self::bare_call(
819 origin,
820 dest,
821 value,
822 gas_limit,
823 storage_deposit_limit,
824 data,
825 DebugInfo::Skip,
826 CollectEvents::Skip,
827 );
828 if let Ok(return_value) = &output.result {
829 if return_value.did_revert() {
830 output.result = Err(<Error<T>>::ContractReverted.into());
831 }
832 }
833 dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call())
834 }
835
836 #[pallet::call_index(2)]
842 #[pallet::weight(
843 T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
844 )]
845 pub fn instantiate(
846 origin: OriginFor<T>,
847 #[pallet::compact] value: BalanceOf<T>,
848 gas_limit: Weight,
849 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
850 code_hash: sp_core::H256,
851 data: Vec<u8>,
852 salt: Option<[u8; 32]>,
853 ) -> DispatchResultWithPostInfo {
854 let data_len = data.len() as u32;
855 let mut output = Self::bare_instantiate(
856 origin,
857 value,
858 gas_limit,
859 storage_deposit_limit,
860 Code::Existing(code_hash),
861 data,
862 salt,
863 DebugInfo::Skip,
864 CollectEvents::Skip,
865 );
866 if let Ok(retval) = &output.result {
867 if retval.result.did_revert() {
868 output.result = Err(<Error<T>>::ContractReverted.into());
869 }
870 }
871 dispatch_result(
872 output.result.map(|result| result.result),
873 output.gas_consumed,
874 T::WeightInfo::instantiate(data_len),
875 )
876 }
877
878 #[pallet::call_index(3)]
906 #[pallet::weight(
907 T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
908 .saturating_add(*gas_limit)
909 )]
910 pub fn instantiate_with_code(
911 origin: OriginFor<T>,
912 #[pallet::compact] value: BalanceOf<T>,
913 gas_limit: Weight,
914 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
915 code: Vec<u8>,
916 data: Vec<u8>,
917 salt: Option<[u8; 32]>,
918 ) -> DispatchResultWithPostInfo {
919 let code_len = code.len() as u32;
920 let data_len = data.len() as u32;
921 let mut output = Self::bare_instantiate(
922 origin,
923 value,
924 gas_limit,
925 storage_deposit_limit,
926 Code::Upload(code),
927 data,
928 salt,
929 DebugInfo::Skip,
930 CollectEvents::Skip,
931 );
932 if let Ok(retval) = &output.result {
933 if retval.result.did_revert() {
934 output.result = Err(<Error<T>>::ContractReverted.into());
935 }
936 }
937 dispatch_result(
938 output.result.map(|result| result.result),
939 output.gas_consumed,
940 T::WeightInfo::instantiate_with_code(code_len, data_len),
941 )
942 }
943
944 #[pallet::call_index(4)]
957 #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))]
958 pub fn upload_code(
959 origin: OriginFor<T>,
960 code: Vec<u8>,
961 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
962 ) -> DispatchResult {
963 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
964 }
965
966 #[pallet::call_index(5)]
971 #[pallet::weight(T::WeightInfo::remove_code())]
972 pub fn remove_code(
973 origin: OriginFor<T>,
974 code_hash: sp_core::H256,
975 ) -> DispatchResultWithPostInfo {
976 let origin = ensure_signed(origin)?;
977 <WasmBlob<T>>::remove(&origin, code_hash)?;
978 Ok(Pays::No.into())
980 }
981
982 #[pallet::call_index(6)]
993 #[pallet::weight(T::WeightInfo::set_code())]
994 pub fn set_code(
995 origin: OriginFor<T>,
996 dest: H160,
997 code_hash: sp_core::H256,
998 ) -> DispatchResult {
999 ensure_root(origin)?;
1000 <ContractInfoOf<T>>::try_mutate(&dest, |contract| {
1001 let contract = if let Some(contract) = contract {
1002 contract
1003 } else {
1004 return Err(<Error<T>>::ContractNotFound.into());
1005 };
1006 <ExecStack<T, WasmBlob<T>>>::increment_refcount(code_hash)?;
1007 <ExecStack<T, WasmBlob<T>>>::decrement_refcount(contract.code_hash);
1008 Self::deposit_event(Event::ContractCodeUpdated {
1009 contract: dest,
1010 new_code_hash: code_hash,
1011 old_code_hash: contract.code_hash,
1012 });
1013 contract.code_hash = code_hash;
1014 Ok(())
1015 })
1016 }
1017
1018 #[pallet::call_index(7)]
1023 #[pallet::weight(T::WeightInfo::map_account())]
1024 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1025 let origin = ensure_signed(origin)?;
1026 T::AddressMapper::map(&origin)
1027 }
1028
1029 #[pallet::call_index(8)]
1034 #[pallet::weight(T::WeightInfo::unmap_account())]
1035 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1036 let origin = ensure_signed(origin)?;
1037 T::AddressMapper::unmap(&origin)
1038 }
1039
1040 #[pallet::call_index(9)]
1046 #[pallet::weight({
1047 let dispatch_info = call.get_dispatch_info();
1048 (
1049 T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1050 dispatch_info.class
1051 )
1052 })]
1053 pub fn dispatch_as_fallback_account(
1054 origin: OriginFor<T>,
1055 call: Box<<T as Config>::RuntimeCall>,
1056 ) -> DispatchResultWithPostInfo {
1057 let origin = ensure_signed(origin)?;
1058 let unmapped_account =
1059 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1060 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1061 }
1062 }
1063}
1064
1065fn dispatch_result<R>(
1067 result: Result<R, DispatchError>,
1068 gas_consumed: Weight,
1069 base_weight: Weight,
1070) -> DispatchResultWithPostInfo {
1071 let post_info = PostDispatchInfo {
1072 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1073 pays_fee: Default::default(),
1074 };
1075
1076 result
1077 .map(|_| post_info)
1078 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1079}
1080
1081impl<T: Config> Pallet<T>
1082where
1083 BalanceOf<T>: Into<U256> + TryFrom<U256>,
1084 MomentOf<T>: Into<U256>,
1085 T::Hash: frame_support::traits::IsType<H256>,
1086{
1087 pub fn bare_call(
1094 origin: OriginFor<T>,
1095 dest: H160,
1096 value: BalanceOf<T>,
1097 gas_limit: Weight,
1098 storage_deposit_limit: BalanceOf<T>,
1099 data: Vec<u8>,
1100 debug: DebugInfo,
1101 collect_events: CollectEvents,
1102 ) -> ContractResult<ExecReturnValue, BalanceOf<T>, EventRecordOf<T>> {
1103 let mut gas_meter = GasMeter::new(gas_limit);
1104 let mut storage_deposit = Default::default();
1105 let mut debug_message = if matches!(debug, DebugInfo::UnsafeDebug) {
1106 Some(DebugBuffer::default())
1107 } else {
1108 None
1109 };
1110 let try_call = || {
1111 let origin = Origin::from_runtime_origin(origin)?;
1112 let mut storage_meter = StorageMeter::new(&origin, storage_deposit_limit, value)?;
1113 let result = ExecStack::<T, WasmBlob<T>>::run_call(
1114 origin.clone(),
1115 dest,
1116 &mut gas_meter,
1117 &mut storage_meter,
1118 value,
1119 data,
1120 debug_message.as_mut(),
1121 )?;
1122 storage_deposit = storage_meter.try_into_deposit(&origin)?;
1123 Ok(result)
1124 };
1125 let result = Self::run_guarded(try_call);
1126 let events = if matches!(collect_events, CollectEvents::UnsafeCollect) {
1127 Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
1128 } else {
1129 None
1130 };
1131 ContractResult {
1132 result: result.map_err(|r| r.error),
1133 gas_consumed: gas_meter.gas_consumed(),
1134 gas_required: gas_meter.gas_required(),
1135 storage_deposit,
1136 debug_message: debug_message.unwrap_or_default().to_vec(),
1137 events,
1138 }
1139 }
1140
1141 pub fn bare_instantiate(
1148 origin: OriginFor<T>,
1149 value: BalanceOf<T>,
1150 gas_limit: Weight,
1151 mut storage_deposit_limit: BalanceOf<T>,
1152 code: Code,
1153 data: Vec<u8>,
1154 salt: Option<[u8; 32]>,
1155 debug: DebugInfo,
1156 collect_events: CollectEvents,
1157 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>, EventRecordOf<T>> {
1158 let mut gas_meter = GasMeter::new(gas_limit);
1159 let mut storage_deposit = Default::default();
1160 let mut debug_message =
1161 if debug == DebugInfo::UnsafeDebug { Some(DebugBuffer::default()) } else { None };
1162 let try_instantiate = || {
1163 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1164 let (executable, upload_deposit) = match code {
1165 Code::Upload(code) => {
1166 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1167 let (executable, upload_deposit) =
1168 Self::try_upload_code(upload_account, code, storage_deposit_limit)?;
1169 storage_deposit_limit.saturating_reduce(upload_deposit);
1170 (executable, upload_deposit)
1171 },
1172 Code::Existing(code_hash) =>
1173 (WasmBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()),
1174 };
1175 let instantiate_origin = Origin::from_account_id(instantiate_account.clone());
1176 let mut storage_meter =
1177 StorageMeter::new(&instantiate_origin, storage_deposit_limit, value)?;
1178 let result = ExecStack::<T, WasmBlob<T>>::run_instantiate(
1179 instantiate_account,
1180 executable,
1181 &mut gas_meter,
1182 &mut storage_meter,
1183 value,
1184 data,
1185 salt.as_ref(),
1186 debug_message.as_mut(),
1187 );
1188 storage_deposit = storage_meter
1189 .try_into_deposit(&instantiate_origin)?
1190 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1191 result
1192 };
1193 let output = Self::run_guarded(try_instantiate);
1194 let events = if matches!(collect_events, CollectEvents::UnsafeCollect) {
1195 Some(System::<T>::read_events_no_consensus().map(|e| *e).collect())
1196 } else {
1197 None
1198 };
1199 ContractResult {
1200 result: output
1201 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1202 .map_err(|e| e.error),
1203 gas_consumed: gas_meter.gas_consumed(),
1204 gas_required: gas_meter.gas_required(),
1205 storage_deposit,
1206 debug_message: debug_message.unwrap_or_default().to_vec(),
1207 events,
1208 }
1209 }
1210
1211 pub fn bare_eth_transact(
1227 origin: T::AccountId,
1228 dest: Option<H160>,
1229 value: BalanceOf<T>,
1230 input: Vec<u8>,
1231 gas_limit: Weight,
1232 storage_deposit_limit: BalanceOf<T>,
1233 utx_encoded_size: impl Fn(Call<T>) -> u32,
1234 debug: DebugInfo,
1235 collect_events: CollectEvents,
1236 ) -> EthContractResult<BalanceOf<T>>
1237 where
1238 T: pallet_transaction_payment::Config,
1239 <T as frame_system::Config>::RuntimeCall:
1240 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1241 <T as Config>::RuntimeCall: From<crate::Call<T>>,
1242 <T as Config>::RuntimeCall: Encode,
1243 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1244 T::Nonce: Into<U256>,
1245 T::Hash: frame_support::traits::IsType<H256>,
1246 {
1247 log::debug!(target: LOG_TARGET, "bare_eth_transact: dest: {dest:?} value: {value:?} gas_limit: {gas_limit:?} storage_deposit_limit: {storage_deposit_limit:?}");
1248 let nonce: T::Nonce = <System<T>>::account_nonce(&origin);
1250
1251 let max_gas_fee: BalanceOf<T> =
1253 (pallet_transaction_payment::Pallet::<T>::weight_to_fee(Weight::MAX) /
1254 GAS_PRICE.into())
1255 .into();
1256
1257 if let Some(dest) = dest {
1259 let result = crate::Pallet::<T>::bare_call(
1261 T::RuntimeOrigin::signed(origin),
1262 dest,
1263 value,
1264 gas_limit,
1265 storage_deposit_limit,
1266 input.clone(),
1267 debug,
1268 collect_events,
1269 );
1270
1271 let tx = TransactionLegacyUnsigned {
1273 value: value.into().saturating_mul(T::NativeToEthRatio::get().into()),
1274 input: input.into(),
1275 nonce: nonce.into(),
1276 chain_id: Some(T::ChainId::get().into()),
1277 gas_price: GAS_PRICE.into(),
1278 gas: max_gas_fee.into(),
1279 to: Some(dest),
1280 ..Default::default()
1281 };
1282
1283 let eth_dispatch_call = crate::Call::<T>::eth_transact {
1284 payload: tx.dummy_signed_payload(),
1285 gas_limit: result.gas_required,
1286 storage_deposit_limit: result.storage_deposit.charge_or_zero(),
1287 };
1288 let encoded_len = utx_encoded_size(eth_dispatch_call);
1289
1290 let dispatch_call: <T as Config>::RuntimeCall = crate::Call::<T>::call {
1292 dest,
1293 value,
1294 gas_limit: result.gas_required,
1295 storage_deposit_limit: result.storage_deposit.charge_or_zero(),
1296 data: tx.input.0,
1297 }
1298 .into();
1299 let dispatch_info = dispatch_call.get_dispatch_info();
1300
1301 let fee = pallet_transaction_payment::Pallet::<T>::compute_fee(
1303 encoded_len,
1304 &dispatch_info,
1305 0u32.into(),
1306 )
1307 .into();
1308
1309 log::trace!(target: LOG_TARGET, "bare_eth_call: len: {encoded_len:?} fee: {fee:?}");
1310 EthContractResult {
1311 gas_required: result.gas_required,
1312 storage_deposit: result.storage_deposit.charge_or_zero(),
1313 result: result.result.map(|v| v.data),
1314 fee,
1315 }
1316 } else {
1318 let (code, data) = match polkavm::ProgramBlob::blob_length(&input) {
1320 Some(blob_len) => blob_len
1321 .try_into()
1322 .ok()
1323 .and_then(|blob_len| (input.split_at_checked(blob_len)))
1324 .unwrap_or_else(|| (&input[..], &[][..])),
1325 _ => {
1326 log::debug!(target: LOG_TARGET, "Failed to extract polkavm blob length");
1327 (&input[..], &[][..])
1328 },
1329 };
1330
1331 let result = crate::Pallet::<T>::bare_instantiate(
1333 T::RuntimeOrigin::signed(origin),
1334 value,
1335 gas_limit,
1336 storage_deposit_limit,
1337 Code::Upload(code.to_vec()),
1338 data.to_vec(),
1339 None,
1340 debug,
1341 collect_events,
1342 );
1343
1344 let tx = TransactionLegacyUnsigned {
1346 gas: max_gas_fee.into(),
1347 nonce: nonce.into(),
1348 value: value.into().saturating_mul(T::NativeToEthRatio::get().into()),
1349 input: input.clone().into(),
1350 gas_price: GAS_PRICE.into(),
1351 chain_id: Some(T::ChainId::get().into()),
1352 ..Default::default()
1353 };
1354 let eth_dispatch_call = crate::Call::<T>::eth_transact {
1355 payload: tx.dummy_signed_payload(),
1356 gas_limit: result.gas_required,
1357 storage_deposit_limit: result.storage_deposit.charge_or_zero(),
1358 };
1359 let encoded_len = utx_encoded_size(eth_dispatch_call);
1360
1361 let dispatch_call: <T as Config>::RuntimeCall =
1363 crate::Call::<T>::instantiate_with_code {
1364 value,
1365 gas_limit: result.gas_required,
1366 storage_deposit_limit: result.storage_deposit.charge_or_zero(),
1367 code: code.to_vec(),
1368 data: data.to_vec(),
1369 salt: None,
1370 }
1371 .into();
1372 let dispatch_info = dispatch_call.get_dispatch_info();
1373
1374 let fee = pallet_transaction_payment::Pallet::<T>::compute_fee(
1376 encoded_len,
1377 &dispatch_info,
1378 0u32.into(),
1379 )
1380 .into();
1381
1382 log::trace!(target: LOG_TARGET, "bare_eth_call: len: {encoded_len:?} fee: {fee:?}");
1383 EthContractResult {
1384 gas_required: result.gas_required,
1385 storage_deposit: result.storage_deposit.charge_or_zero(),
1386 result: result.result.map(|v| v.result.data),
1387 fee,
1388 }
1389 }
1390 }
1391
1392 pub fn bare_upload_code(
1396 origin: OriginFor<T>,
1397 code: Vec<u8>,
1398 storage_deposit_limit: BalanceOf<T>,
1399 ) -> CodeUploadResult<BalanceOf<T>> {
1400 let origin = T::UploadOrigin::ensure_origin(origin)?;
1401 let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit)?;
1402 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
1403 }
1404
1405 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
1407 let contract_info =
1408 ContractInfoOf::<T>::get(&address).ok_or(ContractAccessError::DoesntExist)?;
1409
1410 let maybe_value = contract_info.read(&Key::from_fixed(key));
1411 Ok(maybe_value)
1412 }
1413
1414 fn try_upload_code(
1416 origin: T::AccountId,
1417 code: Vec<u8>,
1418 storage_deposit_limit: BalanceOf<T>,
1419 ) -> Result<(WasmBlob<T>, BalanceOf<T>), DispatchError> {
1420 let mut module = WasmBlob::from_code(code, origin)?;
1421 let deposit = module.store_code()?;
1422 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
1423 Ok((module, deposit))
1424 }
1425
1426 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
1428 executing_contract::using_once(&mut false, || {
1429 executing_contract::with(|f| {
1430 if *f {
1432 return Err(())
1433 }
1434 *f = true;
1436 Ok(())
1437 })
1438 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
1439 .map_err(|_| <Error<T>>::ReenteredPallet.into())
1440 .map(|_| f())
1441 .and_then(|r| r)
1442 })
1443 }
1444}
1445
1446impl<T: Config> Pallet<T> {
1447 fn min_balance() -> BalanceOf<T> {
1449 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
1450 }
1451
1452 fn deposit_event(event: Event<T>) {
1454 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
1455 }
1456}
1457
1458environmental!(executing_contract: bool);
1460
1461sp_api::decl_runtime_apis! {
1462 #[api_version(1)]
1464 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, EventRecord> where
1465 AccountId: Codec,
1466 Balance: Codec,
1467 Nonce: Codec,
1468 BlockNumber: Codec,
1469 EventRecord: Codec,
1470 {
1471 fn balance(address: H160) -> Balance;
1473
1474 fn nonce(address: H160) -> Nonce;
1476
1477 fn call(
1481 origin: AccountId,
1482 dest: H160,
1483 value: Balance,
1484 gas_limit: Option<Weight>,
1485 storage_deposit_limit: Option<Balance>,
1486 input_data: Vec<u8>,
1487 ) -> ContractResult<ExecReturnValue, Balance, EventRecord>;
1488
1489 fn instantiate(
1493 origin: AccountId,
1494 value: Balance,
1495 gas_limit: Option<Weight>,
1496 storage_deposit_limit: Option<Balance>,
1497 code: Code,
1498 data: Vec<u8>,
1499 salt: Option<[u8; 32]>,
1500 ) -> ContractResult<InstantiateReturnValue, Balance, EventRecord>;
1501
1502
1503 fn eth_transact(
1507 origin: H160,
1508 dest: Option<H160>,
1509 value: Balance,
1510 input: Vec<u8>,
1511 gas_limit: Option<Weight>,
1512 storage_deposit_limit: Option<Balance>,
1513 ) -> EthContractResult<Balance>;
1514
1515 fn upload_code(
1519 origin: AccountId,
1520 code: Vec<u8>,
1521 storage_deposit_limit: Option<Balance>,
1522 ) -> CodeUploadResult<Balance>;
1523
1524 fn get_storage(
1530 address: H160,
1531 key: [u8; 32],
1532 ) -> GetStorageResult;
1533 }
1534}