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;
24
25mod address;
26mod benchmarking;
27mod call_builder;
28mod exec;
29mod gas;
30mod impl_fungibles;
31mod limits;
32mod primitives;
33mod storage;
34#[cfg(test)]
35mod tests;
36mod transient_storage;
37mod vm;
38
39pub mod evm;
40pub mod migrations;
41pub mod precompiles;
42pub mod test_utils;
43pub mod tracing;
44pub mod weights;
45
46use crate::{
47 evm::{
48 runtime::GAS_PRICE, CallTracer, GasEncoder, GenericTransaction, PrestateTracer, Trace,
49 Tracer, TracerType, TYPE_EIP1559,
50 },
51 exec::{AccountIdOf, ExecError, Executable, Key, Stack as ExecStack},
52 gas::GasMeter,
53 storage::{
54 meter::Meter as StorageMeter, AccountInfo, AccountType, ContractInfo, DeletionQueueManager,
55 },
56 tracing::if_tracing,
57 vm::{CodeInfo, ContractBlob, RuntimeCosts},
58};
59use alloc::{boxed::Box, format, vec};
60use codec::{Codec, Decode, Encode};
61use environmental::*;
62use frame_support::{
63 dispatch::{
64 DispatchErrorWithPostInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays,
65 PostDispatchInfo, RawOrigin,
66 },
67 ensure,
68 pallet_prelude::DispatchClass,
69 traits::{
70 fungible::{Inspect, Mutate, MutateHold},
71 ConstU32, ConstU64, EnsureOrigin, Get, IsType, OriginTrait, Time,
72 },
73 weights::WeightMeter,
74 BoundedVec, RuntimeDebugNoBound,
75};
76use frame_system::{
77 ensure_signed,
78 pallet_prelude::{BlockNumberFor, OriginFor},
79 Pallet as System,
80};
81use pallet_transaction_payment::OnChargeTransaction;
82use scale_info::TypeInfo;
83use sp_runtime::{
84 traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating},
85 AccountId32, DispatchError,
86};
87
88pub use crate::{
89 address::{
90 create1, create2, is_eth_derived, AccountId32Mapper, AddressMapper, TestAccountMapper,
91 },
92 exec::{MomentOf, Origin},
93 pallet::*,
94};
95pub use codec;
96pub use frame_support::{self, dispatch::DispatchInfo, weights::Weight};
97pub use frame_system::{self, limits::BlockWeights};
98pub use pallet_transaction_payment;
99pub use primitives::*;
100pub use sp_core::{H160, H256, U256};
101pub use sp_runtime;
102pub use weights::WeightInfo;
103
104#[cfg(doc)]
105pub use crate::vm::SyscallDoc;
106
107pub type BalanceOf<T> =
108 <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
109type TrieId = BoundedVec<u8, ConstU32<128>>;
110type CodeVec = BoundedVec<u8, ConstU32<{ limits::code::BLOB_BYTES }>>;
111type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
112pub(crate) type OnChargeTransactionBalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;
113
114const SENTINEL: u32 = u32::MAX;
121
122const LOG_TARGET: &str = "runtime::revive";
128
129#[frame_support::pallet]
130pub mod pallet {
131 use super::*;
132 use frame_support::{pallet_prelude::*, traits::FindAuthor};
133 use frame_system::pallet_prelude::*;
134 use sp_core::U256;
135 use sp_runtime::Perbill;
136
137 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
139
140 #[pallet::pallet]
141 #[pallet::storage_version(STORAGE_VERSION)]
142 pub struct Pallet<T>(_);
143
144 #[pallet::config(with_default)]
145 pub trait Config: frame_system::Config {
146 type Time: Time;
148
149 #[pallet::no_default]
151 type Currency: Inspect<Self::AccountId>
152 + Mutate<Self::AccountId>
153 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
154
155 #[pallet::no_default_bounds]
157 #[allow(deprecated)]
158 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
159
160 #[pallet::no_default_bounds]
162 type RuntimeCall: Parameter
163 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
164 + GetDispatchInfo;
165
166 #[pallet::no_default_bounds]
168 type RuntimeHoldReason: From<HoldReason>;
169
170 #[pallet::no_default_bounds]
173 type WeightPrice: Convert<Weight, BalanceOf<Self>>;
174
175 type WeightInfo: WeightInfo;
178
179 #[pallet::no_default_bounds]
183 #[allow(private_bounds)]
184 type Precompiles: precompiles::Precompiles<Self>;
185
186 type FindAuthor: FindAuthor<Self::AccountId>;
188
189 #[pallet::constant]
195 #[pallet::no_default_bounds]
196 type DepositPerByte: Get<BalanceOf<Self>>;
197
198 #[pallet::constant]
204 #[pallet::no_default_bounds]
205 type DepositPerItem: Get<BalanceOf<Self>>;
206
207 #[pallet::constant]
211 type CodeHashLockupDepositPercent: Get<Perbill>;
212
213 #[pallet::no_default]
215 type AddressMapper: AddressMapper<Self>;
216
217 #[pallet::constant]
227 type UnsafeUnstableInterface: Get<bool>;
228
229 #[pallet::no_default_bounds]
234 type UploadOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
235
236 #[pallet::no_default_bounds]
247 type InstantiateOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
248
249 type RuntimeMemory: Get<u32>;
254
255 type PVFMemory: Get<u32>;
263
264 #[pallet::constant]
269 type ChainId: Get<u64>;
270
271 #[pallet::constant]
273 type NativeToEthRatio: Get<u32>;
274
275 #[pallet::no_default_bounds]
278 type EthGasEncoder: GasEncoder<BalanceOf<Self>>;
279 }
280
281 pub mod config_preludes {
283 use super::*;
284 use frame_support::{
285 derive_impl,
286 traits::{ConstBool, ConstU32},
287 };
288 use frame_system::EnsureSigned;
289 use sp_core::parameter_types;
290
291 type Balance = u64;
292 const UNITS: Balance = 10_000_000_000;
293 const CENTS: Balance = UNITS / 100;
294
295 pub const fn deposit(items: u32, bytes: u32) -> Balance {
296 items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS
297 }
298
299 parameter_types! {
300 pub const DepositPerItem: Balance = deposit(1, 0);
301 pub const DepositPerByte: Balance = deposit(0, 1);
302 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
303 }
304
305 pub struct TestDefaultConfig;
307
308 impl Time for TestDefaultConfig {
309 type Moment = u64;
310 fn now() -> Self::Moment {
311 0u64
312 }
313 }
314
315 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
316 fn convert(w: Weight) -> T {
317 w.ref_time().into()
318 }
319 }
320
321 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
322 impl frame_system::DefaultConfig for TestDefaultConfig {}
323
324 #[frame_support::register_default_impl(TestDefaultConfig)]
325 impl DefaultConfig for TestDefaultConfig {
326 #[inject_runtime_type]
327 type RuntimeEvent = ();
328
329 #[inject_runtime_type]
330 type RuntimeHoldReason = ();
331
332 #[inject_runtime_type]
333 type RuntimeCall = ();
334 type Precompiles = ();
335 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
336 type DepositPerByte = DepositPerByte;
337 type DepositPerItem = DepositPerItem;
338 type Time = Self;
339 type UnsafeUnstableInterface = ConstBool<true>;
340 type UploadOrigin = EnsureSigned<Self::AccountId>;
341 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
342 type WeightInfo = ();
343 type WeightPrice = Self;
344 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
345 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
346 type ChainId = ConstU64<42>;
347 type NativeToEthRatio = ConstU32<1_000_000>;
348 type EthGasEncoder = ();
349 type FindAuthor = ();
350 }
351 }
352
353 #[pallet::event]
354 pub enum Event<T: Config> {
355 ContractEmitted {
357 contract: H160,
359 data: Vec<u8>,
362 topics: Vec<H256>,
365 },
366
367 Instantiated { deployer: H160, contract: H160 },
369 }
370
371 #[pallet::error]
372 #[repr(u8)]
373 pub enum Error<T> {
374 InvalidSchedule = 0x01,
376 InvalidCallFlags = 0x02,
378 OutOfGas = 0x03,
380 TransferFailed = 0x04,
383 MaxCallDepthReached = 0x05,
386 ContractNotFound = 0x06,
388 CodeNotFound = 0x07,
390 CodeInfoNotFound = 0x08,
392 OutOfBounds = 0x09,
394 DecodingFailed = 0x0A,
396 ContractTrapped = 0x0B,
398 ValueTooLarge = 0x0C,
400 TerminatedWhileReentrant = 0x0D,
403 InputForwarded = 0x0E,
405 TooManyTopics = 0x0F,
407 DuplicateContract = 0x12,
409 TerminatedInConstructor = 0x13,
413 ReentranceDenied = 0x14,
415 ReenteredPallet = 0x15,
417 StateChangeDenied = 0x16,
419 StorageDepositNotEnoughFunds = 0x17,
421 StorageDepositLimitExhausted = 0x18,
423 CodeInUse = 0x19,
425 ContractReverted = 0x1A,
430 CodeRejected = 0x1B,
435 BlobTooLarge = 0x1C,
437 StaticMemoryTooLarge = 0x1D,
439 BasicBlockTooLarge = 0x1E,
441 InvalidInstruction = 0x1F,
443 MaxDelegateDependenciesReached = 0x20,
445 DelegateDependencyNotFound = 0x21,
447 DelegateDependencyAlreadyExists = 0x22,
449 CannotAddSelfAsDelegateDependency = 0x23,
451 OutOfTransientStorage = 0x24,
453 InvalidSyscall = 0x25,
455 InvalidStorageFlags = 0x26,
457 ExecutionFailed = 0x27,
459 BalanceConversionFailed = 0x28,
461 InvalidImmutableAccess = 0x2A,
464 AccountUnmapped = 0x2B,
468 AccountAlreadyMapped = 0x2C,
470 InvalidGenericTransaction = 0x2D,
472 RefcountOverOrUnderflow = 0x2E,
474 UnsupportedPrecompileAddress = 0x2F,
476 CallDataTooLarge = 0x30,
478 ReturnDataTooLarge = 0x31,
480 }
481
482 #[pallet::composite_enum]
484 pub enum HoldReason {
485 CodeUploadDepositReserve,
487 StorageDepositReserve,
489 AddressMapping,
491 }
492
493 #[pallet::storage]
495 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, CodeVec>;
496
497 #[pallet::storage]
499 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
500
501 #[pallet::storage]
503 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
504
505 #[pallet::storage]
507 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
508
509 #[pallet::storage]
514 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
515
516 #[pallet::storage]
519 pub(crate) type DeletionQueueCounter<T: Config> =
520 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
521
522 #[pallet::storage]
529 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
530
531 #[pallet::genesis_config]
532 #[derive(frame_support::DefaultNoBound)]
533 pub struct GenesisConfig<T: Config> {
534 pub mapped_accounts: Vec<T::AccountId>,
536 }
537
538 #[pallet::genesis_build]
539 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
540 fn build(&self) {
541 for id in &self.mapped_accounts {
542 if let Err(err) = T::AddressMapper::map(id) {
543 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
544 }
545 }
546 }
547 }
548
549 #[pallet::hooks]
550 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
551 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
552 let mut meter = WeightMeter::with_limit(limit);
553 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
554 meter.consumed()
555 }
556
557 fn integrity_test() {
558 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
559
560 let max_runtime_mem: u32 = T::RuntimeMemory::get();
562
563 const TOTAL_MEMORY_DEVIDER: u32 = 2;
566
567 let memory_left = i64::from(max_runtime_mem)
572 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
573 .saturating_sub(limits::MEMORY_REQUIRED.into());
574
575 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
576
577 assert!(
578 memory_left >= 0,
579 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
580 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
581 );
582
583 let max_block_ref_time = T::BlockWeights::get()
589 .get(DispatchClass::Normal)
590 .max_total
591 .unwrap_or_else(|| T::BlockWeights::get().max_block)
592 .ref_time();
593 let max_payload_size = limits::PAYLOAD_BYTES;
594 let max_key_size =
595 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
596 .expect("Key of maximal size shall be created")
597 .hash()
598 .len() as u32;
599
600 let max_immutable_key_size = T::AccountId::max_encoded_len() as u32;
601 let max_immutable_size: u32 = ((max_block_ref_time /
602 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetImmutableData(
603 limits::IMMUTABLE_BYTES,
604 ))
605 .ref_time()))
606 .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64))
607 .try_into()
608 .expect("Immutable data size too big");
609
610 let max_storage_size: u32 = ((max_block_ref_time /
613 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
614 new_bytes: max_payload_size,
615 old_bytes: 0,
616 })
617 .ref_time()))
618 .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64))
619 .saturating_add(max_immutable_size.into())
620 .try_into()
621 .expect("Storage size too big");
622
623 let max_pvf_mem: u32 = T::PVFMemory::get();
624 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
625
626 assert!(
627 max_storage_size < storage_size_limit,
628 "Maximal storage size {} exceeds the storage limit {}",
629 max_storage_size,
630 storage_size_limit
631 );
632
633 let max_events_size: u32 = ((max_block_ref_time /
637 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
638 num_topic: 0,
639 len: max_payload_size,
640 })
641 .saturating_add(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::HostFn))
642 .ref_time()))
643 .saturating_mul(max_payload_size as u64))
644 .try_into()
645 .expect("Events size too big");
646
647 assert!(
648 max_events_size < storage_size_limit,
649 "Maximal events size {} exceeds the events limit {}",
650 max_events_size,
651 storage_size_limit
652 );
653 }
654 }
655
656 #[pallet::call]
657 impl<T: Config> Pallet<T>
658 where
659 BalanceOf<T>: Into<U256> + TryFrom<U256>,
660 MomentOf<T>: Into<U256>,
661 T::Hash: frame_support::traits::IsType<H256>,
662 {
663 #[allow(unused_variables)]
679 #[pallet::call_index(0)]
680 #[pallet::weight(Weight::MAX)]
681 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
682 Err(frame_system::Error::CallFiltered::<T>.into())
683 }
684
685 #[pallet::call_index(1)]
702 #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))]
703 pub fn call(
704 origin: OriginFor<T>,
705 dest: H160,
706 #[pallet::compact] value: BalanceOf<T>,
707 gas_limit: Weight,
708 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
709 data: Vec<u8>,
710 ) -> DispatchResultWithPostInfo {
711 let mut output = Self::bare_call(
712 origin,
713 dest,
714 Pallet::<T>::convert_native_to_evm(value),
715 gas_limit,
716 DepositLimit::Balance(storage_deposit_limit),
717 data,
718 );
719
720 if let Ok(return_value) = &output.result {
721 if return_value.did_revert() {
722 output.result = Err(<Error<T>>::ContractReverted.into());
723 }
724 }
725 dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call())
726 }
727
728 #[pallet::call_index(2)]
734 #[pallet::weight(
735 T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
736 )]
737 pub fn instantiate(
738 origin: OriginFor<T>,
739 #[pallet::compact] value: BalanceOf<T>,
740 gas_limit: Weight,
741 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
742 code_hash: sp_core::H256,
743 data: Vec<u8>,
744 salt: Option<[u8; 32]>,
745 ) -> DispatchResultWithPostInfo {
746 let data_len = data.len() as u32;
747 let mut output = Self::bare_instantiate(
748 origin,
749 Pallet::<T>::convert_native_to_evm(value),
750 gas_limit,
751 DepositLimit::Balance(storage_deposit_limit),
752 Code::Existing(code_hash),
753 data,
754 salt,
755 BumpNonce::Yes,
756 );
757 if let Ok(retval) = &output.result {
758 if retval.result.did_revert() {
759 output.result = Err(<Error<T>>::ContractReverted.into());
760 }
761 }
762 dispatch_result(
763 output.result.map(|result| result.result),
764 output.gas_consumed,
765 T::WeightInfo::instantiate(data_len),
766 )
767 }
768
769 #[pallet::call_index(3)]
797 #[pallet::weight(
798 T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
799 .saturating_add(*gas_limit)
800 )]
801 pub fn instantiate_with_code(
802 origin: OriginFor<T>,
803 #[pallet::compact] value: BalanceOf<T>,
804 gas_limit: Weight,
805 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
806 code: Vec<u8>,
807 data: Vec<u8>,
808 salt: Option<[u8; 32]>,
809 ) -> DispatchResultWithPostInfo {
810 let code_len = code.len() as u32;
811 let data_len = data.len() as u32;
812 let mut output = Self::bare_instantiate(
813 origin,
814 Pallet::<T>::convert_native_to_evm(value),
815 gas_limit,
816 DepositLimit::Balance(storage_deposit_limit),
817 Code::Upload(code),
818 data,
819 salt,
820 BumpNonce::Yes,
821 );
822 if let Ok(retval) = &output.result {
823 if retval.result.did_revert() {
824 output.result = Err(<Error<T>>::ContractReverted.into());
825 }
826 }
827 dispatch_result(
828 output.result.map(|result| result.result),
829 output.gas_consumed,
830 T::WeightInfo::instantiate_with_code(code_len, data_len),
831 )
832 }
833
834 #[pallet::call_index(10)]
842 #[pallet::weight(
843 T::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
844 .saturating_add(*gas_limit)
845 )]
846 pub fn eth_instantiate_with_code(
847 origin: OriginFor<T>,
848 value: U256,
849 gas_limit: Weight,
850 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
851 code: Vec<u8>,
852 data: Vec<u8>,
853 ) -> DispatchResultWithPostInfo {
854 let code_len = code.len() as u32;
855 let data_len = data.len() as u32;
856 let mut output = Self::bare_instantiate(
857 origin,
858 value,
859 gas_limit,
860 DepositLimit::Balance(storage_deposit_limit),
861 Code::Upload(code),
862 data,
863 None,
864 BumpNonce::No,
865 );
866
867 if let Ok(retval) = &output.result {
868 if retval.result.did_revert() {
869 output.result = Err(<Error<T>>::ContractReverted.into());
870 }
871 }
872 dispatch_result(
873 output.result.map(|result| result.result),
874 output.gas_consumed,
875 T::WeightInfo::eth_instantiate_with_code(
876 code_len,
877 data_len,
878 Pallet::<T>::has_dust(value).into(),
879 ),
880 )
881 }
882
883 #[pallet::call_index(11)]
886 #[pallet::weight(T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into()).saturating_add(*gas_limit))]
887 pub fn eth_call(
888 origin: OriginFor<T>,
889 dest: H160,
890 value: U256,
891 gas_limit: Weight,
892 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
893 data: Vec<u8>,
894 ) -> DispatchResultWithPostInfo {
895 let mut output = Self::bare_call(
896 origin,
897 dest,
898 value,
899 gas_limit,
900 DepositLimit::Balance(storage_deposit_limit),
901 data,
902 );
903
904 if let Ok(return_value) = &output.result {
905 if return_value.did_revert() {
906 output.result = Err(<Error<T>>::ContractReverted.into());
907 }
908 }
909 dispatch_result(
910 output.result,
911 output.gas_consumed,
912 T::WeightInfo::eth_call(Pallet::<T>::has_dust(value).into()),
913 )
914 }
915
916 #[pallet::call_index(4)]
929 #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))]
930 pub fn upload_code(
931 origin: OriginFor<T>,
932 code: Vec<u8>,
933 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
934 ) -> DispatchResult {
935 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
936 }
937
938 #[pallet::call_index(5)]
943 #[pallet::weight(T::WeightInfo::remove_code())]
944 pub fn remove_code(
945 origin: OriginFor<T>,
946 code_hash: sp_core::H256,
947 ) -> DispatchResultWithPostInfo {
948 let origin = ensure_signed(origin)?;
949 <ContractBlob<T>>::remove(&origin, code_hash)?;
950 Ok(Pays::No.into())
952 }
953
954 #[pallet::call_index(6)]
965 #[pallet::weight(T::WeightInfo::set_code())]
966 pub fn set_code(
967 origin: OriginFor<T>,
968 dest: H160,
969 code_hash: sp_core::H256,
970 ) -> DispatchResult {
971 ensure_root(origin)?;
972 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
973 let Some(account) = account else {
974 return Err(<Error<T>>::ContractNotFound.into());
975 };
976
977 let AccountType::Contract(ref mut contract) = account.account_type else {
978 return Err(<Error<T>>::ContractNotFound.into());
979 };
980
981 <CodeInfo<T>>::increment_refcount(code_hash)?;
982 <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
983 contract.code_hash = code_hash;
984
985 Ok(())
986 })
987 }
988
989 #[pallet::call_index(7)]
994 #[pallet::weight(T::WeightInfo::map_account())]
995 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
996 let origin = ensure_signed(origin)?;
997 T::AddressMapper::map(&origin)
998 }
999
1000 #[pallet::call_index(8)]
1005 #[pallet::weight(T::WeightInfo::unmap_account())]
1006 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1007 let origin = ensure_signed(origin)?;
1008 T::AddressMapper::unmap(&origin)
1009 }
1010
1011 #[pallet::call_index(9)]
1017 #[pallet::weight({
1018 let dispatch_info = call.get_dispatch_info();
1019 (
1020 T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1021 dispatch_info.class
1022 )
1023 })]
1024 pub fn dispatch_as_fallback_account(
1025 origin: OriginFor<T>,
1026 call: Box<<T as Config>::RuntimeCall>,
1027 ) -> DispatchResultWithPostInfo {
1028 let origin = ensure_signed(origin)?;
1029 let unmapped_account =
1030 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1031 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1032 }
1033 }
1034}
1035
1036fn dispatch_result<R>(
1038 result: Result<R, DispatchError>,
1039 gas_consumed: Weight,
1040 base_weight: Weight,
1041) -> DispatchResultWithPostInfo {
1042 let post_info = PostDispatchInfo {
1043 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1044 pays_fee: Default::default(),
1045 };
1046
1047 result
1048 .map(|_| post_info)
1049 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1050}
1051
1052impl<T: Config> Pallet<T>
1053where
1054 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
1055 MomentOf<T>: Into<U256>,
1056 T::Hash: frame_support::traits::IsType<H256>,
1057{
1058 pub fn bare_call(
1065 origin: OriginFor<T>,
1066 dest: H160,
1067 evm_value: U256,
1068 gas_limit: Weight,
1069 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1070 data: Vec<u8>,
1071 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1072 let mut gas_meter = GasMeter::new(gas_limit);
1073 let mut storage_deposit = Default::default();
1074
1075 let try_call = || {
1076 let origin = Origin::from_runtime_origin(origin)?;
1077 let mut storage_meter = StorageMeter::new(storage_deposit_limit.limit());
1078 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1079 origin.clone(),
1080 dest,
1081 &mut gas_meter,
1082 &mut storage_meter,
1083 evm_value,
1084 data,
1085 storage_deposit_limit.is_unchecked(),
1086 )?;
1087 storage_deposit = storage_meter
1088 .try_into_deposit(&origin, storage_deposit_limit.is_unchecked())
1089 .inspect_err(|err| {
1090 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1091 })?;
1092 Ok(result)
1093 };
1094 let result = Self::run_guarded(try_call);
1095 ContractResult {
1096 result: result.map_err(|r| r.error),
1097 gas_consumed: gas_meter.gas_consumed(),
1098 gas_required: gas_meter.gas_required(),
1099 storage_deposit,
1100 }
1101 }
1102
1103 pub fn prepare_dry_run(account: &T::AccountId) {
1109 frame_system::Pallet::<T>::inc_account_nonce(account);
1112 }
1113
1114 pub fn bare_instantiate(
1120 origin: OriginFor<T>,
1121 evm_value: U256,
1122 gas_limit: Weight,
1123 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1124 code: Code,
1125 data: Vec<u8>,
1126 salt: Option<[u8; 32]>,
1127 bump_nonce: BumpNonce,
1128 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1129 let mut gas_meter = GasMeter::new(gas_limit);
1130 let mut storage_deposit = Default::default();
1131 let unchecked_deposit_limit = storage_deposit_limit.is_unchecked();
1132 let mut storage_deposit_limit = storage_deposit_limit.limit();
1133 let try_instantiate = || {
1134 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1135
1136 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1137 let (executable, upload_deposit) = match code {
1138 Code::Upload(code) => {
1139 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1140 let (executable, upload_deposit) = Self::try_upload_code(
1141 upload_account,
1142 code,
1143 storage_deposit_limit,
1144 unchecked_deposit_limit,
1145 )?;
1146 storage_deposit_limit.saturating_reduce(upload_deposit);
1147 (executable, upload_deposit)
1148 },
1149 Code::Existing(code_hash) =>
1150 (ContractBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()),
1151 };
1152 let instantiate_origin = Origin::from_account_id(instantiate_account.clone());
1153 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1154 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1155 instantiate_account,
1156 executable,
1157 &mut gas_meter,
1158 &mut storage_meter,
1159 evm_value,
1160 data,
1161 salt.as_ref(),
1162 unchecked_deposit_limit,
1163 bump_nonce,
1164 );
1165 storage_deposit = storage_meter
1166 .try_into_deposit(&instantiate_origin, unchecked_deposit_limit)?
1167 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1168 result
1169 };
1170 let output = Self::run_guarded(try_instantiate);
1171 ContractResult {
1172 result: output
1173 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1174 .map_err(|e| e.error),
1175 gas_consumed: gas_meter.gas_consumed(),
1176 gas_required: gas_meter.gas_required(),
1177 storage_deposit,
1178 }
1179 }
1180
1181 pub fn dry_run_eth_transact(
1190 mut tx: GenericTransaction,
1191 gas_limit: Weight,
1192 tx_fee: impl Fn(<T as Config>::RuntimeCall, <T as Config>::RuntimeCall) -> BalanceOf<T>,
1193 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1194 where
1195 <T as frame_system::Config>::RuntimeCall:
1196 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1197 T: pallet_transaction_payment::Config,
1198 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1199 <T as Config>::RuntimeCall: From<crate::Call<T>>,
1200 <T as Config>::RuntimeCall: Encode,
1201 T::Nonce: Into<U256>,
1202 T::Hash: frame_support::traits::IsType<H256>,
1203 {
1204 log::trace!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?} gas_limit: {gas_limit:?}");
1205
1206 let from = tx.from.unwrap_or_default();
1207 let origin = T::AddressMapper::to_account_id(&from);
1208 Self::prepare_dry_run(&origin);
1209
1210 let storage_deposit_limit = if tx.gas.is_some() {
1211 DepositLimit::Balance(BalanceOf::<T>::max_value())
1212 } else {
1213 DepositLimit::UnsafeOnlyForDryRun
1214 };
1215
1216 if tx.nonce.is_none() {
1217 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1218 }
1219 if tx.chain_id.is_none() {
1220 tx.chain_id = Some(T::ChainId::get().into());
1221 }
1222 if tx.gas_price.is_none() {
1223 tx.gas_price = Some(GAS_PRICE.into());
1224 }
1225 if tx.max_priority_fee_per_gas.is_none() {
1226 tx.max_priority_fee_per_gas = Some(GAS_PRICE.into());
1227 }
1228 if tx.max_fee_per_gas.is_none() {
1229 tx.max_fee_per_gas = Some(GAS_PRICE.into());
1230 }
1231 if tx.gas.is_none() {
1232 tx.gas = Some(Self::evm_block_gas_limit());
1233 }
1234 if tx.r#type.is_none() {
1235 tx.r#type = Some(TYPE_EIP1559.into());
1236 }
1237
1238 let value = tx.value.unwrap_or_default();
1240 let input = tx.input.clone().to_vec();
1241
1242 let extract_error = |err| {
1243 if err == Error::<T>::TransferFailed.into() ||
1244 err == Error::<T>::StorageDepositNotEnoughFunds.into() ||
1245 err == Error::<T>::StorageDepositLimitExhausted.into()
1246 {
1247 let balance = Self::evm_balance(&from);
1248 return Err(EthTransactError::Message(
1249 format!("insufficient funds for gas * price + value: address {from:?} have {balance} (supplied gas {})",
1250 tx.gas.unwrap_or_default()))
1251 );
1252 }
1253
1254 return Err(EthTransactError::Message(format!(
1255 "Failed to instantiate contract: {err:?}"
1256 )));
1257 };
1258
1259 let (mut result, dispatch_call) = match tx.to {
1261 Some(dest) => {
1263 if dest == RUNTIME_PALLETS_ADDR {
1264 let Ok(dispatch_call) = <T as Config>::RuntimeCall::decode(&mut &input[..])
1265 else {
1266 return Err(EthTransactError::Message(format!(
1267 "Failed to decode pallet-call {input:?}"
1268 )));
1269 };
1270
1271 if let Err(err) =
1272 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1273 {
1274 return Err(EthTransactError::Message(format!(
1275 "Failed to dispatch call: {err:?}"
1276 )));
1277 };
1278
1279 let result = EthTransactInfo {
1280 gas_required: dispatch_call.get_dispatch_info().total_weight(),
1281 ..Default::default()
1282 };
1283
1284 (result, dispatch_call)
1285 } else {
1286 let result = crate::Pallet::<T>::bare_call(
1288 T::RuntimeOrigin::signed(origin),
1289 dest,
1290 value,
1291 gas_limit,
1292 storage_deposit_limit,
1293 input.clone(),
1294 );
1295
1296 let data = match result.result {
1297 Ok(return_value) => {
1298 if return_value.did_revert() {
1299 return Err(EthTransactError::Data(return_value.data));
1300 }
1301 return_value.data
1302 },
1303 Err(err) => {
1304 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1305 return extract_error(err);
1306 },
1307 };
1308
1309 let result = EthTransactInfo {
1310 gas_required: result.gas_required,
1311 storage_deposit: result.storage_deposit.charge_or_zero(),
1312 data,
1313 eth_gas: Default::default(),
1314 };
1315
1316 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1317 result.gas_required,
1318 result.storage_deposit,
1319 );
1320 let dispatch_call: <T as Config>::RuntimeCall = crate::Call::<T>::eth_call {
1321 dest,
1322 value,
1323 gas_limit,
1324 storage_deposit_limit,
1325 data: input.clone(),
1326 }
1327 .into();
1328 (result, dispatch_call)
1329 }
1330 },
1331 None => {
1333 let (code, data) = match polkavm::ProgramBlob::blob_length(&input) {
1335 Some(blob_len) => blob_len
1336 .try_into()
1337 .ok()
1338 .and_then(|blob_len| (input.split_at_checked(blob_len)))
1339 .unwrap_or_else(|| (&input[..], &[][..])),
1340 _ => {
1341 log::debug!(target: LOG_TARGET, "Failed to extract polkavm blob length");
1342 (&input[..], &[][..])
1343 },
1344 };
1345
1346 let result = crate::Pallet::<T>::bare_instantiate(
1348 T::RuntimeOrigin::signed(origin),
1349 value,
1350 gas_limit,
1351 storage_deposit_limit,
1352 Code::Upload(code.to_vec()),
1353 data.to_vec(),
1354 None,
1355 BumpNonce::No,
1356 );
1357
1358 let returned_data = match result.result {
1359 Ok(return_value) => {
1360 if return_value.result.did_revert() {
1361 return Err(EthTransactError::Data(return_value.result.data));
1362 }
1363 return_value.result.data
1364 },
1365 Err(err) => {
1366 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1367 return extract_error(err);
1368 },
1369 };
1370
1371 let result = EthTransactInfo {
1372 gas_required: result.gas_required,
1373 storage_deposit: result.storage_deposit.charge_or_zero(),
1374 data: returned_data,
1375 eth_gas: Default::default(),
1376 };
1377
1378 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1380 result.gas_required,
1381 result.storage_deposit,
1382 );
1383 let dispatch_call: <T as Config>::RuntimeCall =
1384 crate::Call::<T>::eth_instantiate_with_code {
1385 value,
1386 gas_limit,
1387 storage_deposit_limit,
1388 code: code.to_vec(),
1389 data: data.to_vec(),
1390 }
1391 .into();
1392 (result, dispatch_call)
1393 },
1394 };
1395
1396 let Ok(unsigned_tx) = tx.clone().try_into_unsigned() else {
1397 return Err(EthTransactError::Message("Invalid transaction".into()));
1398 };
1399
1400 let eth_transact_call =
1401 crate::Call::<T>::eth_transact { payload: unsigned_tx.dummy_signed_payload() };
1402 let fee = tx_fee(eth_transact_call.into(), dispatch_call);
1403 let raw_gas = Self::evm_fee_to_gas(fee);
1404 let eth_gas =
1405 T::EthGasEncoder::encode(raw_gas, result.gas_required, result.storage_deposit);
1406
1407 log::trace!(target: LOG_TARGET, "bare_eth_call: raw_gas: {raw_gas:?} eth_gas: {eth_gas:?}");
1408 result.eth_gas = eth_gas;
1409 Ok(result)
1410 }
1411
1412 pub fn evm_balance(address: &H160) -> U256 {
1414 let balance = AccountInfo::<T>::balance((*address).into());
1415 Self::convert_native_to_evm(balance)
1416 }
1417
1418 pub fn evm_nonce(address: &H160) -> u32
1420 where
1421 T::Nonce: Into<u32>,
1422 {
1423 let account = T::AddressMapper::to_account_id(&address);
1424 System::<T>::account_nonce(account).into()
1425 }
1426
1427 pub fn evm_fee_to_gas(fee: BalanceOf<T>) -> U256 {
1430 let fee = Self::convert_native_to_evm(fee);
1431 let gas_price = GAS_PRICE.into();
1432 let (quotient, remainder) = fee.div_mod(gas_price);
1433 if remainder.is_zero() {
1434 quotient
1435 } else {
1436 quotient + U256::one()
1437 }
1438 }
1439
1440 fn evm_gas_to_fee(gas: U256, gas_price: U256) -> Result<BalanceOf<T>, Error<T>> {
1442 let fee = gas.saturating_mul(gas_price);
1443 let value = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(fee)?;
1444 Ok(value.into_rounded_balance())
1445 }
1446
1447 pub fn evm_gas_from_weight(weight: Weight) -> U256 {
1449 let fee = T::WeightPrice::convert(weight);
1450 Self::evm_fee_to_gas(fee)
1451 }
1452
1453 pub fn evm_block_gas_limit() -> U256
1455 where
1456 <T as frame_system::Config>::RuntimeCall:
1457 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1458 T: pallet_transaction_payment::Config,
1459 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1460 {
1461 let max_block_weight = T::BlockWeights::get()
1462 .get(DispatchClass::Normal)
1463 .max_total
1464 .unwrap_or_else(|| T::BlockWeights::get().max_block);
1465
1466 let length_fee = pallet_transaction_payment::Pallet::<T>::length_to_fee(
1467 5 * 1024 * 1024, );
1469
1470 Self::evm_gas_from_weight(max_block_weight)
1471 .saturating_add(Self::evm_fee_to_gas(length_fee.into()))
1472 }
1473
1474 pub fn evm_gas_price() -> U256 {
1476 GAS_PRICE.into()
1477 }
1478
1479 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
1481 where
1482 T::Nonce: Into<u32>,
1483 {
1484 match tracer_type {
1485 TracerType::CallTracer(config) => CallTracer::new(
1486 config.unwrap_or_default(),
1487 Self::evm_gas_from_weight as fn(Weight) -> U256,
1488 )
1489 .into(),
1490 TracerType::PrestateTracer(config) =>
1491 PrestateTracer::new(config.unwrap_or_default()).into(),
1492 }
1493 }
1494
1495 pub fn bare_upload_code(
1499 origin: OriginFor<T>,
1500 code: Vec<u8>,
1501 storage_deposit_limit: BalanceOf<T>,
1502 ) -> CodeUploadResult<BalanceOf<T>> {
1503 let origin = T::UploadOrigin::ensure_origin(origin)?;
1504 let (module, deposit) = Self::try_upload_code(origin, code, storage_deposit_limit, false)?;
1505 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
1506 }
1507
1508 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
1510 let contract_info =
1511 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1512
1513 let maybe_value = contract_info.read(&Key::from_fixed(key));
1514 Ok(maybe_value)
1515 }
1516
1517 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
1519 let contract_info =
1520 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1521
1522 let maybe_value = contract_info.read(
1523 &Key::try_from_var(key)
1524 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
1525 .into(),
1526 );
1527 Ok(maybe_value)
1528 }
1529
1530 fn try_upload_code(
1532 origin: T::AccountId,
1533 code: Vec<u8>,
1534 storage_deposit_limit: BalanceOf<T>,
1535 skip_transfer: bool,
1536 ) -> Result<(ContractBlob<T>, BalanceOf<T>), DispatchError> {
1537 let mut module = ContractBlob::from_code(code, origin)?;
1538 let deposit = module.store_code(skip_transfer)?;
1539 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
1540 Ok((module, deposit))
1541 }
1542
1543 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
1545 executing_contract::using_once(&mut false, || {
1546 executing_contract::with(|f| {
1547 if *f {
1549 return Err(())
1550 }
1551 *f = true;
1553 Ok(())
1554 })
1555 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
1556 .map_err(|_| <Error<T>>::ReenteredPallet.into())
1557 .map(|_| f())
1558 .and_then(|r| r)
1559 })
1560 }
1561
1562 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
1564 let (value, dust) = value.into().deconstruct();
1565 value
1566 .into()
1567 .saturating_mul(T::NativeToEthRatio::get().into())
1568 .saturating_add(dust.into())
1569 }
1570}
1571
1572impl<T: Config> Pallet<T> {
1573 fn has_dust(value: U256) -> bool {
1575 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
1576 }
1577
1578 fn has_balance(value: U256) -> bool {
1580 value >= U256::from(<T>::NativeToEthRatio::get())
1581 }
1582
1583 fn min_balance() -> BalanceOf<T> {
1585 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
1586 }
1587
1588 fn deposit_event(event: Event<T>) {
1590 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
1591 }
1592
1593 pub fn block_author() -> Option<H160> {
1595 use frame_support::traits::FindAuthor;
1596
1597 let digest = <frame_system::Pallet<T>>::digest();
1598 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
1599
1600 let account_id = T::FindAuthor::find_author(pre_runtime_digests)?;
1601 Some(T::AddressMapper::to_address(&account_id))
1602 }
1603
1604 pub fn code(address: &H160) -> Vec<u8> {
1608 use precompiles::{All, Precompiles};
1609 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
1610 return code.into()
1611 }
1612 AccountInfo::<T>::load_contract(&address)
1613 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
1614 .map(|code| code.into())
1615 .unwrap_or_default()
1616 }
1617}
1618
1619pub const RUNTIME_PALLETS_ADDR: H160 =
1624 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
1625
1626environmental!(executing_contract: bool);
1628
1629sp_api::decl_runtime_apis! {
1630 #[api_version(1)]
1632 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber> where
1633 AccountId: Codec,
1634 Balance: Codec,
1635 Nonce: Codec,
1636 BlockNumber: Codec,
1637 {
1638 fn block_gas_limit() -> U256;
1640
1641 fn balance(address: H160) -> U256;
1643
1644 fn gas_price() -> U256;
1646
1647 fn nonce(address: H160) -> Nonce;
1649
1650 fn call(
1654 origin: AccountId,
1655 dest: H160,
1656 value: Balance,
1657 gas_limit: Option<Weight>,
1658 storage_deposit_limit: Option<Balance>,
1659 input_data: Vec<u8>,
1660 ) -> ContractResult<ExecReturnValue, Balance>;
1661
1662 fn instantiate(
1666 origin: AccountId,
1667 value: Balance,
1668 gas_limit: Option<Weight>,
1669 storage_deposit_limit: Option<Balance>,
1670 code: Code,
1671 data: Vec<u8>,
1672 salt: Option<[u8; 32]>,
1673 ) -> ContractResult<InstantiateReturnValue, Balance>;
1674
1675
1676 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
1680
1681 fn upload_code(
1685 origin: AccountId,
1686 code: Vec<u8>,
1687 storage_deposit_limit: Option<Balance>,
1688 ) -> CodeUploadResult<Balance>;
1689
1690 fn get_storage(
1696 address: H160,
1697 key: [u8; 32],
1698 ) -> GetStorageResult;
1699
1700 fn get_storage_var_key(
1706 address: H160,
1707 key: Vec<u8>,
1708 ) -> GetStorageResult;
1709
1710 fn trace_block(
1717 block: Block,
1718 config: TracerType
1719 ) -> Vec<(u32, Trace)>;
1720
1721 fn trace_tx(
1728 block: Block,
1729 tx_index: u32,
1730 config: TracerType
1731 ) -> Option<Trace>;
1732
1733 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
1737
1738 fn block_author() -> Option<H160>;
1740
1741 fn address(account_id: AccountId) -> H160;
1743
1744 fn runtime_pallets_address() -> H160;
1746
1747 fn code(address: H160) -> Vec<u8>;
1749 }
1750}
1751
1752#[macro_export]
1760macro_rules! impl_runtime_apis_plus_revive {
1761 ($Runtime: ty, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
1762
1763 impl_runtime_apis! {
1764 $($rest)*
1765
1766 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for $Runtime {
1767 fn balance(address: $crate::H160) -> $crate::U256 {
1768 $crate::Pallet::<Self>::evm_balance(&address)
1769 }
1770
1771 fn block_author() -> Option<$crate::H160> {
1772 $crate::Pallet::<Self>::block_author()
1773 }
1774
1775 fn block_gas_limit() -> $crate::U256 {
1776 $crate::Pallet::<Self>::evm_block_gas_limit()
1777 }
1778
1779 fn gas_price() -> $crate::U256 {
1780 $crate::Pallet::<Self>::evm_gas_price()
1781 }
1782
1783 fn nonce(address: $crate::H160) -> Nonce {
1784 use $crate::AddressMapper;
1785 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
1786 $crate::frame_system::Pallet::<Self>::account_nonce(account)
1787 }
1788
1789 fn address(account_id: AccountId) -> $crate::H160 {
1790 use $crate::AddressMapper;
1791 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
1792 }
1793
1794 fn eth_transact(
1795 tx: $crate::evm::GenericTransaction,
1796 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
1797 use $crate::{
1798 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
1799 sp_runtime::traits::TransactionExtension,
1800 sp_runtime::traits::Block as BlockT
1801 };
1802
1803 let tx_fee = |call: <Self as $crate::frame_system::Config>::RuntimeCall, dispatch_call: <Self as $crate::frame_system::Config>::RuntimeCall| {
1804 use $crate::frame_support::dispatch::GetDispatchInfo;
1805
1806 let mut dispatch_info = dispatch_call.get_dispatch_info();
1808 dispatch_info.extension_weight =
1809 <$EthExtra>::get_eth_extension(0, 0u32.into()).weight(&dispatch_call);
1810
1811 let uxt: <Block as BlockT>::Extrinsic =
1813 $crate::sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into();
1814
1815 $crate::pallet_transaction_payment::Pallet::<Self>::compute_fee(
1817 uxt.encoded_size() as u32,
1818 &dispatch_info,
1819 0u32.into(),
1820 )
1821 };
1822
1823 let blockweights: $crate::BlockWeights =
1824 <Self as $crate::frame_system::Config>::BlockWeights::get();
1825 $crate::Pallet::<Self>::dry_run_eth_transact(tx, blockweights.max_block, tx_fee)
1826 }
1827
1828 fn call(
1829 origin: AccountId,
1830 dest: $crate::H160,
1831 value: Balance,
1832 gas_limit: Option<$crate::Weight>,
1833 storage_deposit_limit: Option<Balance>,
1834 input_data: Vec<u8>,
1835 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
1836 use $crate::frame_support::traits::Get;
1837 let blockweights: $crate::BlockWeights =
1838 <Self as $crate::frame_system::Config>::BlockWeights::get();
1839
1840 $crate::Pallet::<Self>::prepare_dry_run(&origin);
1841 $crate::Pallet::<Self>::bare_call(
1842 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
1843 dest,
1844 $crate::Pallet::<Self>::convert_native_to_evm(value),
1845 gas_limit.unwrap_or(blockweights.max_block),
1846 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
1847 input_data,
1848 )
1849 }
1850
1851 fn instantiate(
1852 origin: AccountId,
1853 value: Balance,
1854 gas_limit: Option<$crate::Weight>,
1855 storage_deposit_limit: Option<Balance>,
1856 code: $crate::Code,
1857 data: Vec<u8>,
1858 salt: Option<[u8; 32]>,
1859 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
1860 use $crate::frame_support::traits::Get;
1861 let blockweights: $crate::BlockWeights =
1862 <Self as $crate::frame_system::Config>::BlockWeights::get();
1863
1864 $crate::Pallet::<Self>::prepare_dry_run(&origin);
1865 $crate::Pallet::<Self>::bare_instantiate(
1866 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
1867 $crate::Pallet::<Self>::convert_native_to_evm(value),
1868 gas_limit.unwrap_or(blockweights.max_block),
1869 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
1870 code,
1871 data,
1872 salt,
1873 $crate::BumpNonce::Yes,
1874 )
1875 }
1876
1877 fn upload_code(
1878 origin: AccountId,
1879 code: Vec<u8>,
1880 storage_deposit_limit: Option<Balance>,
1881 ) -> $crate::CodeUploadResult<Balance> {
1882 let origin =
1883 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
1884 $crate::Pallet::<Self>::bare_upload_code(
1885 origin,
1886 code,
1887 storage_deposit_limit.unwrap_or(u128::MAX),
1888 )
1889 }
1890
1891 fn get_storage_var_key(
1892 address: $crate::H160,
1893 key: Vec<u8>,
1894 ) -> $crate::GetStorageResult {
1895 $crate::Pallet::<Self>::get_storage_var_key(address, key)
1896 }
1897
1898 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
1899 $crate::Pallet::<Self>::get_storage(address, key)
1900 }
1901
1902 fn trace_block(
1903 block: Block,
1904 tracer_type: $crate::evm::TracerType,
1905 ) -> Vec<(u32, $crate::evm::Trace)> {
1906 use $crate::{sp_runtime::traits::Block, tracing::trace};
1907 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
1908 let mut traces = vec![];
1909 let (header, extrinsics) = block.deconstruct();
1910 <$Executive>::initialize_block(&header);
1911 for (index, ext) in extrinsics.into_iter().enumerate() {
1912 let t = tracer.as_tracing();
1913 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
1914
1915 if let Some(tx_trace) = tracer.collect_trace() {
1916 traces.push((index as u32, tx_trace));
1917 }
1918 }
1919
1920 traces
1921 }
1922
1923 fn trace_tx(
1924 block: Block,
1925 tx_index: u32,
1926 tracer_type: $crate::evm::TracerType,
1927 ) -> Option<$crate::evm::Trace> {
1928 use $crate::{sp_runtime::traits::Block, tracing::trace};
1929
1930 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
1931 let (header, extrinsics) = block.deconstruct();
1932
1933 <$Executive>::initialize_block(&header);
1934 for (index, ext) in extrinsics.into_iter().enumerate() {
1935 if index as u32 == tx_index {
1936 let t = tracer.as_tracing();
1937 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
1938 break;
1939 } else {
1940 let _ = <$Executive>::apply_extrinsic(ext);
1941 }
1942 }
1943
1944 tracer.collect_trace()
1945 }
1946
1947 fn trace_call(
1948 tx: $crate::evm::GenericTransaction,
1949 tracer_type: $crate::evm::TracerType,
1950 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
1951 use $crate::tracing::trace;
1952 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
1953 let t = tracer.as_tracing();
1954
1955 t.watch_address(&tx.from.unwrap_or_default());
1956 t.watch_address(&$crate::Pallet::<Self>::block_author().unwrap_or_default());
1957 let result = trace(t, || Self::eth_transact(tx));
1958
1959 if let Some(trace) = tracer.collect_trace() {
1960 Ok(trace)
1961 } else if let Err(err) = result {
1962 Err(err)
1963 } else {
1964 Ok(tracer.empty_trace())
1965 }
1966 }
1967
1968 fn runtime_pallets_address() -> $crate::H160 {
1969 $crate::RUNTIME_PALLETS_ADDR
1970 }
1971
1972 fn code(address: $crate::H160) -> Vec<u8> {
1973 $crate::Pallet::<Self>::code(&address)
1974 }
1975 }
1976 }
1977 };
1978}