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 access_list;
26mod address;
27mod benchmarking;
28#[cfg(any(feature = "runtime-benchmarks", test))]
29pub mod call_builder;
30mod debug;
31mod deposit_payment;
32mod exec;
33mod impl_fungibles;
34mod limits;
35mod metering;
36mod primitives;
37#[doc(hidden)]
38pub mod runtime_api;
39#[doc(hidden)]
40pub mod state_overrides;
41mod storage;
42#[cfg(test)]
43mod tests;
44mod transient_storage;
45mod vm;
46mod weightinfo_extension;
47
48pub mod evm;
49pub mod migrations;
50pub mod mock;
51pub mod precompiles;
52pub mod test_utils;
53pub mod tracing;
54pub mod weights;
55
56use crate::{
57 access_list::{StorageAccessKind, Warmth},
58 evm::{
59 CallTracer, CreateCallMode, ExecutionTracer, GenericTransaction, PrestateTracer,
60 TYPE_EIP1559, Tracer, TracerType, block_hash::EthereumBlockBuilderIR, block_storage,
61 fees::InfoT as FeeInfo, runtime::SetWeightLimit,
62 },
63 exec::{AccountIdOf, ExecError, ReentrancyProtection, Stack as ExecStack},
64 sp_runtime::TransactionOutcome,
65 storage::{AccountType, DeletionQueueManager},
66 tracing::if_tracing,
67 vm::{CodeInfo, RuntimeCosts, pvm::extract_code_and_data},
68 weightinfo_extension::OnFinalizeBlockParts,
69};
70use alloc::{boxed::Box, format, vec};
71use codec::{Codec, Decode, Encode};
72use environmental::*;
73use frame_support::{
74 BoundedVec,
75 dispatch::{
76 DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
77 Pays, PostDispatchInfo, RawOrigin,
78 },
79 ensure,
80 pallet_prelude::DispatchClass,
81 storage::with_transaction,
82 traits::{
83 ConstU32, ConstU64, DefensiveResult, EnsureOrigin, Get, IsSubType, IsType, OnUnbalanced,
84 OriginTrait,
85 fungible::{Balanced, Credit, Inspect, Mutate, MutateHold},
86 tokens::Balance,
87 },
88 weights::WeightMeter,
89};
90use frame_system::{
91 Pallet as System, ensure_signed,
92 pallet_prelude::{BlockNumberFor, OriginFor},
93};
94use pallet_revive_types::runtime_api::*;
95use scale_info::TypeInfo;
96use sp_runtime::{
97 AccountId32, DispatchError, FixedPointNumber, FixedU128, SaturatedConversion,
98 traits::{
99 BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedFrom,
100 UniqueSaturatedInto, Zero,
101 },
102};
103
104pub use crate::{
105 address::{AccountId32Mapper, AddressMapper, AutoMapper, TestAccountMapper, create1, create2},
106 debug::DebugSettings,
107 deposit_payment::{Deposit, PGasDeposit},
108 evm::{
109 Address as EthAddress, Block as EthBlock, DryRunConfig, ReceiptInfo, TracingConfig,
110 block_hash::ReceiptGasInfo,
111 },
112 exec::{CallResources, DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
113 limits::TRANSIENT_STORAGE_BYTES as TRANSIENT_STORAGE_LIMIT,
114 metering::{
115 EthTxInfo, FrameMeter, ResourceMeter, Token as WeightToken, TransactionLimits,
116 TransactionMeter,
117 },
118 pallet::{genesis, *},
119 storage::{AccountInfo, ContractInfo},
120 transient_storage::{MeterEntry, StorageMeter as TransientStorageMeter, TransientStorage},
121 vm::{BytecodeType, ContractBlob},
122};
123pub use codec;
124use frame_support::traits::tokens::Precision;
125pub use frame_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
126pub use frame_system::{self, limits::BlockWeights};
127pub use primitives::*;
128pub use sp_core::{H160, H256, U256};
129pub use sp_crypto_hashing::keccak_256;
130pub use sp_runtime;
131pub use weights::WeightInfo;
132
133pub extern crate pallet_revive_types;
135
136#[cfg(doc)]
137pub use crate::vm::pvm::SyscallDoc;
138
139pub type BalanceOf<T> = <T as Config>::Balance;
140pub type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
141type TrieId = BoundedVec<u8, ConstU32<128>>;
142type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
143type CallOf<T> = <T as Config>::RuntimeCall;
144
145const SENTINEL: u32 = u32::MAX;
152
153const LOG_TARGET: &str = "runtime::revive";
159
160#[frame_support::pallet]
161pub mod pallet {
162 use super::*;
163 use frame_support::{pallet_prelude::*, traits::FindAuthor};
164 use frame_system::pallet_prelude::*;
165 use sp_core::U256;
166 use sp_runtime::Perbill;
167
168 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
170
171 #[pallet::pallet]
172 #[pallet::storage_version(STORAGE_VERSION)]
173 pub struct Pallet<T>(_);
174
175 #[pallet::config(with_default)]
176 pub trait Config: frame_system::Config {
177 type Time: Time<Moment: Into<U256>>;
179
180 #[pallet::no_default]
184 type Balance: Balance
185 + TryFrom<U256>
186 + Into<U256>
187 + Bounded
188 + UniqueSaturatedInto<u64>
189 + UniqueSaturatedFrom<u64>
190 + UniqueSaturatedInto<u128>;
191
192 #[pallet::no_default]
194 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
195 + Mutate<Self::AccountId>
196 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
197 + Balanced<Self::AccountId>;
198
199 #[pallet::no_default_bounds]
206 type OnBurn: OnUnbalanced<CreditOf<Self>>;
207
208 #[pallet::no_default_bounds]
210 #[allow(deprecated)]
211 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
212
213 #[pallet::no_default_bounds]
215 type RuntimeCall: Parameter
216 + Dispatchable<
217 RuntimeOrigin = OriginFor<Self>,
218 Info = DispatchInfo,
219 PostInfo = PostDispatchInfo,
220 > + IsType<<Self as frame_system::Config>::RuntimeCall>
221 + From<Call<Self>>
222 + IsSubType<Call<Self>>
223 + GetDispatchInfo;
224
225 #[pallet::no_default_bounds]
227 type RuntimeOrigin: IsType<OriginFor<Self>>
228 + From<Origin<Self>>
229 + Into<Result<Origin<Self>, OriginFor<Self>>>;
230
231 #[pallet::no_default_bounds]
233 type RuntimeHoldReason: From<HoldReason>;
234
235 type WeightInfo: WeightInfo;
238
239 #[pallet::no_default_bounds]
243 #[allow(private_bounds)]
244 type Precompiles: precompiles::Precompiles<Self>;
245
246 type FindAuthor: FindAuthor<Self::AccountId>;
248
249 #[pallet::constant]
255 #[pallet::no_default_bounds]
256 type DepositPerByte: Get<BalanceOf<Self>>;
257
258 #[pallet::constant]
264 #[pallet::no_default_bounds]
265 type DepositPerItem: Get<BalanceOf<Self>>;
266
267 #[pallet::constant]
277 #[pallet::no_default_bounds]
278 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
279
280 #[pallet::constant]
284 type CodeHashLockupDepositPercent: Get<Perbill>;
285
286 #[pallet::no_default]
288 type AddressMapper: AddressMapper<Self>;
289
290 #[pallet::constant]
292 type AllowEVMBytecode: Get<bool>;
293
294 #[pallet::no_default_bounds]
299 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
300
301 #[pallet::no_default_bounds]
312 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
313
314 type RuntimeMemory: Get<u32>;
319
320 type PVFMemory: Get<u32>;
328
329 #[pallet::constant]
334 type ChainId: Get<u64>;
335
336 #[pallet::constant]
338 type NativeToEthRatio: Get<u32>;
339
340 #[pallet::no_default_bounds]
345 type FeeInfo: FeeInfo<Self>;
346
347 #[pallet::no_default_bounds]
350 type Deposit: Deposit<Self>;
351
352 #[pallet::constant]
365 type MaxEthExtrinsicWeight: Get<FixedU128>;
366
367 #[pallet::constant]
369 type DebugEnabled: Get<bool>;
370
371 #[pallet::constant]
378 type AutoMap: Get<bool>;
379
380 #[pallet::constant]
398 #[pallet::no_default_bounds]
399 type GasScale: Get<u32>;
400 }
401
402 pub mod config_preludes {
404 use super::*;
405 use frame_support::{
406 derive_impl,
407 traits::{ConstBool, ConstU32},
408 };
409 use frame_system::EnsureSigned;
410 use sp_core::parameter_types;
411
412 type Balance = u64;
413
414 pub const DOLLARS: Balance = 1_000_000_000_000;
415 pub const CENTS: Balance = DOLLARS / 100;
416 pub const MILLICENTS: Balance = CENTS / 1_000;
417
418 pub const fn deposit(items: u32, bytes: u32) -> Balance {
419 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
420 }
421
422 parameter_types! {
423 pub const DepositPerItem: Balance = deposit(1, 0);
424 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
425 pub const DepositPerByte: Balance = deposit(0, 1);
426 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
427 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
428 pub const GasScale: u32 = 10u32;
429 }
430
431 pub struct TestDefaultConfig;
433
434 impl Time for TestDefaultConfig {
435 type Moment = u64;
436 fn now() -> Self::Moment {
437 0u64
438 }
439 }
440
441 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
442 fn convert(w: Weight) -> T {
443 w.ref_time().into()
444 }
445 }
446
447 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
448 impl frame_system::DefaultConfig for TestDefaultConfig {}
449
450 #[frame_support::register_default_impl(TestDefaultConfig)]
451 impl DefaultConfig for TestDefaultConfig {
452 #[inject_runtime_type]
453 type RuntimeEvent = ();
454
455 #[inject_runtime_type]
456 type RuntimeHoldReason = ();
457
458 #[inject_runtime_type]
459 type RuntimeCall = ();
460
461 #[inject_runtime_type]
462 type RuntimeOrigin = ();
463
464 type Precompiles = ();
465 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
466 type DepositPerByte = DepositPerByte;
467 type DepositPerItem = DepositPerItem;
468 type DepositPerChildTrieItem = DepositPerChildTrieItem;
469 type Time = Self;
470 type AllowEVMBytecode = ConstBool<true>;
471 type UploadOrigin = EnsureSigned<Self::AccountId>;
472 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
473 type WeightInfo = ();
474 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
475 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
476 type ChainId = ConstU64<42>;
477 type NativeToEthRatio = ConstU32<1_000_000>;
478 type FindAuthor = ();
479 type FeeInfo = ();
480 type Deposit = ();
481 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
482 type DebugEnabled = ConstBool<false>;
483 type AutoMap = ConstBool<false>;
484 type GasScale = GasScale;
485 type OnBurn = ();
486 }
487 }
488
489 #[pallet::event]
490 pub enum Event<T: Config> {
491 ContractEmitted {
493 contract: H160,
495 data: Vec<u8>,
498 topics: Vec<H256>,
501 },
502
503 Instantiated { deployer: H160, contract: H160 },
505
506 EthExtrinsicRevert { dispatch_error: DispatchError },
513 }
514
515 #[pallet::error]
516 #[repr(u8)]
517 pub enum Error<T> {
518 InvalidSchedule = 0x01,
520 InvalidCallFlags = 0x02,
522 OutOfGas = 0x03,
524 TransferFailed = 0x04,
527 MaxCallDepthReached = 0x05,
530 ContractNotFound = 0x06,
532 CodeNotFound = 0x07,
534 CodeInfoNotFound = 0x08,
536 OutOfBounds = 0x09,
538 DecodingFailed = 0x0A,
540 ContractTrapped = 0x0B,
542 ValueTooLarge = 0x0C,
544 TerminatedWhileReentrant = 0x0D,
547 InputForwarded = 0x0E,
549 TooManyTopics = 0x0F,
551 DuplicateContract = 0x12,
553 TerminatedInConstructor = 0x13,
557 ReentranceDenied = 0x14,
559 ReenteredPallet = 0x15,
561 StateChangeDenied = 0x16,
563 StorageDepositNotEnoughFunds = 0x17,
565 StorageDepositLimitExhausted = 0x18,
567 CodeInUse = 0x19,
569 ContractReverted = 0x1A,
574 CodeRejected = 0x1B,
579 BlobTooLarge = 0x1C,
581 StaticMemoryTooLarge = 0x1D,
583 BasicBlockTooLarge = 0x1E,
585 InvalidInstruction = 0x1F,
587 MaxDelegateDependenciesReached = 0x20,
589 DelegateDependencyNotFound = 0x21,
591 DelegateDependencyAlreadyExists = 0x22,
593 CannotAddSelfAsDelegateDependency = 0x23,
595 OutOfTransientStorage = 0x24,
597 InvalidSyscall = 0x25,
599 InvalidStorageFlags = 0x26,
601 ExecutionFailed = 0x27,
603 BalanceConversionFailed = 0x28,
605 InvalidImmutableAccess = 0x2A,
608 AccountUnmapped = 0x2B,
612 AccountAlreadyMapped = 0x2C,
614 InvalidGenericTransaction = 0x2D,
616 RefcountOverOrUnderflow = 0x2E,
618 UnsupportedPrecompileAddress = 0x2F,
620 CallDataTooLarge = 0x30,
622 ReturnDataTooLarge = 0x31,
624 InvalidJump = 0x32,
626 StackUnderflow = 0x33,
628 StackOverflow = 0x34,
630 TxFeeOverdraw = 0x35,
634 EvmConstructorNonEmptyData = 0x36,
638 EvmConstructedFromHash = 0x37,
643 StorageRefundNotEnoughFunds = 0x38,
647 StorageRefundLocked = 0x39,
652 PrecompileDelegateDenied = 0x40,
657 EcdsaRecoveryFailed = 0x41,
659 AutoMappingEnabled = 0x42,
661 PendingDepositCleanup = 0x43,
665 #[cfg(feature = "runtime-benchmarks")]
667 BenchmarkingError = 0xFF,
668 }
669
670 #[pallet::composite_enum]
672 pub enum HoldReason {
673 CodeUploadDepositReserve,
675 StorageDepositReserve,
677 AddressMapping,
679 }
680
681 #[pallet::composite_enum]
683 pub enum FreezeReason {
684 PGasMinBalance,
689 }
690
691 #[derive(
692 PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug,
693 )]
694 #[pallet::origin]
695 pub enum Origin<T: Config> {
696 EthTransaction(T::AccountId),
697 }
698
699 #[pallet::storage]
703 #[pallet::unbounded]
704 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
705
706 #[pallet::storage]
708 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
709
710 #[pallet::storage]
712 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
713
714 #[pallet::storage]
725 pub(crate) type NativeDepositOf<T: Config> = StorageDoubleMap<
726 _,
727 Identity,
728 T::AccountId,
729 Identity,
730 T::AccountId,
731 BalanceOf<T>,
732 ValueQuery,
733 >;
734
735 #[pallet::storage]
737 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
738
739 #[pallet::storage]
745 pub(crate) type DeletionQueue<T: Config> =
746 StorageMap<_, Twox64Concat, u32, crate::storage::DeletionQueueItem<T>>;
747
748 #[pallet::storage]
751 pub(crate) type DeletionQueueCounter<T: Config> =
752 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
753
754 #[pallet::storage]
761 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
762
763 #[pallet::storage]
773 #[pallet::unbounded]
774 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
775
776 #[pallet::storage]
780 pub(crate) type BlockHash<T: Config> =
781 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
782
783 #[pallet::storage]
790 #[pallet::unbounded]
791 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
792
793 #[pallet::storage]
795 #[pallet::unbounded]
796 pub(crate) type EthBlockBuilderIR<T: Config> =
797 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
798
799 #[pallet::storage]
804 #[pallet::unbounded]
805 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
806 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
807
808 #[pallet::storage]
810 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
811
812 pub mod genesis {
813 use super::*;
814 use crate::evm::Bytes32;
815
816 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
818 pub struct ContractData {
819 pub code: crate::evm::Bytes,
821 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
823 }
824
825 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
827 pub struct Account<T: Config> {
828 pub address: H160,
830 #[serde(default)]
832 pub balance: U256,
833 #[serde(default)]
835 pub nonce: T::Nonce,
836 #[serde(flatten, skip_serializing_if = "Option::is_none")]
838 pub contract_data: Option<ContractData>,
839 }
840 }
841
842 #[pallet::genesis_config]
843 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
844 pub struct GenesisConfig<T: Config> {
845 #[serde(default, skip_serializing_if = "Vec::is_empty")]
848 pub mapped_accounts: Vec<T::AccountId>,
849
850 #[serde(default, skip_serializing_if = "Vec::is_empty")]
852 pub accounts: Vec<genesis::Account<T>>,
853
854 #[serde(default, skip_serializing_if = "Option::is_none")]
856 pub debug_settings: Option<DebugSettings>,
857 }
858
859 #[pallet::genesis_build]
860 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
861 fn build(&self) {
862 use crate::{exec::Key, vm::ContractBlob};
863 use frame_support::traits::fungible::Mutate;
864
865 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
866 let _ = T::Currency::mint_into(
867 &Pallet::<T>::account_id(),
868 T::Currency::minimum_balance(),
869 );
870 }
871
872 for id in &self.mapped_accounts {
873 if let Err(err) = T::AddressMapper::map_no_deposit_unchecked(id) {
874 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
875 }
876 }
877
878 let owner = Pallet::<T>::account_id();
879
880 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
881 let account_id = T::AddressMapper::to_account_id(address);
882
883 if !System::<T>::account_exists(&account_id) {
884 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
885 }
886
887 frame_system::Account::<T>::mutate(&account_id, |info| {
888 info.nonce = (*nonce).into();
889 });
890
891 match contract_data {
892 None => {
893 AccountInfoOf::<T>::insert(
894 address,
895 AccountInfo { account_type: AccountType::EOA, dust: 0 },
896 );
897 },
898 Some(genesis::ContractData { code, storage }) => {
899 let blob = if code.0.starts_with(&polkavm_common::program::BLOB_MAGIC) {
900 ContractBlob::<T>::from_pvm_code(code.0.clone(), owner.clone())
901 .inspect_err(|err| {
902 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
903 })
904 } else {
905 ContractBlob::<T>::from_evm_runtime_code(code.0.clone(), account_id)
906 .inspect_err(|err| {
907 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
908 })
909 };
910
911 let Ok(blob) = blob else {
912 continue;
913 };
914
915 let code_hash = *blob.code_hash();
916 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
917 .inspect_err(|err| {
918 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
919 })
920 else {
921 continue;
922 };
923
924 AccountInfoOf::<T>::insert(
925 address,
926 AccountInfo { account_type: info.clone().into(), dust: 0 },
927 );
928
929 <PristineCode<T>>::insert(blob.code_hash(), code.0.clone());
930 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
931 for (k, v) in storage {
932 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
933 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
934 });
935 }
936 },
937 }
938
939 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
940 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
941 });
942 }
943
944 block_storage::on_finalize_build_eth_block::<T>(
946 frame_system::Pallet::<T>::block_number(),
949 );
950
951 if let Some(settings) = self.debug_settings.as_ref() {
953 settings.write_to_storage::<T>()
954 }
955 }
956 }
957
958 #[pallet::hooks]
959 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
960 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
961 let mut meter = WeightMeter::with_limit(limit);
962 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
963 meter.consumed()
964 }
965
966 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
967 block_storage::on_initialize::<T>();
969
970 System::<T>::account_exists(&Pallet::<T>::account_id());
972 <T as Config>::WeightInfo::on_finalize_block_fixed()
974 }
975
976 fn on_finalize(block_number: BlockNumberFor<T>) {
977 block_storage::on_finalize_build_eth_block::<T>(block_number);
979 }
980
981 fn integrity_test() {
982 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
983
984 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
985
986 T::FeeInfo::integrity_test();
987
988 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
990
991 const TOTAL_MEMORY_DEVIDER: u64 = 2;
994
995 let max_block_weight = T::BlockWeights::get()
1001 .get(DispatchClass::Normal)
1002 .max_total
1003 .unwrap_or_else(|| T::BlockWeights::get().max_block);
1004 let max_key_size: u64 =
1005 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
1006 .expect("Key of maximal size shall be created")
1007 .hash()
1008 .len()
1009 .try_into()
1010 .unwrap();
1011
1012 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
1013 let max_immutable_size: u64 = max_block_weight
1014 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
1015 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
1016 ))
1017 .unwrap()
1018 .saturating_mul(
1019 u64::from(limits::IMMUTABLE_BYTES)
1020 .saturating_add(max_immutable_key_size)
1021 .into(),
1022 );
1023
1024 let max_pvf_mem: u64 = T::PVFMemory::get().into();
1025 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
1026
1027 let max_events_size = max_block_weight
1031 .checked_div_per_component(
1032 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
1033 num_topic: 0,
1034 len: limits::EVENT_BYTES,
1035 })
1036 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
1037 &RuntimeCosts::HostFn,
1038 ))),
1039 )
1040 .unwrap()
1041 .saturating_mul(limits::EVENT_BYTES.into());
1042
1043 assert!(
1044 max_events_size <= storage_size_limit,
1045 "Maximal events size {} exceeds the events limit {}",
1046 max_events_size,
1047 storage_size_limit
1048 );
1049
1050 let max_eth_block_builder_bytes =
1085 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1086
1087 log::debug!(
1088 target: LOG_TARGET,
1089 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1090 max_eth_block_builder_bytes / 1024,
1091 max_events_size / 1024,
1092 );
1093
1094 let memory_left = i128::from(max_runtime_mem)
1099 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1100 .saturating_sub(limits::MEMORY_REQUIRED.into())
1101 .saturating_sub(max_eth_block_builder_bytes.into());
1102
1103 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1104
1105 assert!(
1106 memory_left >= 0,
1107 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1108 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1109 );
1110
1111 let max_storage_size = max_block_weight
1114 .checked_div_per_component(
1115 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1116 new_bytes: limits::STORAGE_BYTES,
1117 old_bytes: 0,
1118 kind: StorageAccessKind::Persistent(Warmth::Cold { revertible: true }),
1119 })
1120 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1121 )
1122 .unwrap()
1123 .saturating_add(max_immutable_size.into())
1124 .saturating_add(max_eth_block_builder_bytes.into());
1125
1126 assert!(
1127 max_storage_size <= storage_size_limit,
1128 "Maximal storage size {} exceeds the storage limit {}",
1129 max_storage_size,
1130 storage_size_limit
1131 );
1132 }
1133 }
1134
1135 #[pallet::call]
1136 impl<T: Config> Pallet<T> {
1137 #[allow(unused_variables)]
1150 #[pallet::call_index(0)]
1151 #[pallet::weight(Weight::MAX)]
1152 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1153 Err(frame_system::Error::CallFiltered::<T>.into())
1154 }
1155
1156 #[pallet::call_index(1)]
1173 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1174 pub fn call(
1175 origin: OriginFor<T>,
1176 dest: H160,
1177 #[pallet::compact] value: BalanceOf<T>,
1178 weight_limit: Weight,
1179 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1180 data: Vec<u8>,
1181 ) -> DispatchResultWithPostInfo {
1182 Self::ensure_non_contract_if_signed(&origin)?;
1183 let mut output = Self::bare_call(
1184 origin,
1185 dest,
1186 Pallet::<T>::convert_native_to_evm(value),
1187 TransactionLimits::WeightAndDeposit {
1188 weight_limit,
1189 deposit_limit: storage_deposit_limit,
1190 },
1191 data,
1192 &ExecConfig::new_substrate_tx(),
1193 );
1194
1195 if let Ok(return_value) = &output.result &&
1196 return_value.did_revert()
1197 {
1198 output.result = Err(<Error<T>>::ContractReverted.into());
1199 }
1200 dispatch_result(
1201 output.result,
1202 output.weight_consumed,
1203 <T as Config>::WeightInfo::call(),
1204 )
1205 }
1206
1207 #[pallet::call_index(2)]
1213 #[pallet::weight(
1214 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1215 )]
1216 pub fn instantiate(
1217 origin: OriginFor<T>,
1218 #[pallet::compact] value: BalanceOf<T>,
1219 weight_limit: Weight,
1220 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1221 code_hash: sp_core::H256,
1222 data: Vec<u8>,
1223 salt: Option<[u8; 32]>,
1224 ) -> DispatchResultWithPostInfo {
1225 Self::ensure_non_contract_if_signed(&origin)?;
1226 let data_len = data.len() as u32;
1227 let mut output = Self::bare_instantiate(
1228 origin,
1229 Pallet::<T>::convert_native_to_evm(value),
1230 TransactionLimits::WeightAndDeposit {
1231 weight_limit,
1232 deposit_limit: storage_deposit_limit,
1233 },
1234 Code::Existing(code_hash),
1235 data,
1236 salt,
1237 &ExecConfig::new_substrate_tx(),
1238 );
1239 if let Ok(retval) = &output.result &&
1240 retval.result.did_revert()
1241 {
1242 output.result = Err(<Error<T>>::ContractReverted.into());
1243 }
1244 dispatch_result(
1245 output.result.map(|result| result.result),
1246 output.weight_consumed,
1247 <T as Config>::WeightInfo::instantiate(data_len),
1248 )
1249 }
1250
1251 #[pallet::call_index(3)]
1279 #[pallet::weight(
1280 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1281 .saturating_add(*weight_limit)
1282 )]
1283 pub fn instantiate_with_code(
1284 origin: OriginFor<T>,
1285 #[pallet::compact] value: BalanceOf<T>,
1286 weight_limit: Weight,
1287 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1288 code: Vec<u8>,
1289 data: Vec<u8>,
1290 salt: Option<[u8; 32]>,
1291 ) -> DispatchResultWithPostInfo {
1292 Self::ensure_non_contract_if_signed(&origin)?;
1293 let code_len = code.len() as u32;
1294 let data_len = data.len() as u32;
1295 let mut output = Self::bare_instantiate(
1296 origin,
1297 Pallet::<T>::convert_native_to_evm(value),
1298 TransactionLimits::WeightAndDeposit {
1299 weight_limit,
1300 deposit_limit: storage_deposit_limit,
1301 },
1302 Code::Upload(code),
1303 data,
1304 salt,
1305 &ExecConfig::new_substrate_tx(),
1306 );
1307 if let Ok(retval) = &output.result &&
1308 retval.result.did_revert()
1309 {
1310 output.result = Err(<Error<T>>::ContractReverted.into());
1311 }
1312 dispatch_result(
1313 output.result.map(|result| result.result),
1314 output.weight_consumed,
1315 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1316 )
1317 }
1318
1319 #[pallet::call_index(10)]
1341 #[pallet::weight(
1342 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1343 .saturating_add(*weight_limit)
1344 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1345 )]
1346 pub fn eth_instantiate_with_code(
1347 origin: OriginFor<T>,
1348 value: U256,
1349 weight_limit: Weight,
1350 eth_gas_limit: U256,
1351 code: Vec<u8>,
1352 data: Vec<u8>,
1353 transaction_encoded: Vec<u8>,
1354 effective_gas_price: U256,
1355 encoded_len: u32,
1356 ) -> DispatchResultWithPostInfo {
1357 let signer = Self::ensure_eth_signed(origin)?;
1358 let origin = OriginFor::<T>::signed(signer.clone());
1359 Self::ensure_non_contract_if_signed(&origin)?;
1360 let mut call = Call::<T>::eth_instantiate_with_code {
1361 value,
1362 weight_limit,
1363 eth_gas_limit,
1364 code: code.clone(),
1365 data: data.clone(),
1366 transaction_encoded: transaction_encoded.clone(),
1367 effective_gas_price,
1368 encoded_len,
1369 }
1370 .into();
1371 let info = T::FeeInfo::dispatch_info(&call);
1372 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1373 drop(call);
1374
1375 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1376 let extra_weight = base_info.total_weight();
1377 let output = Self::bare_instantiate(
1378 origin,
1379 value,
1380 TransactionLimits::EthereumGas {
1381 eth_gas_limit: eth_gas_limit.saturated_into(),
1382 weight_limit,
1383 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1384 },
1385 Code::Upload(code),
1386 data,
1387 None,
1388 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1389 );
1390
1391 block_storage::EthereumCallResult::new::<T>(
1392 signer,
1393 output.map_result(|r| r.result),
1394 base_info.call_weight,
1395 encoded_len,
1396 &info,
1397 effective_gas_price,
1398 )
1399 })
1400 }
1401
1402 #[pallet::call_index(11)]
1419 #[pallet::weight(
1420 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1421 .saturating_add(*weight_limit)
1422 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1423 )]
1424 pub fn eth_call(
1425 origin: OriginFor<T>,
1426 dest: H160,
1427 value: U256,
1428 weight_limit: Weight,
1429 eth_gas_limit: U256,
1430 data: Vec<u8>,
1431 transaction_encoded: Vec<u8>,
1432 effective_gas_price: U256,
1433 encoded_len: u32,
1434 ) -> DispatchResultWithPostInfo {
1435 let signer = Self::ensure_eth_signed(origin)?;
1436 let origin = OriginFor::<T>::signed(signer.clone());
1437
1438 Self::ensure_non_contract_if_signed(&origin)?;
1439 let mut call = Call::<T>::eth_call {
1440 dest,
1441 value,
1442 weight_limit,
1443 eth_gas_limit,
1444 data: data.clone(),
1445 transaction_encoded: transaction_encoded.clone(),
1446 effective_gas_price,
1447 encoded_len,
1448 }
1449 .into();
1450 let info = T::FeeInfo::dispatch_info(&call);
1451 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1452 drop(call);
1453
1454 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1455 let extra_weight = base_info.total_weight();
1456 let output = Self::bare_call(
1457 origin,
1458 dest,
1459 value,
1460 TransactionLimits::EthereumGas {
1461 eth_gas_limit: eth_gas_limit.saturated_into(),
1462 weight_limit,
1463 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1464 },
1465 data,
1466 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1467 );
1468
1469 block_storage::EthereumCallResult::new::<T>(
1470 signer,
1471 output,
1472 base_info.call_weight,
1473 encoded_len,
1474 &info,
1475 effective_gas_price,
1476 )
1477 })
1478 }
1479
1480 #[pallet::call_index(12)]
1491 #[pallet::weight(
1492 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32)
1493 .saturating_add(call.get_dispatch_info().call_weight)
1494 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1495 )]
1496 pub fn eth_substrate_call(
1497 origin: OriginFor<T>,
1498 call: Box<<T as Config>::RuntimeCall>,
1499 transaction_encoded: Vec<u8>,
1500 ) -> DispatchResultWithPostInfo {
1501 let signer = Self::ensure_eth_signed(origin)?;
1504 Self::ensure_non_contract_if_signed(&OriginFor::<T>::signed(signer.clone()))?;
1505 let tx_len = transaction_encoded.len() as u32;
1506 let weight_overhead = T::WeightInfo::eth_substrate_call(tx_len)
1507 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(tx_len));
1508
1509 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1510 let call_weight = call.get_dispatch_info().call_weight;
1511 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1512
1513 match &mut call_result {
1515 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1516 post_info.actual_weight = Some(
1517 post_info
1518 .actual_weight
1519 .unwrap_or_else(|| call_weight)
1520 .saturating_add(weight_overhead),
1521 );
1522 },
1523 }
1524
1525 block_storage::EthereumCallResult {
1528 receipt_gas_info: ReceiptGasInfo::default(),
1529 result: call_result,
1530 }
1531 })
1532 }
1533
1534 #[pallet::call_index(4)]
1549 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1550 pub fn upload_code(
1551 origin: OriginFor<T>,
1552 code: Vec<u8>,
1553 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1554 ) -> DispatchResult {
1555 Self::ensure_non_contract_if_signed(&origin)?;
1556 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1557 }
1558
1559 #[pallet::call_index(5)]
1564 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1565 pub fn remove_code(
1566 origin: OriginFor<T>,
1567 code_hash: sp_core::H256,
1568 ) -> DispatchResultWithPostInfo {
1569 let origin = ensure_signed(origin)?;
1570 <ContractBlob<T>>::remove(&origin, code_hash)?;
1571 Ok(Pays::No.into())
1573 }
1574
1575 #[pallet::call_index(6)]
1586 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1587 pub fn set_code(
1588 origin: OriginFor<T>,
1589 dest: H160,
1590 code_hash: sp_core::H256,
1591 ) -> DispatchResult {
1592 ensure_root(origin)?;
1593 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1594 let Some(account) = account else {
1595 return Err(<Error<T>>::ContractNotFound.into());
1596 };
1597
1598 let AccountType::Contract(ref mut contract) = account.account_type else {
1599 return Err(<Error<T>>::ContractNotFound.into());
1600 };
1601
1602 <CodeInfo<T>>::increment_refcount(code_hash)?;
1603 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1604 contract.code_hash = code_hash;
1605
1606 Ok(())
1607 })
1608 }
1609
1610 #[pallet::call_index(7)]
1618 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1619 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1620 #[cfg(not(feature = "runtime-benchmarks"))]
1621 if T::AutoMap::get() {
1622 return Ok(());
1623 }
1624
1625 Self::ensure_non_contract_if_signed(&origin)?;
1626 let origin = ensure_signed(origin)?;
1627 T::AddressMapper::map(&origin)
1628 }
1629
1630 #[pallet::call_index(13)]
1632 #[pallet::weight(<T as Config>::WeightInfo::batch_map_accounts(accounts.len().saturated_into::<u32>()))]
1633 pub fn batch_map_accounts(
1634 origin: OriginFor<T>,
1635 accounts: Vec<T::AccountId>,
1636 ) -> DispatchResultWithPostInfo {
1637 ensure_signed(origin.clone())?;
1638 Self::ensure_non_contract_if_signed(&origin)?;
1639
1640 let total: u32 = accounts.len().saturated_into();
1641 let mut mapped = 0;
1642
1643 for account_id in accounts
1644 .iter()
1645 .filter(|&a| !T::AddressMapper::is_eth_derived(a))
1647 .filter(|&a| frame_system::Pallet::<T>::account_exists(a))
1650 {
1651 let mut useful = false;
1652
1653 match T::AddressMapper::map_no_deposit_unchecked(account_id) {
1654 Ok(()) => {
1655 useful = true;
1656 },
1657 Err(err) => log::debug!(
1658 target: LOG_TARGET,
1659 "Failed to map account {account_id:?}: {err:?}",
1660 ),
1661 }
1662
1663 match T::Currency::release_all(
1664 &HoldReason::AddressMapping.into(),
1665 account_id,
1666 Precision::BestEffort,
1667 ) {
1668 Ok(released) if !released.is_zero() => {
1671 useful = true;
1672 },
1673 Ok(_) => {},
1674 Err(err) => log::debug!(
1675 target: LOG_TARGET,
1676 "Failed to release mapping deposit for {account_id:?}: {err:?}",
1677 ),
1678 }
1679
1680 if useful {
1681 mapped = mapped.saturating_add(1);
1682 }
1683 }
1684
1685 if total == 0 || mapped == 0 {
1687 return Ok(Pays::Yes.into());
1688 }
1689
1690 let proportion_mapped = Perbill::from_rational(mapped, total);
1691 if proportion_mapped >= Perbill::from_percent(90) {
1692 Ok(Pays::No.into())
1693 } else {
1694 Ok(Pays::Yes.into())
1695 }
1696 }
1697
1698 #[pallet::call_index(8)]
1706 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1707 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1708 #[cfg(not(feature = "runtime-benchmarks"))]
1709 ensure!(!T::AutoMap::get(), <Error<T>>::AutoMappingEnabled);
1710 let origin = ensure_signed(origin)?;
1711 T::AddressMapper::unmap(&origin)
1712 }
1713
1714 #[pallet::call_index(9)]
1720 #[pallet::weight({
1721 let dispatch_info = call.get_dispatch_info();
1722 (
1723 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1724 dispatch_info.class
1725 )
1726 })]
1727 pub fn dispatch_as_fallback_account(
1728 mut origin: OriginFor<T>,
1729 call: Box<<T as Config>::RuntimeCall>,
1730 ) -> DispatchResultWithPostInfo {
1731 Self::ensure_non_contract_if_signed(&origin)?;
1732 let account_id = origin.as_signer().ok_or(DispatchError::BadOrigin)?;
1733 let unmapped_account = T::AddressMapper::to_fallback_account_id(
1734 &T::AddressMapper::to_address(&account_id),
1735 );
1736 origin.set_caller_from(RawOrigin::Signed(unmapped_account));
1737 call.dispatch(origin)
1738 }
1739 }
1740}
1741
1742fn dispatch_result<R>(
1744 result: Result<R, DispatchError>,
1745 weight_consumed: Weight,
1746 base_weight: Weight,
1747) -> DispatchResultWithPostInfo {
1748 let post_info = PostDispatchInfo {
1749 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1750 pays_fee: Default::default(),
1751 };
1752
1753 result
1754 .map(|_| post_info)
1755 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1756}
1757
1758impl<T: Config> Pallet<T> {
1759 pub fn bare_call(
1766 origin: OriginFor<T>,
1767 dest: H160,
1768 evm_value: U256,
1769 transaction_limits: TransactionLimits<T>,
1770 data: Vec<u8>,
1771 exec_config: &ExecConfig<T>,
1772 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1773 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1774 Ok(transaction_meter) => transaction_meter,
1775 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1776 };
1777 let mut storage_deposit = Default::default();
1778
1779 let try_call = || {
1780 let origin = ExecOrigin::from_runtime_origin(origin)?;
1781 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1782 origin.clone(),
1783 dest,
1784 &mut transaction_meter,
1785 evm_value,
1786 data,
1787 &exec_config,
1788 )?;
1789
1790 storage_deposit = transaction_meter
1791 .execute_postponed_deposits(&origin, &exec_config)
1792 .inspect_err(|err| {
1793 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1794 })?;
1795
1796 Ok(result)
1797 };
1798 let result = Self::run_guarded(try_call);
1799
1800 log::trace!(target: LOG_TARGET, "Bare call ends: \
1801 result={result:?}, \
1802 weight_consumed={:?}, \
1803 weight_required={:?}, \
1804 storage_deposit={:?}, \
1805 gas_consumed={:?}, \
1806 max_storage_deposit={:?}",
1807 transaction_meter.weight_consumed(),
1808 transaction_meter.weight_required(),
1809 storage_deposit,
1810 transaction_meter.total_consumed_gas(),
1811 transaction_meter.deposit_required()
1812 );
1813
1814 ContractResult {
1815 result: result.map_err(|r| r.error),
1816 weight_consumed: transaction_meter.weight_consumed(),
1817 weight_required: transaction_meter.weight_required(),
1818 storage_deposit,
1819 gas_consumed: transaction_meter.total_consumed_gas(),
1820 max_storage_deposit: transaction_meter.deposit_required(),
1821 }
1822 }
1823
1824 pub fn prepare_dry_run(account: &T::AccountId) {
1830 frame_system::Pallet::<T>::inc_account_nonce(account);
1833
1834 if !T::AddressMapper::is_mapped(account) {
1837 let _ = T::AddressMapper::map_no_deposit_unchecked(account);
1838 }
1839 }
1840
1841 pub fn bare_instantiate(
1847 origin: OriginFor<T>,
1848 evm_value: U256,
1849 transaction_limits: TransactionLimits<T>,
1850 code: Code,
1851 data: Vec<u8>,
1852 salt: Option<[u8; 32]>,
1853 exec_config: &ExecConfig<T>,
1854 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1855 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1856 Ok(transaction_meter) => transaction_meter,
1857 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1858 };
1859
1860 let mut storage_deposit = Default::default();
1861
1862 let try_instantiate = || {
1863 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1864
1865 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1866 let executable = match code {
1867 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1868 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1869 let executable = Self::try_upload_code(
1870 upload_account,
1871 code,
1872 BytecodeType::Pvm,
1873 &mut transaction_meter,
1874 &exec_config,
1875 )?;
1876 executable
1877 },
1878 Code::Upload(code) => {
1879 if T::AllowEVMBytecode::get() {
1880 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1881 let origin = T::UploadOrigin::ensure_origin(origin)?;
1882 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1883 executable
1884 } else {
1885 return Err(<Error<T>>::CodeRejected.into());
1886 }
1887 },
1888 Code::Existing(code_hash) => {
1889 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1890 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1891 executable
1892 },
1893 };
1894 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1895 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1896 instantiate_account,
1897 executable,
1898 &mut transaction_meter,
1899 evm_value,
1900 data,
1901 salt.as_ref(),
1902 &exec_config,
1903 );
1904
1905 storage_deposit = transaction_meter
1906 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1907 .inspect_err(|err| {
1908 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1909 })?;
1910 result
1911 };
1912 let output = Self::run_guarded(try_instantiate);
1913
1914 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1915 weight_required={:?} \
1916 storage_deposit={:?} \
1917 gas_consumed={:?} \
1918 max_storage_deposit={:?}",
1919 transaction_meter.weight_consumed(),
1920 transaction_meter.weight_required(),
1921 storage_deposit,
1922 transaction_meter.total_consumed_gas(),
1923 transaction_meter.deposit_required()
1924 );
1925
1926 ContractResult {
1927 result: output
1928 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1929 .map_err(|e| e.error),
1930 weight_consumed: transaction_meter.weight_consumed(),
1931 weight_required: transaction_meter.weight_required(),
1932 storage_deposit,
1933 gas_consumed: transaction_meter.total_consumed_gas(),
1934 max_storage_deposit: transaction_meter.deposit_required(),
1935 }
1936 }
1937
1938 pub fn eth_estimate_gas(
1950 tx: GenericTransaction,
1951 config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1952 ) -> Result<U256, EthTransactError>
1953 where
1954 T::Nonce: Into<U256> + TryFrom<U256>,
1955 CallOf<T>: SetWeightLimit,
1956 {
1957 log::debug!(target: LOG_TARGET, "eth_estimate_gas: {tx:?}");
1958
1959 let mut low = U256::zero();
1960 let mut high = Self::evm_block_gas_limit();
1961
1962 log::trace!(target: LOG_TARGET, "eth_estimate_gas starting with low={low}, high={high}");
1963
1964 let perform_balance_checks = if let Some(gas_limit) = tx.gas {
1968 high = gas_limit;
1969 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the gas limit high={high}");
1970 true
1971 } else {
1972 false
1973 };
1974
1975 let fee_cap = tx.max_fee_per_gas.or(tx.gas_price);
1977 if let (Some(fee_cap), Some(from), true) = (fee_cap, tx.from, perform_balance_checks) {
1978 let mut available_balance = Self::evm_balance(&from);
1979 if let Some(value) = tx.value {
1980 available_balance = available_balance.checked_sub(value).ok_or_else(|| {
1981 EthTransactError::Message("insufficient funds for value transfer".into())
1982 })?;
1983 }
1984 if let Some(allowance) = available_balance.checked_div(fee_cap) {
1985 if high > allowance && allowance != U256::zero() {
1986 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the user's allowance high={high} allowance={allowance}");
1987 high = allowance
1988 }
1989 }
1990 }
1991
1992 let dry_run_at = |gas: U256| {
1996 let mut transaction = tx.clone();
1997 transaction.gas = Some(gas);
1998 let dry_run_config = config.clone().with_perform_balance_checks(perform_balance_checks);
1999 with_transaction(|| {
2000 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
2001 transaction,
2002 dry_run_config,
2003 )))
2004 })
2005 .expect("Rollback shouldn't error out")
2006 };
2007
2008 let is_simple_transfer = with_transaction(|| {
2011 let probe = config
2012 .state_overrides
2013 .clone()
2014 .map_or(Ok(()), state_overrides::apply_state_overrides::<T>)
2015 .map(|()| Self::is_simple_transfer(&tx));
2016 TransactionOutcome::Rollback(Ok::<_, DispatchError>(probe))
2017 })
2018 .expect("Rollback shouldn't error out")?;
2019
2020 if is_simple_transfer {
2021 let dry_run_result = dry_run_at(high)?;
2022 log::trace!(
2023 target: LOG_TARGET,
2024 "eth_estimate_gas short-circuited simple transfer to {:?} with eth_gas={}",
2025 tx.to,
2026 dry_run_result.eth_gas,
2027 );
2028 return Ok(dry_run_result.eth_gas);
2029 }
2030
2031 let dry_run_results = [high, Self::evm_max_extrinsic_weight_in_gas()]
2036 .map(|gas_limit| (gas_limit, dry_run_at(gas_limit)));
2037 let (gas_limit, first_dry_run_result) = match dry_run_results {
2038 [(gas_limit1, Ok(dry_run_result1)), (gas_limit2, Ok(dry_run_result2))] => {
2039 if dry_run_result2.eth_gas >= gas_limit2 {
2040 (gas_limit1, dry_run_result1)
2041 } else {
2042 (gas_limit2, dry_run_result2)
2043 }
2044 },
2045 [(gas_limit, Ok(dry_run_result)), (_, Err(_))] |
2046 [(_, Err(_)), (gas_limit, Ok(dry_run_result))] => (gas_limit, dry_run_result),
2047 [(_, Err(err)), (_, Err(..))] => return Err(err),
2048 };
2049 log::trace!(
2050 target: LOG_TARGET,
2051 "eth_estimate_gas first dry run succeeded with gas_limit={} consumed={}",
2052 gas_limit,
2053 first_dry_run_result.eth_gas
2054 );
2055 low = first_dry_run_result.eth_gas;
2056 high = gas_limit;
2057
2058 while low + U256::one() < high {
2059 log::trace!(target: LOG_TARGET, "eth_estimate_gas estimation iteration with low={low} high={high}");
2060 let error_ratio = high
2061 .checked_sub(low)
2062 .and_then(|value| value.checked_mul(U256::from(1000)))
2063 .and_then(|value| value.checked_div(high))
2064 .ok_or_else(|| {
2065 EthTransactError::Message(
2066 "failed to calculate error ratio in gas estimation".into(),
2067 )
2068 })?;
2069 if error_ratio <= U256::from(15) {
2070 log::trace!(
2071 target: LOG_TARGET,
2072 "eth_estimate_gas finished due to error ratio being less than 1.5% high={}",
2073 high
2074 );
2075 break;
2076 }
2077
2078 let mut midpoint = high
2079 .checked_sub(low)
2080 .and_then(|value| value.checked_div(U256::from(2)))
2081 .and_then(|value| value.checked_add(low))
2082 .ok_or_else(|| {
2083 EthTransactError::Message(
2084 "failed to calculate midpoint in gas estimation".into(),
2085 )
2086 })?;
2087
2088 if let Some(other_midpoint) = low.checked_mul(U256::from(2)) {
2089 if other_midpoint != U256::zero() {
2090 midpoint = midpoint.min(other_midpoint)
2091 }
2092 };
2093
2094 let dry_run_result = dry_run_at(midpoint);
2095 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run result with midpoint={midpoint} is dry_run_result={dry_run_result:?}");
2096 match dry_run_result {
2097 Ok(..) => {
2098 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run succeeded, new high={midpoint}");
2099 high = midpoint
2100 },
2101 Err(..) => {
2102 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run failed, new low={midpoint}");
2103 low = midpoint
2104 },
2105 }
2106 }
2107
2108 log::trace!(target: LOG_TARGET, "eth_estimate_gas completed. high={high}");
2109 Ok(high)
2110 }
2111
2112 pub(crate) fn is_simple_transfer(tx: &GenericTransaction) -> bool {
2114 tx.to
2115 .map(|to| tx.has_simple_transfer_fields() && Self::address_runs_no_code(&to))
2116 .unwrap_or(false)
2117 }
2118
2119 fn address_runs_no_code(address: &H160) -> bool {
2122 *address != RUNTIME_PALLETS_ADDR &&
2125 !exec::is_precompile::<T, ContractBlob<T>>(address) &&
2126 !<AccountInfo<T>>::is_contract(address)
2127 }
2128
2129 pub fn eth_pre_dispatch_weight(transaction_encoded: Vec<u8>) -> Result<Weight, EthTransactError>
2137 where
2138 CallOf<T>: SetWeightLimit,
2139 {
2140 let signed_tx =
2141 crate::evm::TransactionSigned::decode(&transaction_encoded).map_err(|err| {
2142 EthTransactError::Message(format!("Failed to decode transaction: {err:?}"))
2143 })?;
2144 let signer_addr = signed_tx.recover_eth_address().map_err(|err| {
2145 EthTransactError::Message(format!("Failed to recover signer: {err:?}"))
2146 })?;
2147 let tx =
2148 GenericTransaction::from_signed(signed_tx, Self::evm_base_fee(), Some(signer_addr));
2149 let encoded_len = T::FeeInfo::encoded_len(
2150 crate::Call::<T>::eth_transact { payload: transaction_encoded.clone() }.into(),
2151 );
2152 let call_info = tx
2153 .into_call::<T>(CreateCallMode::ExtrinsicExecution(encoded_len, transaction_encoded))
2154 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2155 let info = T::FeeInfo::dispatch_info(&call_info.call);
2156
2157 Ok(frame_system::calculate_consumed_extrinsic_weight::<CallOf<T>>(
2158 &T::BlockWeights::get(),
2159 &info,
2160 call_info.encoded_len as usize,
2161 ))
2162 }
2163
2164 pub fn dry_run_eth_transact(
2170 mut tx: GenericTransaction,
2171 mut dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
2172 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
2173 where
2174 T::Nonce: Into<U256> + TryFrom<U256>,
2175 CallOf<T>: SetWeightLimit,
2176 {
2177 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
2178
2179 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
2180 Self::prepare_dry_run(&origin);
2181
2182 if let Some(overrides) = dry_run_config.state_overrides.take() {
2183 state_overrides::apply_state_overrides::<T>(overrides)?;
2184 }
2185
2186 let base_fee = Self::evm_base_fee();
2187 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
2188
2189 if effective_gas_price < base_fee {
2190 Err(EthTransactError::Message(format!(
2191 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
2192 )))?;
2193 }
2194
2195 if tx.nonce.is_none() {
2196 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
2197 }
2198 if tx.chain_id.is_none() {
2199 tx.chain_id = Some(T::ChainId::get().into());
2200 }
2201
2202 tx.gas_price = Some(effective_gas_price);
2204 tx.max_priority_fee_per_gas = Some(0.into());
2207 if tx.max_fee_per_gas.is_none() {
2208 tx.max_fee_per_gas = Some(effective_gas_price);
2209 }
2210
2211 let gas = tx.gas;
2212 if tx.gas.is_none() {
2213 tx.gas = Some(Self::evm_block_gas_limit());
2214 }
2215 if tx.r#type.is_none() {
2216 tx.r#type = Some(TYPE_EIP1559.into());
2217 }
2218
2219 let value = tx.value.unwrap_or_default();
2221 let input = tx.input.clone().to_vec();
2222 let from = tx.from;
2223 let to = tx.to;
2224
2225 let mut call_info = tx
2228 .into_call::<T>(CreateCallMode::DryRun)
2229 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2230
2231 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
2235 let base_weight = base_info.total_weight();
2236 let perform_balance_checks = dry_run_config.perform_balance_checks;
2237 let exec_config =
2238 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
2239 .with_dry_run(dry_run_config);
2240
2241 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
2243 if let Some(from) = &from {
2244 let fees = if gas.is_some() && matches!(perform_balance_checks, Some(true)) {
2245 fees
2246 } else {
2247 Zero::zero()
2248 };
2249 let balance = Self::evm_balance(from);
2250 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
2251 return Err(EthTransactError::Message(format!(
2252 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
2253 )));
2254 }
2255 }
2256
2257 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
2260
2261 let extract_error = |err| {
2262 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
2263 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
2264 } else {
2265 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
2266 }
2267 };
2268
2269 let transaction_limits = TransactionLimits::EthereumGas {
2270 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
2271 weight_limit: Self::evm_max_extrinsic_weight(),
2272 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
2273 };
2274
2275 let mut dry_run = match to {
2277 Some(dest) => {
2279 if dest == RUNTIME_PALLETS_ADDR {
2280 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
2281 return Err(EthTransactError::Message(format!(
2282 "Failed to decode pallet-call {input:?}"
2283 )));
2284 };
2285
2286 if let Err(result) =
2287 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
2288 {
2289 return Err(EthTransactError::Message(format!(
2290 "Failed to dispatch call: {:?}",
2291 result.error,
2292 )));
2293 };
2294
2295 Default::default()
2296 } else {
2297 let result = crate::Pallet::<T>::bare_call(
2299 OriginFor::<T>::signed(origin),
2300 dest,
2301 value,
2302 transaction_limits,
2303 input.clone(),
2304 &exec_config,
2305 );
2306
2307 let data = match result.result {
2308 Ok(return_value) => {
2309 if return_value.did_revert() {
2310 return Err(EthTransactError::Data(return_value.data));
2311 }
2312 return_value.data
2313 },
2314 Err(err) => {
2315 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
2316 return extract_error(err);
2317 },
2318 };
2319
2320 EthTransactInfo {
2321 weight_required: result.weight_required,
2322 storage_deposit: result.storage_deposit.charge_or_zero(),
2323 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2324 data,
2325 eth_gas: Default::default(),
2326 }
2327 }
2328 },
2329 None => {
2331 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2333 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
2334 } else {
2335 (input, vec![])
2336 };
2337
2338 let result = crate::Pallet::<T>::bare_instantiate(
2340 OriginFor::<T>::signed(origin),
2341 value,
2342 transaction_limits,
2343 Code::Upload(code.clone()),
2344 data.clone(),
2345 None,
2346 &exec_config,
2347 );
2348
2349 let returned_data = match result.result {
2350 Ok(return_value) => {
2351 if return_value.result.did_revert() {
2352 return Err(EthTransactError::Data(return_value.result.data));
2353 }
2354 return_value.result.data
2355 },
2356 Err(err) => {
2357 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
2358 return extract_error(err);
2359 },
2360 };
2361
2362 EthTransactInfo {
2363 weight_required: result.weight_required,
2364 storage_deposit: result.storage_deposit.charge_or_zero(),
2365 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2366 data: returned_data,
2367 eth_gas: Default::default(),
2368 }
2369 },
2370 };
2371
2372 call_info.call.set_weight_limit(dry_run.weight_required);
2374
2375 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
2377 let max_weight = Self::evm_max_extrinsic_weight();
2378 if total_weight.any_gt(max_weight) {
2379 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
2380 total_weight={total_weight:?} \
2381 max_weight={max_weight:?}",
2382 );
2383
2384 Err(EthTransactError::Message(format!(
2385 "\
2386 The transaction consumes more than the allowed weight. \
2387 needed={total_weight} \
2388 allowed={max_weight} \
2389 overweight_by={}\
2390 ",
2391 total_weight.saturating_sub(max_weight),
2392 )))?;
2393 }
2394
2395 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
2397 let available_fee = T::FeeInfo::remaining_txfee();
2398 if transaction_fee > available_fee {
2399 Err(EthTransactError::Message(format!(
2400 "Not enough gas supplied: Off by: {:?}",
2401 transaction_fee.saturating_sub(available_fee),
2402 )))?;
2403 }
2404
2405 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2406 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2407 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2408 if !rest.is_zero() {
2409 eth_gas = eth_gas.saturating_add(1_u32.into());
2410 }
2411
2412 log::debug!(target: LOG_TARGET, "\
2413 dry_run_eth_transact finished: \
2414 weight_limit={}, \
2415 total_weight={total_weight}, \
2416 max_weight={max_weight}, \
2417 weight_left={}, \
2418 eth_gas={eth_gas}, \
2419 encoded_len={}, \
2420 tx_fee={transaction_fee:?}, \
2421 storage_deposit={:?}, \
2422 max_storage_deposit={:?}\
2423 ",
2424 dry_run.weight_required,
2425 max_weight.saturating_sub(total_weight),
2426 call_info.encoded_len,
2427 dry_run.storage_deposit,
2428 dry_run.max_storage_deposit,
2429
2430 );
2431 dry_run.eth_gas = eth_gas;
2432 Ok(dry_run)
2433 }
2434
2435 pub fn evm_balance(address: &H160) -> U256 {
2439 let balance = AccountInfo::<T>::balance_of((*address).into());
2440 Self::convert_native_to_evm(balance)
2441 }
2442
2443 pub fn eth_block() -> EthBlock {
2445 EthereumBlock::<T>::get()
2446 }
2447
2448 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2455 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2456 let hash = <BlockHash<T>>::get(number);
2457 if hash == H256::zero() { None } else { Some(hash) }
2458 }
2459
2460 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2462 ReceiptInfoData::<T>::get()
2463 }
2464
2465 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2471 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2472 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2473 let account_id = T::AddressMapper::to_account_id(&address);
2474 T::Currency::set_balance(&account_id, balance);
2475 AccountInfoOf::<T>::mutate(&address, |account| {
2476 if let Some(account) = account {
2477 account.dust = dust;
2478 } else {
2479 *account = Some(AccountInfo { dust, ..Default::default() });
2480 }
2481 });
2482
2483 Ok(())
2484 }
2485
2486 pub fn new_balance_with_dust(
2490 evm_value: U256,
2491 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2492 let ed = T::Currency::minimum_balance();
2493 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2494 let (value, dust) = balance_with_dust.deconstruct();
2495
2496 Ok((ed.saturating_add(value), dust))
2497 }
2498
2499 pub fn evm_nonce(address: &H160) -> u32
2501 where
2502 T::Nonce: Into<u32>,
2503 {
2504 let account = T::AddressMapper::to_account_id(&address);
2505 System::<T>::account_nonce(account).into()
2506 }
2507
2508 pub fn evm_block_gas_limit() -> U256 {
2510 u64::MAX.into()
2517 }
2518
2519 pub fn evm_max_extrinsic_weight_in_gas() -> U256 {
2521 let max_extrinsic_fee = T::FeeInfo::weight_to_fee(&Self::evm_max_extrinsic_weight());
2522 let gas_scale: BalanceOf<T> = T::GasScale::get().into();
2523 (max_extrinsic_fee / gas_scale).into()
2524 }
2525
2526 pub fn evm_max_extrinsic_weight() -> Weight {
2528 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2529 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2530 .get(DispatchClass::Normal)
2531 .max_extrinsic
2532 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2533 Weight::from_parts(
2534 factor.saturating_mul_int(max_weight.ref_time()),
2535 factor.saturating_mul_int(max_weight.proof_size()),
2536 )
2537 }
2538
2539 pub fn evm_base_fee() -> U256 {
2541 let gas_scale = <T as Config>::GasScale::get();
2542 let multiplier = T::FeeInfo::next_fee_multiplier();
2543 multiplier
2544 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2545 .saturating_mul(gas_scale.saturated_into())
2546 .into()
2547 }
2548
2549 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2551 where
2552 T::Nonce: Into<u32>,
2553 {
2554 match tracer_type {
2555 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2556 TracerType::PrestateTracer(config) => {
2557 PrestateTracer::new(config.unwrap_or_default()).into()
2558 },
2559 TracerType::ExecutionTracer(config) => {
2560 ExecutionTracer::new(config.unwrap_or_default()).into()
2561 },
2562 }
2563 }
2564
2565 pub fn bare_upload_code(
2569 origin: OriginFor<T>,
2570 code: Vec<u8>,
2571 storage_deposit_limit: BalanceOf<T>,
2572 ) -> CodeUploadResult<BalanceOf<T>> {
2573 let origin = T::UploadOrigin::ensure_origin(origin)?;
2574
2575 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2576 BytecodeType::Pvm
2577 } else {
2578 if !T::AllowEVMBytecode::get() {
2579 return Err(<Error<T>>::CodeRejected.into());
2580 }
2581 BytecodeType::Evm
2582 };
2583
2584 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2585 weight_limit: Default::default(),
2586 deposit_limit: storage_deposit_limit,
2587 })?;
2588
2589 let module = Self::try_upload_code(
2590 origin,
2591 code,
2592 bytecode_type,
2593 &mut meter,
2594 &ExecConfig::new_substrate_tx(),
2595 )?;
2596 Ok(CodeUploadReturnValue {
2597 code_hash: *module.code_hash(),
2598 deposit: meter.deposit_consumed().charge_or_zero(),
2599 })
2600 }
2601
2602 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2604 let contract_info =
2605 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2606
2607 let maybe_value = contract_info.read(&Key::from_fixed(key));
2608 Ok(maybe_value)
2609 }
2610
2611 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2615 let immutable_data = <ImmutableDataOf<T>>::get(address);
2616 immutable_data
2617 }
2618
2619 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2627 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2628 <ImmutableDataOf<T>>::insert(address, data);
2629 Ok(())
2630 }
2631
2632 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2634 let contract_info =
2635 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2636
2637 let maybe_value = contract_info.read(
2638 &Key::try_from_var(key)
2639 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2640 .into(),
2641 );
2642 Ok(maybe_value)
2643 }
2644
2645 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2647 let (value, dust) = value.into().deconstruct();
2648 value
2649 .into()
2650 .saturating_mul(T::NativeToEthRatio::get().into())
2651 .saturating_add(dust.into())
2652 }
2653
2654 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2664 let contract_info =
2665 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2666
2667 contract_info
2668 .write(&Key::from_fixed(key), value, None, false)
2669 .map_err(ContractAccessError::StorageWriteFailed)
2670 }
2671
2672 pub fn set_storage_var_key(
2683 address: H160,
2684 key: Vec<u8>,
2685 value: Option<Vec<u8>>,
2686 ) -> SetStorageResult {
2687 let contract_info =
2688 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2689
2690 contract_info
2691 .write(
2692 &Key::try_from_var(key)
2693 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2694 .into(),
2695 value,
2696 None,
2697 false,
2698 )
2699 .map_err(ContractAccessError::StorageWriteFailed)
2700 }
2701
2702 pub fn account_id() -> T::AccountId {
2704 use frame_support::PalletId;
2705 use sp_runtime::traits::AccountIdConversion;
2706 PalletId(*b"py/reviv").into_account_truncating()
2707 }
2708
2709 pub fn block_author() -> H160 {
2711 use frame_support::traits::FindAuthor;
2712
2713 let digest = <frame_system::Pallet<T>>::digest();
2714 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2715
2716 T::FindAuthor::find_author(pre_runtime_digests)
2717 .map(|account_id| T::AddressMapper::to_address(&account_id))
2718 .unwrap_or_default()
2719 }
2720
2721 pub fn code(address: &H160) -> Vec<u8> {
2725 use precompiles::{All, Precompiles};
2726 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2727 return code.into();
2728 }
2729 AccountInfo::<T>::load_contract(&address)
2730 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2731 .map(|code| code.into())
2732 .unwrap_or_default()
2733 }
2734
2735 pub fn try_upload_code(
2737 origin: T::AccountId,
2738 code: Vec<u8>,
2739 code_type: BytecodeType,
2740 meter: &mut TransactionMeter<T>,
2741 exec_config: &ExecConfig<T>,
2742 ) -> Result<ContractBlob<T>, DispatchError> {
2743 let mut module = match code_type {
2744 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2745 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2746 };
2747 module.store_code(exec_config, meter)?;
2748 Ok(module)
2749 }
2750
2751 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2753 executing_contract::using_once(&mut false, || {
2754 executing_contract::with(|f| {
2755 if *f {
2757 return Err(())
2758 }
2759 *f = true;
2761 Ok(())
2762 })
2763 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2764 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2765 .map(|_| f())
2766 .and_then(|r| r)
2767 })
2768 }
2769
2770 fn charge_deposit(
2775 hold_reason: HoldReason,
2776 from: &T::AccountId,
2777 to: &T::AccountId,
2778 amount: BalanceOf<T>,
2779 exec_config: &ExecConfig<T>,
2780 ) -> DispatchResult {
2781 if amount.is_zero() {
2782 return Ok(());
2783 }
2784
2785 T::Deposit::charge_and_hold(hold_reason, exec_config.funds(from), to, amount)
2786 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2787 Ok(())
2788 }
2789
2790 fn refund_deposit(
2795 hold_reason: HoldReason,
2796 from: &T::AccountId,
2797 dst: deposit_payment::Funds<T::AccountId>,
2798 amount: BalanceOf<T>,
2799 ) -> Result<(), DispatchError> {
2800 if amount.is_zero() {
2801 return Ok(());
2802 }
2803
2804 let to = match &dst {
2805 deposit_payment::Funds::Balance(to) | deposit_payment::Funds::TxFee(to) => *to,
2806 };
2807 let result = T::Deposit::refund_on_hold(hold_reason, from, dst, amount);
2808
2809 result.defensive_map_err(|err| {
2810 let available = T::Deposit::total_on_hold(hold_reason, from);
2811 if available < amount {
2812 log::error!(
2815 target: LOG_TARGET,
2816 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}. Not enough deposit: {available:?}. This is a bug.",
2817 );
2818 Error::<T>::StorageRefundNotEnoughFunds.into()
2819 } else {
2820 log::warn!(
2825 target: LOG_TARGET,
2826 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}: {err:?}. First remove locks (staking, governance) from the contracts account.",
2827 );
2828 Error::<T>::StorageRefundLocked.into()
2829 }
2830 })
2831 }
2832
2833 fn has_dust(value: U256) -> bool {
2835 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2836 }
2837
2838 fn has_balance(value: U256) -> bool {
2840 value >= U256::from(<T>::NativeToEthRatio::get())
2841 }
2842
2843 #[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime", test))]
2845 fn min_balance() -> BalanceOf<T> {
2846 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2847 }
2848
2849 fn deposit_event(event: Event<T>) {
2854 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2855 }
2856
2857 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2859 match <T as Config>::RuntimeOrigin::from(origin).into() {
2860 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2861 _ => Err(BadOrigin.into()),
2862 }
2863 }
2864
2865 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2869 if DebugSettings::bypass_eip_3607::<T>() {
2870 return Ok(());
2871 }
2872 let Some(address) = origin
2873 .as_system_ref()
2874 .and_then(|o| o.as_signed())
2875 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2876 else {
2877 return Ok(());
2878 };
2879 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2880 <AccountInfo<T>>::is_contract(&address)
2881 {
2882 log::debug!(
2883 target: crate::LOG_TARGET,
2884 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2885 );
2886 Err(DispatchError::BadOrigin)
2887 } else {
2888 Ok(())
2889 }
2890 }
2891}
2892
2893pub const RUNTIME_PALLETS_ADDR: H160 =
2898 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2899
2900environmental!(executing_contract: bool);
2902
2903sp_api::decl_runtime_apis! {
2904 #[api_version(2)]
2906 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2907 AccountId: Codec,
2908 Balance: Codec,
2909 Nonce: Codec,
2910 BlockNumber: Codec,
2911 Moment: Codec,
2912 {
2913 fn eth_block() -> EthBlock;
2917
2918 fn eth_block_hash(number: U256) -> Option<H256>;
2920
2921 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2927
2928 fn block_gas_limit() -> U256;
2930
2931 fn max_extrinsic_weight_in_gas() -> U256;
2933
2934 fn balance(address: H160) -> U256;
2936
2937 fn gas_price() -> U256;
2939
2940 fn nonce(address: H160) -> Nonce;
2942
2943 fn call(
2947 origin: AccountId,
2948 dest: H160,
2949 value: Balance,
2950 gas_limit: Option<Weight>,
2951 storage_deposit_limit: Option<Balance>,
2952 input_data: Vec<u8>,
2953 ) -> ContractResult<ExecReturnValue, Balance>;
2954
2955 fn instantiate(
2959 origin: AccountId,
2960 value: Balance,
2961 gas_limit: Option<Weight>,
2962 storage_deposit_limit: Option<Balance>,
2963 code: Code,
2964 data: Vec<u8>,
2965 salt: Option<[u8; 32]>,
2966 ) -> ContractResult<InstantiateReturnValue, Balance>;
2967
2968
2969 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2974
2975 fn eth_transact_with_config(
2979 tx: GenericTransaction,
2980 config: DryRunConfig<Moment>,
2981 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2982
2983 fn eth_estimate_gas(
2989 tx: GenericTransaction,
2990 config: DryRunConfig<Moment>
2991 ) -> Result<U256, EthTransactError>;
2992
2993 fn eth_pre_dispatch_weight(tx: Vec<u8>) -> Result<Weight, EthTransactError>;
2995
2996 fn upload_code(
3000 origin: AccountId,
3001 code: Vec<u8>,
3002 storage_deposit_limit: Option<Balance>,
3003 ) -> CodeUploadResult<Balance>;
3004
3005 fn get_storage(
3011 address: H160,
3012 key: [u8; 32],
3013 ) -> GetStorageResult;
3014
3015 fn get_storage_var_key(
3021 address: H160,
3022 key: Vec<u8>,
3023 ) -> GetStorageResult;
3024
3025 #[deprecated(note = "Use the versioned equivalent `trace_block_versioned` if available on your runtime")]
3032 fn trace_block(
3033 block: Block,
3034 config: TracerTypeV1
3035 ) -> Vec<(u32, TraceV1)>;
3036
3037 fn trace_tx(
3044 block: Block,
3045 tx_index: u32,
3046 config: TracerTypeV1
3047 ) -> Option<TraceV1>;
3048
3049 fn trace_call(tx: GenericTransaction, config: TracerTypeV1) -> Result<TraceV1, EthTransactError>;
3053
3054 fn trace_call_with_config(
3060 tx: GenericTransaction,
3061 tracer_type: TracerTypeV1,
3062 config: TracingConfig,
3063 ) -> Result<TraceV1, EthTransactError>;
3064
3065 fn block_author() -> H160;
3067
3068 fn address(account_id: AccountId) -> H160;
3070
3071 fn account_id(address: H160) -> AccountId;
3073
3074 fn runtime_pallets_address() -> H160;
3076
3077 fn code(address: H160) -> Vec<u8>;
3079
3080 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
3082
3083 #[api_version(2)]
3086 fn trace_block_versioned(input: TraceBlockVersionedInputPayload<Block>) -> TraceBlockVersionedOutputPayload;
3087 }
3088}
3089
3090#[macro_export]
3104macro_rules! impl_runtime_apis_plus_revive_traits {
3105 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
3106
3107 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
3108
3109 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
3110 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
3111 use $crate::pallet::Call as ReviveCall;
3112 match self {
3113 Self::$Revive(
3114 ReviveCall::eth_call{ weight_limit, .. } |
3115 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
3116 ) => {
3117 let old = *weight_limit;
3118 *weight_limit = new_weight_limit;
3119 old
3120 },
3121 _ => Weight::default(),
3122 }
3123 }
3124 }
3125
3126 impl_runtime_apis! {
3127 $($rest)*
3128
3129 #[api_version(2)]
3130 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
3131 {
3132 fn eth_block() -> $crate::EthBlock {
3133 $crate::Pallet::<Self>::eth_block()
3134 }
3135
3136 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
3137 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
3138 }
3139
3140 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
3141 $crate::Pallet::<Self>::eth_receipt_data()
3142 }
3143
3144 fn balance(address: $crate::H160) -> $crate::U256 {
3145 $crate::Pallet::<Self>::evm_balance(&address)
3146 }
3147
3148 fn block_author() -> $crate::H160 {
3149 $crate::Pallet::<Self>::block_author()
3150 }
3151
3152 fn block_gas_limit() -> $crate::U256 {
3153 $crate::Pallet::<Self>::evm_block_gas_limit()
3154 }
3155
3156 fn max_extrinsic_weight_in_gas() -> $crate::U256 {
3157 $crate::Pallet::<Self>::evm_max_extrinsic_weight_in_gas()
3158 }
3159
3160 fn gas_price() -> $crate::U256 {
3161 $crate::Pallet::<Self>::evm_base_fee()
3162 }
3163
3164 fn nonce(address: $crate::H160) -> Nonce {
3165 use $crate::AddressMapper;
3166 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
3167 $crate::frame_system::Pallet::<Self>::account_nonce(account)
3168 }
3169
3170 fn address(account_id: AccountId) -> $crate::H160 {
3171 use $crate::AddressMapper;
3172 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
3173 }
3174
3175 fn eth_transact(
3176 tx: $crate::evm::GenericTransaction,
3177 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3178 use $crate::{
3179 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3180 sp_runtime::traits::TransactionExtension,
3181 sp_runtime::traits::Block as BlockT
3182 };
3183 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
3184 }
3185
3186 fn eth_transact_with_config(
3187 tx: $crate::evm::GenericTransaction,
3188 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3189 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3190 use $crate::{
3191 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3192 sp_runtime::traits::TransactionExtension,
3193 sp_runtime::traits::Block as BlockT
3194 };
3195 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
3196 }
3197
3198 fn eth_estimate_gas(
3199 tx: $crate::evm::GenericTransaction,
3200 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3201 ) -> Result<$crate::U256, $crate::EthTransactError> {
3202 use $crate::{
3203 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3204 sp_runtime::traits::TransactionExtension,
3205 sp_runtime::traits::Block as BlockT
3206 };
3207 $crate::Pallet::<Self>::eth_estimate_gas(tx, config)
3208 }
3209
3210 fn eth_pre_dispatch_weight(
3211 tx: Vec<u8>,
3212 ) -> Result<$crate::Weight, $crate::EthTransactError> {
3213 $crate::Pallet::<Self>::eth_pre_dispatch_weight(tx)
3214 }
3215
3216 fn call(
3217 origin: AccountId,
3218 dest: $crate::H160,
3219 value: Balance,
3220 weight_limit: Option<$crate::Weight>,
3221 storage_deposit_limit: Option<Balance>,
3222 input_data: Vec<u8>,
3223 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
3224 use $crate::frame_support::traits::Get;
3225 let blockweights: $crate::BlockWeights =
3226 <Self as $crate::frame_system::Config>::BlockWeights::get();
3227
3228 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3229 $crate::Pallet::<Self>::bare_call(
3230 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3231 dest,
3232 $crate::Pallet::<Self>::convert_native_to_evm(value),
3233 $crate::TransactionLimits::WeightAndDeposit {
3234 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3235 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3236 },
3237 input_data,
3238 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3239 )
3240 }
3241
3242 fn instantiate(
3243 origin: AccountId,
3244 value: Balance,
3245 weight_limit: Option<$crate::Weight>,
3246 storage_deposit_limit: Option<Balance>,
3247 code: $crate::Code,
3248 data: Vec<u8>,
3249 salt: Option<[u8; 32]>,
3250 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
3251 use $crate::frame_support::traits::Get;
3252 let blockweights: $crate::BlockWeights =
3253 <Self as $crate::frame_system::Config>::BlockWeights::get();
3254
3255 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3256 $crate::Pallet::<Self>::bare_instantiate(
3257 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3258 $crate::Pallet::<Self>::convert_native_to_evm(value),
3259 $crate::TransactionLimits::WeightAndDeposit {
3260 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3261 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3262 },
3263 code,
3264 data,
3265 salt,
3266 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3267 )
3268 }
3269
3270 fn upload_code(
3271 origin: AccountId,
3272 code: Vec<u8>,
3273 storage_deposit_limit: Option<Balance>,
3274 ) -> $crate::CodeUploadResult<Balance> {
3275 let origin =
3276 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
3277 $crate::Pallet::<Self>::bare_upload_code(
3278 origin,
3279 code,
3280 storage_deposit_limit.unwrap_or(u128::MAX),
3281 )
3282 }
3283
3284 fn get_storage_var_key(
3285 address: $crate::H160,
3286 key: Vec<u8>,
3287 ) -> $crate::GetStorageResult {
3288 $crate::Pallet::<Self>::get_storage_var_key(address, key)
3289 }
3290
3291 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
3292 $crate::Pallet::<Self>::get_storage(address, key)
3293 }
3294
3295 fn trace_block(
3296 block: Block,
3297 tracer_type: $crate::pallet_revive_types::runtime_api::TracerTypeV1,
3298 ) -> Vec<(u32, $crate::pallet_revive_types::runtime_api::TraceV1)> {
3299 use $crate::pallet_revive_types::runtime_api::*;
3300
3301 let input = TraceBlockVersionedInputPayload::from(TraceBlockInputPayloadV1 {
3302 block,
3303 config: tracer_type
3304 });
3305 let output = Self::trace_block_versioned(input);
3306 TraceBlockOutputPayloadV1::try_from(output)
3307 .expect("qed; v1 input must produce v1 output")
3308 .traces
3309 }
3310
3311 fn trace_tx(
3312 block: Block,
3313 tx_index: u32,
3314 tracer_type: $crate::pallet_revive_types::runtime_api::TracerTypeV1,
3315 ) -> Option<$crate::pallet_revive_types::runtime_api::TraceV1> {
3316 use $crate::{sp_runtime::traits::Block, tracing::trace};
3317
3318 let tracer_type = $crate::evm::TracerType::from(tracer_type);
3319 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3320 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3321 {
3322 return None
3323 }
3324
3325 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
3326 let (header, extrinsics) = block.deconstruct();
3327
3328 <$Executive>::initialize_block(&header);
3329 for (index, ext) in extrinsics.into_iter().enumerate() {
3330 if index as u32 == tx_index {
3331 let t = tracer.as_tracing();
3332 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3333 break;
3334 } else {
3335 let _ = <$Executive>::apply_extrinsic(ext);
3336 }
3337 }
3338
3339 tracer.collect_trace().map(Into::into)
3340 }
3341
3342 fn trace_call(
3343 tx: $crate::evm::GenericTransaction,
3344 tracer_type: $crate::pallet_revive_types::runtime_api::TracerTypeV1,
3345 ) -> Result<$crate::pallet_revive_types::runtime_api::TraceV1, $crate::EthTransactError> {
3346 use $crate::tracing::trace;
3347
3348 let tracer_type = $crate::evm::TracerType::from(tracer_type);
3349 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3350 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3351 {
3352 return Err($crate::EthTransactError::Message("Execution Tracing is disabled".into()))
3353 }
3354
3355 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3356 let t = tracer.as_tracing();
3357
3358 t.watch_address(&tx.from.unwrap_or_default());
3359 t.watch_address(&$crate::Pallet::<Self>::block_author());
3360 let result = trace(t, || Self::eth_transact(tx));
3361
3362 if let Some(trace) = tracer.collect_trace() {
3363 Ok(trace)
3364 } else if let Err(err) = result {
3365 Err(err)
3366 } else {
3367 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
3368 }
3369 .map(Into::into)
3370 }
3371
3372 fn trace_call_with_config(
3373 tx: $crate::evm::GenericTransaction,
3374 tracer_type: $crate::pallet_revive_types::runtime_api::TracerTypeV1,
3375 config: $crate::evm::TracingConfig,
3376 ) -> Result<$crate::pallet_revive_types::runtime_api::TraceV1, $crate::EthTransactError> {
3377 let $crate::evm::TracingConfig { state_overrides } = config;
3378
3379 if let Some(overrides) = state_overrides {
3380 $crate::state_overrides::apply_state_overrides::<Runtime>(overrides)?;
3381 }
3382
3383 Self::trace_call(tx, tracer_type)
3384 }
3385
3386 fn runtime_pallets_address() -> $crate::H160 {
3387 $crate::RUNTIME_PALLETS_ADDR
3388 }
3389
3390 fn code(address: $crate::H160) -> Vec<u8> {
3391 $crate::Pallet::<Self>::code(&address)
3392 }
3393
3394 fn account_id(address: $crate::H160) -> AccountId {
3395 use $crate::AddressMapper;
3396 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
3397 }
3398
3399 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
3400 $crate::Pallet::<Self>::new_balance_with_dust(balance)
3401 }
3402
3403 fn trace_block_versioned(
3405 input: $crate::pallet_revive_types::runtime_api::TraceBlockVersionedInputPayload<Block>
3406 ) -> $crate::pallet_revive_types::runtime_api::TraceBlockVersionedOutputPayload {
3407 use $crate::{
3408 sp_runtime::traits::Block,
3409 tracing::trace,
3410 runtime_api::*,
3411 pallet_revive_types::runtime_api::*
3412 };
3413 use alloc::boxed::Box;
3414
3415 let (input, output_wrapper): (_, Box<dyn Fn(TraceBlockOutputPayload) -> TraceBlockVersionedOutputPayload>) = match input {
3416 TraceBlockVersionedInputPayload::V1(payload) => (
3417 TraceBlockInputPayload::from(payload),
3418 Box::new(|output| TraceBlockVersionedOutputPayload::V1(output.into()))
3419 ),
3420 TraceBlockVersionedInputPayload::V2(payload) => (
3421 TraceBlockInputPayload::from(payload),
3422 Box::new(|output| TraceBlockVersionedOutputPayload::V2(output.into()))
3423 ),
3424 };
3425
3426 if matches!(input.config, $crate::evm::TracerType::ExecutionTracer(_)) &&
3427 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3428 {
3429 return output_wrapper(Default::default())
3430 }
3431
3432 let mut traces = vec![];
3433 let (header, extrinsics) = input.block.deconstruct();
3434 <$Executive>::initialize_block(&header);
3435 for (index, ext) in extrinsics.into_iter().enumerate() {
3436 let mut tracer = $crate::Pallet::<Self>::evm_tracer(input.config.clone());
3437 let t = tracer.as_tracing();
3438 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3439
3440 if let Some(tx_trace) = tracer.collect_trace() {
3441 traces.push((index as u32, tx_trace));
3442 }
3443 }
3444
3445 let output = TraceBlockOutputPayload { traces };
3446 output_wrapper(output)
3447 }
3448 }
3449 }
3450 };
3451}