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;
27#[cfg(any(feature = "runtime-benchmarks", test))]
28pub mod call_builder;
29mod debug;
30mod deposit_payment;
31mod exec;
32mod impl_fungibles;
33mod limits;
34mod metering;
35mod primitives;
36#[doc(hidden)]
37pub mod state_overrides;
38mod storage;
39#[cfg(test)]
40mod tests;
41mod transient_storage;
42mod vm;
43mod weightinfo_extension;
44
45pub mod evm;
46pub mod migrations;
47pub mod mock;
48pub mod precompiles;
49pub mod test_utils;
50pub mod tracing;
51pub mod weights;
52
53use crate::{
54 evm::{
55 CallTracer, CreateCallMode, ExecutionTracer, GenericTransaction, PrestateTracer,
56 TYPE_EIP1559, Trace, Tracer, TracerType, block_hash::EthereumBlockBuilderIR, block_storage,
57 fees::InfoT as FeeInfo, runtime::SetWeightLimit,
58 },
59 exec::{AccountIdOf, ExecError, ReentrancyProtection, Stack as ExecStack},
60 sp_runtime::TransactionOutcome,
61 storage::{AccountType, DeletionQueueManager},
62 tracing::if_tracing,
63 vm::{CodeInfo, RuntimeCosts, pvm::extract_code_and_data},
64 weightinfo_extension::OnFinalizeBlockParts,
65};
66use alloc::{boxed::Box, format, vec};
67use codec::{Codec, Decode, Encode};
68use environmental::*;
69use frame_support::{
70 BoundedVec,
71 dispatch::{
72 DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
73 Pays, PostDispatchInfo, RawOrigin,
74 },
75 ensure,
76 pallet_prelude::DispatchClass,
77 storage::with_transaction,
78 traits::{
79 ConstU32, ConstU64, DefensiveResult, EnsureOrigin, Get, IsSubType, IsType, OnUnbalanced,
80 OriginTrait,
81 fungible::{Balanced, Credit, Inspect, Mutate, MutateHold},
82 tokens::Balance,
83 },
84 weights::WeightMeter,
85};
86use frame_system::{
87 Pallet as System, ensure_signed,
88 pallet_prelude::{BlockNumberFor, OriginFor},
89};
90use scale_info::TypeInfo;
91use sp_runtime::{
92 AccountId32, DispatchError, FixedPointNumber, FixedU128, SaturatedConversion,
93 traits::{
94 BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedFrom,
95 UniqueSaturatedInto, Zero,
96 },
97};
98
99pub use crate::{
100 address::{AccountId32Mapper, AddressMapper, AutoMapper, TestAccountMapper, create1, create2},
101 debug::DebugSettings,
102 deposit_payment::{Deposit, PGasDeposit},
103 evm::{
104 Address as EthAddress, Block as EthBlock, DryRunConfig, ReceiptInfo, TracingConfig,
105 block_hash::ReceiptGasInfo,
106 },
107 exec::{CallResources, DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
108 limits::TRANSIENT_STORAGE_BYTES as TRANSIENT_STORAGE_LIMIT,
109 metering::{
110 EthTxInfo, FrameMeter, ResourceMeter, Token as WeightToken, TransactionLimits,
111 TransactionMeter,
112 },
113 pallet::{genesis, *},
114 storage::{AccountInfo, ContractInfo},
115 transient_storage::{MeterEntry, StorageMeter as TransientStorageMeter, TransientStorage},
116 vm::{BytecodeType, ContractBlob},
117};
118pub use codec;
119use frame_support::traits::tokens::Precision;
120pub use frame_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
121pub use frame_system::{self, limits::BlockWeights};
122pub use primitives::*;
123pub use sp_core::{H160, H256, U256};
124pub use sp_crypto_hashing::keccak_256;
125pub use sp_runtime;
126pub use weights::WeightInfo;
127
128#[cfg(doc)]
129pub use crate::vm::pvm::SyscallDoc;
130
131pub type BalanceOf<T> = <T as Config>::Balance;
132pub type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
133type TrieId = BoundedVec<u8, ConstU32<128>>;
134type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
135type CallOf<T> = <T as Config>::RuntimeCall;
136
137const SENTINEL: u32 = u32::MAX;
144
145const LOG_TARGET: &str = "runtime::revive";
151
152#[frame_support::pallet]
153pub mod pallet {
154 use super::*;
155 use frame_support::{pallet_prelude::*, traits::FindAuthor};
156 use frame_system::pallet_prelude::*;
157 use sp_core::U256;
158 use sp_runtime::Perbill;
159
160 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
162
163 #[pallet::pallet]
164 #[pallet::storage_version(STORAGE_VERSION)]
165 pub struct Pallet<T>(_);
166
167 #[pallet::config(with_default)]
168 pub trait Config: frame_system::Config {
169 type Time: Time<Moment: Into<U256>>;
171
172 #[pallet::no_default]
176 type Balance: Balance
177 + TryFrom<U256>
178 + Into<U256>
179 + Bounded
180 + UniqueSaturatedInto<u64>
181 + UniqueSaturatedFrom<u64>
182 + UniqueSaturatedInto<u128>;
183
184 #[pallet::no_default]
186 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
187 + Mutate<Self::AccountId>
188 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
189 + Balanced<Self::AccountId>;
190
191 #[pallet::no_default_bounds]
198 type OnBurn: OnUnbalanced<CreditOf<Self>>;
199
200 #[pallet::no_default_bounds]
202 #[allow(deprecated)]
203 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
204
205 #[pallet::no_default_bounds]
207 type RuntimeCall: Parameter
208 + Dispatchable<
209 RuntimeOrigin = OriginFor<Self>,
210 Info = DispatchInfo,
211 PostInfo = PostDispatchInfo,
212 > + IsType<<Self as frame_system::Config>::RuntimeCall>
213 + From<Call<Self>>
214 + IsSubType<Call<Self>>
215 + GetDispatchInfo;
216
217 #[pallet::no_default_bounds]
219 type RuntimeOrigin: IsType<OriginFor<Self>>
220 + From<Origin<Self>>
221 + Into<Result<Origin<Self>, OriginFor<Self>>>;
222
223 #[pallet::no_default_bounds]
225 type RuntimeHoldReason: From<HoldReason>;
226
227 type WeightInfo: WeightInfo;
230
231 #[pallet::no_default_bounds]
235 #[allow(private_bounds)]
236 type Precompiles: precompiles::Precompiles<Self>;
237
238 type FindAuthor: FindAuthor<Self::AccountId>;
240
241 #[pallet::constant]
247 #[pallet::no_default_bounds]
248 type DepositPerByte: Get<BalanceOf<Self>>;
249
250 #[pallet::constant]
256 #[pallet::no_default_bounds]
257 type DepositPerItem: Get<BalanceOf<Self>>;
258
259 #[pallet::constant]
269 #[pallet::no_default_bounds]
270 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
271
272 #[pallet::constant]
276 type CodeHashLockupDepositPercent: Get<Perbill>;
277
278 #[pallet::no_default]
280 type AddressMapper: AddressMapper<Self>;
281
282 #[pallet::constant]
284 type AllowEVMBytecode: Get<bool>;
285
286 #[pallet::no_default_bounds]
291 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
292
293 #[pallet::no_default_bounds]
304 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
305
306 type RuntimeMemory: Get<u32>;
311
312 type PVFMemory: Get<u32>;
320
321 #[pallet::constant]
326 type ChainId: Get<u64>;
327
328 #[pallet::constant]
330 type NativeToEthRatio: Get<u32>;
331
332 #[pallet::no_default_bounds]
337 type FeeInfo: FeeInfo<Self>;
338
339 #[pallet::no_default_bounds]
342 type Deposit: Deposit<Self>;
343
344 #[pallet::constant]
357 type MaxEthExtrinsicWeight: Get<FixedU128>;
358
359 #[pallet::constant]
361 type DebugEnabled: Get<bool>;
362
363 #[pallet::constant]
370 type AutoMap: Get<bool>;
371
372 #[pallet::constant]
390 #[pallet::no_default_bounds]
391 type GasScale: Get<u32>;
392 }
393
394 pub mod config_preludes {
396 use super::*;
397 use frame_support::{
398 derive_impl,
399 traits::{ConstBool, ConstU32},
400 };
401 use frame_system::EnsureSigned;
402 use sp_core::parameter_types;
403
404 type Balance = u64;
405
406 pub const DOLLARS: Balance = 1_000_000_000_000;
407 pub const CENTS: Balance = DOLLARS / 100;
408 pub const MILLICENTS: Balance = CENTS / 1_000;
409
410 pub const fn deposit(items: u32, bytes: u32) -> Balance {
411 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
412 }
413
414 parameter_types! {
415 pub const DepositPerItem: Balance = deposit(1, 0);
416 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
417 pub const DepositPerByte: Balance = deposit(0, 1);
418 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
419 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
420 pub const GasScale: u32 = 10u32;
421 }
422
423 pub struct TestDefaultConfig;
425
426 impl Time for TestDefaultConfig {
427 type Moment = u64;
428 fn now() -> Self::Moment {
429 0u64
430 }
431 }
432
433 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
434 fn convert(w: Weight) -> T {
435 w.ref_time().into()
436 }
437 }
438
439 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
440 impl frame_system::DefaultConfig for TestDefaultConfig {}
441
442 #[frame_support::register_default_impl(TestDefaultConfig)]
443 impl DefaultConfig for TestDefaultConfig {
444 #[inject_runtime_type]
445 type RuntimeEvent = ();
446
447 #[inject_runtime_type]
448 type RuntimeHoldReason = ();
449
450 #[inject_runtime_type]
451 type RuntimeCall = ();
452
453 #[inject_runtime_type]
454 type RuntimeOrigin = ();
455
456 type Precompiles = ();
457 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
458 type DepositPerByte = DepositPerByte;
459 type DepositPerItem = DepositPerItem;
460 type DepositPerChildTrieItem = DepositPerChildTrieItem;
461 type Time = Self;
462 type AllowEVMBytecode = ConstBool<true>;
463 type UploadOrigin = EnsureSigned<Self::AccountId>;
464 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
465 type WeightInfo = ();
466 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
467 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
468 type ChainId = ConstU64<42>;
469 type NativeToEthRatio = ConstU32<1_000_000>;
470 type FindAuthor = ();
471 type FeeInfo = ();
472 type Deposit = ();
473 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
474 type DebugEnabled = ConstBool<false>;
475 type AutoMap = ConstBool<false>;
476 type GasScale = GasScale;
477 type OnBurn = ();
478 }
479 }
480
481 #[pallet::event]
482 pub enum Event<T: Config> {
483 ContractEmitted {
485 contract: H160,
487 data: Vec<u8>,
490 topics: Vec<H256>,
493 },
494
495 Instantiated { deployer: H160, contract: H160 },
497
498 EthExtrinsicRevert { dispatch_error: DispatchError },
505 }
506
507 #[pallet::error]
508 #[repr(u8)]
509 pub enum Error<T> {
510 InvalidSchedule = 0x01,
512 InvalidCallFlags = 0x02,
514 OutOfGas = 0x03,
516 TransferFailed = 0x04,
519 MaxCallDepthReached = 0x05,
522 ContractNotFound = 0x06,
524 CodeNotFound = 0x07,
526 CodeInfoNotFound = 0x08,
528 OutOfBounds = 0x09,
530 DecodingFailed = 0x0A,
532 ContractTrapped = 0x0B,
534 ValueTooLarge = 0x0C,
536 TerminatedWhileReentrant = 0x0D,
539 InputForwarded = 0x0E,
541 TooManyTopics = 0x0F,
543 DuplicateContract = 0x12,
545 TerminatedInConstructor = 0x13,
549 ReentranceDenied = 0x14,
551 ReenteredPallet = 0x15,
553 StateChangeDenied = 0x16,
555 StorageDepositNotEnoughFunds = 0x17,
557 StorageDepositLimitExhausted = 0x18,
559 CodeInUse = 0x19,
561 ContractReverted = 0x1A,
566 CodeRejected = 0x1B,
571 BlobTooLarge = 0x1C,
573 StaticMemoryTooLarge = 0x1D,
575 BasicBlockTooLarge = 0x1E,
577 InvalidInstruction = 0x1F,
579 MaxDelegateDependenciesReached = 0x20,
581 DelegateDependencyNotFound = 0x21,
583 DelegateDependencyAlreadyExists = 0x22,
585 CannotAddSelfAsDelegateDependency = 0x23,
587 OutOfTransientStorage = 0x24,
589 InvalidSyscall = 0x25,
591 InvalidStorageFlags = 0x26,
593 ExecutionFailed = 0x27,
595 BalanceConversionFailed = 0x28,
597 InvalidImmutableAccess = 0x2A,
600 AccountUnmapped = 0x2B,
604 AccountAlreadyMapped = 0x2C,
606 InvalidGenericTransaction = 0x2D,
608 RefcountOverOrUnderflow = 0x2E,
610 UnsupportedPrecompileAddress = 0x2F,
612 CallDataTooLarge = 0x30,
614 ReturnDataTooLarge = 0x31,
616 InvalidJump = 0x32,
618 StackUnderflow = 0x33,
620 StackOverflow = 0x34,
622 TxFeeOverdraw = 0x35,
626 EvmConstructorNonEmptyData = 0x36,
630 EvmConstructedFromHash = 0x37,
635 StorageRefundNotEnoughFunds = 0x38,
639 StorageRefundLocked = 0x39,
644 PrecompileDelegateDenied = 0x40,
649 EcdsaRecoveryFailed = 0x41,
651 AutoMappingEnabled = 0x42,
653 PendingDepositCleanup = 0x43,
657 #[cfg(feature = "runtime-benchmarks")]
659 BenchmarkingError = 0xFF,
660 }
661
662 #[pallet::composite_enum]
664 pub enum HoldReason {
665 CodeUploadDepositReserve,
667 StorageDepositReserve,
669 AddressMapping,
671 }
672
673 #[pallet::composite_enum]
675 pub enum FreezeReason {
676 PGasMinBalance,
681 }
682
683 #[derive(
684 PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug,
685 )]
686 #[pallet::origin]
687 pub enum Origin<T: Config> {
688 EthTransaction(T::AccountId),
689 }
690
691 #[pallet::storage]
695 #[pallet::unbounded]
696 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
697
698 #[pallet::storage]
700 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
701
702 #[pallet::storage]
704 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
705
706 #[pallet::storage]
717 pub(crate) type NativeDepositOf<T: Config> = StorageDoubleMap<
718 _,
719 Identity,
720 T::AccountId,
721 Identity,
722 T::AccountId,
723 BalanceOf<T>,
724 ValueQuery,
725 >;
726
727 #[pallet::storage]
729 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
730
731 #[pallet::storage]
737 pub(crate) type DeletionQueue<T: Config> =
738 StorageMap<_, Twox64Concat, u32, crate::storage::DeletionQueueItem<T>>;
739
740 #[pallet::storage]
743 pub(crate) type DeletionQueueCounter<T: Config> =
744 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
745
746 #[pallet::storage]
753 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
754
755 #[pallet::storage]
765 #[pallet::unbounded]
766 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
767
768 #[pallet::storage]
772 pub(crate) type BlockHash<T: Config> =
773 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
774
775 #[pallet::storage]
782 #[pallet::unbounded]
783 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
784
785 #[pallet::storage]
787 #[pallet::unbounded]
788 pub(crate) type EthBlockBuilderIR<T: Config> =
789 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
790
791 #[pallet::storage]
796 #[pallet::unbounded]
797 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
798 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
799
800 #[pallet::storage]
802 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
803
804 pub mod genesis {
805 use super::*;
806 use crate::evm::Bytes32;
807
808 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
810 pub struct ContractData {
811 pub code: crate::evm::Bytes,
813 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
815 }
816
817 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
819 pub struct Account<T: Config> {
820 pub address: H160,
822 #[serde(default)]
824 pub balance: U256,
825 #[serde(default)]
827 pub nonce: T::Nonce,
828 #[serde(flatten, skip_serializing_if = "Option::is_none")]
830 pub contract_data: Option<ContractData>,
831 }
832 }
833
834 #[pallet::genesis_config]
835 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
836 pub struct GenesisConfig<T: Config> {
837 #[serde(default, skip_serializing_if = "Vec::is_empty")]
840 pub mapped_accounts: Vec<T::AccountId>,
841
842 #[serde(default, skip_serializing_if = "Vec::is_empty")]
844 pub accounts: Vec<genesis::Account<T>>,
845
846 #[serde(default, skip_serializing_if = "Option::is_none")]
848 pub debug_settings: Option<DebugSettings>,
849 }
850
851 #[pallet::genesis_build]
852 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
853 fn build(&self) {
854 use crate::{exec::Key, vm::ContractBlob};
855 use frame_support::traits::fungible::Mutate;
856
857 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
858 let _ = T::Currency::mint_into(
859 &Pallet::<T>::account_id(),
860 T::Currency::minimum_balance(),
861 );
862 }
863
864 for id in &self.mapped_accounts {
865 if let Err(err) = T::AddressMapper::map_no_deposit_unchecked(id) {
866 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
867 }
868 }
869
870 let owner = Pallet::<T>::account_id();
871
872 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
873 let account_id = T::AddressMapper::to_account_id(address);
874
875 if !System::<T>::account_exists(&account_id) {
876 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
877 }
878
879 frame_system::Account::<T>::mutate(&account_id, |info| {
880 info.nonce = (*nonce).into();
881 });
882
883 match contract_data {
884 None => {
885 AccountInfoOf::<T>::insert(
886 address,
887 AccountInfo { account_type: AccountType::EOA, dust: 0 },
888 );
889 },
890 Some(genesis::ContractData { code, storage }) => {
891 let blob = if code.0.starts_with(&polkavm_common::program::BLOB_MAGIC) {
892 ContractBlob::<T>::from_pvm_code(code.0.clone(), owner.clone())
893 .inspect_err(|err| {
894 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
895 })
896 } else {
897 ContractBlob::<T>::from_evm_runtime_code(code.0.clone(), account_id)
898 .inspect_err(|err| {
899 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
900 })
901 };
902
903 let Ok(blob) = blob else {
904 continue;
905 };
906
907 let code_hash = *blob.code_hash();
908 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
909 .inspect_err(|err| {
910 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
911 })
912 else {
913 continue;
914 };
915
916 AccountInfoOf::<T>::insert(
917 address,
918 AccountInfo { account_type: info.clone().into(), dust: 0 },
919 );
920
921 <PristineCode<T>>::insert(blob.code_hash(), code.0.clone());
922 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
923 for (k, v) in storage {
924 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
925 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
926 });
927 }
928 },
929 }
930
931 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
932 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
933 });
934 }
935
936 block_storage::on_finalize_build_eth_block::<T>(
938 frame_system::Pallet::<T>::block_number(),
941 );
942
943 if let Some(settings) = self.debug_settings.as_ref() {
945 settings.write_to_storage::<T>()
946 }
947 }
948 }
949
950 #[pallet::hooks]
951 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
952 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
953 let mut meter = WeightMeter::with_limit(limit);
954 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
955 meter.consumed()
956 }
957
958 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
959 block_storage::on_initialize::<T>();
961
962 System::<T>::account_exists(&Pallet::<T>::account_id());
964 <T as Config>::WeightInfo::on_finalize_block_fixed()
966 }
967
968 fn on_finalize(block_number: BlockNumberFor<T>) {
969 block_storage::on_finalize_build_eth_block::<T>(block_number);
971 }
972
973 fn integrity_test() {
974 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
975
976 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
977
978 T::FeeInfo::integrity_test();
979
980 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
982
983 const TOTAL_MEMORY_DEVIDER: u64 = 2;
986
987 let max_block_weight = T::BlockWeights::get()
993 .get(DispatchClass::Normal)
994 .max_total
995 .unwrap_or_else(|| T::BlockWeights::get().max_block);
996 let max_key_size: u64 =
997 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
998 .expect("Key of maximal size shall be created")
999 .hash()
1000 .len()
1001 .try_into()
1002 .unwrap();
1003
1004 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
1005 let max_immutable_size: u64 = max_block_weight
1006 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
1007 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
1008 ))
1009 .unwrap()
1010 .saturating_mul(
1011 u64::from(limits::IMMUTABLE_BYTES)
1012 .saturating_add(max_immutable_key_size)
1013 .into(),
1014 );
1015
1016 let max_pvf_mem: u64 = T::PVFMemory::get().into();
1017 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
1018
1019 let max_events_size = max_block_weight
1023 .checked_div_per_component(
1024 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
1025 num_topic: 0,
1026 len: limits::EVENT_BYTES,
1027 })
1028 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
1029 &RuntimeCosts::HostFn,
1030 ))),
1031 )
1032 .unwrap()
1033 .saturating_mul(limits::EVENT_BYTES.into());
1034
1035 assert!(
1036 max_events_size <= storage_size_limit,
1037 "Maximal events size {} exceeds the events limit {}",
1038 max_events_size,
1039 storage_size_limit
1040 );
1041
1042 let max_eth_block_builder_bytes =
1077 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1078
1079 log::debug!(
1080 target: LOG_TARGET,
1081 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1082 max_eth_block_builder_bytes / 1024,
1083 max_events_size / 1024,
1084 );
1085
1086 let memory_left = i128::from(max_runtime_mem)
1091 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1092 .saturating_sub(limits::MEMORY_REQUIRED.into())
1093 .saturating_sub(max_eth_block_builder_bytes.into());
1094
1095 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1096
1097 assert!(
1098 memory_left >= 0,
1099 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1100 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1101 );
1102
1103 let max_storage_size = max_block_weight
1106 .checked_div_per_component(
1107 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1108 new_bytes: limits::STORAGE_BYTES,
1109 old_bytes: 0,
1110 })
1111 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1112 )
1113 .unwrap()
1114 .saturating_add(max_immutable_size.into())
1115 .saturating_add(max_eth_block_builder_bytes.into());
1116
1117 assert!(
1118 max_storage_size <= storage_size_limit,
1119 "Maximal storage size {} exceeds the storage limit {}",
1120 max_storage_size,
1121 storage_size_limit
1122 );
1123 }
1124 }
1125
1126 #[pallet::call]
1127 impl<T: Config> Pallet<T> {
1128 #[allow(unused_variables)]
1141 #[pallet::call_index(0)]
1142 #[pallet::weight(Weight::MAX)]
1143 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1144 Err(frame_system::Error::CallFiltered::<T>.into())
1145 }
1146
1147 #[pallet::call_index(1)]
1164 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1165 pub fn call(
1166 origin: OriginFor<T>,
1167 dest: H160,
1168 #[pallet::compact] value: BalanceOf<T>,
1169 weight_limit: Weight,
1170 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1171 data: Vec<u8>,
1172 ) -> DispatchResultWithPostInfo {
1173 Self::ensure_non_contract_if_signed(&origin)?;
1174 let mut output = Self::bare_call(
1175 origin,
1176 dest,
1177 Pallet::<T>::convert_native_to_evm(value),
1178 TransactionLimits::WeightAndDeposit {
1179 weight_limit,
1180 deposit_limit: storage_deposit_limit,
1181 },
1182 data,
1183 &ExecConfig::new_substrate_tx(),
1184 );
1185
1186 if let Ok(return_value) = &output.result &&
1187 return_value.did_revert()
1188 {
1189 output.result = Err(<Error<T>>::ContractReverted.into());
1190 }
1191 dispatch_result(
1192 output.result,
1193 output.weight_consumed,
1194 <T as Config>::WeightInfo::call(),
1195 )
1196 }
1197
1198 #[pallet::call_index(2)]
1204 #[pallet::weight(
1205 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1206 )]
1207 pub fn instantiate(
1208 origin: OriginFor<T>,
1209 #[pallet::compact] value: BalanceOf<T>,
1210 weight_limit: Weight,
1211 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1212 code_hash: sp_core::H256,
1213 data: Vec<u8>,
1214 salt: Option<[u8; 32]>,
1215 ) -> DispatchResultWithPostInfo {
1216 Self::ensure_non_contract_if_signed(&origin)?;
1217 let data_len = data.len() as u32;
1218 let mut output = Self::bare_instantiate(
1219 origin,
1220 Pallet::<T>::convert_native_to_evm(value),
1221 TransactionLimits::WeightAndDeposit {
1222 weight_limit,
1223 deposit_limit: storage_deposit_limit,
1224 },
1225 Code::Existing(code_hash),
1226 data,
1227 salt,
1228 &ExecConfig::new_substrate_tx(),
1229 );
1230 if let Ok(retval) = &output.result &&
1231 retval.result.did_revert()
1232 {
1233 output.result = Err(<Error<T>>::ContractReverted.into());
1234 }
1235 dispatch_result(
1236 output.result.map(|result| result.result),
1237 output.weight_consumed,
1238 <T as Config>::WeightInfo::instantiate(data_len),
1239 )
1240 }
1241
1242 #[pallet::call_index(3)]
1270 #[pallet::weight(
1271 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1272 .saturating_add(*weight_limit)
1273 )]
1274 pub fn instantiate_with_code(
1275 origin: OriginFor<T>,
1276 #[pallet::compact] value: BalanceOf<T>,
1277 weight_limit: Weight,
1278 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1279 code: Vec<u8>,
1280 data: Vec<u8>,
1281 salt: Option<[u8; 32]>,
1282 ) -> DispatchResultWithPostInfo {
1283 Self::ensure_non_contract_if_signed(&origin)?;
1284 let code_len = code.len() as u32;
1285 let data_len = data.len() as u32;
1286 let mut output = Self::bare_instantiate(
1287 origin,
1288 Pallet::<T>::convert_native_to_evm(value),
1289 TransactionLimits::WeightAndDeposit {
1290 weight_limit,
1291 deposit_limit: storage_deposit_limit,
1292 },
1293 Code::Upload(code),
1294 data,
1295 salt,
1296 &ExecConfig::new_substrate_tx(),
1297 );
1298 if let Ok(retval) = &output.result &&
1299 retval.result.did_revert()
1300 {
1301 output.result = Err(<Error<T>>::ContractReverted.into());
1302 }
1303 dispatch_result(
1304 output.result.map(|result| result.result),
1305 output.weight_consumed,
1306 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1307 )
1308 }
1309
1310 #[pallet::call_index(10)]
1332 #[pallet::weight(
1333 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1334 .saturating_add(*weight_limit)
1335 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1336 )]
1337 pub fn eth_instantiate_with_code(
1338 origin: OriginFor<T>,
1339 value: U256,
1340 weight_limit: Weight,
1341 eth_gas_limit: U256,
1342 code: Vec<u8>,
1343 data: Vec<u8>,
1344 transaction_encoded: Vec<u8>,
1345 effective_gas_price: U256,
1346 encoded_len: u32,
1347 ) -> DispatchResultWithPostInfo {
1348 let signer = Self::ensure_eth_signed(origin)?;
1349 let origin = OriginFor::<T>::signed(signer.clone());
1350 Self::ensure_non_contract_if_signed(&origin)?;
1351 let mut call = Call::<T>::eth_instantiate_with_code {
1352 value,
1353 weight_limit,
1354 eth_gas_limit,
1355 code: code.clone(),
1356 data: data.clone(),
1357 transaction_encoded: transaction_encoded.clone(),
1358 effective_gas_price,
1359 encoded_len,
1360 }
1361 .into();
1362 let info = T::FeeInfo::dispatch_info(&call);
1363 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1364 drop(call);
1365
1366 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1367 let extra_weight = base_info.total_weight();
1368 let output = Self::bare_instantiate(
1369 origin,
1370 value,
1371 TransactionLimits::EthereumGas {
1372 eth_gas_limit: eth_gas_limit.saturated_into(),
1373 weight_limit,
1374 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1375 },
1376 Code::Upload(code),
1377 data,
1378 None,
1379 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1380 );
1381
1382 block_storage::EthereumCallResult::new::<T>(
1383 signer,
1384 output.map_result(|r| r.result),
1385 base_info.call_weight,
1386 encoded_len,
1387 &info,
1388 effective_gas_price,
1389 )
1390 })
1391 }
1392
1393 #[pallet::call_index(11)]
1410 #[pallet::weight(
1411 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1412 .saturating_add(*weight_limit)
1413 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1414 )]
1415 pub fn eth_call(
1416 origin: OriginFor<T>,
1417 dest: H160,
1418 value: U256,
1419 weight_limit: Weight,
1420 eth_gas_limit: U256,
1421 data: Vec<u8>,
1422 transaction_encoded: Vec<u8>,
1423 effective_gas_price: U256,
1424 encoded_len: u32,
1425 ) -> DispatchResultWithPostInfo {
1426 let signer = Self::ensure_eth_signed(origin)?;
1427 let origin = OriginFor::<T>::signed(signer.clone());
1428
1429 Self::ensure_non_contract_if_signed(&origin)?;
1430 let mut call = Call::<T>::eth_call {
1431 dest,
1432 value,
1433 weight_limit,
1434 eth_gas_limit,
1435 data: data.clone(),
1436 transaction_encoded: transaction_encoded.clone(),
1437 effective_gas_price,
1438 encoded_len,
1439 }
1440 .into();
1441 let info = T::FeeInfo::dispatch_info(&call);
1442 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1443 drop(call);
1444
1445 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1446 let extra_weight = base_info.total_weight();
1447 let output = Self::bare_call(
1448 origin,
1449 dest,
1450 value,
1451 TransactionLimits::EthereumGas {
1452 eth_gas_limit: eth_gas_limit.saturated_into(),
1453 weight_limit,
1454 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1455 },
1456 data,
1457 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1458 );
1459
1460 block_storage::EthereumCallResult::new::<T>(
1461 signer,
1462 output,
1463 base_info.call_weight,
1464 encoded_len,
1465 &info,
1466 effective_gas_price,
1467 )
1468 })
1469 }
1470
1471 #[pallet::call_index(12)]
1482 #[pallet::weight(
1483 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32)
1484 .saturating_add(call.get_dispatch_info().call_weight)
1485 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1486 )]
1487 pub fn eth_substrate_call(
1488 origin: OriginFor<T>,
1489 call: Box<<T as Config>::RuntimeCall>,
1490 transaction_encoded: Vec<u8>,
1491 ) -> DispatchResultWithPostInfo {
1492 let signer = Self::ensure_eth_signed(origin)?;
1495 Self::ensure_non_contract_if_signed(&OriginFor::<T>::signed(signer.clone()))?;
1496 let tx_len = transaction_encoded.len() as u32;
1497 let weight_overhead = T::WeightInfo::eth_substrate_call(tx_len)
1498 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(tx_len));
1499
1500 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1501 let call_weight = call.get_dispatch_info().call_weight;
1502 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1503
1504 match &mut call_result {
1506 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1507 post_info.actual_weight = Some(
1508 post_info
1509 .actual_weight
1510 .unwrap_or_else(|| call_weight)
1511 .saturating_add(weight_overhead),
1512 );
1513 },
1514 }
1515
1516 block_storage::EthereumCallResult {
1519 receipt_gas_info: ReceiptGasInfo::default(),
1520 result: call_result,
1521 }
1522 })
1523 }
1524
1525 #[pallet::call_index(4)]
1540 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1541 pub fn upload_code(
1542 origin: OriginFor<T>,
1543 code: Vec<u8>,
1544 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1545 ) -> DispatchResult {
1546 Self::ensure_non_contract_if_signed(&origin)?;
1547 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1548 }
1549
1550 #[pallet::call_index(5)]
1555 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1556 pub fn remove_code(
1557 origin: OriginFor<T>,
1558 code_hash: sp_core::H256,
1559 ) -> DispatchResultWithPostInfo {
1560 let origin = ensure_signed(origin)?;
1561 <ContractBlob<T>>::remove(&origin, code_hash)?;
1562 Ok(Pays::No.into())
1564 }
1565
1566 #[pallet::call_index(6)]
1577 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1578 pub fn set_code(
1579 origin: OriginFor<T>,
1580 dest: H160,
1581 code_hash: sp_core::H256,
1582 ) -> DispatchResult {
1583 ensure_root(origin)?;
1584 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1585 let Some(account) = account else {
1586 return Err(<Error<T>>::ContractNotFound.into());
1587 };
1588
1589 let AccountType::Contract(ref mut contract) = account.account_type else {
1590 return Err(<Error<T>>::ContractNotFound.into());
1591 };
1592
1593 <CodeInfo<T>>::increment_refcount(code_hash)?;
1594 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1595 contract.code_hash = code_hash;
1596
1597 Ok(())
1598 })
1599 }
1600
1601 #[pallet::call_index(7)]
1609 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1610 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1611 #[cfg(not(feature = "runtime-benchmarks"))]
1612 if T::AutoMap::get() {
1613 return Ok(());
1614 }
1615
1616 Self::ensure_non_contract_if_signed(&origin)?;
1617 let origin = ensure_signed(origin)?;
1618 T::AddressMapper::map(&origin)
1619 }
1620
1621 #[pallet::call_index(13)]
1623 #[pallet::weight(<T as Config>::WeightInfo::batch_map_accounts(accounts.len().saturated_into::<u32>()))]
1624 pub fn batch_map_accounts(
1625 origin: OriginFor<T>,
1626 accounts: Vec<T::AccountId>,
1627 ) -> DispatchResultWithPostInfo {
1628 ensure_signed(origin.clone())?;
1629 Self::ensure_non_contract_if_signed(&origin)?;
1630
1631 let total: u32 = accounts.len().saturated_into();
1632 let mut mapped = 0;
1633
1634 for account_id in accounts
1635 .iter()
1636 .filter(|&a| !T::AddressMapper::is_eth_derived(a))
1638 .filter(|&a| frame_system::Pallet::<T>::account_exists(a))
1641 {
1642 let mut useful = false;
1643
1644 match T::AddressMapper::map_no_deposit_unchecked(account_id) {
1645 Ok(()) => {
1646 useful = true;
1647 },
1648 Err(err) => log::debug!(
1649 target: LOG_TARGET,
1650 "Failed to map account {account_id:?}: {err:?}",
1651 ),
1652 }
1653
1654 match T::Currency::release_all(
1655 &HoldReason::AddressMapping.into(),
1656 account_id,
1657 Precision::BestEffort,
1658 ) {
1659 Ok(released) if !released.is_zero() => {
1662 useful = true;
1663 },
1664 Ok(_) => {},
1665 Err(err) => log::debug!(
1666 target: LOG_TARGET,
1667 "Failed to release mapping deposit for {account_id:?}: {err:?}",
1668 ),
1669 }
1670
1671 if useful {
1672 mapped = mapped.saturating_add(1);
1673 }
1674 }
1675
1676 if total == 0 || mapped == 0 {
1678 return Ok(Pays::Yes.into());
1679 }
1680
1681 let proportion_mapped = Perbill::from_rational(mapped, total);
1682 if proportion_mapped >= Perbill::from_percent(90) {
1683 Ok(Pays::No.into())
1684 } else {
1685 Ok(Pays::Yes.into())
1686 }
1687 }
1688
1689 #[pallet::call_index(8)]
1697 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1698 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1699 #[cfg(not(feature = "runtime-benchmarks"))]
1700 ensure!(!T::AutoMap::get(), <Error<T>>::AutoMappingEnabled);
1701 let origin = ensure_signed(origin)?;
1702 T::AddressMapper::unmap(&origin)
1703 }
1704
1705 #[pallet::call_index(9)]
1711 #[pallet::weight({
1712 let dispatch_info = call.get_dispatch_info();
1713 (
1714 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1715 dispatch_info.class
1716 )
1717 })]
1718 pub fn dispatch_as_fallback_account(
1719 mut origin: OriginFor<T>,
1720 call: Box<<T as Config>::RuntimeCall>,
1721 ) -> DispatchResultWithPostInfo {
1722 Self::ensure_non_contract_if_signed(&origin)?;
1723 let account_id = origin.as_signer().ok_or(DispatchError::BadOrigin)?;
1724 let unmapped_account = T::AddressMapper::to_fallback_account_id(
1725 &T::AddressMapper::to_address(&account_id),
1726 );
1727 origin.set_caller_from(RawOrigin::Signed(unmapped_account));
1728 call.dispatch(origin)
1729 }
1730 }
1731}
1732
1733fn dispatch_result<R>(
1735 result: Result<R, DispatchError>,
1736 weight_consumed: Weight,
1737 base_weight: Weight,
1738) -> DispatchResultWithPostInfo {
1739 let post_info = PostDispatchInfo {
1740 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1741 pays_fee: Default::default(),
1742 };
1743
1744 result
1745 .map(|_| post_info)
1746 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1747}
1748
1749impl<T: Config> Pallet<T> {
1750 pub fn bare_call(
1757 origin: OriginFor<T>,
1758 dest: H160,
1759 evm_value: U256,
1760 transaction_limits: TransactionLimits<T>,
1761 data: Vec<u8>,
1762 exec_config: &ExecConfig<T>,
1763 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1764 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1765 Ok(transaction_meter) => transaction_meter,
1766 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1767 };
1768 let mut storage_deposit = Default::default();
1769
1770 let try_call = || {
1771 let origin = ExecOrigin::from_runtime_origin(origin)?;
1772 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1773 origin.clone(),
1774 dest,
1775 &mut transaction_meter,
1776 evm_value,
1777 data,
1778 &exec_config,
1779 )?;
1780
1781 storage_deposit = transaction_meter
1782 .execute_postponed_deposits(&origin, &exec_config)
1783 .inspect_err(|err| {
1784 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1785 })?;
1786
1787 Ok(result)
1788 };
1789 let result = Self::run_guarded(try_call);
1790
1791 log::trace!(target: LOG_TARGET, "Bare call ends: \
1792 result={result:?}, \
1793 weight_consumed={:?}, \
1794 weight_required={:?}, \
1795 storage_deposit={:?}, \
1796 gas_consumed={:?}, \
1797 max_storage_deposit={:?}",
1798 transaction_meter.weight_consumed(),
1799 transaction_meter.weight_required(),
1800 storage_deposit,
1801 transaction_meter.total_consumed_gas(),
1802 transaction_meter.deposit_required()
1803 );
1804
1805 ContractResult {
1806 result: result.map_err(|r| r.error),
1807 weight_consumed: transaction_meter.weight_consumed(),
1808 weight_required: transaction_meter.weight_required(),
1809 storage_deposit,
1810 gas_consumed: transaction_meter.total_consumed_gas(),
1811 max_storage_deposit: transaction_meter.deposit_required(),
1812 }
1813 }
1814
1815 pub fn prepare_dry_run(account: &T::AccountId) {
1821 frame_system::Pallet::<T>::inc_account_nonce(account);
1824
1825 if !T::AddressMapper::is_mapped(account) {
1828 let _ = T::AddressMapper::map_no_deposit_unchecked(account);
1829 }
1830 }
1831
1832 pub fn bare_instantiate(
1838 origin: OriginFor<T>,
1839 evm_value: U256,
1840 transaction_limits: TransactionLimits<T>,
1841 code: Code,
1842 data: Vec<u8>,
1843 salt: Option<[u8; 32]>,
1844 exec_config: &ExecConfig<T>,
1845 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1846 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1847 Ok(transaction_meter) => transaction_meter,
1848 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1849 };
1850
1851 let mut storage_deposit = Default::default();
1852
1853 let try_instantiate = || {
1854 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1855
1856 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1857 let executable = match code {
1858 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1859 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1860 let executable = Self::try_upload_code(
1861 upload_account,
1862 code,
1863 BytecodeType::Pvm,
1864 &mut transaction_meter,
1865 &exec_config,
1866 )?;
1867 executable
1868 },
1869 Code::Upload(code) => {
1870 if T::AllowEVMBytecode::get() {
1871 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1872 let origin = T::UploadOrigin::ensure_origin(origin)?;
1873 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1874 executable
1875 } else {
1876 return Err(<Error<T>>::CodeRejected.into());
1877 }
1878 },
1879 Code::Existing(code_hash) => {
1880 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1881 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1882 executable
1883 },
1884 };
1885 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1886 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1887 instantiate_account,
1888 executable,
1889 &mut transaction_meter,
1890 evm_value,
1891 data,
1892 salt.as_ref(),
1893 &exec_config,
1894 );
1895
1896 storage_deposit = transaction_meter
1897 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1898 .inspect_err(|err| {
1899 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1900 })?;
1901 result
1902 };
1903 let output = Self::run_guarded(try_instantiate);
1904
1905 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1906 weight_required={:?} \
1907 storage_deposit={:?} \
1908 gas_consumed={:?} \
1909 max_storage_deposit={:?}",
1910 transaction_meter.weight_consumed(),
1911 transaction_meter.weight_required(),
1912 storage_deposit,
1913 transaction_meter.total_consumed_gas(),
1914 transaction_meter.deposit_required()
1915 );
1916
1917 ContractResult {
1918 result: output
1919 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1920 .map_err(|e| e.error),
1921 weight_consumed: transaction_meter.weight_consumed(),
1922 weight_required: transaction_meter.weight_required(),
1923 storage_deposit,
1924 gas_consumed: transaction_meter.total_consumed_gas(),
1925 max_storage_deposit: transaction_meter.deposit_required(),
1926 }
1927 }
1928
1929 pub fn eth_estimate_gas(
1941 tx: GenericTransaction,
1942 config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1943 ) -> Result<U256, EthTransactError>
1944 where
1945 T::Nonce: Into<U256> + TryFrom<U256>,
1946 CallOf<T>: SetWeightLimit,
1947 {
1948 log::debug!(target: LOG_TARGET, "eth_estimate_gas: {tx:?}");
1949
1950 let mut low = U256::zero();
1951 let mut high = Self::evm_block_gas_limit();
1952
1953 log::trace!(target: LOG_TARGET, "eth_estimate_gas starting with low={low}, high={high}");
1954
1955 let perform_balance_checks = if let Some(gas_limit) = tx.gas {
1959 high = gas_limit;
1960 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the gas limit high={high}");
1961 true
1962 } else {
1963 false
1964 };
1965
1966 let fee_cap = tx.max_fee_per_gas.or(tx.gas_price);
1968 if let (Some(fee_cap), Some(from), true) = (fee_cap, tx.from, perform_balance_checks) {
1969 let mut available_balance = Self::evm_balance(&from);
1970 if let Some(value) = tx.value {
1971 available_balance = available_balance.checked_sub(value).ok_or_else(|| {
1972 EthTransactError::Message("insufficient funds for value transfer".into())
1973 })?;
1974 }
1975 if let Some(allowance) = available_balance.checked_div(fee_cap) {
1976 if high > allowance && allowance != U256::zero() {
1977 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the user's allowance high={high} allowance={allowance}");
1978 high = allowance
1979 }
1980 }
1981 }
1982
1983 let dry_run_results = [high, Self::evm_max_extrinsic_weight_in_gas()].map(|gas_limit| {
1991 let mut transaction = tx.clone();
1992 transaction.gas = Some(gas_limit);
1993 let dry_run_config = config.clone().with_perform_balance_checks(perform_balance_checks);
1994 let eth_transact_result = with_transaction(|| {
1995 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
1996 transaction,
1997 dry_run_config,
1998 )))
1999 })
2000 .expect("Rollback shouldn't error out");
2001 (gas_limit, eth_transact_result)
2002 });
2003 let (gas_limit, first_dry_run_result) = match dry_run_results {
2004 [(gas_limit1, Ok(dry_run_result1)), (gas_limit2, Ok(dry_run_result2))] => {
2005 if dry_run_result2.eth_gas >= gas_limit2 {
2006 (gas_limit1, dry_run_result1)
2007 } else {
2008 (gas_limit2, dry_run_result2)
2009 }
2010 },
2011 [(gas_limit, Ok(dry_run_result)), (_, Err(_))] |
2012 [(_, Err(_)), (gas_limit, Ok(dry_run_result))] => (gas_limit, dry_run_result),
2013 [(_, Err(err)), (_, Err(..))] => return Err(err),
2014 };
2015 log::trace!(
2016 target: LOG_TARGET,
2017 "eth_estimate_gas first dry run succeeded with gas_limit={} consumed={}",
2018 gas_limit,
2019 first_dry_run_result.eth_gas
2020 );
2021 low = first_dry_run_result.eth_gas;
2022 high = gas_limit;
2023
2024 while low + U256::one() < high {
2025 log::trace!(target: LOG_TARGET, "eth_estimate_gas estimation iteration with low={low} high={high}");
2026 let error_ratio = high
2027 .checked_sub(low)
2028 .and_then(|value| value.checked_mul(U256::from(1000)))
2029 .and_then(|value| value.checked_div(high))
2030 .ok_or_else(|| {
2031 EthTransactError::Message(
2032 "failed to calculate error ratio in gas estimation".into(),
2033 )
2034 })?;
2035 if error_ratio <= U256::from(15) {
2036 log::trace!(
2037 target: LOG_TARGET,
2038 "eth_estimate_gas finished due to error ratio being less than 1.5% high={}",
2039 high
2040 );
2041 break;
2042 }
2043
2044 let mut midpoint = high
2045 .checked_sub(low)
2046 .and_then(|value| value.checked_div(U256::from(2)))
2047 .and_then(|value| value.checked_add(low))
2048 .ok_or_else(|| {
2049 EthTransactError::Message(
2050 "failed to calculate midpoint in gas estimation".into(),
2051 )
2052 })?;
2053
2054 if let Some(other_midpoint) = low.checked_mul(U256::from(2)) {
2055 if other_midpoint != U256::zero() {
2056 midpoint = midpoint.min(other_midpoint)
2057 }
2058 };
2059
2060 let mut transaction = tx.clone();
2061 transaction.gas = Some(midpoint);
2062 let dry_run_config = config.clone().with_perform_balance_checks(perform_balance_checks);
2063 let dry_run_result = with_transaction(|| {
2064 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
2065 transaction,
2066 dry_run_config,
2067 )))
2068 })
2069 .expect("Rollback shouldn't error out");
2070 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run result with midpoint={midpoint} is dry_run_result={dry_run_result:?}");
2071 match dry_run_result {
2072 Ok(..) => {
2073 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run succeeded, new high={midpoint}");
2074 high = midpoint
2075 },
2076 Err(..) => {
2077 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run failed, new low={midpoint}");
2078 low = midpoint
2079 },
2080 }
2081 }
2082
2083 log::trace!(target: LOG_TARGET, "eth_estimate_gas completed. high={high}");
2084 Ok(high)
2085 }
2086
2087 pub fn eth_pre_dispatch_weight(transaction_encoded: Vec<u8>) -> Result<Weight, EthTransactError>
2095 where
2096 CallOf<T>: SetWeightLimit,
2097 {
2098 let signed_tx =
2099 crate::evm::TransactionSigned::decode(&transaction_encoded).map_err(|err| {
2100 EthTransactError::Message(format!("Failed to decode transaction: {err:?}"))
2101 })?;
2102 let signer_addr = signed_tx.recover_eth_address().map_err(|err| {
2103 EthTransactError::Message(format!("Failed to recover signer: {err:?}"))
2104 })?;
2105 let tx =
2106 GenericTransaction::from_signed(signed_tx, Self::evm_base_fee(), Some(signer_addr));
2107 let encoded_len = T::FeeInfo::encoded_len(
2108 crate::Call::<T>::eth_transact { payload: transaction_encoded.clone() }.into(),
2109 );
2110 let call_info = tx
2111 .into_call::<T>(CreateCallMode::ExtrinsicExecution(encoded_len, transaction_encoded))
2112 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2113 let info = T::FeeInfo::dispatch_info(&call_info.call);
2114
2115 Ok(frame_system::calculate_consumed_extrinsic_weight::<CallOf<T>>(
2116 &T::BlockWeights::get(),
2117 &info,
2118 call_info.encoded_len as usize,
2119 ))
2120 }
2121
2122 pub fn dry_run_eth_transact(
2128 mut tx: GenericTransaction,
2129 mut dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
2130 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
2131 where
2132 T::Nonce: Into<U256> + TryFrom<U256>,
2133 CallOf<T>: SetWeightLimit,
2134 {
2135 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
2136
2137 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
2138 Self::prepare_dry_run(&origin);
2139
2140 if let Some(overrides) = dry_run_config.state_overrides.take() {
2141 state_overrides::apply_state_overrides::<T>(overrides)?;
2142 }
2143
2144 let base_fee = Self::evm_base_fee();
2145 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
2146
2147 if effective_gas_price < base_fee {
2148 Err(EthTransactError::Message(format!(
2149 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
2150 )))?;
2151 }
2152
2153 if tx.nonce.is_none() {
2154 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
2155 }
2156 if tx.chain_id.is_none() {
2157 tx.chain_id = Some(T::ChainId::get().into());
2158 }
2159
2160 tx.gas_price = Some(effective_gas_price);
2162 tx.max_priority_fee_per_gas = Some(0.into());
2165 if tx.max_fee_per_gas.is_none() {
2166 tx.max_fee_per_gas = Some(effective_gas_price);
2167 }
2168
2169 let gas = tx.gas;
2170 if tx.gas.is_none() {
2171 tx.gas = Some(Self::evm_block_gas_limit());
2172 }
2173 if tx.r#type.is_none() {
2174 tx.r#type = Some(TYPE_EIP1559.into());
2175 }
2176
2177 let value = tx.value.unwrap_or_default();
2179 let input = tx.input.clone().to_vec();
2180 let from = tx.from;
2181 let to = tx.to;
2182
2183 let mut call_info = tx
2186 .into_call::<T>(CreateCallMode::DryRun)
2187 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2188
2189 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
2193 let base_weight = base_info.total_weight();
2194 let perform_balance_checks = dry_run_config.perform_balance_checks;
2195 let exec_config =
2196 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
2197 .with_dry_run(dry_run_config);
2198
2199 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
2201 if let Some(from) = &from {
2202 let fees = if gas.is_some() && matches!(perform_balance_checks, Some(true)) {
2203 fees
2204 } else {
2205 Zero::zero()
2206 };
2207 let balance = Self::evm_balance(from);
2208 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
2209 return Err(EthTransactError::Message(format!(
2210 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
2211 )));
2212 }
2213 }
2214
2215 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
2218
2219 let extract_error = |err| {
2220 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
2221 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
2222 } else {
2223 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
2224 }
2225 };
2226
2227 let transaction_limits = TransactionLimits::EthereumGas {
2228 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
2229 weight_limit: Self::evm_max_extrinsic_weight(),
2230 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
2231 };
2232
2233 let mut dry_run = match to {
2235 Some(dest) => {
2237 if dest == RUNTIME_PALLETS_ADDR {
2238 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
2239 return Err(EthTransactError::Message(format!(
2240 "Failed to decode pallet-call {input:?}"
2241 )));
2242 };
2243
2244 if let Err(result) =
2245 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
2246 {
2247 return Err(EthTransactError::Message(format!(
2248 "Failed to dispatch call: {:?}",
2249 result.error,
2250 )));
2251 };
2252
2253 Default::default()
2254 } else {
2255 let result = crate::Pallet::<T>::bare_call(
2257 OriginFor::<T>::signed(origin),
2258 dest,
2259 value,
2260 transaction_limits,
2261 input.clone(),
2262 &exec_config,
2263 );
2264
2265 let data = match result.result {
2266 Ok(return_value) => {
2267 if return_value.did_revert() {
2268 return Err(EthTransactError::Data(return_value.data));
2269 }
2270 return_value.data
2271 },
2272 Err(err) => {
2273 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
2274 return extract_error(err);
2275 },
2276 };
2277
2278 EthTransactInfo {
2279 weight_required: result.weight_required,
2280 storage_deposit: result.storage_deposit.charge_or_zero(),
2281 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2282 data,
2283 eth_gas: Default::default(),
2284 }
2285 }
2286 },
2287 None => {
2289 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2291 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
2292 } else {
2293 (input, vec![])
2294 };
2295
2296 let result = crate::Pallet::<T>::bare_instantiate(
2298 OriginFor::<T>::signed(origin),
2299 value,
2300 transaction_limits,
2301 Code::Upload(code.clone()),
2302 data.clone(),
2303 None,
2304 &exec_config,
2305 );
2306
2307 let returned_data = match result.result {
2308 Ok(return_value) => {
2309 if return_value.result.did_revert() {
2310 return Err(EthTransactError::Data(return_value.result.data));
2311 }
2312 return_value.result.data
2313 },
2314 Err(err) => {
2315 log::debug!(target: LOG_TARGET, "Failed to instantiate: {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: returned_data,
2325 eth_gas: Default::default(),
2326 }
2327 },
2328 };
2329
2330 call_info.call.set_weight_limit(dry_run.weight_required);
2332
2333 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
2335 let max_weight = Self::evm_max_extrinsic_weight();
2336 if total_weight.any_gt(max_weight) {
2337 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
2338 total_weight={total_weight:?} \
2339 max_weight={max_weight:?}",
2340 );
2341
2342 Err(EthTransactError::Message(format!(
2343 "\
2344 The transaction consumes more than the allowed weight. \
2345 needed={total_weight} \
2346 allowed={max_weight} \
2347 overweight_by={}\
2348 ",
2349 total_weight.saturating_sub(max_weight),
2350 )))?;
2351 }
2352
2353 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
2355 let available_fee = T::FeeInfo::remaining_txfee();
2356 if transaction_fee > available_fee {
2357 Err(EthTransactError::Message(format!(
2358 "Not enough gas supplied: Off by: {:?}",
2359 transaction_fee.saturating_sub(available_fee),
2360 )))?;
2361 }
2362
2363 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2364 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2365 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2366 if !rest.is_zero() {
2367 eth_gas = eth_gas.saturating_add(1_u32.into());
2368 }
2369
2370 log::debug!(target: LOG_TARGET, "\
2371 dry_run_eth_transact finished: \
2372 weight_limit={}, \
2373 total_weight={total_weight}, \
2374 max_weight={max_weight}, \
2375 weight_left={}, \
2376 eth_gas={eth_gas}, \
2377 encoded_len={}, \
2378 tx_fee={transaction_fee:?}, \
2379 storage_deposit={:?}, \
2380 max_storage_deposit={:?}\
2381 ",
2382 dry_run.weight_required,
2383 max_weight.saturating_sub(total_weight),
2384 call_info.encoded_len,
2385 dry_run.storage_deposit,
2386 dry_run.max_storage_deposit,
2387
2388 );
2389 dry_run.eth_gas = eth_gas;
2390 Ok(dry_run)
2391 }
2392
2393 pub fn evm_balance(address: &H160) -> U256 {
2397 let balance = AccountInfo::<T>::balance_of((*address).into());
2398 Self::convert_native_to_evm(balance)
2399 }
2400
2401 pub fn eth_block() -> EthBlock {
2403 EthereumBlock::<T>::get()
2404 }
2405
2406 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2413 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2414 let hash = <BlockHash<T>>::get(number);
2415 if hash == H256::zero() { None } else { Some(hash) }
2416 }
2417
2418 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2420 ReceiptInfoData::<T>::get()
2421 }
2422
2423 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2429 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2430 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2431 let account_id = T::AddressMapper::to_account_id(&address);
2432 T::Currency::set_balance(&account_id, balance);
2433 AccountInfoOf::<T>::mutate(&address, |account| {
2434 if let Some(account) = account {
2435 account.dust = dust;
2436 } else {
2437 *account = Some(AccountInfo { dust, ..Default::default() });
2438 }
2439 });
2440
2441 Ok(())
2442 }
2443
2444 pub fn new_balance_with_dust(
2448 evm_value: U256,
2449 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2450 let ed = T::Currency::minimum_balance();
2451 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2452 let (value, dust) = balance_with_dust.deconstruct();
2453
2454 Ok((ed.saturating_add(value), dust))
2455 }
2456
2457 pub fn evm_nonce(address: &H160) -> u32
2459 where
2460 T::Nonce: Into<u32>,
2461 {
2462 let account = T::AddressMapper::to_account_id(&address);
2463 System::<T>::account_nonce(account).into()
2464 }
2465
2466 pub fn evm_block_gas_limit() -> U256 {
2468 u64::MAX.into()
2475 }
2476
2477 pub fn evm_max_extrinsic_weight_in_gas() -> U256 {
2479 let max_extrinsic_fee = T::FeeInfo::weight_to_fee(&Self::evm_max_extrinsic_weight());
2480 let gas_scale: BalanceOf<T> = T::GasScale::get().into();
2481 (max_extrinsic_fee / gas_scale).into()
2482 }
2483
2484 pub fn evm_max_extrinsic_weight() -> Weight {
2486 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2487 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2488 .get(DispatchClass::Normal)
2489 .max_extrinsic
2490 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2491 Weight::from_parts(
2492 factor.saturating_mul_int(max_weight.ref_time()),
2493 factor.saturating_mul_int(max_weight.proof_size()),
2494 )
2495 }
2496
2497 pub fn evm_base_fee() -> U256 {
2499 let gas_scale = <T as Config>::GasScale::get();
2500 let multiplier = T::FeeInfo::next_fee_multiplier();
2501 multiplier
2502 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2503 .saturating_mul(gas_scale.saturated_into())
2504 .into()
2505 }
2506
2507 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2509 where
2510 T::Nonce: Into<u32>,
2511 {
2512 match tracer_type {
2513 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2514 TracerType::PrestateTracer(config) => {
2515 PrestateTracer::new(config.unwrap_or_default()).into()
2516 },
2517 TracerType::ExecutionTracer(config) => {
2518 ExecutionTracer::new(config.unwrap_or_default()).into()
2519 },
2520 }
2521 }
2522
2523 pub fn bare_upload_code(
2527 origin: OriginFor<T>,
2528 code: Vec<u8>,
2529 storage_deposit_limit: BalanceOf<T>,
2530 ) -> CodeUploadResult<BalanceOf<T>> {
2531 let origin = T::UploadOrigin::ensure_origin(origin)?;
2532
2533 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2534 BytecodeType::Pvm
2535 } else {
2536 if !T::AllowEVMBytecode::get() {
2537 return Err(<Error<T>>::CodeRejected.into());
2538 }
2539 BytecodeType::Evm
2540 };
2541
2542 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2543 weight_limit: Default::default(),
2544 deposit_limit: storage_deposit_limit,
2545 })?;
2546
2547 let module = Self::try_upload_code(
2548 origin,
2549 code,
2550 bytecode_type,
2551 &mut meter,
2552 &ExecConfig::new_substrate_tx(),
2553 )?;
2554 Ok(CodeUploadReturnValue {
2555 code_hash: *module.code_hash(),
2556 deposit: meter.deposit_consumed().charge_or_zero(),
2557 })
2558 }
2559
2560 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2562 let contract_info =
2563 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2564
2565 let maybe_value = contract_info.read(&Key::from_fixed(key));
2566 Ok(maybe_value)
2567 }
2568
2569 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2573 let immutable_data = <ImmutableDataOf<T>>::get(address);
2574 immutable_data
2575 }
2576
2577 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2585 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2586 <ImmutableDataOf<T>>::insert(address, data);
2587 Ok(())
2588 }
2589
2590 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2592 let contract_info =
2593 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2594
2595 let maybe_value = contract_info.read(
2596 &Key::try_from_var(key)
2597 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2598 .into(),
2599 );
2600 Ok(maybe_value)
2601 }
2602
2603 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2605 let (value, dust) = value.into().deconstruct();
2606 value
2607 .into()
2608 .saturating_mul(T::NativeToEthRatio::get().into())
2609 .saturating_add(dust.into())
2610 }
2611
2612 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2622 let contract_info =
2623 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2624
2625 contract_info
2626 .write(&Key::from_fixed(key), value, None, false)
2627 .map_err(ContractAccessError::StorageWriteFailed)
2628 }
2629
2630 pub fn set_storage_var_key(
2641 address: H160,
2642 key: Vec<u8>,
2643 value: Option<Vec<u8>>,
2644 ) -> SetStorageResult {
2645 let contract_info =
2646 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2647
2648 contract_info
2649 .write(
2650 &Key::try_from_var(key)
2651 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2652 .into(),
2653 value,
2654 None,
2655 false,
2656 )
2657 .map_err(ContractAccessError::StorageWriteFailed)
2658 }
2659
2660 pub fn account_id() -> T::AccountId {
2662 use frame_support::PalletId;
2663 use sp_runtime::traits::AccountIdConversion;
2664 PalletId(*b"py/reviv").into_account_truncating()
2665 }
2666
2667 pub fn block_author() -> H160 {
2669 use frame_support::traits::FindAuthor;
2670
2671 let digest = <frame_system::Pallet<T>>::digest();
2672 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2673
2674 T::FindAuthor::find_author(pre_runtime_digests)
2675 .map(|account_id| T::AddressMapper::to_address(&account_id))
2676 .unwrap_or_default()
2677 }
2678
2679 pub fn code(address: &H160) -> Vec<u8> {
2683 use precompiles::{All, Precompiles};
2684 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2685 return code.into();
2686 }
2687 AccountInfo::<T>::load_contract(&address)
2688 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2689 .map(|code| code.into())
2690 .unwrap_or_default()
2691 }
2692
2693 pub fn try_upload_code(
2695 origin: T::AccountId,
2696 code: Vec<u8>,
2697 code_type: BytecodeType,
2698 meter: &mut TransactionMeter<T>,
2699 exec_config: &ExecConfig<T>,
2700 ) -> Result<ContractBlob<T>, DispatchError> {
2701 let mut module = match code_type {
2702 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2703 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2704 };
2705 module.store_code(exec_config, meter)?;
2706 Ok(module)
2707 }
2708
2709 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2711 executing_contract::using_once(&mut false, || {
2712 executing_contract::with(|f| {
2713 if *f {
2715 return Err(())
2716 }
2717 *f = true;
2719 Ok(())
2720 })
2721 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2722 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2723 .map(|_| f())
2724 .and_then(|r| r)
2725 })
2726 }
2727
2728 fn charge_deposit(
2733 hold_reason: HoldReason,
2734 from: &T::AccountId,
2735 to: &T::AccountId,
2736 amount: BalanceOf<T>,
2737 exec_config: &ExecConfig<T>,
2738 ) -> DispatchResult {
2739 if amount.is_zero() {
2740 return Ok(());
2741 }
2742
2743 T::Deposit::charge_and_hold(hold_reason, exec_config.funds(from), to, amount)
2744 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2745 Ok(())
2746 }
2747
2748 fn refund_deposit(
2753 hold_reason: HoldReason,
2754 from: &T::AccountId,
2755 dst: deposit_payment::Funds<T::AccountId>,
2756 amount: BalanceOf<T>,
2757 ) -> Result<(), DispatchError> {
2758 if amount.is_zero() {
2759 return Ok(());
2760 }
2761
2762 let to = match &dst {
2763 deposit_payment::Funds::Balance(to) | deposit_payment::Funds::TxFee(to) => *to,
2764 };
2765 let result = T::Deposit::refund_on_hold(hold_reason, from, dst, amount);
2766
2767 result.defensive_map_err(|err| {
2768 let available = T::Deposit::total_on_hold(hold_reason, from);
2769 if available < amount {
2770 log::error!(
2773 target: LOG_TARGET,
2774 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}. Not enough deposit: {available:?}. This is a bug.",
2775 );
2776 Error::<T>::StorageRefundNotEnoughFunds.into()
2777 } else {
2778 log::warn!(
2783 target: LOG_TARGET,
2784 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}: {err:?}. First remove locks (staking, governance) from the contracts account.",
2785 );
2786 Error::<T>::StorageRefundLocked.into()
2787 }
2788 })
2789 }
2790
2791 fn has_dust(value: U256) -> bool {
2793 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2794 }
2795
2796 fn has_balance(value: U256) -> bool {
2798 value >= U256::from(<T>::NativeToEthRatio::get())
2799 }
2800
2801 #[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime", test))]
2803 fn min_balance() -> BalanceOf<T> {
2804 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2805 }
2806
2807 fn deposit_event(event: Event<T>) {
2812 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2813 }
2814
2815 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2817 match <T as Config>::RuntimeOrigin::from(origin).into() {
2818 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2819 _ => Err(BadOrigin.into()),
2820 }
2821 }
2822
2823 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2827 if DebugSettings::bypass_eip_3607::<T>() {
2828 return Ok(());
2829 }
2830 let Some(address) = origin
2831 .as_system_ref()
2832 .and_then(|o| o.as_signed())
2833 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2834 else {
2835 return Ok(());
2836 };
2837 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2838 <AccountInfo<T>>::is_contract(&address)
2839 {
2840 log::debug!(
2841 target: crate::LOG_TARGET,
2842 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2843 );
2844 Err(DispatchError::BadOrigin)
2845 } else {
2846 Ok(())
2847 }
2848 }
2849}
2850
2851pub const RUNTIME_PALLETS_ADDR: H160 =
2856 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2857
2858environmental!(executing_contract: bool);
2860
2861sp_api::decl_runtime_apis! {
2862 #[api_version(1)]
2864 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2865 AccountId: Codec,
2866 Balance: Codec,
2867 Nonce: Codec,
2868 BlockNumber: Codec,
2869 Moment: Codec,
2870 {
2871 fn eth_block() -> EthBlock;
2875
2876 fn eth_block_hash(number: U256) -> Option<H256>;
2878
2879 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2885
2886 fn block_gas_limit() -> U256;
2888
2889 fn max_extrinsic_weight_in_gas() -> U256;
2891
2892 fn balance(address: H160) -> U256;
2894
2895 fn gas_price() -> U256;
2897
2898 fn nonce(address: H160) -> Nonce;
2900
2901 fn call(
2905 origin: AccountId,
2906 dest: H160,
2907 value: Balance,
2908 gas_limit: Option<Weight>,
2909 storage_deposit_limit: Option<Balance>,
2910 input_data: Vec<u8>,
2911 ) -> ContractResult<ExecReturnValue, Balance>;
2912
2913 fn instantiate(
2917 origin: AccountId,
2918 value: Balance,
2919 gas_limit: Option<Weight>,
2920 storage_deposit_limit: Option<Balance>,
2921 code: Code,
2922 data: Vec<u8>,
2923 salt: Option<[u8; 32]>,
2924 ) -> ContractResult<InstantiateReturnValue, Balance>;
2925
2926
2927 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2932
2933 fn eth_transact_with_config(
2937 tx: GenericTransaction,
2938 config: DryRunConfig<Moment>,
2939 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2940
2941 fn eth_estimate_gas(
2947 tx: GenericTransaction,
2948 config: DryRunConfig<Moment>
2949 ) -> Result<U256, EthTransactError>;
2950
2951 fn eth_pre_dispatch_weight(tx: Vec<u8>) -> Result<Weight, EthTransactError>;
2953
2954 fn upload_code(
2958 origin: AccountId,
2959 code: Vec<u8>,
2960 storage_deposit_limit: Option<Balance>,
2961 ) -> CodeUploadResult<Balance>;
2962
2963 fn get_storage(
2969 address: H160,
2970 key: [u8; 32],
2971 ) -> GetStorageResult;
2972
2973 fn get_storage_var_key(
2979 address: H160,
2980 key: Vec<u8>,
2981 ) -> GetStorageResult;
2982
2983 fn trace_block(
2990 block: Block,
2991 config: TracerType
2992 ) -> Vec<(u32, Trace)>;
2993
2994 fn trace_tx(
3001 block: Block,
3002 tx_index: u32,
3003 config: TracerType
3004 ) -> Option<Trace>;
3005
3006 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
3010
3011 fn trace_call_with_config(
3017 tx: GenericTransaction,
3018 tracer_type: TracerType,
3019 config: TracingConfig,
3020 ) -> Result<Trace, EthTransactError>;
3021
3022 fn block_author() -> H160;
3024
3025 fn address(account_id: AccountId) -> H160;
3027
3028 fn account_id(address: H160) -> AccountId;
3030
3031 fn runtime_pallets_address() -> H160;
3033
3034 fn code(address: H160) -> Vec<u8>;
3036
3037 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
3039 }
3040}
3041
3042#[macro_export]
3056macro_rules! impl_runtime_apis_plus_revive_traits {
3057 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
3058
3059 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
3060
3061 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
3062 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
3063 use $crate::pallet::Call as ReviveCall;
3064 match self {
3065 Self::$Revive(
3066 ReviveCall::eth_call{ weight_limit, .. } |
3067 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
3068 ) => {
3069 let old = *weight_limit;
3070 *weight_limit = new_weight_limit;
3071 old
3072 },
3073 _ => Weight::default(),
3074 }
3075 }
3076 }
3077
3078 impl_runtime_apis! {
3079 $($rest)*
3080
3081
3082 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
3083 {
3084 fn eth_block() -> $crate::EthBlock {
3085 $crate::Pallet::<Self>::eth_block()
3086 }
3087
3088 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
3089 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
3090 }
3091
3092 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
3093 $crate::Pallet::<Self>::eth_receipt_data()
3094 }
3095
3096 fn balance(address: $crate::H160) -> $crate::U256 {
3097 $crate::Pallet::<Self>::evm_balance(&address)
3098 }
3099
3100 fn block_author() -> $crate::H160 {
3101 $crate::Pallet::<Self>::block_author()
3102 }
3103
3104 fn block_gas_limit() -> $crate::U256 {
3105 $crate::Pallet::<Self>::evm_block_gas_limit()
3106 }
3107
3108 fn max_extrinsic_weight_in_gas() -> $crate::U256 {
3109 $crate::Pallet::<Self>::evm_max_extrinsic_weight_in_gas()
3110 }
3111
3112 fn gas_price() -> $crate::U256 {
3113 $crate::Pallet::<Self>::evm_base_fee()
3114 }
3115
3116 fn nonce(address: $crate::H160) -> Nonce {
3117 use $crate::AddressMapper;
3118 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
3119 $crate::frame_system::Pallet::<Self>::account_nonce(account)
3120 }
3121
3122 fn address(account_id: AccountId) -> $crate::H160 {
3123 use $crate::AddressMapper;
3124 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
3125 }
3126
3127 fn eth_transact(
3128 tx: $crate::evm::GenericTransaction,
3129 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3130 use $crate::{
3131 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3132 sp_runtime::traits::TransactionExtension,
3133 sp_runtime::traits::Block as BlockT
3134 };
3135 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
3136 }
3137
3138 fn eth_transact_with_config(
3139 tx: $crate::evm::GenericTransaction,
3140 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3141 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3142 use $crate::{
3143 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3144 sp_runtime::traits::TransactionExtension,
3145 sp_runtime::traits::Block as BlockT
3146 };
3147 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
3148 }
3149
3150 fn eth_estimate_gas(
3151 tx: $crate::evm::GenericTransaction,
3152 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3153 ) -> Result<$crate::U256, $crate::EthTransactError> {
3154 use $crate::{
3155 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3156 sp_runtime::traits::TransactionExtension,
3157 sp_runtime::traits::Block as BlockT
3158 };
3159 $crate::Pallet::<Self>::eth_estimate_gas(tx, config)
3160 }
3161
3162 fn eth_pre_dispatch_weight(
3163 tx: Vec<u8>,
3164 ) -> Result<$crate::Weight, $crate::EthTransactError> {
3165 $crate::Pallet::<Self>::eth_pre_dispatch_weight(tx)
3166 }
3167
3168 fn call(
3169 origin: AccountId,
3170 dest: $crate::H160,
3171 value: Balance,
3172 weight_limit: Option<$crate::Weight>,
3173 storage_deposit_limit: Option<Balance>,
3174 input_data: Vec<u8>,
3175 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
3176 use $crate::frame_support::traits::Get;
3177 let blockweights: $crate::BlockWeights =
3178 <Self as $crate::frame_system::Config>::BlockWeights::get();
3179
3180 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3181 $crate::Pallet::<Self>::bare_call(
3182 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3183 dest,
3184 $crate::Pallet::<Self>::convert_native_to_evm(value),
3185 $crate::TransactionLimits::WeightAndDeposit {
3186 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3187 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3188 },
3189 input_data,
3190 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3191 )
3192 }
3193
3194 fn instantiate(
3195 origin: AccountId,
3196 value: Balance,
3197 weight_limit: Option<$crate::Weight>,
3198 storage_deposit_limit: Option<Balance>,
3199 code: $crate::Code,
3200 data: Vec<u8>,
3201 salt: Option<[u8; 32]>,
3202 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
3203 use $crate::frame_support::traits::Get;
3204 let blockweights: $crate::BlockWeights =
3205 <Self as $crate::frame_system::Config>::BlockWeights::get();
3206
3207 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3208 $crate::Pallet::<Self>::bare_instantiate(
3209 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3210 $crate::Pallet::<Self>::convert_native_to_evm(value),
3211 $crate::TransactionLimits::WeightAndDeposit {
3212 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3213 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3214 },
3215 code,
3216 data,
3217 salt,
3218 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3219 )
3220 }
3221
3222 fn upload_code(
3223 origin: AccountId,
3224 code: Vec<u8>,
3225 storage_deposit_limit: Option<Balance>,
3226 ) -> $crate::CodeUploadResult<Balance> {
3227 let origin =
3228 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
3229 $crate::Pallet::<Self>::bare_upload_code(
3230 origin,
3231 code,
3232 storage_deposit_limit.unwrap_or(u128::MAX),
3233 )
3234 }
3235
3236 fn get_storage_var_key(
3237 address: $crate::H160,
3238 key: Vec<u8>,
3239 ) -> $crate::GetStorageResult {
3240 $crate::Pallet::<Self>::get_storage_var_key(address, key)
3241 }
3242
3243 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
3244 $crate::Pallet::<Self>::get_storage(address, key)
3245 }
3246
3247 fn trace_block(
3248 block: Block,
3249 tracer_type: $crate::evm::TracerType,
3250 ) -> Vec<(u32, $crate::evm::Trace)> {
3251 use $crate::{sp_runtime::traits::Block, tracing::trace};
3252
3253 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3254 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3255 {
3256 return Default::default()
3257 }
3258
3259 let mut traces = vec![];
3260 let (header, extrinsics) = block.deconstruct();
3261 <$Executive>::initialize_block(&header);
3262 for (index, ext) in extrinsics.into_iter().enumerate() {
3263 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3264 let t = tracer.as_tracing();
3265 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3266
3267 if let Some(tx_trace) = tracer.collect_trace() {
3268 traces.push((index as u32, tx_trace));
3269 }
3270 }
3271
3272 traces
3273 }
3274
3275 fn trace_tx(
3276 block: Block,
3277 tx_index: u32,
3278 tracer_type: $crate::evm::TracerType,
3279 ) -> Option<$crate::evm::Trace> {
3280 use $crate::{sp_runtime::traits::Block, tracing::trace};
3281
3282 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3283 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3284 {
3285 return None
3286 }
3287
3288 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
3289 let (header, extrinsics) = block.deconstruct();
3290
3291 <$Executive>::initialize_block(&header);
3292 for (index, ext) in extrinsics.into_iter().enumerate() {
3293 if index as u32 == tx_index {
3294 let t = tracer.as_tracing();
3295 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3296 break;
3297 } else {
3298 let _ = <$Executive>::apply_extrinsic(ext);
3299 }
3300 }
3301
3302 tracer.collect_trace()
3303 }
3304
3305 fn trace_call(
3306 tx: $crate::evm::GenericTransaction,
3307 tracer_type: $crate::evm::TracerType,
3308 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
3309 use $crate::tracing::trace;
3310
3311 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3312 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3313 {
3314 return Err($crate::EthTransactError::Message("Execution Tracing is disabled".into()))
3315 }
3316
3317 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3318 let t = tracer.as_tracing();
3319
3320 t.watch_address(&tx.from.unwrap_or_default());
3321 t.watch_address(&$crate::Pallet::<Self>::block_author());
3322 let result = trace(t, || Self::eth_transact(tx));
3323
3324 if let Some(trace) = tracer.collect_trace() {
3325 Ok(trace)
3326 } else if let Err(err) = result {
3327 Err(err)
3328 } else {
3329 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
3330 }
3331 }
3332
3333 fn trace_call_with_config(
3334 tx: $crate::evm::GenericTransaction,
3335 tracer_type: $crate::evm::TracerType,
3336 config: $crate::evm::TracingConfig,
3337 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
3338 let $crate::evm::TracingConfig { state_overrides } = config;
3339
3340 if let Some(overrides) = state_overrides {
3341 $crate::state_overrides::apply_state_overrides::<Runtime>(overrides)?;
3342 }
3343
3344 Self::trace_call(tx, tracer_type)
3345 }
3346
3347 fn runtime_pallets_address() -> $crate::H160 {
3348 $crate::RUNTIME_PALLETS_ADDR
3349 }
3350
3351 fn code(address: $crate::H160) -> Vec<u8> {
3352 $crate::Pallet::<Self>::code(&address)
3353 }
3354
3355 fn account_id(address: $crate::H160) -> AccountId {
3356 use $crate::AddressMapper;
3357 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
3358 }
3359
3360 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
3361 $crate::Pallet::<Self>::new_balance_with_dust(balance)
3362 }
3363 }
3364 }
3365 };
3366}