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, keccak_256};
124pub use sp_runtime;
125pub use weights::WeightInfo;
126
127#[cfg(doc)]
128pub use crate::vm::pvm::SyscallDoc;
129
130pub type BalanceOf<T> = <T as Config>::Balance;
131pub type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
132type TrieId = BoundedVec<u8, ConstU32<128>>;
133type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
134type CallOf<T> = <T as Config>::RuntimeCall;
135
136const SENTINEL: u32 = u32::MAX;
143
144const LOG_TARGET: &str = "runtime::revive";
150
151#[frame_support::pallet]
152pub mod pallet {
153 use super::*;
154 use frame_support::{pallet_prelude::*, traits::FindAuthor};
155 use frame_system::pallet_prelude::*;
156 use sp_core::U256;
157 use sp_runtime::Perbill;
158
159 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
161
162 #[pallet::pallet]
163 #[pallet::storage_version(STORAGE_VERSION)]
164 pub struct Pallet<T>(_);
165
166 #[pallet::config(with_default)]
167 pub trait Config: frame_system::Config {
168 type Time: Time<Moment: Into<U256>>;
170
171 #[pallet::no_default]
175 type Balance: Balance
176 + TryFrom<U256>
177 + Into<U256>
178 + Bounded
179 + UniqueSaturatedInto<u64>
180 + UniqueSaturatedFrom<u64>
181 + UniqueSaturatedInto<u128>;
182
183 #[pallet::no_default]
185 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
186 + Mutate<Self::AccountId>
187 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
188 + Balanced<Self::AccountId>;
189
190 #[pallet::no_default_bounds]
197 type OnBurn: OnUnbalanced<CreditOf<Self>>;
198
199 #[pallet::no_default_bounds]
201 #[allow(deprecated)]
202 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
203
204 #[pallet::no_default_bounds]
206 type RuntimeCall: Parameter
207 + Dispatchable<
208 RuntimeOrigin = OriginFor<Self>,
209 Info = DispatchInfo,
210 PostInfo = PostDispatchInfo,
211 > + IsType<<Self as frame_system::Config>::RuntimeCall>
212 + From<Call<Self>>
213 + IsSubType<Call<Self>>
214 + GetDispatchInfo;
215
216 #[pallet::no_default_bounds]
218 type RuntimeOrigin: IsType<OriginFor<Self>>
219 + From<Origin<Self>>
220 + Into<Result<Origin<Self>, OriginFor<Self>>>;
221
222 #[pallet::no_default_bounds]
224 type RuntimeHoldReason: From<HoldReason>;
225
226 type WeightInfo: WeightInfo;
229
230 #[pallet::no_default_bounds]
234 #[allow(private_bounds)]
235 type Precompiles: precompiles::Precompiles<Self>;
236
237 type FindAuthor: FindAuthor<Self::AccountId>;
239
240 #[pallet::constant]
246 #[pallet::no_default_bounds]
247 type DepositPerByte: Get<BalanceOf<Self>>;
248
249 #[pallet::constant]
255 #[pallet::no_default_bounds]
256 type DepositPerItem: Get<BalanceOf<Self>>;
257
258 #[pallet::constant]
268 #[pallet::no_default_bounds]
269 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
270
271 #[pallet::constant]
275 type CodeHashLockupDepositPercent: Get<Perbill>;
276
277 #[pallet::no_default]
279 type AddressMapper: AddressMapper<Self>;
280
281 #[pallet::constant]
283 type AllowEVMBytecode: Get<bool>;
284
285 #[pallet::no_default_bounds]
290 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
291
292 #[pallet::no_default_bounds]
303 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
304
305 type RuntimeMemory: Get<u32>;
310
311 type PVFMemory: Get<u32>;
319
320 #[pallet::constant]
325 type ChainId: Get<u64>;
326
327 #[pallet::constant]
329 type NativeToEthRatio: Get<u32>;
330
331 #[pallet::no_default_bounds]
336 type FeeInfo: FeeInfo<Self>;
337
338 #[pallet::no_default_bounds]
341 type Deposit: Deposit<Self>;
342
343 #[pallet::constant]
356 type MaxEthExtrinsicWeight: Get<FixedU128>;
357
358 #[pallet::constant]
360 type DebugEnabled: Get<bool>;
361
362 #[pallet::constant]
369 type AutoMap: Get<bool>;
370
371 #[pallet::constant]
389 #[pallet::no_default_bounds]
390 type GasScale: Get<u32>;
391 }
392
393 pub mod config_preludes {
395 use super::*;
396 use frame_support::{
397 derive_impl,
398 traits::{ConstBool, ConstU32},
399 };
400 use frame_system::EnsureSigned;
401 use sp_core::parameter_types;
402
403 type Balance = u64;
404
405 pub const DOLLARS: Balance = 1_000_000_000_000;
406 pub const CENTS: Balance = DOLLARS / 100;
407 pub const MILLICENTS: Balance = CENTS / 1_000;
408
409 pub const fn deposit(items: u32, bytes: u32) -> Balance {
410 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
411 }
412
413 parameter_types! {
414 pub const DepositPerItem: Balance = deposit(1, 0);
415 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
416 pub const DepositPerByte: Balance = deposit(0, 1);
417 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
418 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
419 pub const GasScale: u32 = 10u32;
420 }
421
422 pub struct TestDefaultConfig;
424
425 impl Time for TestDefaultConfig {
426 type Moment = u64;
427 fn now() -> Self::Moment {
428 0u64
429 }
430 }
431
432 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
433 fn convert(w: Weight) -> T {
434 w.ref_time().into()
435 }
436 }
437
438 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
439 impl frame_system::DefaultConfig for TestDefaultConfig {}
440
441 #[frame_support::register_default_impl(TestDefaultConfig)]
442 impl DefaultConfig for TestDefaultConfig {
443 #[inject_runtime_type]
444 type RuntimeEvent = ();
445
446 #[inject_runtime_type]
447 type RuntimeHoldReason = ();
448
449 #[inject_runtime_type]
450 type RuntimeCall = ();
451
452 #[inject_runtime_type]
453 type RuntimeOrigin = ();
454
455 type Precompiles = ();
456 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
457 type DepositPerByte = DepositPerByte;
458 type DepositPerItem = DepositPerItem;
459 type DepositPerChildTrieItem = DepositPerChildTrieItem;
460 type Time = Self;
461 type AllowEVMBytecode = ConstBool<true>;
462 type UploadOrigin = EnsureSigned<Self::AccountId>;
463 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
464 type WeightInfo = ();
465 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
466 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
467 type ChainId = ConstU64<42>;
468 type NativeToEthRatio = ConstU32<1_000_000>;
469 type FindAuthor = ();
470 type FeeInfo = ();
471 type Deposit = ();
472 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
473 type DebugEnabled = ConstBool<false>;
474 type AutoMap = ConstBool<false>;
475 type GasScale = GasScale;
476 type OnBurn = ();
477 }
478 }
479
480 #[pallet::event]
481 pub enum Event<T: Config> {
482 ContractEmitted {
484 contract: H160,
486 data: Vec<u8>,
489 topics: Vec<H256>,
492 },
493
494 Instantiated { deployer: H160, contract: H160 },
496
497 EthExtrinsicRevert { dispatch_error: DispatchError },
504 }
505
506 #[pallet::error]
507 #[repr(u8)]
508 pub enum Error<T> {
509 InvalidSchedule = 0x01,
511 InvalidCallFlags = 0x02,
513 OutOfGas = 0x03,
515 TransferFailed = 0x04,
518 MaxCallDepthReached = 0x05,
521 ContractNotFound = 0x06,
523 CodeNotFound = 0x07,
525 CodeInfoNotFound = 0x08,
527 OutOfBounds = 0x09,
529 DecodingFailed = 0x0A,
531 ContractTrapped = 0x0B,
533 ValueTooLarge = 0x0C,
535 TerminatedWhileReentrant = 0x0D,
538 InputForwarded = 0x0E,
540 TooManyTopics = 0x0F,
542 DuplicateContract = 0x12,
544 TerminatedInConstructor = 0x13,
548 ReentranceDenied = 0x14,
550 ReenteredPallet = 0x15,
552 StateChangeDenied = 0x16,
554 StorageDepositNotEnoughFunds = 0x17,
556 StorageDepositLimitExhausted = 0x18,
558 CodeInUse = 0x19,
560 ContractReverted = 0x1A,
565 CodeRejected = 0x1B,
570 BlobTooLarge = 0x1C,
572 StaticMemoryTooLarge = 0x1D,
574 BasicBlockTooLarge = 0x1E,
576 InvalidInstruction = 0x1F,
578 MaxDelegateDependenciesReached = 0x20,
580 DelegateDependencyNotFound = 0x21,
582 DelegateDependencyAlreadyExists = 0x22,
584 CannotAddSelfAsDelegateDependency = 0x23,
586 OutOfTransientStorage = 0x24,
588 InvalidSyscall = 0x25,
590 InvalidStorageFlags = 0x26,
592 ExecutionFailed = 0x27,
594 BalanceConversionFailed = 0x28,
596 InvalidImmutableAccess = 0x2A,
599 AccountUnmapped = 0x2B,
603 AccountAlreadyMapped = 0x2C,
605 InvalidGenericTransaction = 0x2D,
607 RefcountOverOrUnderflow = 0x2E,
609 UnsupportedPrecompileAddress = 0x2F,
611 CallDataTooLarge = 0x30,
613 ReturnDataTooLarge = 0x31,
615 InvalidJump = 0x32,
617 StackUnderflow = 0x33,
619 StackOverflow = 0x34,
621 TxFeeOverdraw = 0x35,
625 EvmConstructorNonEmptyData = 0x36,
629 EvmConstructedFromHash = 0x37,
634 StorageRefundNotEnoughFunds = 0x38,
638 StorageRefundLocked = 0x39,
643 PrecompileDelegateDenied = 0x40,
648 EcdsaRecoveryFailed = 0x41,
650 AutoMappingEnabled = 0x42,
652 PendingDepositCleanup = 0x43,
656 #[cfg(feature = "runtime-benchmarks")]
658 BenchmarkingError = 0xFF,
659 }
660
661 #[pallet::composite_enum]
663 pub enum HoldReason {
664 CodeUploadDepositReserve,
666 StorageDepositReserve,
668 AddressMapping,
670 }
671
672 #[pallet::composite_enum]
674 pub enum FreezeReason {
675 PGasMinBalance,
680 }
681
682 #[derive(
683 PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug,
684 )]
685 #[pallet::origin]
686 pub enum Origin<T: Config> {
687 EthTransaction(T::AccountId),
688 }
689
690 #[pallet::storage]
694 #[pallet::unbounded]
695 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
696
697 #[pallet::storage]
699 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
700
701 #[pallet::storage]
703 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
704
705 #[pallet::storage]
716 pub(crate) type NativeDepositOf<T: Config> = StorageDoubleMap<
717 _,
718 Identity,
719 T::AccountId,
720 Identity,
721 T::AccountId,
722 BalanceOf<T>,
723 ValueQuery,
724 >;
725
726 #[pallet::storage]
728 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
729
730 #[pallet::storage]
736 pub(crate) type DeletionQueue<T: Config> =
737 StorageMap<_, Twox64Concat, u32, crate::storage::DeletionQueueItem<T>>;
738
739 #[pallet::storage]
742 pub(crate) type DeletionQueueCounter<T: Config> =
743 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
744
745 #[pallet::storage]
752 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
753
754 #[pallet::storage]
764 #[pallet::unbounded]
765 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
766
767 #[pallet::storage]
771 pub(crate) type BlockHash<T: Config> =
772 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
773
774 #[pallet::storage]
781 #[pallet::unbounded]
782 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
783
784 #[pallet::storage]
786 #[pallet::unbounded]
787 pub(crate) type EthBlockBuilderIR<T: Config> =
788 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
789
790 #[pallet::storage]
795 #[pallet::unbounded]
796 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
797 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
798
799 #[pallet::storage]
801 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
802
803 pub mod genesis {
804 use super::*;
805 use crate::evm::Bytes32;
806
807 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
809 pub struct ContractData {
810 pub code: crate::evm::Bytes,
812 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
814 }
815
816 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
818 pub struct Account<T: Config> {
819 pub address: H160,
821 #[serde(default)]
823 pub balance: U256,
824 #[serde(default)]
826 pub nonce: T::Nonce,
827 #[serde(flatten, skip_serializing_if = "Option::is_none")]
829 pub contract_data: Option<ContractData>,
830 }
831 }
832
833 #[pallet::genesis_config]
834 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
835 pub struct GenesisConfig<T: Config> {
836 #[serde(default, skip_serializing_if = "Vec::is_empty")]
839 pub mapped_accounts: Vec<T::AccountId>,
840
841 #[serde(default, skip_serializing_if = "Vec::is_empty")]
843 pub accounts: Vec<genesis::Account<T>>,
844
845 #[serde(default, skip_serializing_if = "Option::is_none")]
847 pub debug_settings: Option<DebugSettings>,
848 }
849
850 #[pallet::genesis_build]
851 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
852 fn build(&self) {
853 use crate::{exec::Key, vm::ContractBlob};
854 use frame_support::traits::fungible::Mutate;
855
856 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
857 let _ = T::Currency::mint_into(
858 &Pallet::<T>::account_id(),
859 T::Currency::minimum_balance(),
860 );
861 }
862
863 for id in &self.mapped_accounts {
864 if let Err(err) = T::AddressMapper::map_no_deposit_unchecked(id) {
865 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
866 }
867 }
868
869 let owner = Pallet::<T>::account_id();
870
871 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
872 let account_id = T::AddressMapper::to_account_id(address);
873
874 if !System::<T>::account_exists(&account_id) {
875 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
876 }
877
878 frame_system::Account::<T>::mutate(&account_id, |info| {
879 info.nonce = (*nonce).into();
880 });
881
882 match contract_data {
883 None => {
884 AccountInfoOf::<T>::insert(
885 address,
886 AccountInfo { account_type: AccountType::EOA, dust: 0 },
887 );
888 },
889 Some(genesis::ContractData { code, storage }) => {
890 let blob = if code.0.starts_with(&polkavm_common::program::BLOB_MAGIC) {
891 ContractBlob::<T>::from_pvm_code(code.0.clone(), owner.clone())
892 .inspect_err(|err| {
893 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
894 })
895 } else {
896 ContractBlob::<T>::from_evm_runtime_code(code.0.clone(), account_id)
897 .inspect_err(|err| {
898 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
899 })
900 };
901
902 let Ok(blob) = blob else {
903 continue;
904 };
905
906 let code_hash = *blob.code_hash();
907 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
908 .inspect_err(|err| {
909 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
910 })
911 else {
912 continue;
913 };
914
915 AccountInfoOf::<T>::insert(
916 address,
917 AccountInfo { account_type: info.clone().into(), dust: 0 },
918 );
919
920 <PristineCode<T>>::insert(blob.code_hash(), code.0.clone());
921 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
922 for (k, v) in storage {
923 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
924 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
925 });
926 }
927 },
928 }
929
930 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
931 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
932 });
933 }
934
935 block_storage::on_finalize_build_eth_block::<T>(
937 frame_system::Pallet::<T>::block_number(),
940 );
941
942 if let Some(settings) = self.debug_settings.as_ref() {
944 settings.write_to_storage::<T>()
945 }
946 }
947 }
948
949 #[pallet::hooks]
950 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
951 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
952 let mut meter = WeightMeter::with_limit(limit);
953 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
954 meter.consumed()
955 }
956
957 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
958 block_storage::on_initialize::<T>();
960
961 System::<T>::account_exists(&Pallet::<T>::account_id());
963 <T as Config>::WeightInfo::on_finalize_block_fixed()
965 }
966
967 fn on_finalize(block_number: BlockNumberFor<T>) {
968 block_storage::on_finalize_build_eth_block::<T>(block_number);
970 }
971
972 fn integrity_test() {
973 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
974
975 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
976
977 T::FeeInfo::integrity_test();
978
979 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
981
982 const TOTAL_MEMORY_DEVIDER: u64 = 2;
985
986 let max_block_weight = T::BlockWeights::get()
992 .get(DispatchClass::Normal)
993 .max_total
994 .unwrap_or_else(|| T::BlockWeights::get().max_block);
995 let max_key_size: u64 =
996 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
997 .expect("Key of maximal size shall be created")
998 .hash()
999 .len()
1000 .try_into()
1001 .unwrap();
1002
1003 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
1004 let max_immutable_size: u64 = max_block_weight
1005 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
1006 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
1007 ))
1008 .unwrap()
1009 .saturating_mul(
1010 u64::from(limits::IMMUTABLE_BYTES)
1011 .saturating_add(max_immutable_key_size)
1012 .into(),
1013 );
1014
1015 let max_pvf_mem: u64 = T::PVFMemory::get().into();
1016 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
1017
1018 let max_events_size = max_block_weight
1022 .checked_div_per_component(
1023 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
1024 num_topic: 0,
1025 len: limits::EVENT_BYTES,
1026 })
1027 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
1028 &RuntimeCosts::HostFn,
1029 ))),
1030 )
1031 .unwrap()
1032 .saturating_mul(limits::EVENT_BYTES.into());
1033
1034 assert!(
1035 max_events_size <= storage_size_limit,
1036 "Maximal events size {} exceeds the events limit {}",
1037 max_events_size,
1038 storage_size_limit
1039 );
1040
1041 let max_eth_block_builder_bytes =
1076 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1077
1078 log::debug!(
1079 target: LOG_TARGET,
1080 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1081 max_eth_block_builder_bytes / 1024,
1082 max_events_size / 1024,
1083 );
1084
1085 let memory_left = i128::from(max_runtime_mem)
1090 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1091 .saturating_sub(limits::MEMORY_REQUIRED.into())
1092 .saturating_sub(max_eth_block_builder_bytes.into());
1093
1094 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1095
1096 assert!(
1097 memory_left >= 0,
1098 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1099 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1100 );
1101
1102 let max_storage_size = max_block_weight
1105 .checked_div_per_component(
1106 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1107 new_bytes: limits::STORAGE_BYTES,
1108 old_bytes: 0,
1109 })
1110 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1111 )
1112 .unwrap()
1113 .saturating_add(max_immutable_size.into())
1114 .saturating_add(max_eth_block_builder_bytes.into());
1115
1116 assert!(
1117 max_storage_size <= storage_size_limit,
1118 "Maximal storage size {} exceeds the storage limit {}",
1119 max_storage_size,
1120 storage_size_limit
1121 );
1122 }
1123 }
1124
1125 #[pallet::call]
1126 impl<T: Config> Pallet<T> {
1127 #[allow(unused_variables)]
1140 #[pallet::call_index(0)]
1141 #[pallet::weight(Weight::MAX)]
1142 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1143 Err(frame_system::Error::CallFiltered::<T>.into())
1144 }
1145
1146 #[pallet::call_index(1)]
1163 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1164 pub fn call(
1165 origin: OriginFor<T>,
1166 dest: H160,
1167 #[pallet::compact] value: BalanceOf<T>,
1168 weight_limit: Weight,
1169 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1170 data: Vec<u8>,
1171 ) -> DispatchResultWithPostInfo {
1172 Self::ensure_non_contract_if_signed(&origin)?;
1173 let mut output = Self::bare_call(
1174 origin,
1175 dest,
1176 Pallet::<T>::convert_native_to_evm(value),
1177 TransactionLimits::WeightAndDeposit {
1178 weight_limit,
1179 deposit_limit: storage_deposit_limit,
1180 },
1181 data,
1182 &ExecConfig::new_substrate_tx(),
1183 );
1184
1185 if let Ok(return_value) = &output.result &&
1186 return_value.did_revert()
1187 {
1188 output.result = Err(<Error<T>>::ContractReverted.into());
1189 }
1190 dispatch_result(
1191 output.result,
1192 output.weight_consumed,
1193 <T as Config>::WeightInfo::call(),
1194 )
1195 }
1196
1197 #[pallet::call_index(2)]
1203 #[pallet::weight(
1204 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1205 )]
1206 pub fn instantiate(
1207 origin: OriginFor<T>,
1208 #[pallet::compact] value: BalanceOf<T>,
1209 weight_limit: Weight,
1210 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1211 code_hash: sp_core::H256,
1212 data: Vec<u8>,
1213 salt: Option<[u8; 32]>,
1214 ) -> DispatchResultWithPostInfo {
1215 Self::ensure_non_contract_if_signed(&origin)?;
1216 let data_len = data.len() as u32;
1217 let mut output = Self::bare_instantiate(
1218 origin,
1219 Pallet::<T>::convert_native_to_evm(value),
1220 TransactionLimits::WeightAndDeposit {
1221 weight_limit,
1222 deposit_limit: storage_deposit_limit,
1223 },
1224 Code::Existing(code_hash),
1225 data,
1226 salt,
1227 &ExecConfig::new_substrate_tx(),
1228 );
1229 if let Ok(retval) = &output.result &&
1230 retval.result.did_revert()
1231 {
1232 output.result = Err(<Error<T>>::ContractReverted.into());
1233 }
1234 dispatch_result(
1235 output.result.map(|result| result.result),
1236 output.weight_consumed,
1237 <T as Config>::WeightInfo::instantiate(data_len),
1238 )
1239 }
1240
1241 #[pallet::call_index(3)]
1269 #[pallet::weight(
1270 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1271 .saturating_add(*weight_limit)
1272 )]
1273 pub fn instantiate_with_code(
1274 origin: OriginFor<T>,
1275 #[pallet::compact] value: BalanceOf<T>,
1276 weight_limit: Weight,
1277 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1278 code: Vec<u8>,
1279 data: Vec<u8>,
1280 salt: Option<[u8; 32]>,
1281 ) -> DispatchResultWithPostInfo {
1282 Self::ensure_non_contract_if_signed(&origin)?;
1283 let code_len = code.len() as u32;
1284 let data_len = data.len() as u32;
1285 let mut output = Self::bare_instantiate(
1286 origin,
1287 Pallet::<T>::convert_native_to_evm(value),
1288 TransactionLimits::WeightAndDeposit {
1289 weight_limit,
1290 deposit_limit: storage_deposit_limit,
1291 },
1292 Code::Upload(code),
1293 data,
1294 salt,
1295 &ExecConfig::new_substrate_tx(),
1296 );
1297 if let Ok(retval) = &output.result &&
1298 retval.result.did_revert()
1299 {
1300 output.result = Err(<Error<T>>::ContractReverted.into());
1301 }
1302 dispatch_result(
1303 output.result.map(|result| result.result),
1304 output.weight_consumed,
1305 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1306 )
1307 }
1308
1309 #[pallet::call_index(10)]
1331 #[pallet::weight(
1332 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1333 .saturating_add(*weight_limit)
1334 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1335 )]
1336 pub fn eth_instantiate_with_code(
1337 origin: OriginFor<T>,
1338 value: U256,
1339 weight_limit: Weight,
1340 eth_gas_limit: U256,
1341 code: Vec<u8>,
1342 data: Vec<u8>,
1343 transaction_encoded: Vec<u8>,
1344 effective_gas_price: U256,
1345 encoded_len: u32,
1346 ) -> DispatchResultWithPostInfo {
1347 let signer = Self::ensure_eth_signed(origin)?;
1348 let origin = OriginFor::<T>::signed(signer.clone());
1349 Self::ensure_non_contract_if_signed(&origin)?;
1350 let mut call = Call::<T>::eth_instantiate_with_code {
1351 value,
1352 weight_limit,
1353 eth_gas_limit,
1354 code: code.clone(),
1355 data: data.clone(),
1356 transaction_encoded: transaction_encoded.clone(),
1357 effective_gas_price,
1358 encoded_len,
1359 }
1360 .into();
1361 let info = T::FeeInfo::dispatch_info(&call);
1362 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1363 drop(call);
1364
1365 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1366 let extra_weight = base_info.total_weight();
1367 let output = Self::bare_instantiate(
1368 origin,
1369 value,
1370 TransactionLimits::EthereumGas {
1371 eth_gas_limit: eth_gas_limit.saturated_into(),
1372 weight_limit,
1373 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1374 },
1375 Code::Upload(code),
1376 data,
1377 None,
1378 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1379 );
1380
1381 block_storage::EthereumCallResult::new::<T>(
1382 signer,
1383 output.map_result(|r| r.result),
1384 base_info.call_weight,
1385 encoded_len,
1386 &info,
1387 effective_gas_price,
1388 )
1389 })
1390 }
1391
1392 #[pallet::call_index(11)]
1409 #[pallet::weight(
1410 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1411 .saturating_add(*weight_limit)
1412 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1413 )]
1414 pub fn eth_call(
1415 origin: OriginFor<T>,
1416 dest: H160,
1417 value: U256,
1418 weight_limit: Weight,
1419 eth_gas_limit: U256,
1420 data: Vec<u8>,
1421 transaction_encoded: Vec<u8>,
1422 effective_gas_price: U256,
1423 encoded_len: u32,
1424 ) -> DispatchResultWithPostInfo {
1425 let signer = Self::ensure_eth_signed(origin)?;
1426 let origin = OriginFor::<T>::signed(signer.clone());
1427
1428 Self::ensure_non_contract_if_signed(&origin)?;
1429 let mut call = Call::<T>::eth_call {
1430 dest,
1431 value,
1432 weight_limit,
1433 eth_gas_limit,
1434 data: data.clone(),
1435 transaction_encoded: transaction_encoded.clone(),
1436 effective_gas_price,
1437 encoded_len,
1438 }
1439 .into();
1440 let info = T::FeeInfo::dispatch_info(&call);
1441 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1442 drop(call);
1443
1444 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1445 let extra_weight = base_info.total_weight();
1446 let output = Self::bare_call(
1447 origin,
1448 dest,
1449 value,
1450 TransactionLimits::EthereumGas {
1451 eth_gas_limit: eth_gas_limit.saturated_into(),
1452 weight_limit,
1453 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1454 },
1455 data,
1456 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1457 );
1458
1459 block_storage::EthereumCallResult::new::<T>(
1460 signer,
1461 output,
1462 base_info.call_weight,
1463 encoded_len,
1464 &info,
1465 effective_gas_price,
1466 )
1467 })
1468 }
1469
1470 #[pallet::call_index(12)]
1481 #[pallet::weight(
1482 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32)
1483 .saturating_add(call.get_dispatch_info().call_weight)
1484 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1485 )]
1486 pub fn eth_substrate_call(
1487 origin: OriginFor<T>,
1488 call: Box<<T as Config>::RuntimeCall>,
1489 transaction_encoded: Vec<u8>,
1490 ) -> DispatchResultWithPostInfo {
1491 let signer = Self::ensure_eth_signed(origin)?;
1494 Self::ensure_non_contract_if_signed(&OriginFor::<T>::signed(signer.clone()))?;
1495 let tx_len = transaction_encoded.len() as u32;
1496 let weight_overhead = T::WeightInfo::eth_substrate_call(tx_len)
1497 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(tx_len));
1498
1499 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1500 let call_weight = call.get_dispatch_info().call_weight;
1501 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1502
1503 match &mut call_result {
1505 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1506 post_info.actual_weight = Some(
1507 post_info
1508 .actual_weight
1509 .unwrap_or_else(|| call_weight)
1510 .saturating_add(weight_overhead),
1511 );
1512 },
1513 }
1514
1515 block_storage::EthereumCallResult {
1518 receipt_gas_info: ReceiptGasInfo::default(),
1519 result: call_result,
1520 }
1521 })
1522 }
1523
1524 #[pallet::call_index(4)]
1539 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1540 pub fn upload_code(
1541 origin: OriginFor<T>,
1542 code: Vec<u8>,
1543 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1544 ) -> DispatchResult {
1545 Self::ensure_non_contract_if_signed(&origin)?;
1546 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1547 }
1548
1549 #[pallet::call_index(5)]
1554 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1555 pub fn remove_code(
1556 origin: OriginFor<T>,
1557 code_hash: sp_core::H256,
1558 ) -> DispatchResultWithPostInfo {
1559 let origin = ensure_signed(origin)?;
1560 <ContractBlob<T>>::remove(&origin, code_hash)?;
1561 Ok(Pays::No.into())
1563 }
1564
1565 #[pallet::call_index(6)]
1576 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1577 pub fn set_code(
1578 origin: OriginFor<T>,
1579 dest: H160,
1580 code_hash: sp_core::H256,
1581 ) -> DispatchResult {
1582 ensure_root(origin)?;
1583 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1584 let Some(account) = account else {
1585 return Err(<Error<T>>::ContractNotFound.into());
1586 };
1587
1588 let AccountType::Contract(ref mut contract) = account.account_type else {
1589 return Err(<Error<T>>::ContractNotFound.into());
1590 };
1591
1592 <CodeInfo<T>>::increment_refcount(code_hash)?;
1593 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1594 contract.code_hash = code_hash;
1595
1596 Ok(())
1597 })
1598 }
1599
1600 #[pallet::call_index(7)]
1608 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1609 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1610 #[cfg(not(feature = "runtime-benchmarks"))]
1611 if T::AutoMap::get() {
1612 return Ok(());
1613 }
1614
1615 Self::ensure_non_contract_if_signed(&origin)?;
1616 let origin = ensure_signed(origin)?;
1617 T::AddressMapper::map(&origin)
1618 }
1619
1620 #[pallet::call_index(13)]
1622 #[pallet::weight(<T as Config>::WeightInfo::batch_map_accounts(accounts.len().saturated_into::<u32>()))]
1623 pub fn batch_map_accounts(
1624 origin: OriginFor<T>,
1625 accounts: Vec<T::AccountId>,
1626 ) -> DispatchResultWithPostInfo {
1627 ensure_signed(origin.clone())?;
1628 Self::ensure_non_contract_if_signed(&origin)?;
1629
1630 let total: u32 = accounts.len().saturated_into();
1631 let mut mapped = 0;
1632
1633 for account_id in accounts
1634 .iter()
1635 .filter(|&a| !T::AddressMapper::is_eth_derived(a))
1637 .filter(|&a| frame_system::Pallet::<T>::account_exists(a))
1640 {
1641 let mut useful = false;
1642
1643 match T::AddressMapper::map_no_deposit_unchecked(account_id) {
1644 Ok(()) => {
1645 useful = true;
1646 },
1647 Err(err) => log::debug!(
1648 target: LOG_TARGET,
1649 "Failed to map account {account_id:?}: {err:?}",
1650 ),
1651 }
1652
1653 match T::Currency::release_all(
1654 &HoldReason::AddressMapping.into(),
1655 account_id,
1656 Precision::BestEffort,
1657 ) {
1658 Ok(released) if !released.is_zero() => {
1661 useful = true;
1662 },
1663 Ok(_) => {},
1664 Err(err) => log::debug!(
1665 target: LOG_TARGET,
1666 "Failed to release mapping deposit for {account_id:?}: {err:?}",
1667 ),
1668 }
1669
1670 if useful {
1671 mapped = mapped.saturating_add(1);
1672 }
1673 }
1674
1675 if total == 0 || mapped == 0 {
1677 return Ok(Pays::Yes.into());
1678 }
1679
1680 let proportion_mapped = Perbill::from_rational(mapped, total);
1681 if proportion_mapped >= Perbill::from_percent(90) {
1682 Ok(Pays::No.into())
1683 } else {
1684 Ok(Pays::Yes.into())
1685 }
1686 }
1687
1688 #[pallet::call_index(8)]
1696 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1697 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1698 #[cfg(not(feature = "runtime-benchmarks"))]
1699 ensure!(!T::AutoMap::get(), <Error<T>>::AutoMappingEnabled);
1700 let origin = ensure_signed(origin)?;
1701 T::AddressMapper::unmap(&origin)
1702 }
1703
1704 #[pallet::call_index(9)]
1710 #[pallet::weight({
1711 let dispatch_info = call.get_dispatch_info();
1712 (
1713 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1714 dispatch_info.class
1715 )
1716 })]
1717 pub fn dispatch_as_fallback_account(
1718 mut origin: OriginFor<T>,
1719 call: Box<<T as Config>::RuntimeCall>,
1720 ) -> DispatchResultWithPostInfo {
1721 Self::ensure_non_contract_if_signed(&origin)?;
1722 let account_id = origin.as_signer().ok_or(DispatchError::BadOrigin)?;
1723 let unmapped_account = T::AddressMapper::to_fallback_account_id(
1724 &T::AddressMapper::to_address(&account_id),
1725 );
1726 origin.set_caller_from(RawOrigin::Signed(unmapped_account));
1727 call.dispatch(origin)
1728 }
1729 }
1730}
1731
1732fn dispatch_result<R>(
1734 result: Result<R, DispatchError>,
1735 weight_consumed: Weight,
1736 base_weight: Weight,
1737) -> DispatchResultWithPostInfo {
1738 let post_info = PostDispatchInfo {
1739 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1740 pays_fee: Default::default(),
1741 };
1742
1743 result
1744 .map(|_| post_info)
1745 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1746}
1747
1748impl<T: Config> Pallet<T> {
1749 pub fn bare_call(
1756 origin: OriginFor<T>,
1757 dest: H160,
1758 evm_value: U256,
1759 transaction_limits: TransactionLimits<T>,
1760 data: Vec<u8>,
1761 exec_config: &ExecConfig<T>,
1762 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1763 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1764 Ok(transaction_meter) => transaction_meter,
1765 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1766 };
1767 let mut storage_deposit = Default::default();
1768
1769 let try_call = || {
1770 let origin = ExecOrigin::from_runtime_origin(origin)?;
1771 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1772 origin.clone(),
1773 dest,
1774 &mut transaction_meter,
1775 evm_value,
1776 data,
1777 &exec_config,
1778 )?;
1779
1780 storage_deposit = transaction_meter
1781 .execute_postponed_deposits(&origin, &exec_config)
1782 .inspect_err(|err| {
1783 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1784 })?;
1785
1786 Ok(result)
1787 };
1788 let result = Self::run_guarded(try_call);
1789
1790 log::trace!(target: LOG_TARGET, "Bare call ends: \
1791 result={result:?}, \
1792 weight_consumed={:?}, \
1793 weight_required={:?}, \
1794 storage_deposit={:?}, \
1795 gas_consumed={:?}, \
1796 max_storage_deposit={:?}",
1797 transaction_meter.weight_consumed(),
1798 transaction_meter.weight_required(),
1799 storage_deposit,
1800 transaction_meter.total_consumed_gas(),
1801 transaction_meter.deposit_required()
1802 );
1803
1804 ContractResult {
1805 result: result.map_err(|r| r.error),
1806 weight_consumed: transaction_meter.weight_consumed(),
1807 weight_required: transaction_meter.weight_required(),
1808 storage_deposit,
1809 gas_consumed: transaction_meter.total_consumed_gas(),
1810 max_storage_deposit: transaction_meter.deposit_required(),
1811 }
1812 }
1813
1814 pub fn prepare_dry_run(account: &T::AccountId) {
1820 frame_system::Pallet::<T>::inc_account_nonce(account);
1823 }
1824
1825 pub fn bare_instantiate(
1831 origin: OriginFor<T>,
1832 evm_value: U256,
1833 transaction_limits: TransactionLimits<T>,
1834 code: Code,
1835 data: Vec<u8>,
1836 salt: Option<[u8; 32]>,
1837 exec_config: &ExecConfig<T>,
1838 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1839 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1840 Ok(transaction_meter) => transaction_meter,
1841 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1842 };
1843
1844 let mut storage_deposit = Default::default();
1845
1846 let try_instantiate = || {
1847 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1848
1849 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1850 let executable = match code {
1851 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1852 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1853 let executable = Self::try_upload_code(
1854 upload_account,
1855 code,
1856 BytecodeType::Pvm,
1857 &mut transaction_meter,
1858 &exec_config,
1859 )?;
1860 executable
1861 },
1862 Code::Upload(code) => {
1863 if T::AllowEVMBytecode::get() {
1864 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1865 let origin = T::UploadOrigin::ensure_origin(origin)?;
1866 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1867 executable
1868 } else {
1869 return Err(<Error<T>>::CodeRejected.into());
1870 }
1871 },
1872 Code::Existing(code_hash) => {
1873 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1874 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1875 executable
1876 },
1877 };
1878 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1879 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1880 instantiate_account,
1881 executable,
1882 &mut transaction_meter,
1883 evm_value,
1884 data,
1885 salt.as_ref(),
1886 &exec_config,
1887 );
1888
1889 storage_deposit = transaction_meter
1890 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1891 .inspect_err(|err| {
1892 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1893 })?;
1894 result
1895 };
1896 let output = Self::run_guarded(try_instantiate);
1897
1898 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1899 weight_required={:?} \
1900 storage_deposit={:?} \
1901 gas_consumed={:?} \
1902 max_storage_deposit={:?}",
1903 transaction_meter.weight_consumed(),
1904 transaction_meter.weight_required(),
1905 storage_deposit,
1906 transaction_meter.total_consumed_gas(),
1907 transaction_meter.deposit_required()
1908 );
1909
1910 ContractResult {
1911 result: output
1912 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1913 .map_err(|e| e.error),
1914 weight_consumed: transaction_meter.weight_consumed(),
1915 weight_required: transaction_meter.weight_required(),
1916 storage_deposit,
1917 gas_consumed: transaction_meter.total_consumed_gas(),
1918 max_storage_deposit: transaction_meter.deposit_required(),
1919 }
1920 }
1921
1922 pub fn eth_estimate_gas(
1934 tx: GenericTransaction,
1935 config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1936 ) -> Result<U256, EthTransactError>
1937 where
1938 T::Nonce: Into<U256> + TryFrom<U256>,
1939 CallOf<T>: SetWeightLimit,
1940 {
1941 log::debug!(target: LOG_TARGET, "eth_estimate_gas: {tx:?}");
1942
1943 let mut low = U256::zero();
1944 let mut high = Self::evm_block_gas_limit();
1945
1946 log::trace!(target: LOG_TARGET, "eth_estimate_gas starting with low={low}, high={high}");
1947
1948 let perform_balance_checks = if let Some(gas_limit) = tx.gas {
1952 high = gas_limit;
1953 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the gas limit high={high}");
1954 true
1955 } else {
1956 false
1957 };
1958
1959 let fee_cap = tx.max_fee_per_gas.or(tx.gas_price);
1961 if let (Some(fee_cap), Some(from), true) = (fee_cap, tx.from, perform_balance_checks) {
1962 let mut available_balance = Self::evm_balance(&from);
1963 if let Some(value) = tx.value {
1964 available_balance = available_balance.checked_sub(value).ok_or_else(|| {
1965 EthTransactError::Message("insufficient funds for value transfer".into())
1966 })?;
1967 }
1968 if let Some(allowance) = available_balance.checked_div(fee_cap) {
1969 if high > allowance && allowance != U256::zero() {
1970 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the user's allowance high={high} allowance={allowance}");
1971 high = allowance
1972 }
1973 }
1974 }
1975
1976 let dry_run_results = [high, Self::evm_max_extrinsic_weight_in_gas()].map(|gas_limit| {
1984 let mut transaction = tx.clone();
1985 transaction.gas = Some(gas_limit);
1986 let dry_run_config = config.clone().with_perform_balance_checks(perform_balance_checks);
1987 let eth_transact_result = with_transaction(|| {
1988 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
1989 transaction,
1990 dry_run_config,
1991 )))
1992 })
1993 .expect("Rollback shouldn't error out");
1994 (gas_limit, eth_transact_result)
1995 });
1996 let (gas_limit, first_dry_run_result) = match dry_run_results {
1997 [(gas_limit1, Ok(dry_run_result1)), (gas_limit2, Ok(dry_run_result2))] => {
1998 if dry_run_result2.eth_gas >= gas_limit2 {
1999 (gas_limit1, dry_run_result1)
2000 } else {
2001 (gas_limit2, dry_run_result2)
2002 }
2003 },
2004 [(gas_limit, Ok(dry_run_result)), (_, Err(_))] |
2005 [(_, Err(_)), (gas_limit, Ok(dry_run_result))] => (gas_limit, dry_run_result),
2006 [(_, Err(err)), (_, Err(..))] => return Err(err),
2007 };
2008 log::trace!(
2009 target: LOG_TARGET,
2010 "eth_estimate_gas first dry run succeeded with gas_limit={} consumed={}",
2011 gas_limit,
2012 first_dry_run_result.eth_gas
2013 );
2014 low = first_dry_run_result.eth_gas;
2015 high = gas_limit;
2016
2017 while low + U256::one() < high {
2018 log::trace!(target: LOG_TARGET, "eth_estimate_gas estimation iteration with low={low} high={high}");
2019 let error_ratio = high
2020 .checked_sub(low)
2021 .and_then(|value| value.checked_mul(U256::from(1000)))
2022 .and_then(|value| value.checked_div(high))
2023 .ok_or_else(|| {
2024 EthTransactError::Message(
2025 "failed to calculate error ratio in gas estimation".into(),
2026 )
2027 })?;
2028 if error_ratio <= U256::from(15) {
2029 log::trace!(
2030 target: LOG_TARGET,
2031 "eth_estimate_gas finished due to error ratio being less than 1.5% high={}",
2032 high
2033 );
2034 break;
2035 }
2036
2037 let mut midpoint = high
2038 .checked_sub(low)
2039 .and_then(|value| value.checked_div(U256::from(2)))
2040 .and_then(|value| value.checked_add(low))
2041 .ok_or_else(|| {
2042 EthTransactError::Message(
2043 "failed to calculate midpoint in gas estimation".into(),
2044 )
2045 })?;
2046
2047 if let Some(other_midpoint) = low.checked_mul(U256::from(2)) {
2048 if other_midpoint != U256::zero() {
2049 midpoint = midpoint.min(other_midpoint)
2050 }
2051 };
2052
2053 let mut transaction = tx.clone();
2054 transaction.gas = Some(midpoint);
2055 let dry_run_config = config.clone().with_perform_balance_checks(perform_balance_checks);
2056 let dry_run_result = with_transaction(|| {
2057 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
2058 transaction,
2059 dry_run_config,
2060 )))
2061 })
2062 .expect("Rollback shouldn't error out");
2063 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run result with midpoint={midpoint} is dry_run_result={dry_run_result:?}");
2064 match dry_run_result {
2065 Ok(..) => {
2066 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run succeeded, new high={midpoint}");
2067 high = midpoint
2068 },
2069 Err(..) => {
2070 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run failed, new low={midpoint}");
2071 low = midpoint
2072 },
2073 }
2074 }
2075
2076 log::trace!(target: LOG_TARGET, "eth_estimate_gas completed. high={high}");
2077 Ok(high)
2078 }
2079
2080 pub fn eth_pre_dispatch_weight(transaction_encoded: Vec<u8>) -> Result<Weight, EthTransactError>
2088 where
2089 CallOf<T>: SetWeightLimit,
2090 {
2091 let signed_tx =
2092 crate::evm::TransactionSigned::decode(&transaction_encoded).map_err(|err| {
2093 EthTransactError::Message(format!("Failed to decode transaction: {err:?}"))
2094 })?;
2095 let signer_addr = signed_tx.recover_eth_address().map_err(|err| {
2096 EthTransactError::Message(format!("Failed to recover signer: {err:?}"))
2097 })?;
2098 let tx =
2099 GenericTransaction::from_signed(signed_tx, Self::evm_base_fee(), Some(signer_addr));
2100 let encoded_len = T::FeeInfo::encoded_len(
2101 crate::Call::<T>::eth_transact { payload: transaction_encoded.clone() }.into(),
2102 );
2103 let call_info = tx
2104 .into_call::<T>(CreateCallMode::ExtrinsicExecution(encoded_len, transaction_encoded))
2105 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2106 let info = T::FeeInfo::dispatch_info(&call_info.call);
2107
2108 Ok(frame_system::calculate_consumed_extrinsic_weight::<CallOf<T>>(
2109 &T::BlockWeights::get(),
2110 &info,
2111 call_info.encoded_len as usize,
2112 ))
2113 }
2114
2115 pub fn dry_run_eth_transact(
2121 mut tx: GenericTransaction,
2122 mut dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
2123 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
2124 where
2125 T::Nonce: Into<U256> + TryFrom<U256>,
2126 CallOf<T>: SetWeightLimit,
2127 {
2128 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
2129
2130 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
2131 Self::prepare_dry_run(&origin);
2132
2133 if let Some(overrides) = dry_run_config.state_overrides.take() {
2134 state_overrides::apply_state_overrides::<T>(overrides)?;
2135 }
2136
2137 let base_fee = Self::evm_base_fee();
2138 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
2139
2140 if effective_gas_price < base_fee {
2141 Err(EthTransactError::Message(format!(
2142 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
2143 )))?;
2144 }
2145
2146 if tx.nonce.is_none() {
2147 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
2148 }
2149 if tx.chain_id.is_none() {
2150 tx.chain_id = Some(T::ChainId::get().into());
2151 }
2152
2153 tx.gas_price = Some(effective_gas_price);
2155 tx.max_priority_fee_per_gas = Some(0.into());
2158 if tx.max_fee_per_gas.is_none() {
2159 tx.max_fee_per_gas = Some(effective_gas_price);
2160 }
2161
2162 let gas = tx.gas;
2163 if tx.gas.is_none() {
2164 tx.gas = Some(Self::evm_block_gas_limit());
2165 }
2166 if tx.r#type.is_none() {
2167 tx.r#type = Some(TYPE_EIP1559.into());
2168 }
2169
2170 let value = tx.value.unwrap_or_default();
2172 let input = tx.input.clone().to_vec();
2173 let from = tx.from;
2174 let to = tx.to;
2175
2176 let mut call_info = tx
2179 .into_call::<T>(CreateCallMode::DryRun)
2180 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2181
2182 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
2186 let base_weight = base_info.total_weight();
2187 let perform_balance_checks = dry_run_config.perform_balance_checks;
2188 let exec_config =
2189 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
2190 .with_dry_run(dry_run_config);
2191
2192 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
2194 if let Some(from) = &from {
2195 let fees = if gas.is_some() && matches!(perform_balance_checks, Some(true)) {
2196 fees
2197 } else {
2198 Zero::zero()
2199 };
2200 let balance = Self::evm_balance(from);
2201 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
2202 return Err(EthTransactError::Message(format!(
2203 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
2204 )));
2205 }
2206 }
2207
2208 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
2211
2212 let extract_error = |err| {
2213 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
2214 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
2215 } else {
2216 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
2217 }
2218 };
2219
2220 let transaction_limits = TransactionLimits::EthereumGas {
2221 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
2222 weight_limit: Self::evm_max_extrinsic_weight(),
2223 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
2224 };
2225
2226 let mut dry_run = match to {
2228 Some(dest) => {
2230 if dest == RUNTIME_PALLETS_ADDR {
2231 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
2232 return Err(EthTransactError::Message(format!(
2233 "Failed to decode pallet-call {input:?}"
2234 )));
2235 };
2236
2237 if let Err(result) =
2238 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
2239 {
2240 return Err(EthTransactError::Message(format!(
2241 "Failed to dispatch call: {:?}",
2242 result.error,
2243 )));
2244 };
2245
2246 Default::default()
2247 } else {
2248 let result = crate::Pallet::<T>::bare_call(
2250 OriginFor::<T>::signed(origin),
2251 dest,
2252 value,
2253 transaction_limits,
2254 input.clone(),
2255 &exec_config,
2256 );
2257
2258 let data = match result.result {
2259 Ok(return_value) => {
2260 if return_value.did_revert() {
2261 return Err(EthTransactError::Data(return_value.data));
2262 }
2263 return_value.data
2264 },
2265 Err(err) => {
2266 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
2267 return extract_error(err);
2268 },
2269 };
2270
2271 EthTransactInfo {
2272 weight_required: result.weight_required,
2273 storage_deposit: result.storage_deposit.charge_or_zero(),
2274 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2275 data,
2276 eth_gas: Default::default(),
2277 }
2278 }
2279 },
2280 None => {
2282 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2284 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
2285 } else {
2286 (input, vec![])
2287 };
2288
2289 let result = crate::Pallet::<T>::bare_instantiate(
2291 OriginFor::<T>::signed(origin),
2292 value,
2293 transaction_limits,
2294 Code::Upload(code.clone()),
2295 data.clone(),
2296 None,
2297 &exec_config,
2298 );
2299
2300 let returned_data = match result.result {
2301 Ok(return_value) => {
2302 if return_value.result.did_revert() {
2303 return Err(EthTransactError::Data(return_value.result.data));
2304 }
2305 return_value.result.data
2306 },
2307 Err(err) => {
2308 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
2309 return extract_error(err);
2310 },
2311 };
2312
2313 EthTransactInfo {
2314 weight_required: result.weight_required,
2315 storage_deposit: result.storage_deposit.charge_or_zero(),
2316 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2317 data: returned_data,
2318 eth_gas: Default::default(),
2319 }
2320 },
2321 };
2322
2323 call_info.call.set_weight_limit(dry_run.weight_required);
2325
2326 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
2328 let max_weight = Self::evm_max_extrinsic_weight();
2329 if total_weight.any_gt(max_weight) {
2330 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
2331 total_weight={total_weight:?} \
2332 max_weight={max_weight:?}",
2333 );
2334
2335 Err(EthTransactError::Message(format!(
2336 "\
2337 The transaction consumes more than the allowed weight. \
2338 needed={total_weight} \
2339 allowed={max_weight} \
2340 overweight_by={}\
2341 ",
2342 total_weight.saturating_sub(max_weight),
2343 )))?;
2344 }
2345
2346 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
2348 let available_fee = T::FeeInfo::remaining_txfee();
2349 if transaction_fee > available_fee {
2350 Err(EthTransactError::Message(format!(
2351 "Not enough gas supplied: Off by: {:?}",
2352 transaction_fee.saturating_sub(available_fee),
2353 )))?;
2354 }
2355
2356 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2357 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2358 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2359 if !rest.is_zero() {
2360 eth_gas = eth_gas.saturating_add(1_u32.into());
2361 }
2362
2363 log::debug!(target: LOG_TARGET, "\
2364 dry_run_eth_transact finished: \
2365 weight_limit={}, \
2366 total_weight={total_weight}, \
2367 max_weight={max_weight}, \
2368 weight_left={}, \
2369 eth_gas={eth_gas}, \
2370 encoded_len={}, \
2371 tx_fee={transaction_fee:?}, \
2372 storage_deposit={:?}, \
2373 max_storage_deposit={:?}\
2374 ",
2375 dry_run.weight_required,
2376 max_weight.saturating_sub(total_weight),
2377 call_info.encoded_len,
2378 dry_run.storage_deposit,
2379 dry_run.max_storage_deposit,
2380
2381 );
2382 dry_run.eth_gas = eth_gas;
2383 Ok(dry_run)
2384 }
2385
2386 pub fn evm_balance(address: &H160) -> U256 {
2390 let balance = AccountInfo::<T>::balance_of((*address).into());
2391 Self::convert_native_to_evm(balance)
2392 }
2393
2394 pub fn eth_block() -> EthBlock {
2396 EthereumBlock::<T>::get()
2397 }
2398
2399 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2406 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2407 let hash = <BlockHash<T>>::get(number);
2408 if hash == H256::zero() { None } else { Some(hash) }
2409 }
2410
2411 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2413 ReceiptInfoData::<T>::get()
2414 }
2415
2416 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2422 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2423 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2424 let account_id = T::AddressMapper::to_account_id(&address);
2425 T::Currency::set_balance(&account_id, balance);
2426 AccountInfoOf::<T>::mutate(&address, |account| {
2427 if let Some(account) = account {
2428 account.dust = dust;
2429 } else {
2430 *account = Some(AccountInfo { dust, ..Default::default() });
2431 }
2432 });
2433
2434 Ok(())
2435 }
2436
2437 pub fn new_balance_with_dust(
2441 evm_value: U256,
2442 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2443 let ed = T::Currency::minimum_balance();
2444 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2445 let (value, dust) = balance_with_dust.deconstruct();
2446
2447 Ok((ed.saturating_add(value), dust))
2448 }
2449
2450 pub fn evm_nonce(address: &H160) -> u32
2452 where
2453 T::Nonce: Into<u32>,
2454 {
2455 let account = T::AddressMapper::to_account_id(&address);
2456 System::<T>::account_nonce(account).into()
2457 }
2458
2459 pub fn evm_block_gas_limit() -> U256 {
2461 u64::MAX.into()
2468 }
2469
2470 pub fn evm_max_extrinsic_weight_in_gas() -> U256 {
2472 let max_extrinsic_fee = T::FeeInfo::weight_to_fee(&Self::evm_max_extrinsic_weight());
2473 let gas_scale: BalanceOf<T> = T::GasScale::get().into();
2474 (max_extrinsic_fee / gas_scale).into()
2475 }
2476
2477 pub fn evm_max_extrinsic_weight() -> Weight {
2479 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2480 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2481 .get(DispatchClass::Normal)
2482 .max_extrinsic
2483 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2484 Weight::from_parts(
2485 factor.saturating_mul_int(max_weight.ref_time()),
2486 factor.saturating_mul_int(max_weight.proof_size()),
2487 )
2488 }
2489
2490 pub fn evm_base_fee() -> U256 {
2492 let gas_scale = <T as Config>::GasScale::get();
2493 let multiplier = T::FeeInfo::next_fee_multiplier();
2494 multiplier
2495 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2496 .saturating_mul(gas_scale.saturated_into())
2497 .into()
2498 }
2499
2500 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2502 where
2503 T::Nonce: Into<u32>,
2504 {
2505 match tracer_type {
2506 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2507 TracerType::PrestateTracer(config) => {
2508 PrestateTracer::new(config.unwrap_or_default()).into()
2509 },
2510 TracerType::ExecutionTracer(config) => {
2511 ExecutionTracer::new(config.unwrap_or_default()).into()
2512 },
2513 }
2514 }
2515
2516 pub fn bare_upload_code(
2520 origin: OriginFor<T>,
2521 code: Vec<u8>,
2522 storage_deposit_limit: BalanceOf<T>,
2523 ) -> CodeUploadResult<BalanceOf<T>> {
2524 let origin = T::UploadOrigin::ensure_origin(origin)?;
2525
2526 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2527 BytecodeType::Pvm
2528 } else {
2529 if !T::AllowEVMBytecode::get() {
2530 return Err(<Error<T>>::CodeRejected.into());
2531 }
2532 BytecodeType::Evm
2533 };
2534
2535 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2536 weight_limit: Default::default(),
2537 deposit_limit: storage_deposit_limit,
2538 })?;
2539
2540 let module = Self::try_upload_code(
2541 origin,
2542 code,
2543 bytecode_type,
2544 &mut meter,
2545 &ExecConfig::new_substrate_tx(),
2546 )?;
2547 Ok(CodeUploadReturnValue {
2548 code_hash: *module.code_hash(),
2549 deposit: meter.deposit_consumed().charge_or_zero(),
2550 })
2551 }
2552
2553 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2555 let contract_info =
2556 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2557
2558 let maybe_value = contract_info.read(&Key::from_fixed(key));
2559 Ok(maybe_value)
2560 }
2561
2562 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2566 let immutable_data = <ImmutableDataOf<T>>::get(address);
2567 immutable_data
2568 }
2569
2570 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2578 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2579 <ImmutableDataOf<T>>::insert(address, data);
2580 Ok(())
2581 }
2582
2583 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2585 let contract_info =
2586 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2587
2588 let maybe_value = contract_info.read(
2589 &Key::try_from_var(key)
2590 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2591 .into(),
2592 );
2593 Ok(maybe_value)
2594 }
2595
2596 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2598 let (value, dust) = value.into().deconstruct();
2599 value
2600 .into()
2601 .saturating_mul(T::NativeToEthRatio::get().into())
2602 .saturating_add(dust.into())
2603 }
2604
2605 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2615 let contract_info =
2616 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2617
2618 contract_info
2619 .write(&Key::from_fixed(key), value, None, false)
2620 .map_err(ContractAccessError::StorageWriteFailed)
2621 }
2622
2623 pub fn set_storage_var_key(
2634 address: H160,
2635 key: Vec<u8>,
2636 value: Option<Vec<u8>>,
2637 ) -> SetStorageResult {
2638 let contract_info =
2639 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2640
2641 contract_info
2642 .write(
2643 &Key::try_from_var(key)
2644 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2645 .into(),
2646 value,
2647 None,
2648 false,
2649 )
2650 .map_err(ContractAccessError::StorageWriteFailed)
2651 }
2652
2653 pub fn account_id() -> T::AccountId {
2655 use frame_support::PalletId;
2656 use sp_runtime::traits::AccountIdConversion;
2657 PalletId(*b"py/reviv").into_account_truncating()
2658 }
2659
2660 pub fn block_author() -> H160 {
2662 use frame_support::traits::FindAuthor;
2663
2664 let digest = <frame_system::Pallet<T>>::digest();
2665 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2666
2667 T::FindAuthor::find_author(pre_runtime_digests)
2668 .map(|account_id| T::AddressMapper::to_address(&account_id))
2669 .unwrap_or_default()
2670 }
2671
2672 pub fn code(address: &H160) -> Vec<u8> {
2676 use precompiles::{All, Precompiles};
2677 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2678 return code.into();
2679 }
2680 AccountInfo::<T>::load_contract(&address)
2681 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2682 .map(|code| code.into())
2683 .unwrap_or_default()
2684 }
2685
2686 pub fn try_upload_code(
2688 origin: T::AccountId,
2689 code: Vec<u8>,
2690 code_type: BytecodeType,
2691 meter: &mut TransactionMeter<T>,
2692 exec_config: &ExecConfig<T>,
2693 ) -> Result<ContractBlob<T>, DispatchError> {
2694 let mut module = match code_type {
2695 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2696 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2697 };
2698 module.store_code(exec_config, meter)?;
2699 Ok(module)
2700 }
2701
2702 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2704 executing_contract::using_once(&mut false, || {
2705 executing_contract::with(|f| {
2706 if *f {
2708 return Err(())
2709 }
2710 *f = true;
2712 Ok(())
2713 })
2714 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2715 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2716 .map(|_| f())
2717 .and_then(|r| r)
2718 })
2719 }
2720
2721 fn charge_deposit(
2726 hold_reason: HoldReason,
2727 from: &T::AccountId,
2728 to: &T::AccountId,
2729 amount: BalanceOf<T>,
2730 exec_config: &ExecConfig<T>,
2731 ) -> DispatchResult {
2732 if amount.is_zero() {
2733 return Ok(());
2734 }
2735
2736 T::Deposit::charge_and_hold(hold_reason, exec_config.funds(from), to, amount)
2737 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2738 Ok(())
2739 }
2740
2741 fn refund_deposit(
2746 hold_reason: HoldReason,
2747 from: &T::AccountId,
2748 dst: deposit_payment::Funds<T::AccountId>,
2749 amount: BalanceOf<T>,
2750 ) -> Result<(), DispatchError> {
2751 if amount.is_zero() {
2752 return Ok(());
2753 }
2754
2755 let to = match &dst {
2756 deposit_payment::Funds::Balance(to) | deposit_payment::Funds::TxFee(to) => *to,
2757 };
2758 let result = T::Deposit::refund_on_hold(hold_reason, from, dst, amount);
2759
2760 result.defensive_map_err(|err| {
2761 let available = T::Deposit::total_on_hold(hold_reason, from);
2762 if available < amount {
2763 log::error!(
2766 target: LOG_TARGET,
2767 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}. Not enough deposit: {available:?}. This is a bug.",
2768 );
2769 Error::<T>::StorageRefundNotEnoughFunds.into()
2770 } else {
2771 log::warn!(
2776 target: LOG_TARGET,
2777 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}: {err:?}. First remove locks (staking, governance) from the contracts account.",
2778 );
2779 Error::<T>::StorageRefundLocked.into()
2780 }
2781 })
2782 }
2783
2784 fn has_dust(value: U256) -> bool {
2786 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2787 }
2788
2789 fn has_balance(value: U256) -> bool {
2791 value >= U256::from(<T>::NativeToEthRatio::get())
2792 }
2793
2794 #[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime", test))]
2796 fn min_balance() -> BalanceOf<T> {
2797 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2798 }
2799
2800 fn deposit_event(event: Event<T>) {
2805 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2806 }
2807
2808 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2810 match <T as Config>::RuntimeOrigin::from(origin).into() {
2811 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2812 _ => Err(BadOrigin.into()),
2813 }
2814 }
2815
2816 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2820 if DebugSettings::bypass_eip_3607::<T>() {
2821 return Ok(());
2822 }
2823 let Some(address) = origin
2824 .as_system_ref()
2825 .and_then(|o| o.as_signed())
2826 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2827 else {
2828 return Ok(());
2829 };
2830 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2831 <AccountInfo<T>>::is_contract(&address)
2832 {
2833 log::debug!(
2834 target: crate::LOG_TARGET,
2835 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2836 );
2837 Err(DispatchError::BadOrigin)
2838 } else {
2839 Ok(())
2840 }
2841 }
2842}
2843
2844pub const RUNTIME_PALLETS_ADDR: H160 =
2849 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2850
2851environmental!(executing_contract: bool);
2853
2854sp_api::decl_runtime_apis! {
2855 #[api_version(1)]
2857 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2858 AccountId: Codec,
2859 Balance: Codec,
2860 Nonce: Codec,
2861 BlockNumber: Codec,
2862 Moment: Codec,
2863 {
2864 fn eth_block() -> EthBlock;
2868
2869 fn eth_block_hash(number: U256) -> Option<H256>;
2871
2872 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2878
2879 fn block_gas_limit() -> U256;
2881
2882 fn max_extrinsic_weight_in_gas() -> U256;
2884
2885 fn balance(address: H160) -> U256;
2887
2888 fn gas_price() -> U256;
2890
2891 fn nonce(address: H160) -> Nonce;
2893
2894 fn call(
2898 origin: AccountId,
2899 dest: H160,
2900 value: Balance,
2901 gas_limit: Option<Weight>,
2902 storage_deposit_limit: Option<Balance>,
2903 input_data: Vec<u8>,
2904 ) -> ContractResult<ExecReturnValue, Balance>;
2905
2906 fn instantiate(
2910 origin: AccountId,
2911 value: Balance,
2912 gas_limit: Option<Weight>,
2913 storage_deposit_limit: Option<Balance>,
2914 code: Code,
2915 data: Vec<u8>,
2916 salt: Option<[u8; 32]>,
2917 ) -> ContractResult<InstantiateReturnValue, Balance>;
2918
2919
2920 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2925
2926 fn eth_transact_with_config(
2930 tx: GenericTransaction,
2931 config: DryRunConfig<Moment>,
2932 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2933
2934 fn eth_estimate_gas(
2940 tx: GenericTransaction,
2941 config: DryRunConfig<Moment>
2942 ) -> Result<U256, EthTransactError>;
2943
2944 fn eth_pre_dispatch_weight(tx: Vec<u8>) -> Result<Weight, EthTransactError>;
2946
2947 fn upload_code(
2951 origin: AccountId,
2952 code: Vec<u8>,
2953 storage_deposit_limit: Option<Balance>,
2954 ) -> CodeUploadResult<Balance>;
2955
2956 fn get_storage(
2962 address: H160,
2963 key: [u8; 32],
2964 ) -> GetStorageResult;
2965
2966 fn get_storage_var_key(
2972 address: H160,
2973 key: Vec<u8>,
2974 ) -> GetStorageResult;
2975
2976 fn trace_block(
2983 block: Block,
2984 config: TracerType
2985 ) -> Vec<(u32, Trace)>;
2986
2987 fn trace_tx(
2994 block: Block,
2995 tx_index: u32,
2996 config: TracerType
2997 ) -> Option<Trace>;
2998
2999 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
3003
3004 fn trace_call_with_config(
3010 tx: GenericTransaction,
3011 tracer_type: TracerType,
3012 config: TracingConfig,
3013 ) -> Result<Trace, EthTransactError>;
3014
3015 fn block_author() -> H160;
3017
3018 fn address(account_id: AccountId) -> H160;
3020
3021 fn account_id(address: H160) -> AccountId;
3023
3024 fn runtime_pallets_address() -> H160;
3026
3027 fn code(address: H160) -> Vec<u8>;
3029
3030 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
3032 }
3033}
3034
3035#[macro_export]
3049macro_rules! impl_runtime_apis_plus_revive_traits {
3050 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
3051
3052 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
3053
3054 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
3055 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
3056 use $crate::pallet::Call as ReviveCall;
3057 match self {
3058 Self::$Revive(
3059 ReviveCall::eth_call{ weight_limit, .. } |
3060 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
3061 ) => {
3062 let old = *weight_limit;
3063 *weight_limit = new_weight_limit;
3064 old
3065 },
3066 _ => Weight::default(),
3067 }
3068 }
3069 }
3070
3071 impl_runtime_apis! {
3072 $($rest)*
3073
3074
3075 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
3076 {
3077 fn eth_block() -> $crate::EthBlock {
3078 $crate::Pallet::<Self>::eth_block()
3079 }
3080
3081 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
3082 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
3083 }
3084
3085 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
3086 $crate::Pallet::<Self>::eth_receipt_data()
3087 }
3088
3089 fn balance(address: $crate::H160) -> $crate::U256 {
3090 $crate::Pallet::<Self>::evm_balance(&address)
3091 }
3092
3093 fn block_author() -> $crate::H160 {
3094 $crate::Pallet::<Self>::block_author()
3095 }
3096
3097 fn block_gas_limit() -> $crate::U256 {
3098 $crate::Pallet::<Self>::evm_block_gas_limit()
3099 }
3100
3101 fn max_extrinsic_weight_in_gas() -> $crate::U256 {
3102 $crate::Pallet::<Self>::evm_max_extrinsic_weight_in_gas()
3103 }
3104
3105 fn gas_price() -> $crate::U256 {
3106 $crate::Pallet::<Self>::evm_base_fee()
3107 }
3108
3109 fn nonce(address: $crate::H160) -> Nonce {
3110 use $crate::AddressMapper;
3111 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
3112 $crate::frame_system::Pallet::<Self>::account_nonce(account)
3113 }
3114
3115 fn address(account_id: AccountId) -> $crate::H160 {
3116 use $crate::AddressMapper;
3117 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
3118 }
3119
3120 fn eth_transact(
3121 tx: $crate::evm::GenericTransaction,
3122 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3123 use $crate::{
3124 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3125 sp_runtime::traits::TransactionExtension,
3126 sp_runtime::traits::Block as BlockT
3127 };
3128 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
3129 }
3130
3131 fn eth_transact_with_config(
3132 tx: $crate::evm::GenericTransaction,
3133 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3134 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3135 use $crate::{
3136 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3137 sp_runtime::traits::TransactionExtension,
3138 sp_runtime::traits::Block as BlockT
3139 };
3140 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
3141 }
3142
3143 fn eth_estimate_gas(
3144 tx: $crate::evm::GenericTransaction,
3145 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3146 ) -> Result<$crate::U256, $crate::EthTransactError> {
3147 use $crate::{
3148 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3149 sp_runtime::traits::TransactionExtension,
3150 sp_runtime::traits::Block as BlockT
3151 };
3152 $crate::Pallet::<Self>::eth_estimate_gas(tx, config)
3153 }
3154
3155 fn eth_pre_dispatch_weight(
3156 tx: Vec<u8>,
3157 ) -> Result<$crate::Weight, $crate::EthTransactError> {
3158 $crate::Pallet::<Self>::eth_pre_dispatch_weight(tx)
3159 }
3160
3161 fn call(
3162 origin: AccountId,
3163 dest: $crate::H160,
3164 value: Balance,
3165 weight_limit: Option<$crate::Weight>,
3166 storage_deposit_limit: Option<Balance>,
3167 input_data: Vec<u8>,
3168 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
3169 use $crate::frame_support::traits::Get;
3170 let blockweights: $crate::BlockWeights =
3171 <Self as $crate::frame_system::Config>::BlockWeights::get();
3172
3173 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3174 $crate::Pallet::<Self>::bare_call(
3175 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3176 dest,
3177 $crate::Pallet::<Self>::convert_native_to_evm(value),
3178 $crate::TransactionLimits::WeightAndDeposit {
3179 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3180 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3181 },
3182 input_data,
3183 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3184 )
3185 }
3186
3187 fn instantiate(
3188 origin: AccountId,
3189 value: Balance,
3190 weight_limit: Option<$crate::Weight>,
3191 storage_deposit_limit: Option<Balance>,
3192 code: $crate::Code,
3193 data: Vec<u8>,
3194 salt: Option<[u8; 32]>,
3195 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
3196 use $crate::frame_support::traits::Get;
3197 let blockweights: $crate::BlockWeights =
3198 <Self as $crate::frame_system::Config>::BlockWeights::get();
3199
3200 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3201 $crate::Pallet::<Self>::bare_instantiate(
3202 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3203 $crate::Pallet::<Self>::convert_native_to_evm(value),
3204 $crate::TransactionLimits::WeightAndDeposit {
3205 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3206 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3207 },
3208 code,
3209 data,
3210 salt,
3211 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3212 )
3213 }
3214
3215 fn upload_code(
3216 origin: AccountId,
3217 code: Vec<u8>,
3218 storage_deposit_limit: Option<Balance>,
3219 ) -> $crate::CodeUploadResult<Balance> {
3220 let origin =
3221 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
3222 $crate::Pallet::<Self>::bare_upload_code(
3223 origin,
3224 code,
3225 storage_deposit_limit.unwrap_or(u128::MAX),
3226 )
3227 }
3228
3229 fn get_storage_var_key(
3230 address: $crate::H160,
3231 key: Vec<u8>,
3232 ) -> $crate::GetStorageResult {
3233 $crate::Pallet::<Self>::get_storage_var_key(address, key)
3234 }
3235
3236 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
3237 $crate::Pallet::<Self>::get_storage(address, key)
3238 }
3239
3240 fn trace_block(
3241 block: Block,
3242 tracer_type: $crate::evm::TracerType,
3243 ) -> Vec<(u32, $crate::evm::Trace)> {
3244 use $crate::{sp_runtime::traits::Block, tracing::trace};
3245
3246 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3247 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3248 {
3249 return Default::default()
3250 }
3251
3252 let mut traces = vec![];
3253 let (header, extrinsics) = block.deconstruct();
3254 <$Executive>::initialize_block(&header);
3255 for (index, ext) in extrinsics.into_iter().enumerate() {
3256 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3257 let t = tracer.as_tracing();
3258 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3259
3260 if let Some(tx_trace) = tracer.collect_trace() {
3261 traces.push((index as u32, tx_trace));
3262 }
3263 }
3264
3265 traces
3266 }
3267
3268 fn trace_tx(
3269 block: Block,
3270 tx_index: u32,
3271 tracer_type: $crate::evm::TracerType,
3272 ) -> Option<$crate::evm::Trace> {
3273 use $crate::{sp_runtime::traits::Block, tracing::trace};
3274
3275 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3276 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3277 {
3278 return None
3279 }
3280
3281 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
3282 let (header, extrinsics) = block.deconstruct();
3283
3284 <$Executive>::initialize_block(&header);
3285 for (index, ext) in extrinsics.into_iter().enumerate() {
3286 if index as u32 == tx_index {
3287 let t = tracer.as_tracing();
3288 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3289 break;
3290 } else {
3291 let _ = <$Executive>::apply_extrinsic(ext);
3292 }
3293 }
3294
3295 tracer.collect_trace()
3296 }
3297
3298 fn trace_call(
3299 tx: $crate::evm::GenericTransaction,
3300 tracer_type: $crate::evm::TracerType,
3301 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
3302 use $crate::tracing::trace;
3303
3304 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3305 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3306 {
3307 return Err($crate::EthTransactError::Message("Execution Tracing is disabled".into()))
3308 }
3309
3310 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3311 let t = tracer.as_tracing();
3312
3313 t.watch_address(&tx.from.unwrap_or_default());
3314 t.watch_address(&$crate::Pallet::<Self>::block_author());
3315 let result = trace(t, || Self::eth_transact(tx));
3316
3317 if let Some(trace) = tracer.collect_trace() {
3318 Ok(trace)
3319 } else if let Err(err) = result {
3320 Err(err)
3321 } else {
3322 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
3323 }
3324 }
3325
3326 fn trace_call_with_config(
3327 tx: $crate::evm::GenericTransaction,
3328 tracer_type: $crate::evm::TracerType,
3329 config: $crate::evm::TracingConfig,
3330 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
3331 let $crate::evm::TracingConfig { state_overrides } = config;
3332
3333 if let Some(overrides) = state_overrides {
3334 $crate::state_overrides::apply_state_overrides::<Runtime>(overrides)?;
3335 }
3336
3337 Self::trace_call(tx, tracer_type)
3338 }
3339
3340 fn runtime_pallets_address() -> $crate::H160 {
3341 $crate::RUNTIME_PALLETS_ADDR
3342 }
3343
3344 fn code(address: $crate::H160) -> Vec<u8> {
3345 $crate::Pallet::<Self>::code(&address)
3346 }
3347
3348 fn account_id(address: $crate::H160) -> AccountId {
3349 use $crate::AddressMapper;
3350 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
3351 }
3352
3353 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
3354 $crate::Pallet::<Self>::new_balance_with_dust(balance)
3355 }
3356 }
3357 }
3358 };
3359}