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 debug;
29mod exec;
30mod gas;
31mod impl_fungibles;
32mod limits;
33mod primitives;
34mod storage;
35#[cfg(test)]
36mod tests;
37mod transient_storage;
38mod vm;
39mod weightinfo_extension;
40
41pub mod evm;
42pub mod migrations;
43pub mod mock;
44pub mod precompiles;
45pub mod test_utils;
46pub mod tracing;
47pub mod weights;
48
49use crate::{
50 evm::{
51 block_hash::EthereumBlockBuilderIR,
52 block_storage, create_call,
53 fees::{Combinator, InfoT as FeeInfo},
54 runtime::SetWeightLimit,
55 CallTracer, GenericTransaction, PrestateTracer, Trace, Tracer, TracerType, TYPE_EIP1559,
56 },
57 exec::{AccountIdOf, ExecError, Stack as ExecStack},
58 gas::GasMeter,
59 storage::{meter::Meter as StorageMeter, AccountType, DeletionQueueManager},
60 tracing::if_tracing,
61 vm::{pvm::extract_code_and_data, CodeInfo, RuntimeCosts},
62 weightinfo_extension::OnFinalizeBlockParts,
63};
64use alloc::{boxed::Box, format, vec};
65use codec::{Codec, Decode, Encode};
66use environmental::*;
67use frame_support::{
68 dispatch::{
69 DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
70 Pays, PostDispatchInfo, RawOrigin,
71 },
72 ensure,
73 pallet_prelude::DispatchClass,
74 traits::{
75 fungible::{Balanced, Inspect, Mutate, MutateHold},
76 tokens::Balance,
77 ConstU32, ConstU64, EnsureOrigin, Get, IsSubType, IsType, OriginTrait,
78 },
79 weights::WeightMeter,
80 BoundedVec, RuntimeDebugNoBound,
81};
82use frame_system::{
83 ensure_signed,
84 pallet_prelude::{BlockNumberFor, OriginFor},
85 Pallet as System,
86};
87use scale_info::TypeInfo;
88use sp_runtime::{
89 traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedInto, Zero},
90 AccountId32, DispatchError, FixedPointNumber, FixedU128,
91};
92
93pub use crate::{
94 address::{
95 create1, create2, is_eth_derived, AccountId32Mapper, AddressMapper, TestAccountMapper,
96 },
97 debug::DebugSettings,
98 evm::{
99 block_hash::ReceiptGasInfo, Address as EthAddress, Block as EthBlock, DryRunConfig,
100 ReceiptInfo,
101 },
102 exec::{DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
103 pallet::{genesis, *},
104 storage::{AccountInfo, ContractInfo},
105 vm::{BytecodeType, ContractBlob},
106};
107pub use codec;
108pub use frame_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
109pub use frame_system::{self, limits::BlockWeights};
110pub use primitives::*;
111pub use sp_core::{keccak_256, H160, H256, U256};
112pub use sp_runtime;
113pub use weights::WeightInfo;
114
115#[cfg(doc)]
116pub use crate::vm::pvm::SyscallDoc;
117
118pub type BalanceOf<T> = <T as Config>::Balance;
119type TrieId = BoundedVec<u8, ConstU32<128>>;
120type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
121type CallOf<T> = <T as Config>::RuntimeCall;
122
123const SENTINEL: u32 = u32::MAX;
130
131const LOG_TARGET: &str = "runtime::revive";
137
138#[frame_support::pallet]
139pub mod pallet {
140 use super::*;
141 use frame_support::{pallet_prelude::*, traits::FindAuthor};
142 use frame_system::pallet_prelude::*;
143 use sp_core::U256;
144 use sp_runtime::Perbill;
145
146 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
148
149 #[pallet::pallet]
150 #[pallet::storage_version(STORAGE_VERSION)]
151 pub struct Pallet<T>(_);
152
153 #[pallet::config(with_default)]
154 pub trait Config: frame_system::Config {
155 type Time: Time<Moment: Into<U256>>;
157
158 #[pallet::no_default]
162 type Balance: Balance + TryFrom<U256> + Into<U256> + Bounded + UniqueSaturatedInto<u64>;
163
164 #[pallet::no_default]
166 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
167 + Mutate<Self::AccountId>
168 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
169 + Balanced<Self::AccountId>;
170
171 #[pallet::no_default_bounds]
173 #[allow(deprecated)]
174 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
175
176 #[pallet::no_default_bounds]
178 type RuntimeCall: Parameter
179 + Dispatchable<
180 RuntimeOrigin = OriginFor<Self>,
181 Info = DispatchInfo,
182 PostInfo = PostDispatchInfo,
183 > + IsType<<Self as frame_system::Config>::RuntimeCall>
184 + From<Call<Self>>
185 + IsSubType<Call<Self>>
186 + GetDispatchInfo;
187
188 #[pallet::no_default_bounds]
190 type RuntimeOrigin: IsType<OriginFor<Self>>
191 + From<Origin<Self>>
192 + Into<Result<Origin<Self>, OriginFor<Self>>>;
193
194 #[pallet::no_default_bounds]
196 type RuntimeHoldReason: From<HoldReason>;
197
198 type WeightInfo: WeightInfo;
201
202 #[pallet::no_default_bounds]
206 #[allow(private_bounds)]
207 type Precompiles: precompiles::Precompiles<Self>;
208
209 type FindAuthor: FindAuthor<Self::AccountId>;
211
212 #[pallet::constant]
218 #[pallet::no_default_bounds]
219 type DepositPerByte: Get<BalanceOf<Self>>;
220
221 #[pallet::constant]
227 #[pallet::no_default_bounds]
228 type DepositPerItem: Get<BalanceOf<Self>>;
229
230 #[pallet::constant]
240 #[pallet::no_default_bounds]
241 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
242
243 #[pallet::constant]
247 type CodeHashLockupDepositPercent: Get<Perbill>;
248
249 #[pallet::no_default]
251 type AddressMapper: AddressMapper<Self>;
252
253 #[pallet::constant]
263 type UnsafeUnstableInterface: Get<bool>;
264
265 #[pallet::constant]
267 type AllowEVMBytecode: Get<bool>;
268
269 #[pallet::no_default_bounds]
274 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
275
276 #[pallet::no_default_bounds]
287 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
288
289 type RuntimeMemory: Get<u32>;
294
295 type PVFMemory: Get<u32>;
303
304 #[pallet::constant]
309 type ChainId: Get<u64>;
310
311 #[pallet::constant]
313 type NativeToEthRatio: Get<u32>;
314
315 #[pallet::no_default_bounds]
320 type FeeInfo: FeeInfo<Self>;
321
322 #[pallet::constant]
335 type MaxEthExtrinsicWeight: Get<FixedU128>;
336
337 #[pallet::constant]
339 type DebugEnabled: Get<bool>;
340 }
341
342 pub mod config_preludes {
344 use super::*;
345 use frame_support::{
346 derive_impl,
347 traits::{ConstBool, ConstU32},
348 };
349 use frame_system::EnsureSigned;
350 use sp_core::parameter_types;
351
352 type Balance = u64;
353
354 pub const DOLLARS: Balance = 1_000_000_000_000;
355 pub const CENTS: Balance = DOLLARS / 100;
356 pub const MILLICENTS: Balance = CENTS / 1_000;
357
358 pub const fn deposit(items: u32, bytes: u32) -> Balance {
359 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
360 }
361
362 parameter_types! {
363 pub const DepositPerItem: Balance = deposit(1, 0);
364 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
365 pub const DepositPerByte: Balance = deposit(0, 1);
366 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
367 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
368 }
369
370 pub struct TestDefaultConfig;
372
373 impl Time for TestDefaultConfig {
374 type Moment = u64;
375 fn now() -> Self::Moment {
376 0u64
377 }
378 }
379
380 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
381 fn convert(w: Weight) -> T {
382 w.ref_time().into()
383 }
384 }
385
386 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
387 impl frame_system::DefaultConfig for TestDefaultConfig {}
388
389 #[frame_support::register_default_impl(TestDefaultConfig)]
390 impl DefaultConfig for TestDefaultConfig {
391 #[inject_runtime_type]
392 type RuntimeEvent = ();
393
394 #[inject_runtime_type]
395 type RuntimeHoldReason = ();
396
397 #[inject_runtime_type]
398 type RuntimeCall = ();
399
400 #[inject_runtime_type]
401 type RuntimeOrigin = ();
402
403 type Precompiles = ();
404 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
405 type DepositPerByte = DepositPerByte;
406 type DepositPerItem = DepositPerItem;
407 type DepositPerChildTrieItem = DepositPerChildTrieItem;
408 type Time = Self;
409 type UnsafeUnstableInterface = ConstBool<true>;
410 type AllowEVMBytecode = ConstBool<true>;
411 type UploadOrigin = EnsureSigned<Self::AccountId>;
412 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
413 type WeightInfo = ();
414 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
415 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
416 type ChainId = ConstU64<42>;
417 type NativeToEthRatio = ConstU32<1_000_000>;
418 type FindAuthor = ();
419 type FeeInfo = ();
420 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
421 type DebugEnabled = ConstBool<false>;
422 }
423 }
424
425 #[pallet::event]
426 pub enum Event<T: Config> {
427 ContractEmitted {
429 contract: H160,
431 data: Vec<u8>,
434 topics: Vec<H256>,
437 },
438
439 Instantiated { deployer: H160, contract: H160 },
441
442 EthExtrinsicRevert { dispatch_error: DispatchError },
449 }
450
451 #[pallet::error]
452 #[repr(u8)]
453 pub enum Error<T> {
454 InvalidSchedule = 0x01,
456 InvalidCallFlags = 0x02,
458 OutOfGas = 0x03,
460 TransferFailed = 0x04,
463 MaxCallDepthReached = 0x05,
466 ContractNotFound = 0x06,
468 CodeNotFound = 0x07,
470 CodeInfoNotFound = 0x08,
472 OutOfBounds = 0x09,
474 DecodingFailed = 0x0A,
476 ContractTrapped = 0x0B,
478 ValueTooLarge = 0x0C,
480 TerminatedWhileReentrant = 0x0D,
483 InputForwarded = 0x0E,
485 TooManyTopics = 0x0F,
487 DuplicateContract = 0x12,
489 TerminatedInConstructor = 0x13,
493 ReentranceDenied = 0x14,
495 ReenteredPallet = 0x15,
497 StateChangeDenied = 0x16,
499 StorageDepositNotEnoughFunds = 0x17,
501 StorageDepositLimitExhausted = 0x18,
503 CodeInUse = 0x19,
505 ContractReverted = 0x1A,
510 CodeRejected = 0x1B,
515 BlobTooLarge = 0x1C,
517 StaticMemoryTooLarge = 0x1D,
519 BasicBlockTooLarge = 0x1E,
521 InvalidInstruction = 0x1F,
523 MaxDelegateDependenciesReached = 0x20,
525 DelegateDependencyNotFound = 0x21,
527 DelegateDependencyAlreadyExists = 0x22,
529 CannotAddSelfAsDelegateDependency = 0x23,
531 OutOfTransientStorage = 0x24,
533 InvalidSyscall = 0x25,
535 InvalidStorageFlags = 0x26,
537 ExecutionFailed = 0x27,
539 BalanceConversionFailed = 0x28,
541 InvalidImmutableAccess = 0x2A,
544 AccountUnmapped = 0x2B,
548 AccountAlreadyMapped = 0x2C,
550 InvalidGenericTransaction = 0x2D,
552 RefcountOverOrUnderflow = 0x2E,
554 UnsupportedPrecompileAddress = 0x2F,
556 CallDataTooLarge = 0x30,
558 ReturnDataTooLarge = 0x31,
560 InvalidJump = 0x32,
562 StackUnderflow = 0x33,
564 StackOverflow = 0x34,
566 TxFeeOverdraw = 0x35,
570 EvmConstructorNonEmptyData = 0x36,
574 EvmConstructedFromHash = 0x37,
579 #[cfg(feature = "runtime-benchmarks")]
581 BenchmarkingError = 0xFF,
582 }
583
584 #[pallet::composite_enum]
586 pub enum HoldReason {
587 CodeUploadDepositReserve,
589 StorageDepositReserve,
591 AddressMapping,
593 }
594
595 #[derive(
596 PartialEq,
597 Eq,
598 Clone,
599 MaxEncodedLen,
600 Encode,
601 Decode,
602 DecodeWithMemTracking,
603 TypeInfo,
604 RuntimeDebug,
605 )]
606 #[pallet::origin]
607 pub enum Origin<T: Config> {
608 EthTransaction(T::AccountId),
609 }
610
611 #[pallet::storage]
615 #[pallet::unbounded]
616 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
617
618 #[pallet::storage]
620 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
621
622 #[pallet::storage]
624 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
625
626 #[pallet::storage]
628 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
629
630 #[pallet::storage]
635 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
636
637 #[pallet::storage]
640 pub(crate) type DeletionQueueCounter<T: Config> =
641 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
642
643 #[pallet::storage]
650 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
651
652 #[pallet::storage]
662 #[pallet::unbounded]
663 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
664
665 #[pallet::storage]
669 pub(crate) type BlockHash<T: Config> =
670 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
671
672 #[pallet::storage]
679 #[pallet::unbounded]
680 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
681
682 #[pallet::storage]
684 #[pallet::unbounded]
685 pub(crate) type EthBlockBuilderIR<T: Config> =
686 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
687
688 #[pallet::storage]
693 #[pallet::unbounded]
694 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
695 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
696
697 #[pallet::storage]
699 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
700
701 pub mod genesis {
702 use super::*;
703 use crate::evm::Bytes32;
704
705 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
707 pub struct ContractData {
708 pub code: Vec<u8>,
710 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
712 }
713
714 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
716 pub struct Account<T: Config> {
717 pub address: H160,
719 #[serde(default)]
721 pub balance: U256,
722 #[serde(default)]
724 pub nonce: T::Nonce,
725 #[serde(flatten, skip_serializing_if = "Option::is_none")]
727 pub contract_data: Option<ContractData>,
728 }
729 }
730
731 #[pallet::genesis_config]
732 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
733 pub struct GenesisConfig<T: Config> {
734 #[serde(default, skip_serializing_if = "Vec::is_empty")]
737 pub mapped_accounts: Vec<T::AccountId>,
738
739 #[serde(default, skip_serializing_if = "Vec::is_empty")]
741 pub accounts: Vec<genesis::Account<T>>,
742
743 #[serde(default, skip_serializing_if = "Option::is_none")]
745 pub debug_settings: Option<DebugSettings>,
746 }
747
748 #[pallet::genesis_build]
749 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
750 fn build(&self) {
751 use crate::{exec::Key, vm::ContractBlob};
752 use frame_support::traits::fungible::Mutate;
753
754 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
755 let _ = T::Currency::mint_into(
756 &Pallet::<T>::account_id(),
757 T::Currency::minimum_balance(),
758 );
759 }
760
761 for id in &self.mapped_accounts {
762 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
763 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
764 }
765 }
766
767 let owner = Pallet::<T>::account_id();
768
769 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
770 let account_id = T::AddressMapper::to_account_id(address);
771
772 if !System::<T>::account_exists(&account_id) {
773 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
774 }
775
776 frame_system::Account::<T>::mutate(&account_id, |info| {
777 info.nonce = (*nonce).into();
778 });
779
780 match contract_data {
781 None => {
782 AccountInfoOf::<T>::insert(
783 address,
784 AccountInfo { account_type: AccountType::EOA, dust: 0 },
785 );
786 },
787 Some(genesis::ContractData { code, storage }) => {
788 let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
789 ContractBlob::<T>::from_pvm_code( code.clone(), owner.clone()).inspect_err(|err| {
790 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
791 })
792 } else {
793 ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
794 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
795 })
796 };
797
798 let Ok(blob) = blob else {
799 continue;
800 };
801
802 let code_hash = *blob.code_hash();
803 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
804 .inspect_err(|err| {
805 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
806 })
807 else {
808 continue;
809 };
810
811 AccountInfoOf::<T>::insert(
812 address,
813 AccountInfo { account_type: info.clone().into(), dust: 0 },
814 );
815
816 <PristineCode<T>>::insert(blob.code_hash(), code);
817 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
818 for (k, v) in storage {
819 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
820 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
821 });
822 }
823 },
824 }
825
826 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
827 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
828 });
829 }
830
831 block_storage::on_finalize_build_eth_block::<T>(
833 frame_system::Pallet::<T>::block_number(),
836 );
837
838 if let Some(settings) = self.debug_settings.as_ref() {
840 settings.write_to_storage::<T>()
841 }
842 }
843 }
844
845 #[pallet::hooks]
846 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
847 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
848 let mut meter = WeightMeter::with_limit(limit);
849 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
850 meter.consumed()
851 }
852
853 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
854 block_storage::on_initialize::<T>();
856
857 System::<T>::account_exists(&Pallet::<T>::account_id());
859 <T as Config>::WeightInfo::on_finalize_block_fixed()
861 }
862
863 fn on_finalize(block_number: BlockNumberFor<T>) {
864 block_storage::on_finalize_build_eth_block::<T>(block_number);
866 }
867
868 fn integrity_test() {
869 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
870
871 T::FeeInfo::integrity_test();
872
873 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
875
876 const TOTAL_MEMORY_DEVIDER: u64 = 2;
879
880 let max_block_weight = T::BlockWeights::get()
886 .get(DispatchClass::Normal)
887 .max_total
888 .unwrap_or_else(|| T::BlockWeights::get().max_block);
889 let max_key_size: u64 =
890 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
891 .expect("Key of maximal size shall be created")
892 .hash()
893 .len()
894 .try_into()
895 .unwrap();
896
897 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
898 let max_immutable_size: u64 = max_block_weight
899 .checked_div_per_component(&<RuntimeCosts as gas::Token<T>>::weight(
900 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
901 ))
902 .unwrap()
903 .saturating_mul(
904 u64::from(limits::IMMUTABLE_BYTES)
905 .saturating_add(max_immutable_key_size)
906 .into(),
907 );
908
909 let max_pvf_mem: u64 = T::PVFMemory::get().into();
910 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
911
912 let max_events_size = max_block_weight
916 .checked_div_per_component(
917 &(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
918 num_topic: 0,
919 len: limits::EVENT_BYTES,
920 })
921 .saturating_add(<RuntimeCosts as gas::Token<T>>::weight(
922 &RuntimeCosts::HostFn,
923 ))),
924 )
925 .unwrap()
926 .saturating_mul(limits::EVENT_BYTES.into());
927
928 assert!(
929 max_events_size < storage_size_limit,
930 "Maximal events size {} exceeds the events limit {}",
931 max_events_size,
932 storage_size_limit
933 );
934
935 let max_eth_block_builder_bytes =
970 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
971
972 log::debug!(
973 target: LOG_TARGET,
974 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
975 max_eth_block_builder_bytes / 1024,
976 max_events_size / 1024,
977 );
978
979 let memory_left = i128::from(max_runtime_mem)
984 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
985 .saturating_sub(limits::MEMORY_REQUIRED.into())
986 .saturating_sub(max_eth_block_builder_bytes.into());
987
988 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
989
990 assert!(
991 memory_left >= 0,
992 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
993 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
994 );
995
996 let max_storage_size = max_block_weight
999 .checked_div_per_component(
1000 &<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
1001 new_bytes: limits::STORAGE_BYTES,
1002 old_bytes: 0,
1003 })
1004 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1005 )
1006 .unwrap()
1007 .saturating_add(max_immutable_size.into())
1008 .saturating_add(max_eth_block_builder_bytes.into());
1009
1010 assert!(
1011 max_storage_size < storage_size_limit,
1012 "Maximal storage size {} exceeds the storage limit {}",
1013 max_storage_size,
1014 storage_size_limit
1015 );
1016 }
1017 }
1018
1019 #[pallet::call]
1020 impl<T: Config> Pallet<T> {
1021 #[allow(unused_variables)]
1034 #[pallet::call_index(0)]
1035 #[pallet::weight(Weight::MAX)]
1036 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1037 Err(frame_system::Error::CallFiltered::<T>.into())
1038 }
1039
1040 #[pallet::call_index(1)]
1057 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*gas_limit))]
1058 pub fn call(
1059 origin: OriginFor<T>,
1060 dest: H160,
1061 #[pallet::compact] value: BalanceOf<T>,
1062 gas_limit: Weight,
1063 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1064 data: Vec<u8>,
1065 ) -> DispatchResultWithPostInfo {
1066 Self::ensure_non_contract_if_signed(&origin)?;
1067 let mut output = Self::bare_call(
1068 origin,
1069 dest,
1070 Pallet::<T>::convert_native_to_evm(value),
1071 gas_limit,
1072 storage_deposit_limit,
1073 data,
1074 ExecConfig::new_substrate_tx(),
1075 );
1076
1077 if let Ok(return_value) = &output.result {
1078 if return_value.did_revert() {
1079 output.result = Err(<Error<T>>::ContractReverted.into());
1080 }
1081 }
1082 dispatch_result(output.result, output.gas_consumed, <T as Config>::WeightInfo::call())
1083 }
1084
1085 #[pallet::call_index(2)]
1091 #[pallet::weight(
1092 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
1093 )]
1094 pub fn instantiate(
1095 origin: OriginFor<T>,
1096 #[pallet::compact] value: BalanceOf<T>,
1097 gas_limit: Weight,
1098 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1099 code_hash: sp_core::H256,
1100 data: Vec<u8>,
1101 salt: Option<[u8; 32]>,
1102 ) -> DispatchResultWithPostInfo {
1103 Self::ensure_non_contract_if_signed(&origin)?;
1104 let data_len = data.len() as u32;
1105 let mut output = Self::bare_instantiate(
1106 origin,
1107 Pallet::<T>::convert_native_to_evm(value),
1108 gas_limit,
1109 storage_deposit_limit,
1110 Code::Existing(code_hash),
1111 data,
1112 salt,
1113 ExecConfig::new_substrate_tx(),
1114 );
1115 if let Ok(retval) = &output.result {
1116 if retval.result.did_revert() {
1117 output.result = Err(<Error<T>>::ContractReverted.into());
1118 }
1119 }
1120 dispatch_result(
1121 output.result.map(|result| result.result),
1122 output.gas_consumed,
1123 <T as Config>::WeightInfo::instantiate(data_len),
1124 )
1125 }
1126
1127 #[pallet::call_index(3)]
1155 #[pallet::weight(
1156 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1157 .saturating_add(*gas_limit)
1158 )]
1159 pub fn instantiate_with_code(
1160 origin: OriginFor<T>,
1161 #[pallet::compact] value: BalanceOf<T>,
1162 gas_limit: Weight,
1163 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1164 code: Vec<u8>,
1165 data: Vec<u8>,
1166 salt: Option<[u8; 32]>,
1167 ) -> DispatchResultWithPostInfo {
1168 Self::ensure_non_contract_if_signed(&origin)?;
1169 let code_len = code.len() as u32;
1170 let data_len = data.len() as u32;
1171 let mut output = Self::bare_instantiate(
1172 origin,
1173 Pallet::<T>::convert_native_to_evm(value),
1174 gas_limit,
1175 storage_deposit_limit,
1176 Code::Upload(code),
1177 data,
1178 salt,
1179 ExecConfig::new_substrate_tx(),
1180 );
1181 if let Ok(retval) = &output.result {
1182 if retval.result.did_revert() {
1183 output.result = Err(<Error<T>>::ContractReverted.into());
1184 }
1185 }
1186 dispatch_result(
1187 output.result.map(|result| result.result),
1188 output.gas_consumed,
1189 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1190 )
1191 }
1192
1193 #[pallet::call_index(10)]
1215 #[pallet::weight(
1216 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1217 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1218 .saturating_add(*gas_limit)
1219 )]
1220 pub fn eth_instantiate_with_code(
1221 origin: OriginFor<T>,
1222 value: U256,
1223 gas_limit: Weight,
1224 code: Vec<u8>,
1225 data: Vec<u8>,
1226 transaction_encoded: Vec<u8>,
1227 effective_gas_price: U256,
1228 encoded_len: u32,
1229 ) -> DispatchResultWithPostInfo {
1230 let signer = Self::ensure_eth_signed(origin)?;
1231 let origin = OriginFor::<T>::signed(signer.clone());
1232 Self::ensure_non_contract_if_signed(&origin)?;
1233 let mut call = Call::<T>::eth_instantiate_with_code {
1234 value,
1235 gas_limit,
1236 code: code.clone(),
1237 data: data.clone(),
1238 transaction_encoded: transaction_encoded.clone(),
1239 effective_gas_price,
1240 encoded_len,
1241 }
1242 .into();
1243 let info = T::FeeInfo::dispatch_info(&call);
1244 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1245 drop(call);
1246
1247 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1248 let output = Self::bare_instantiate(
1249 origin,
1250 value,
1251 gas_limit,
1252 BalanceOf::<T>::max_value(),
1253 Code::Upload(code),
1254 data,
1255 None,
1256 ExecConfig::new_eth_tx(
1257 effective_gas_price,
1258 encoded_len,
1259 base_info.total_weight(),
1260 ),
1261 );
1262
1263 block_storage::EthereumCallResult::new::<T>(
1264 signer,
1265 output.map_result(|r| r.result),
1266 base_info.call_weight,
1267 encoded_len,
1268 &info,
1269 effective_gas_price,
1270 )
1271 })
1272 }
1273
1274 #[pallet::call_index(11)]
1277 #[pallet::weight(
1278 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1279 .saturating_add(*gas_limit)
1280 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1281 )]
1282 pub fn eth_call(
1283 origin: OriginFor<T>,
1284 dest: H160,
1285 value: U256,
1286 gas_limit: Weight,
1287 data: Vec<u8>,
1288 transaction_encoded: Vec<u8>,
1289 effective_gas_price: U256,
1290 encoded_len: u32,
1291 ) -> DispatchResultWithPostInfo {
1292 let signer = Self::ensure_eth_signed(origin)?;
1293 let origin = OriginFor::<T>::signed(signer.clone());
1294
1295 Self::ensure_non_contract_if_signed(&origin)?;
1296 let mut call = Call::<T>::eth_call {
1297 dest,
1298 value,
1299 gas_limit,
1300 data: data.clone(),
1301 transaction_encoded: transaction_encoded.clone(),
1302 effective_gas_price,
1303 encoded_len,
1304 }
1305 .into();
1306 let info = T::FeeInfo::dispatch_info(&call);
1307 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1308 drop(call);
1309
1310 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1311 let output = Self::bare_call(
1312 origin,
1313 dest,
1314 value,
1315 gas_limit,
1316 BalanceOf::<T>::max_value(),
1317 data,
1318 ExecConfig::new_eth_tx(
1319 effective_gas_price,
1320 encoded_len,
1321 base_info.total_weight(),
1322 ),
1323 );
1324
1325 block_storage::EthereumCallResult::new::<T>(
1326 signer,
1327 output,
1328 base_info.call_weight,
1329 encoded_len,
1330 &info,
1331 effective_gas_price,
1332 )
1333 })
1334 }
1335
1336 #[pallet::call_index(12)]
1347 #[pallet::weight(T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1348 pub fn eth_substrate_call(
1349 origin: OriginFor<T>,
1350 call: Box<<T as Config>::RuntimeCall>,
1351 transaction_encoded: Vec<u8>,
1352 ) -> DispatchResultWithPostInfo {
1353 let signer = Self::ensure_eth_signed(origin)?;
1356 let weight_overhead =
1357 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32);
1358
1359 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1360 let call_weight = call.get_dispatch_info().call_weight;
1361 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1362
1363 match &mut call_result {
1365 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1366 post_info.actual_weight = Some(
1367 post_info
1368 .actual_weight
1369 .unwrap_or_else(|| call_weight)
1370 .saturating_add(weight_overhead),
1371 );
1372 },
1373 }
1374
1375 block_storage::EthereumCallResult {
1378 receipt_gas_info: ReceiptGasInfo::default(),
1379 result: call_result,
1380 }
1381 })
1382 }
1383
1384 #[pallet::call_index(4)]
1399 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1400 pub fn upload_code(
1401 origin: OriginFor<T>,
1402 code: Vec<u8>,
1403 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1404 ) -> DispatchResult {
1405 Self::ensure_non_contract_if_signed(&origin)?;
1406 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1407 }
1408
1409 #[pallet::call_index(5)]
1414 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1415 pub fn remove_code(
1416 origin: OriginFor<T>,
1417 code_hash: sp_core::H256,
1418 ) -> DispatchResultWithPostInfo {
1419 let origin = ensure_signed(origin)?;
1420 <ContractBlob<T>>::remove(&origin, code_hash)?;
1421 Ok(Pays::No.into())
1423 }
1424
1425 #[pallet::call_index(6)]
1436 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1437 pub fn set_code(
1438 origin: OriginFor<T>,
1439 dest: H160,
1440 code_hash: sp_core::H256,
1441 ) -> DispatchResult {
1442 ensure_root(origin)?;
1443 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1444 let Some(account) = account else {
1445 return Err(<Error<T>>::ContractNotFound.into());
1446 };
1447
1448 let AccountType::Contract(ref mut contract) = account.account_type else {
1449 return Err(<Error<T>>::ContractNotFound.into());
1450 };
1451
1452 <CodeInfo<T>>::increment_refcount(code_hash)?;
1453 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1454 contract.code_hash = code_hash;
1455
1456 Ok(())
1457 })
1458 }
1459
1460 #[pallet::call_index(7)]
1465 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1466 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1467 Self::ensure_non_contract_if_signed(&origin)?;
1468 let origin = ensure_signed(origin)?;
1469 T::AddressMapper::map(&origin)
1470 }
1471
1472 #[pallet::call_index(8)]
1477 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1478 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1479 let origin = ensure_signed(origin)?;
1480 T::AddressMapper::unmap(&origin)
1481 }
1482
1483 #[pallet::call_index(9)]
1489 #[pallet::weight({
1490 let dispatch_info = call.get_dispatch_info();
1491 (
1492 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1493 dispatch_info.class
1494 )
1495 })]
1496 pub fn dispatch_as_fallback_account(
1497 origin: OriginFor<T>,
1498 call: Box<<T as Config>::RuntimeCall>,
1499 ) -> DispatchResultWithPostInfo {
1500 Self::ensure_non_contract_if_signed(&origin)?;
1501 let origin = ensure_signed(origin)?;
1502 let unmapped_account =
1503 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1504 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1505 }
1506 }
1507}
1508
1509fn dispatch_result<R>(
1511 result: Result<R, DispatchError>,
1512 gas_consumed: Weight,
1513 base_weight: Weight,
1514) -> DispatchResultWithPostInfo {
1515 let post_info = PostDispatchInfo {
1516 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1517 pays_fee: Default::default(),
1518 };
1519
1520 result
1521 .map(|_| post_info)
1522 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1523}
1524
1525impl<T: Config> Pallet<T> {
1526 pub fn bare_call(
1533 origin: OriginFor<T>,
1534 dest: H160,
1535 evm_value: U256,
1536 gas_limit: Weight,
1537 storage_deposit_limit: BalanceOf<T>,
1538 data: Vec<u8>,
1539 exec_config: ExecConfig<T>,
1540 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1541 let mut gas_meter = GasMeter::new(gas_limit);
1542 let mut storage_deposit = Default::default();
1543
1544 let try_call = || {
1545 let origin = ExecOrigin::from_runtime_origin(origin)?;
1546 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1547 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1548 origin.clone(),
1549 dest,
1550 &mut gas_meter,
1551 &mut storage_meter,
1552 evm_value,
1553 data,
1554 &exec_config,
1555 )?;
1556 storage_deposit =
1557 storage_meter.try_into_deposit(&origin, &exec_config).inspect_err(|err| {
1558 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1559 })?;
1560 Ok(result)
1561 };
1562 let result = Self::run_guarded(try_call);
1563 ContractResult {
1564 result: result.map_err(|r| r.error),
1565 gas_consumed: gas_meter.gas_consumed(),
1566 gas_required: gas_meter.gas_required(),
1567 storage_deposit,
1568 }
1569 }
1570
1571 pub fn prepare_dry_run(account: &T::AccountId) {
1577 frame_system::Pallet::<T>::inc_account_nonce(account);
1580 }
1581
1582 pub fn bare_instantiate(
1588 origin: OriginFor<T>,
1589 evm_value: U256,
1590 gas_limit: Weight,
1591 mut storage_deposit_limit: BalanceOf<T>,
1592 code: Code,
1593 data: Vec<u8>,
1594 salt: Option<[u8; 32]>,
1595 exec_config: ExecConfig<T>,
1596 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1597 let mut gas_meter = GasMeter::new(gas_limit);
1598 let mut storage_deposit = Default::default();
1599 let try_instantiate = || {
1600 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1601
1602 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1603 let (executable, upload_deposit) = match code {
1604 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1605 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1606 let (executable, upload_deposit) = Self::try_upload_code(
1607 upload_account,
1608 code,
1609 BytecodeType::Pvm,
1610 storage_deposit_limit,
1611 &exec_config,
1612 )?;
1613 storage_deposit_limit.saturating_reduce(upload_deposit);
1614 (executable, upload_deposit)
1615 },
1616 Code::Upload(code) =>
1617 if T::AllowEVMBytecode::get() {
1618 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1619 let origin = T::UploadOrigin::ensure_origin(origin)?;
1620 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1621 (executable, Default::default())
1622 } else {
1623 return Err(<Error<T>>::CodeRejected.into())
1624 },
1625 Code::Existing(code_hash) => {
1626 let executable = ContractBlob::from_storage(code_hash, &mut gas_meter)?;
1627 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1628 (executable, Default::default())
1629 },
1630 };
1631 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1632 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1633 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1634 instantiate_account,
1635 executable,
1636 &mut gas_meter,
1637 &mut storage_meter,
1638 evm_value,
1639 data,
1640 salt.as_ref(),
1641 &exec_config,
1642 );
1643 storage_deposit = storage_meter
1644 .try_into_deposit(&instantiate_origin, &exec_config)?
1645 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1646 result
1647 };
1648 let output = Self::run_guarded(try_instantiate);
1649 ContractResult {
1650 result: output
1651 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1652 .map_err(|e| e.error),
1653 gas_consumed: gas_meter.gas_consumed(),
1654 gas_required: gas_meter.gas_required(),
1655 storage_deposit,
1656 }
1657 }
1658
1659 pub fn dry_run_eth_transact(
1665 mut tx: GenericTransaction,
1666 dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1667 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1668 where
1669 T::Nonce: Into<U256>,
1670 CallOf<T>: SetWeightLimit,
1671 {
1672 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1673
1674 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1675 Self::prepare_dry_run(&origin);
1676
1677 let base_fee = Self::evm_base_fee();
1678 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1679
1680 if effective_gas_price < base_fee {
1681 Err(EthTransactError::Message(format!(
1682 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1683 )))?;
1684 }
1685
1686 if tx.nonce.is_none() {
1687 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1688 }
1689 if tx.chain_id.is_none() {
1690 tx.chain_id = Some(T::ChainId::get().into());
1691 }
1692 if tx.gas_price.is_none() {
1693 tx.gas_price = Some(effective_gas_price);
1694 }
1695 if tx.max_priority_fee_per_gas.is_none() {
1696 tx.max_priority_fee_per_gas = Some(effective_gas_price);
1697 }
1698 if tx.max_fee_per_gas.is_none() {
1699 tx.max_fee_per_gas = Some(effective_gas_price);
1700 }
1701
1702 let gas = tx.gas;
1703 if tx.gas.is_none() {
1704 tx.gas = Some(Self::evm_block_gas_limit());
1705 }
1706 if tx.r#type.is_none() {
1707 tx.r#type = Some(TYPE_EIP1559.into());
1708 }
1709
1710 let value = tx.value.unwrap_or_default();
1712 let input = tx.input.clone().to_vec();
1713 let from = tx.from;
1714 let to = tx.to;
1715
1716 let mut call_info = create_call::<T>(tx, None, false)
1719 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1720
1721 let exec_config = {
1725 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1726 ExecConfig::new_eth_tx(
1727 effective_gas_price,
1728 call_info.encoded_len,
1729 base_info.total_weight(),
1730 )
1731 .with_dry_run(dry_run_config)
1732 };
1733
1734 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
1736 if let Some(from) = &from {
1737 let fees = if gas.is_some() { fees } else { Zero::zero() };
1738 let balance = Self::evm_balance(from);
1739 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
1740 return Err(EthTransactError::Message(format!(
1741 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
1742 )));
1743 }
1744 }
1745
1746 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
1749
1750 let extract_error = |err| {
1751 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
1752 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
1753 } else {
1754 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
1755 }
1756 };
1757
1758 let mut dry_run = match to {
1760 Some(dest) => {
1762 if dest == RUNTIME_PALLETS_ADDR {
1763 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
1764 return Err(EthTransactError::Message(format!(
1765 "Failed to decode pallet-call {input:?}"
1766 )));
1767 };
1768
1769 if let Err(result) =
1770 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1771 {
1772 return Err(EthTransactError::Message(format!(
1773 "Failed to dispatch call: {:?}",
1774 result.error,
1775 )));
1776 };
1777
1778 Default::default()
1779 } else {
1780 let result = crate::Pallet::<T>::bare_call(
1782 OriginFor::<T>::signed(origin),
1783 dest,
1784 value,
1785 call_info.weight_limit,
1786 BalanceOf::<T>::max_value(),
1787 input.clone(),
1788 exec_config,
1789 );
1790
1791 let data = match result.result {
1792 Ok(return_value) => {
1793 if return_value.did_revert() {
1794 return Err(EthTransactError::Data(return_value.data));
1795 }
1796 return_value.data
1797 },
1798 Err(err) => {
1799 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1800 return extract_error(err);
1801 },
1802 };
1803
1804 EthTransactInfo {
1805 gas_required: result.gas_required,
1806 storage_deposit: result.storage_deposit.charge_or_zero(),
1807 data,
1808 eth_gas: Default::default(),
1809 }
1810 }
1811 },
1812 None => {
1814 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1816 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1817 } else {
1818 (input, vec![])
1819 };
1820
1821 let result = crate::Pallet::<T>::bare_instantiate(
1823 OriginFor::<T>::signed(origin),
1824 value,
1825 call_info.weight_limit,
1826 BalanceOf::<T>::max_value(),
1827 Code::Upload(code.clone()),
1828 data.clone(),
1829 None,
1830 exec_config,
1831 );
1832
1833 let returned_data = match result.result {
1834 Ok(return_value) => {
1835 if return_value.result.did_revert() {
1836 return Err(EthTransactError::Data(return_value.result.data));
1837 }
1838 return_value.result.data
1839 },
1840 Err(err) => {
1841 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1842 return extract_error(err);
1843 },
1844 };
1845
1846 EthTransactInfo {
1847 gas_required: result.gas_required,
1848 storage_deposit: result.storage_deposit.charge_or_zero(),
1849 data: returned_data,
1850 eth_gas: Default::default(),
1851 }
1852 },
1853 };
1854
1855 call_info.call.set_weight_limit(dry_run.gas_required);
1857
1858 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
1860 let max_weight = Self::evm_max_extrinsic_weight();
1861 if total_weight.any_gt(max_weight) {
1862 Err(EthTransactError::Message(format!(
1863 "\
1864 The transaction consumes more than the allowed weight. \
1865 needed={total_weight} \
1866 allowed={max_weight} \
1867 overweight_by={}\
1868 ",
1869 total_weight.saturating_sub(max_weight),
1870 )))?;
1871 }
1872
1873 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
1875 let available_fee = T::FeeInfo::remaining_txfee();
1876 if transaction_fee > available_fee {
1877 Err(EthTransactError::Message(format!(
1878 "Not enough gas supplied: Off by: {:?}",
1879 call_info.tx_fee.saturating_sub(available_fee),
1880 )))?;
1881 }
1882
1883 let eth_gas: U256 = T::FeeInfo::next_fee_multiplier_reciprocal()
1886 .saturating_mul_int(transaction_fee.saturating_add(dry_run.storage_deposit))
1887 .saturating_add(1_u32.into())
1888 .into();
1889
1890 log::debug!(target: LOG_TARGET, "\
1891 dry_run_eth_transact: \
1892 weight_limit={} \
1893 total_weight={total_weight} \
1894 max_weight={max_weight} \
1895 weight_left={} \
1896 eth_gas={eth_gas}) \
1897 encoded_len={} \
1898 tx_fee={transaction_fee:?} \
1899 storage_deposit={:?}\
1900 ",
1901 dry_run.gas_required,
1902 max_weight.saturating_sub(total_weight),
1903 call_info.encoded_len,
1904 dry_run.storage_deposit,
1905
1906 );
1907 dry_run.eth_gas = eth_gas;
1908 Ok(dry_run)
1909 }
1910
1911 pub fn evm_balance(address: &H160) -> U256 {
1915 let balance = AccountInfo::<T>::balance_of((*address).into());
1916 Self::convert_native_to_evm(balance)
1917 }
1918
1919 pub fn eth_block() -> EthBlock {
1921 EthereumBlock::<T>::get()
1922 }
1923
1924 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
1931 let number = BlockNumberFor::<T>::try_from(number).ok()?;
1932 let hash = <BlockHash<T>>::get(number);
1933 if hash == H256::zero() {
1934 None
1935 } else {
1936 Some(hash)
1937 }
1938 }
1939
1940 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
1942 ReceiptInfoData::<T>::get()
1943 }
1944
1945 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
1951 let (balance, dust) = Self::new_balance_with_dust(evm_value)
1952 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
1953 let account_id = T::AddressMapper::to_account_id(&address);
1954 T::Currency::set_balance(&account_id, balance);
1955 AccountInfoOf::<T>::mutate(&address, |account| {
1956 if let Some(account) = account {
1957 account.dust = dust;
1958 } else {
1959 *account = Some(AccountInfo { dust, ..Default::default() });
1960 }
1961 });
1962
1963 Ok(())
1964 }
1965
1966 pub fn new_balance_with_dust(
1970 evm_value: U256,
1971 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
1972 let ed = T::Currency::minimum_balance();
1973 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
1974 let (value, dust) = balance_with_dust.deconstruct();
1975
1976 Ok((ed.saturating_add(value), dust))
1977 }
1978
1979 pub fn evm_nonce(address: &H160) -> u32
1981 where
1982 T::Nonce: Into<u32>,
1983 {
1984 let account = T::AddressMapper::to_account_id(&address);
1985 System::<T>::account_nonce(account).into()
1986 }
1987
1988 pub fn evm_block_gas_limit() -> U256 {
1990 let max_block_weight = T::BlockWeights::get()
1991 .get(DispatchClass::Normal)
1992 .max_total
1993 .unwrap_or_else(|| T::BlockWeights::get().max_block);
1994
1995 let length_fee = T::FeeInfo::next_fee_multiplier_reciprocal().saturating_mul_int(
1996 T::FeeInfo::length_to_fee(*T::BlockLength::get().max.get(DispatchClass::Normal)),
1997 );
1998
1999 Self::evm_gas_from_weight(max_block_weight).saturating_add(length_fee.into())
2000 }
2001
2002 pub fn evm_max_extrinsic_weight() -> Weight {
2004 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2005 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2006 .get(DispatchClass::Normal)
2007 .max_extrinsic
2008 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2009 Weight::from_parts(
2010 factor.saturating_mul_int(max_weight.ref_time()),
2011 factor.saturating_mul_int(max_weight.proof_size()),
2012 )
2013 }
2014
2015 pub fn evm_base_fee() -> U256 {
2017 let multiplier = T::FeeInfo::next_fee_multiplier();
2018 multiplier.saturating_mul_int::<u128>(T::NativeToEthRatio::get().into()).into()
2019 }
2020
2021 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2023 where
2024 T::Nonce: Into<u32>,
2025 {
2026 match tracer_type {
2027 TracerType::CallTracer(config) => CallTracer::new(
2028 config.unwrap_or_default(),
2029 Self::evm_gas_from_weight as fn(Weight) -> U256,
2030 )
2031 .into(),
2032 TracerType::PrestateTracer(config) =>
2033 PrestateTracer::new(config.unwrap_or_default()).into(),
2034 }
2035 }
2036
2037 pub fn bare_upload_code(
2041 origin: OriginFor<T>,
2042 code: Vec<u8>,
2043 storage_deposit_limit: BalanceOf<T>,
2044 ) -> CodeUploadResult<BalanceOf<T>> {
2045 let origin = T::UploadOrigin::ensure_origin(origin)?;
2046
2047 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2048 BytecodeType::Pvm
2049 } else {
2050 if !T::AllowEVMBytecode::get() {
2051 return Err(<Error<T>>::CodeRejected.into())
2052 }
2053 BytecodeType::Evm
2054 };
2055
2056 let (module, deposit) = Self::try_upload_code(
2057 origin,
2058 code,
2059 bytecode_type,
2060 storage_deposit_limit,
2061 &ExecConfig::new_substrate_tx(),
2062 )?;
2063 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
2064 }
2065
2066 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2068 let contract_info =
2069 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2070
2071 let maybe_value = contract_info.read(&Key::from_fixed(key));
2072 Ok(maybe_value)
2073 }
2074
2075 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2079 let immutable_data = <ImmutableDataOf<T>>::get(address);
2080 immutable_data
2081 }
2082
2083 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2091 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2092 <ImmutableDataOf<T>>::insert(address, data);
2093 Ok(())
2094 }
2095
2096 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2098 let contract_info =
2099 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2100
2101 let maybe_value = contract_info.read(
2102 &Key::try_from_var(key)
2103 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2104 .into(),
2105 );
2106 Ok(maybe_value)
2107 }
2108
2109 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2111 let (value, dust) = value.into().deconstruct();
2112 value
2113 .into()
2114 .saturating_mul(T::NativeToEthRatio::get().into())
2115 .saturating_add(dust.into())
2116 }
2117
2118 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2128 let contract_info =
2129 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2130
2131 contract_info
2132 .write(&Key::from_fixed(key), value, None, false)
2133 .map_err(ContractAccessError::StorageWriteFailed)
2134 }
2135
2136 pub fn set_storage_var_key(
2147 address: H160,
2148 key: Vec<u8>,
2149 value: Option<Vec<u8>>,
2150 ) -> SetStorageResult {
2151 let contract_info =
2152 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2153
2154 contract_info
2155 .write(
2156 &Key::try_from_var(key)
2157 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2158 .into(),
2159 value,
2160 None,
2161 false,
2162 )
2163 .map_err(ContractAccessError::StorageWriteFailed)
2164 }
2165
2166 pub fn account_id() -> T::AccountId {
2168 use frame_support::PalletId;
2169 use sp_runtime::traits::AccountIdConversion;
2170 PalletId(*b"py/reviv").into_account_truncating()
2171 }
2172
2173 pub fn block_author() -> H160 {
2175 use frame_support::traits::FindAuthor;
2176
2177 let digest = <frame_system::Pallet<T>>::digest();
2178 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2179
2180 T::FindAuthor::find_author(pre_runtime_digests)
2181 .map(|account_id| T::AddressMapper::to_address(&account_id))
2182 .unwrap_or_default()
2183 }
2184
2185 pub fn code(address: &H160) -> Vec<u8> {
2189 use precompiles::{All, Precompiles};
2190 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2191 return code.into()
2192 }
2193 AccountInfo::<T>::load_contract(&address)
2194 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2195 .map(|code| code.into())
2196 .unwrap_or_default()
2197 }
2198
2199 pub fn try_upload_code(
2201 origin: T::AccountId,
2202 code: Vec<u8>,
2203 code_type: BytecodeType,
2204 storage_deposit_limit: BalanceOf<T>,
2205 exec_config: &ExecConfig<T>,
2206 ) -> Result<(ContractBlob<T>, BalanceOf<T>), DispatchError> {
2207 let mut module = match code_type {
2208 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2209 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2210 };
2211 let deposit = module.store_code(exec_config, None)?;
2212 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
2213 Ok((module, deposit))
2214 }
2215
2216 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2218 executing_contract::using_once(&mut false, || {
2219 executing_contract::with(|f| {
2220 if *f {
2222 return Err(())
2223 }
2224 *f = true;
2226 Ok(())
2227 })
2228 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2229 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2230 .map(|_| f())
2231 .and_then(|r| r)
2232 })
2233 }
2234
2235 pub fn evm_gas_from_weight(weight: Weight) -> U256 {
2237 T::FeeInfo::weight_to_fee(&weight, Combinator::Max).into()
2238 }
2239
2240 fn charge_deposit(
2245 hold_reason: Option<HoldReason>,
2246 from: &T::AccountId,
2247 to: &T::AccountId,
2248 amount: BalanceOf<T>,
2249 exec_config: &ExecConfig<T>,
2250 ) -> DispatchResult {
2251 use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
2252 match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2253 (true, hold_reason) => {
2254 T::FeeInfo::withdraw_txfee(amount)
2255 .ok_or(())
2256 .and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2257 .and_then(|_| {
2258 if let Some(hold_reason) = hold_reason {
2259 T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2260 }
2261 Ok(())
2262 })
2263 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2264 },
2265 (false, Some(hold_reason)) => {
2266 T::Currency::transfer_and_hold(
2267 &hold_reason.into(),
2268 from,
2269 to,
2270 amount,
2271 Precision::Exact,
2272 Preservation::Preserve,
2273 Fortitude::Polite,
2274 )
2275 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2276 },
2277 (false, None) => {
2278 T::Currency::transfer(from, to, amount, Preservation::Preserve)
2279 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2280 },
2281 }
2282 Ok(())
2283 }
2284
2285 fn refund_deposit(
2290 hold_reason: HoldReason,
2291 from: &T::AccountId,
2292 to: &T::AccountId,
2293 amount: BalanceOf<T>,
2294 exec_config: &ExecConfig<T>,
2295 ) -> Result<BalanceOf<T>, DispatchError> {
2296 use frame_support::traits::{
2297 tokens::{Fortitude, Precision, Preservation, Restriction},
2298 Imbalance,
2299 };
2300 if exec_config.collect_deposit_from_hold.is_some() {
2301 let amount =
2302 T::Currency::release(&hold_reason.into(), from, amount, Precision::BestEffort)
2303 .and_then(|amount| {
2304 T::Currency::withdraw(
2305 from,
2306 amount,
2307 Precision::Exact,
2308 Preservation::Preserve,
2309 Fortitude::Polite,
2310 )
2311 .and_then(|credit| {
2312 let amount = credit.peek();
2313 T::FeeInfo::deposit_txfee(credit);
2314 Ok(amount)
2315 })
2316 })
2317 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2318 amount
2319 } else {
2320 let amount = T::Currency::transfer_on_hold(
2321 &hold_reason.into(),
2322 from,
2323 to,
2324 amount,
2325 Precision::BestEffort,
2326 Restriction::Free,
2327 Fortitude::Polite,
2328 )
2329 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2330 amount
2331 };
2332
2333 Ok(amount)
2334 }
2335
2336 fn has_dust(value: U256) -> bool {
2338 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2339 }
2340
2341 fn has_balance(value: U256) -> bool {
2343 value >= U256::from(<T>::NativeToEthRatio::get())
2344 }
2345
2346 fn min_balance() -> BalanceOf<T> {
2348 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2349 }
2350
2351 fn deposit_event(event: Event<T>) {
2356 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2357 }
2358
2359 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2361 match <T as Config>::RuntimeOrigin::from(origin).into() {
2362 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2363 _ => Err(BadOrigin.into()),
2364 }
2365 }
2366
2367 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2371 let Some(address) = origin
2372 .as_system_ref()
2373 .and_then(|o| o.as_signed())
2374 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2375 else {
2376 return Ok(())
2377 };
2378 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2379 <AccountInfo<T>>::is_contract(&address)
2380 {
2381 log::debug!(
2382 target: crate::LOG_TARGET,
2383 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2384 );
2385 Err(DispatchError::BadOrigin)
2386 } else {
2387 Ok(())
2388 }
2389 }
2390}
2391
2392pub const RUNTIME_PALLETS_ADDR: H160 =
2397 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2398
2399environmental!(executing_contract: bool);
2401
2402sp_api::decl_runtime_apis! {
2403 #[api_version(1)]
2405 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2406 AccountId: Codec,
2407 Balance: Codec,
2408 Nonce: Codec,
2409 BlockNumber: Codec,
2410 Moment: Codec,
2411 {
2412 fn eth_block() -> EthBlock;
2416
2417 fn eth_block_hash(number: U256) -> Option<H256>;
2419
2420 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2426
2427 fn block_gas_limit() -> U256;
2429
2430 fn balance(address: H160) -> U256;
2432
2433 fn gas_price() -> U256;
2435
2436 fn nonce(address: H160) -> Nonce;
2438
2439 fn call(
2443 origin: AccountId,
2444 dest: H160,
2445 value: Balance,
2446 gas_limit: Option<Weight>,
2447 storage_deposit_limit: Option<Balance>,
2448 input_data: Vec<u8>,
2449 ) -> ContractResult<ExecReturnValue, Balance>;
2450
2451 fn instantiate(
2455 origin: AccountId,
2456 value: Balance,
2457 gas_limit: Option<Weight>,
2458 storage_deposit_limit: Option<Balance>,
2459 code: Code,
2460 data: Vec<u8>,
2461 salt: Option<[u8; 32]>,
2462 ) -> ContractResult<InstantiateReturnValue, Balance>;
2463
2464
2465 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2470
2471 fn eth_transact_with_config(
2475 tx: GenericTransaction,
2476 config: DryRunConfig<Moment>,
2477 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2478
2479 fn upload_code(
2483 origin: AccountId,
2484 code: Vec<u8>,
2485 storage_deposit_limit: Option<Balance>,
2486 ) -> CodeUploadResult<Balance>;
2487
2488 fn get_storage(
2494 address: H160,
2495 key: [u8; 32],
2496 ) -> GetStorageResult;
2497
2498 fn get_storage_var_key(
2504 address: H160,
2505 key: Vec<u8>,
2506 ) -> GetStorageResult;
2507
2508 fn trace_block(
2515 block: Block,
2516 config: TracerType
2517 ) -> Vec<(u32, Trace)>;
2518
2519 fn trace_tx(
2526 block: Block,
2527 tx_index: u32,
2528 config: TracerType
2529 ) -> Option<Trace>;
2530
2531 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2535
2536 fn block_author() -> H160;
2538
2539 fn address(account_id: AccountId) -> H160;
2541
2542 fn account_id(address: H160) -> AccountId;
2544
2545 fn runtime_pallets_address() -> H160;
2547
2548 fn code(address: H160) -> Vec<u8>;
2550
2551 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2553 }
2554}
2555
2556#[macro_export]
2570macro_rules! impl_runtime_apis_plus_revive_traits {
2571 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2572
2573 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2574
2575 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2576 fn set_weight_limit(&mut self, weight_limit: Weight) -> Weight {
2577 use $crate::pallet::Call as ReviveCall;
2578 match self {
2579 Self::$Revive(
2580 ReviveCall::eth_call{ gas_limit, .. } |
2581 ReviveCall::eth_instantiate_with_code{ gas_limit, .. }
2582 ) => {
2583 let old = *gas_limit;
2584 *gas_limit = weight_limit;
2585 old
2586 },
2587 _ => Weight::default(),
2588 }
2589 }
2590 }
2591
2592 impl_runtime_apis! {
2593 $($rest)*
2594
2595
2596 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2597 {
2598 fn eth_block() -> $crate::EthBlock {
2599 $crate::Pallet::<Self>::eth_block()
2600 }
2601
2602 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2603 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
2604 }
2605
2606 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2607 $crate::Pallet::<Self>::eth_receipt_data()
2608 }
2609
2610 fn balance(address: $crate::H160) -> $crate::U256 {
2611 $crate::Pallet::<Self>::evm_balance(&address)
2612 }
2613
2614 fn block_author() -> $crate::H160 {
2615 $crate::Pallet::<Self>::block_author()
2616 }
2617
2618 fn block_gas_limit() -> $crate::U256 {
2619 $crate::Pallet::<Self>::evm_block_gas_limit()
2620 }
2621
2622 fn gas_price() -> $crate::U256 {
2623 $crate::Pallet::<Self>::evm_base_fee()
2624 }
2625
2626 fn nonce(address: $crate::H160) -> Nonce {
2627 use $crate::AddressMapper;
2628 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2629 $crate::frame_system::Pallet::<Self>::account_nonce(account)
2630 }
2631
2632 fn address(account_id: AccountId) -> $crate::H160 {
2633 use $crate::AddressMapper;
2634 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2635 }
2636
2637 fn eth_transact(
2638 tx: $crate::evm::GenericTransaction,
2639 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2640 use $crate::{
2641 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2642 sp_runtime::traits::TransactionExtension,
2643 sp_runtime::traits::Block as BlockT
2644 };
2645 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
2646 }
2647
2648 fn eth_transact_with_config(
2649 tx: $crate::evm::GenericTransaction,
2650 config: $crate::DryRunConfig<__ReviveMacroMoment>,
2651 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2652 use $crate::{
2653 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2654 sp_runtime::traits::TransactionExtension,
2655 sp_runtime::traits::Block as BlockT
2656 };
2657 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
2658 }
2659
2660 fn call(
2661 origin: AccountId,
2662 dest: $crate::H160,
2663 value: Balance,
2664 gas_limit: Option<$crate::Weight>,
2665 storage_deposit_limit: Option<Balance>,
2666 input_data: Vec<u8>,
2667 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2668 use $crate::frame_support::traits::Get;
2669 let blockweights: $crate::BlockWeights =
2670 <Self as $crate::frame_system::Config>::BlockWeights::get();
2671
2672 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2673 $crate::Pallet::<Self>::bare_call(
2674 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2675 dest,
2676 $crate::Pallet::<Self>::convert_native_to_evm(value),
2677 gas_limit.unwrap_or(blockweights.max_block),
2678 storage_deposit_limit.unwrap_or(u128::MAX),
2679 input_data,
2680 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2681 )
2682 }
2683
2684 fn instantiate(
2685 origin: AccountId,
2686 value: Balance,
2687 gas_limit: Option<$crate::Weight>,
2688 storage_deposit_limit: Option<Balance>,
2689 code: $crate::Code,
2690 data: Vec<u8>,
2691 salt: Option<[u8; 32]>,
2692 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2693 use $crate::frame_support::traits::Get;
2694 let blockweights: $crate::BlockWeights =
2695 <Self as $crate::frame_system::Config>::BlockWeights::get();
2696
2697 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2698 $crate::Pallet::<Self>::bare_instantiate(
2699 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2700 $crate::Pallet::<Self>::convert_native_to_evm(value),
2701 gas_limit.unwrap_or(blockweights.max_block),
2702 storage_deposit_limit.unwrap_or(u128::MAX),
2703 code,
2704 data,
2705 salt,
2706 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2707 )
2708 }
2709
2710 fn upload_code(
2711 origin: AccountId,
2712 code: Vec<u8>,
2713 storage_deposit_limit: Option<Balance>,
2714 ) -> $crate::CodeUploadResult<Balance> {
2715 let origin =
2716 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
2717 $crate::Pallet::<Self>::bare_upload_code(
2718 origin,
2719 code,
2720 storage_deposit_limit.unwrap_or(u128::MAX),
2721 )
2722 }
2723
2724 fn get_storage_var_key(
2725 address: $crate::H160,
2726 key: Vec<u8>,
2727 ) -> $crate::GetStorageResult {
2728 $crate::Pallet::<Self>::get_storage_var_key(address, key)
2729 }
2730
2731 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2732 $crate::Pallet::<Self>::get_storage(address, key)
2733 }
2734
2735 fn trace_block(
2736 block: Block,
2737 tracer_type: $crate::evm::TracerType,
2738 ) -> Vec<(u32, $crate::evm::Trace)> {
2739 use $crate::{sp_runtime::traits::Block, tracing::trace};
2740 let mut traces = vec![];
2741 let (header, extrinsics) = block.deconstruct();
2742 <$Executive>::initialize_block(&header);
2743 for (index, ext) in extrinsics.into_iter().enumerate() {
2744 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2745 let t = tracer.as_tracing();
2746 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2747
2748 if let Some(tx_trace) = tracer.collect_trace() {
2749 traces.push((index as u32, tx_trace));
2750 }
2751 }
2752
2753 traces
2754 }
2755
2756 fn trace_tx(
2757 block: Block,
2758 tx_index: u32,
2759 tracer_type: $crate::evm::TracerType,
2760 ) -> Option<$crate::evm::Trace> {
2761 use $crate::{sp_runtime::traits::Block, tracing::trace};
2762
2763 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2764 let (header, extrinsics) = block.deconstruct();
2765
2766 <$Executive>::initialize_block(&header);
2767 for (index, ext) in extrinsics.into_iter().enumerate() {
2768 if index as u32 == tx_index {
2769 let t = tracer.as_tracing();
2770 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2771 break;
2772 } else {
2773 let _ = <$Executive>::apply_extrinsic(ext);
2774 }
2775 }
2776
2777 tracer.collect_trace()
2778 }
2779
2780 fn trace_call(
2781 tx: $crate::evm::GenericTransaction,
2782 tracer_type: $crate::evm::TracerType,
2783 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2784 use $crate::tracing::trace;
2785 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2786 let t = tracer.as_tracing();
2787
2788 t.watch_address(&tx.from.unwrap_or_default());
2789 t.watch_address(&$crate::Pallet::<Self>::block_author());
2790 let result = trace(t, || Self::eth_transact(tx));
2791
2792 if let Some(trace) = tracer.collect_trace() {
2793 Ok(trace)
2794 } else if let Err(err) = result {
2795 Err(err)
2796 } else {
2797 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
2798 }
2799 }
2800
2801 fn runtime_pallets_address() -> $crate::H160 {
2802 $crate::RUNTIME_PALLETS_ADDR
2803 }
2804
2805 fn code(address: $crate::H160) -> Vec<u8> {
2806 $crate::Pallet::<Self>::code(&address)
2807 }
2808
2809 fn account_id(address: $crate::H160) -> AccountId {
2810 use $crate::AddressMapper;
2811 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
2812 }
2813
2814 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2815 $crate::Pallet::<Self>::new_balance_with_dust(balance)
2816 }
2817 }
2818 }
2819 };
2820}