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 exec;
31mod impl_fungibles;
32mod limits;
33mod metering;
34mod primitives;
35mod storage;
36#[cfg(test)]
37mod tests;
38mod transient_storage;
39mod vm;
40mod weightinfo_extension;
41
42pub mod evm;
43pub mod migrations;
44pub mod mock;
45pub mod precompiles;
46pub mod test_utils;
47pub mod tracing;
48pub mod weights;
49
50use crate::{
51 evm::{
52 CallTracer, CreateCallMode, ExecutionTracer, GenericTransaction, PrestateTracer,
53 TYPE_EIP1559, Trace, Tracer, TracerType, block_hash::EthereumBlockBuilderIR, block_storage,
54 fees::InfoT as FeeInfo, runtime::SetWeightLimit,
55 },
56 exec::{AccountIdOf, ExecError, ReentrancyProtection, Stack as ExecStack},
57 sp_runtime::TransactionOutcome,
58 storage::{AccountType, DeletionQueueManager},
59 tracing::if_tracing,
60 vm::{CodeInfo, RuntimeCosts, pvm::extract_code_and_data},
61 weightinfo_extension::OnFinalizeBlockParts,
62};
63use alloc::{boxed::Box, format, vec};
64use codec::{Codec, Decode, Encode};
65use environmental::*;
66use frame_support::{
67 BoundedVec,
68 dispatch::{
69 DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
70 Pays, PostDispatchInfo, RawOrigin,
71 },
72 ensure,
73 pallet_prelude::DispatchClass,
74 storage::with_transaction,
75 traits::{
76 ConstU32, ConstU64, EnsureOrigin, Get, IsSubType, IsType, OnUnbalanced, OriginTrait,
77 fungible::{Balanced, Credit, Inspect, Mutate, MutateHold},
78 tokens::Balance,
79 },
80 weights::WeightMeter,
81};
82use frame_system::{
83 Pallet as System, ensure_signed,
84 pallet_prelude::{BlockNumberFor, OriginFor},
85};
86use scale_info::TypeInfo;
87use sp_runtime::{
88 AccountId32, DispatchError, FixedPointNumber, FixedU128, SaturatedConversion,
89 traits::{
90 BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedFrom,
91 UniqueSaturatedInto, Zero,
92 },
93};
94
95pub use crate::{
96 address::{
97 AccountId32Mapper, AddressMapper, TestAccountMapper, create1, create2, is_eth_derived,
98 },
99 debug::DebugSettings,
100 evm::{
101 Address as EthAddress, Block as EthBlock, DryRunConfig, ReceiptInfo,
102 block_hash::ReceiptGasInfo,
103 },
104 exec::{CallResources, DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
105 limits::TRANSIENT_STORAGE_BYTES as TRANSIENT_STORAGE_LIMIT,
106 metering::{
107 EthTxInfo, FrameMeter, ResourceMeter, Token as WeightToken, TransactionLimits,
108 TransactionMeter,
109 },
110 pallet::{genesis, *},
111 storage::{AccountInfo, ContractInfo},
112 transient_storage::{MeterEntry, StorageMeter as TransientStorageMeter, TransientStorage},
113 vm::{BytecodeType, ContractBlob},
114};
115pub use codec;
116pub use frame_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
117pub use frame_system::{self, limits::BlockWeights};
118pub use primitives::*;
119pub use sp_core::{H160, H256, U256, keccak_256};
120pub use sp_runtime;
121pub use weights::WeightInfo;
122
123#[cfg(doc)]
124pub use crate::vm::pvm::SyscallDoc;
125
126pub type BalanceOf<T> = <T as Config>::Balance;
127pub type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
128type TrieId = BoundedVec<u8, ConstU32<128>>;
129type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
130type CallOf<T> = <T as Config>::RuntimeCall;
131
132const SENTINEL: u32 = u32::MAX;
139
140const LOG_TARGET: &str = "runtime::revive";
146
147#[frame_support::pallet]
148pub mod pallet {
149 use super::*;
150 use frame_support::{pallet_prelude::*, traits::FindAuthor};
151 use frame_system::pallet_prelude::*;
152 use sp_core::U256;
153 use sp_runtime::Perbill;
154
155 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
157
158 #[pallet::pallet]
159 #[pallet::storage_version(STORAGE_VERSION)]
160 pub struct Pallet<T>(_);
161
162 #[pallet::config(with_default)]
163 pub trait Config: frame_system::Config {
164 type Time: Time<Moment: Into<U256>>;
166
167 #[pallet::no_default]
171 type Balance: Balance
172 + TryFrom<U256>
173 + Into<U256>
174 + Bounded
175 + UniqueSaturatedInto<u64>
176 + UniqueSaturatedFrom<u64>
177 + UniqueSaturatedInto<u128>;
178
179 #[pallet::no_default]
181 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
182 + Mutate<Self::AccountId>
183 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
184 + Balanced<Self::AccountId>;
185
186 #[pallet::no_default_bounds]
193 type OnBurn: OnUnbalanced<CreditOf<Self>>;
194
195 #[pallet::no_default_bounds]
197 #[allow(deprecated)]
198 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
199
200 #[pallet::no_default_bounds]
202 type RuntimeCall: Parameter
203 + Dispatchable<
204 RuntimeOrigin = OriginFor<Self>,
205 Info = DispatchInfo,
206 PostInfo = PostDispatchInfo,
207 > + IsType<<Self as frame_system::Config>::RuntimeCall>
208 + From<Call<Self>>
209 + IsSubType<Call<Self>>
210 + GetDispatchInfo;
211
212 #[pallet::no_default_bounds]
214 type RuntimeOrigin: IsType<OriginFor<Self>>
215 + From<Origin<Self>>
216 + Into<Result<Origin<Self>, OriginFor<Self>>>;
217
218 #[pallet::no_default_bounds]
220 type RuntimeHoldReason: From<HoldReason>;
221
222 type WeightInfo: WeightInfo;
225
226 #[pallet::no_default_bounds]
230 #[allow(private_bounds)]
231 type Precompiles: precompiles::Precompiles<Self>;
232
233 type FindAuthor: FindAuthor<Self::AccountId>;
235
236 #[pallet::constant]
242 #[pallet::no_default_bounds]
243 type DepositPerByte: Get<BalanceOf<Self>>;
244
245 #[pallet::constant]
251 #[pallet::no_default_bounds]
252 type DepositPerItem: Get<BalanceOf<Self>>;
253
254 #[pallet::constant]
264 #[pallet::no_default_bounds]
265 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
266
267 #[pallet::constant]
271 type CodeHashLockupDepositPercent: Get<Perbill>;
272
273 #[pallet::no_default]
275 type AddressMapper: AddressMapper<Self>;
276
277 #[pallet::constant]
279 type AllowEVMBytecode: Get<bool>;
280
281 #[pallet::no_default_bounds]
286 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
287
288 #[pallet::no_default_bounds]
299 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
300
301 type RuntimeMemory: Get<u32>;
306
307 type PVFMemory: Get<u32>;
315
316 #[pallet::constant]
321 type ChainId: Get<u64>;
322
323 #[pallet::constant]
325 type NativeToEthRatio: Get<u32>;
326
327 #[pallet::no_default_bounds]
332 type FeeInfo: FeeInfo<Self>;
333
334 #[pallet::constant]
347 type MaxEthExtrinsicWeight: Get<FixedU128>;
348
349 #[pallet::constant]
351 type DebugEnabled: Get<bool>;
352
353 #[pallet::constant]
371 #[pallet::no_default_bounds]
372 type GasScale: Get<u32>;
373 }
374
375 pub mod config_preludes {
377 use super::*;
378 use frame_support::{
379 derive_impl,
380 traits::{ConstBool, ConstU32},
381 };
382 use frame_system::EnsureSigned;
383 use sp_core::parameter_types;
384
385 type Balance = u64;
386
387 pub const DOLLARS: Balance = 1_000_000_000_000;
388 pub const CENTS: Balance = DOLLARS / 100;
389 pub const MILLICENTS: Balance = CENTS / 1_000;
390
391 pub const fn deposit(items: u32, bytes: u32) -> Balance {
392 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
393 }
394
395 parameter_types! {
396 pub const DepositPerItem: Balance = deposit(1, 0);
397 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
398 pub const DepositPerByte: Balance = deposit(0, 1);
399 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
400 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
401 pub const GasScale: u32 = 10u32;
402 }
403
404 pub struct TestDefaultConfig;
406
407 impl Time for TestDefaultConfig {
408 type Moment = u64;
409 fn now() -> Self::Moment {
410 0u64
411 }
412 }
413
414 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
415 fn convert(w: Weight) -> T {
416 w.ref_time().into()
417 }
418 }
419
420 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
421 impl frame_system::DefaultConfig for TestDefaultConfig {}
422
423 #[frame_support::register_default_impl(TestDefaultConfig)]
424 impl DefaultConfig for TestDefaultConfig {
425 #[inject_runtime_type]
426 type RuntimeEvent = ();
427
428 #[inject_runtime_type]
429 type RuntimeHoldReason = ();
430
431 #[inject_runtime_type]
432 type RuntimeCall = ();
433
434 #[inject_runtime_type]
435 type RuntimeOrigin = ();
436
437 type Precompiles = ();
438 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
439 type DepositPerByte = DepositPerByte;
440 type DepositPerItem = DepositPerItem;
441 type DepositPerChildTrieItem = DepositPerChildTrieItem;
442 type Time = Self;
443 type AllowEVMBytecode = ConstBool<true>;
444 type UploadOrigin = EnsureSigned<Self::AccountId>;
445 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
446 type WeightInfo = ();
447 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
448 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
449 type ChainId = ConstU64<42>;
450 type NativeToEthRatio = ConstU32<1_000_000>;
451 type FindAuthor = ();
452 type FeeInfo = ();
453 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
454 type DebugEnabled = ConstBool<false>;
455 type GasScale = GasScale;
456 type OnBurn = ();
457 }
458 }
459
460 #[pallet::event]
461 pub enum Event<T: Config> {
462 ContractEmitted {
464 contract: H160,
466 data: Vec<u8>,
469 topics: Vec<H256>,
472 },
473
474 Instantiated { deployer: H160, contract: H160 },
476
477 EthExtrinsicRevert { dispatch_error: DispatchError },
484 }
485
486 #[pallet::error]
487 #[repr(u8)]
488 pub enum Error<T> {
489 InvalidSchedule = 0x01,
491 InvalidCallFlags = 0x02,
493 OutOfGas = 0x03,
495 TransferFailed = 0x04,
498 MaxCallDepthReached = 0x05,
501 ContractNotFound = 0x06,
503 CodeNotFound = 0x07,
505 CodeInfoNotFound = 0x08,
507 OutOfBounds = 0x09,
509 DecodingFailed = 0x0A,
511 ContractTrapped = 0x0B,
513 ValueTooLarge = 0x0C,
515 TerminatedWhileReentrant = 0x0D,
518 InputForwarded = 0x0E,
520 TooManyTopics = 0x0F,
522 DuplicateContract = 0x12,
524 TerminatedInConstructor = 0x13,
528 ReentranceDenied = 0x14,
530 ReenteredPallet = 0x15,
532 StateChangeDenied = 0x16,
534 StorageDepositNotEnoughFunds = 0x17,
536 StorageDepositLimitExhausted = 0x18,
538 CodeInUse = 0x19,
540 ContractReverted = 0x1A,
545 CodeRejected = 0x1B,
550 BlobTooLarge = 0x1C,
552 StaticMemoryTooLarge = 0x1D,
554 BasicBlockTooLarge = 0x1E,
556 InvalidInstruction = 0x1F,
558 MaxDelegateDependenciesReached = 0x20,
560 DelegateDependencyNotFound = 0x21,
562 DelegateDependencyAlreadyExists = 0x22,
564 CannotAddSelfAsDelegateDependency = 0x23,
566 OutOfTransientStorage = 0x24,
568 InvalidSyscall = 0x25,
570 InvalidStorageFlags = 0x26,
572 ExecutionFailed = 0x27,
574 BalanceConversionFailed = 0x28,
576 InvalidImmutableAccess = 0x2A,
579 AccountUnmapped = 0x2B,
583 AccountAlreadyMapped = 0x2C,
585 InvalidGenericTransaction = 0x2D,
587 RefcountOverOrUnderflow = 0x2E,
589 UnsupportedPrecompileAddress = 0x2F,
591 CallDataTooLarge = 0x30,
593 ReturnDataTooLarge = 0x31,
595 InvalidJump = 0x32,
597 StackUnderflow = 0x33,
599 StackOverflow = 0x34,
601 TxFeeOverdraw = 0x35,
605 EvmConstructorNonEmptyData = 0x36,
609 EvmConstructedFromHash = 0x37,
614 StorageRefundNotEnoughFunds = 0x38,
618 StorageRefundLocked = 0x39,
623 PrecompileDelegateDenied = 0x40,
628 EcdsaRecoveryFailed = 0x41,
630 #[cfg(feature = "runtime-benchmarks")]
632 BenchmarkingError = 0xFF,
633 }
634
635 #[pallet::composite_enum]
637 pub enum HoldReason {
638 CodeUploadDepositReserve,
640 StorageDepositReserve,
642 AddressMapping,
644 }
645
646 #[derive(
647 PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug,
648 )]
649 #[pallet::origin]
650 pub enum Origin<T: Config> {
651 EthTransaction(T::AccountId),
652 }
653
654 #[pallet::storage]
658 #[pallet::unbounded]
659 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
660
661 #[pallet::storage]
663 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
664
665 #[pallet::storage]
667 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
668
669 #[pallet::storage]
671 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
672
673 #[pallet::storage]
678 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
679
680 #[pallet::storage]
683 pub(crate) type DeletionQueueCounter<T: Config> =
684 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
685
686 #[pallet::storage]
693 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
694
695 #[pallet::storage]
705 #[pallet::unbounded]
706 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
707
708 #[pallet::storage]
712 pub(crate) type BlockHash<T: Config> =
713 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
714
715 #[pallet::storage]
722 #[pallet::unbounded]
723 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
724
725 #[pallet::storage]
727 #[pallet::unbounded]
728 pub(crate) type EthBlockBuilderIR<T: Config> =
729 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
730
731 #[pallet::storage]
736 #[pallet::unbounded]
737 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
738 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
739
740 #[pallet::storage]
742 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
743
744 pub mod genesis {
745 use super::*;
746 use crate::evm::Bytes32;
747
748 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
750 pub struct ContractData {
751 pub code: crate::evm::Bytes,
753 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
755 }
756
757 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
759 pub struct Account<T: Config> {
760 pub address: H160,
762 #[serde(default)]
764 pub balance: U256,
765 #[serde(default)]
767 pub nonce: T::Nonce,
768 #[serde(flatten, skip_serializing_if = "Option::is_none")]
770 pub contract_data: Option<ContractData>,
771 }
772 }
773
774 #[pallet::genesis_config]
775 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
776 pub struct GenesisConfig<T: Config> {
777 #[serde(default, skip_serializing_if = "Vec::is_empty")]
780 pub mapped_accounts: Vec<T::AccountId>,
781
782 #[serde(default, skip_serializing_if = "Vec::is_empty")]
784 pub accounts: Vec<genesis::Account<T>>,
785
786 #[serde(default, skip_serializing_if = "Option::is_none")]
788 pub debug_settings: Option<DebugSettings>,
789 }
790
791 #[pallet::genesis_build]
792 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
793 fn build(&self) {
794 use crate::{exec::Key, vm::ContractBlob};
795 use frame_support::traits::fungible::Mutate;
796
797 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
798 let _ = T::Currency::mint_into(
799 &Pallet::<T>::account_id(),
800 T::Currency::minimum_balance(),
801 );
802 }
803
804 for id in &self.mapped_accounts {
805 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
806 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
807 }
808 }
809
810 let owner = Pallet::<T>::account_id();
811
812 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
813 let account_id = T::AddressMapper::to_account_id(address);
814
815 if !System::<T>::account_exists(&account_id) {
816 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
817 }
818
819 frame_system::Account::<T>::mutate(&account_id, |info| {
820 info.nonce = (*nonce).into();
821 });
822
823 match contract_data {
824 None => {
825 AccountInfoOf::<T>::insert(
826 address,
827 AccountInfo { account_type: AccountType::EOA, dust: 0 },
828 );
829 },
830 Some(genesis::ContractData { code, storage }) => {
831 let blob = if code.0.starts_with(&polkavm_common::program::BLOB_MAGIC) {
832 ContractBlob::<T>::from_pvm_code(code.0.clone(), owner.clone())
833 .inspect_err(|err| {
834 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
835 })
836 } else {
837 ContractBlob::<T>::from_evm_runtime_code(code.0.clone(), account_id)
838 .inspect_err(|err| {
839 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
840 })
841 };
842
843 let Ok(blob) = blob else {
844 continue;
845 };
846
847 let code_hash = *blob.code_hash();
848 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
849 .inspect_err(|err| {
850 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
851 })
852 else {
853 continue;
854 };
855
856 AccountInfoOf::<T>::insert(
857 address,
858 AccountInfo { account_type: info.clone().into(), dust: 0 },
859 );
860
861 <PristineCode<T>>::insert(blob.code_hash(), code.0.clone());
862 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
863 for (k, v) in storage {
864 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
865 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
866 });
867 }
868 },
869 }
870
871 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
872 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
873 });
874 }
875
876 block_storage::on_finalize_build_eth_block::<T>(
878 frame_system::Pallet::<T>::block_number(),
881 );
882
883 if let Some(settings) = self.debug_settings.as_ref() {
885 settings.write_to_storage::<T>()
886 }
887 }
888 }
889
890 #[pallet::hooks]
891 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
892 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
893 let mut meter = WeightMeter::with_limit(limit);
894 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
895 meter.consumed()
896 }
897
898 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
899 block_storage::on_initialize::<T>();
901
902 System::<T>::account_exists(&Pallet::<T>::account_id());
904 <T as Config>::WeightInfo::on_finalize_block_fixed()
906 }
907
908 fn on_finalize(block_number: BlockNumberFor<T>) {
909 block_storage::on_finalize_build_eth_block::<T>(block_number);
911 }
912
913 fn integrity_test() {
914 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
915
916 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
917
918 T::FeeInfo::integrity_test();
919
920 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
922
923 const TOTAL_MEMORY_DEVIDER: u64 = 2;
926
927 let max_block_weight = T::BlockWeights::get()
933 .get(DispatchClass::Normal)
934 .max_total
935 .unwrap_or_else(|| T::BlockWeights::get().max_block);
936 let max_key_size: u64 =
937 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
938 .expect("Key of maximal size shall be created")
939 .hash()
940 .len()
941 .try_into()
942 .unwrap();
943
944 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
945 let max_immutable_size: u64 = max_block_weight
946 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
947 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
948 ))
949 .unwrap()
950 .saturating_mul(
951 u64::from(limits::IMMUTABLE_BYTES)
952 .saturating_add(max_immutable_key_size)
953 .into(),
954 );
955
956 let max_pvf_mem: u64 = T::PVFMemory::get().into();
957 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
958
959 let max_events_size = max_block_weight
963 .checked_div_per_component(
964 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
965 num_topic: 0,
966 len: limits::EVENT_BYTES,
967 })
968 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
969 &RuntimeCosts::HostFn,
970 ))),
971 )
972 .unwrap()
973 .saturating_mul(limits::EVENT_BYTES.into());
974
975 assert!(
976 max_events_size <= storage_size_limit,
977 "Maximal events size {} exceeds the events limit {}",
978 max_events_size,
979 storage_size_limit
980 );
981
982 let max_eth_block_builder_bytes =
1017 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1018
1019 log::debug!(
1020 target: LOG_TARGET,
1021 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1022 max_eth_block_builder_bytes / 1024,
1023 max_events_size / 1024,
1024 );
1025
1026 let memory_left = i128::from(max_runtime_mem)
1031 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1032 .saturating_sub(limits::MEMORY_REQUIRED.into())
1033 .saturating_sub(max_eth_block_builder_bytes.into());
1034
1035 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1036
1037 assert!(
1038 memory_left >= 0,
1039 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1040 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1041 );
1042
1043 let max_storage_size = max_block_weight
1046 .checked_div_per_component(
1047 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1048 new_bytes: limits::STORAGE_BYTES,
1049 old_bytes: 0,
1050 })
1051 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1052 )
1053 .unwrap()
1054 .saturating_add(max_immutable_size.into())
1055 .saturating_add(max_eth_block_builder_bytes.into());
1056
1057 assert!(
1058 max_storage_size <= storage_size_limit,
1059 "Maximal storage size {} exceeds the storage limit {}",
1060 max_storage_size,
1061 storage_size_limit
1062 );
1063 }
1064 }
1065
1066 #[pallet::call]
1067 impl<T: Config> Pallet<T> {
1068 #[allow(unused_variables)]
1081 #[pallet::call_index(0)]
1082 #[pallet::weight(Weight::MAX)]
1083 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1084 Err(frame_system::Error::CallFiltered::<T>.into())
1085 }
1086
1087 #[pallet::call_index(1)]
1104 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1105 pub fn call(
1106 origin: OriginFor<T>,
1107 dest: H160,
1108 #[pallet::compact] value: BalanceOf<T>,
1109 weight_limit: Weight,
1110 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1111 data: Vec<u8>,
1112 ) -> DispatchResultWithPostInfo {
1113 Self::ensure_non_contract_if_signed(&origin)?;
1114 let mut output = Self::bare_call(
1115 origin,
1116 dest,
1117 Pallet::<T>::convert_native_to_evm(value),
1118 TransactionLimits::WeightAndDeposit {
1119 weight_limit,
1120 deposit_limit: storage_deposit_limit,
1121 },
1122 data,
1123 &ExecConfig::new_substrate_tx(),
1124 );
1125
1126 if let Ok(return_value) = &output.result &&
1127 return_value.did_revert()
1128 {
1129 output.result = Err(<Error<T>>::ContractReverted.into());
1130 }
1131 dispatch_result(
1132 output.result,
1133 output.weight_consumed,
1134 <T as Config>::WeightInfo::call(),
1135 )
1136 }
1137
1138 #[pallet::call_index(2)]
1144 #[pallet::weight(
1145 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1146 )]
1147 pub fn instantiate(
1148 origin: OriginFor<T>,
1149 #[pallet::compact] value: BalanceOf<T>,
1150 weight_limit: Weight,
1151 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1152 code_hash: sp_core::H256,
1153 data: Vec<u8>,
1154 salt: Option<[u8; 32]>,
1155 ) -> DispatchResultWithPostInfo {
1156 Self::ensure_non_contract_if_signed(&origin)?;
1157 let data_len = data.len() as u32;
1158 let mut output = Self::bare_instantiate(
1159 origin,
1160 Pallet::<T>::convert_native_to_evm(value),
1161 TransactionLimits::WeightAndDeposit {
1162 weight_limit,
1163 deposit_limit: storage_deposit_limit,
1164 },
1165 Code::Existing(code_hash),
1166 data,
1167 salt,
1168 &ExecConfig::new_substrate_tx(),
1169 );
1170 if let Ok(retval) = &output.result &&
1171 retval.result.did_revert()
1172 {
1173 output.result = Err(<Error<T>>::ContractReverted.into());
1174 }
1175 dispatch_result(
1176 output.result.map(|result| result.result),
1177 output.weight_consumed,
1178 <T as Config>::WeightInfo::instantiate(data_len),
1179 )
1180 }
1181
1182 #[pallet::call_index(3)]
1210 #[pallet::weight(
1211 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1212 .saturating_add(*weight_limit)
1213 )]
1214 pub fn instantiate_with_code(
1215 origin: OriginFor<T>,
1216 #[pallet::compact] value: BalanceOf<T>,
1217 weight_limit: Weight,
1218 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1219 code: Vec<u8>,
1220 data: Vec<u8>,
1221 salt: Option<[u8; 32]>,
1222 ) -> DispatchResultWithPostInfo {
1223 Self::ensure_non_contract_if_signed(&origin)?;
1224 let code_len = code.len() as u32;
1225 let data_len = data.len() as u32;
1226 let mut output = Self::bare_instantiate(
1227 origin,
1228 Pallet::<T>::convert_native_to_evm(value),
1229 TransactionLimits::WeightAndDeposit {
1230 weight_limit,
1231 deposit_limit: storage_deposit_limit,
1232 },
1233 Code::Upload(code),
1234 data,
1235 salt,
1236 &ExecConfig::new_substrate_tx(),
1237 );
1238 if let Ok(retval) = &output.result &&
1239 retval.result.did_revert()
1240 {
1241 output.result = Err(<Error<T>>::ContractReverted.into());
1242 }
1243 dispatch_result(
1244 output.result.map(|result| result.result),
1245 output.weight_consumed,
1246 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1247 )
1248 }
1249
1250 #[pallet::call_index(10)]
1272 #[pallet::weight(
1273 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1274 .saturating_add(*weight_limit)
1275 )]
1276 pub fn eth_instantiate_with_code(
1277 origin: OriginFor<T>,
1278 value: U256,
1279 weight_limit: Weight,
1280 eth_gas_limit: U256,
1281 code: Vec<u8>,
1282 data: Vec<u8>,
1283 transaction_encoded: Vec<u8>,
1284 effective_gas_price: U256,
1285 encoded_len: u32,
1286 ) -> DispatchResultWithPostInfo {
1287 let signer = Self::ensure_eth_signed(origin)?;
1288 let origin = OriginFor::<T>::signed(signer.clone());
1289 Self::ensure_non_contract_if_signed(&origin)?;
1290 let mut call = Call::<T>::eth_instantiate_with_code {
1291 value,
1292 weight_limit,
1293 eth_gas_limit,
1294 code: code.clone(),
1295 data: data.clone(),
1296 transaction_encoded: transaction_encoded.clone(),
1297 effective_gas_price,
1298 encoded_len,
1299 }
1300 .into();
1301 let info = T::FeeInfo::dispatch_info(&call);
1302 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1303 drop(call);
1304
1305 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1306 let extra_weight = base_info.total_weight();
1307 let output = Self::bare_instantiate(
1308 origin,
1309 value,
1310 TransactionLimits::EthereumGas {
1311 eth_gas_limit: eth_gas_limit.saturated_into(),
1312 weight_limit,
1313 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1314 },
1315 Code::Upload(code),
1316 data,
1317 None,
1318 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1319 );
1320
1321 block_storage::EthereumCallResult::new::<T>(
1322 signer,
1323 output.map_result(|r| r.result),
1324 base_info.call_weight,
1325 encoded_len,
1326 &info,
1327 effective_gas_price,
1328 )
1329 })
1330 }
1331
1332 #[pallet::call_index(11)]
1349 #[pallet::weight(
1350 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1351 .saturating_add(*weight_limit)
1352 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1353 )]
1354 pub fn eth_call(
1355 origin: OriginFor<T>,
1356 dest: H160,
1357 value: U256,
1358 weight_limit: Weight,
1359 eth_gas_limit: U256,
1360 data: Vec<u8>,
1361 transaction_encoded: Vec<u8>,
1362 effective_gas_price: U256,
1363 encoded_len: u32,
1364 ) -> DispatchResultWithPostInfo {
1365 let signer = Self::ensure_eth_signed(origin)?;
1366 let origin = OriginFor::<T>::signed(signer.clone());
1367
1368 Self::ensure_non_contract_if_signed(&origin)?;
1369 let mut call = Call::<T>::eth_call {
1370 dest,
1371 value,
1372 weight_limit,
1373 eth_gas_limit,
1374 data: data.clone(),
1375 transaction_encoded: transaction_encoded.clone(),
1376 effective_gas_price,
1377 encoded_len,
1378 }
1379 .into();
1380 let info = T::FeeInfo::dispatch_info(&call);
1381 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1382 drop(call);
1383
1384 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1385 let extra_weight = base_info.total_weight();
1386 let output = Self::bare_call(
1387 origin,
1388 dest,
1389 value,
1390 TransactionLimits::EthereumGas {
1391 eth_gas_limit: eth_gas_limit.saturated_into(),
1392 weight_limit,
1393 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1394 },
1395 data,
1396 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1397 );
1398
1399 block_storage::EthereumCallResult::new::<T>(
1400 signer,
1401 output,
1402 base_info.call_weight,
1403 encoded_len,
1404 &info,
1405 effective_gas_price,
1406 )
1407 })
1408 }
1409
1410 #[pallet::call_index(12)]
1421 #[pallet::weight(T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1422 pub fn eth_substrate_call(
1423 origin: OriginFor<T>,
1424 call: Box<<T as Config>::RuntimeCall>,
1425 transaction_encoded: Vec<u8>,
1426 ) -> DispatchResultWithPostInfo {
1427 let signer = Self::ensure_eth_signed(origin)?;
1430 let weight_overhead =
1431 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32);
1432
1433 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1434 let call_weight = call.get_dispatch_info().call_weight;
1435 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1436
1437 match &mut call_result {
1439 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1440 post_info.actual_weight = Some(
1441 post_info
1442 .actual_weight
1443 .unwrap_or_else(|| call_weight)
1444 .saturating_add(weight_overhead),
1445 );
1446 },
1447 }
1448
1449 block_storage::EthereumCallResult {
1452 receipt_gas_info: ReceiptGasInfo::default(),
1453 result: call_result,
1454 }
1455 })
1456 }
1457
1458 #[pallet::call_index(4)]
1473 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1474 pub fn upload_code(
1475 origin: OriginFor<T>,
1476 code: Vec<u8>,
1477 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1478 ) -> DispatchResult {
1479 Self::ensure_non_contract_if_signed(&origin)?;
1480 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1481 }
1482
1483 #[pallet::call_index(5)]
1488 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1489 pub fn remove_code(
1490 origin: OriginFor<T>,
1491 code_hash: sp_core::H256,
1492 ) -> DispatchResultWithPostInfo {
1493 let origin = ensure_signed(origin)?;
1494 <ContractBlob<T>>::remove(&origin, code_hash)?;
1495 Ok(Pays::No.into())
1497 }
1498
1499 #[pallet::call_index(6)]
1510 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1511 pub fn set_code(
1512 origin: OriginFor<T>,
1513 dest: H160,
1514 code_hash: sp_core::H256,
1515 ) -> DispatchResult {
1516 ensure_root(origin)?;
1517 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1518 let Some(account) = account else {
1519 return Err(<Error<T>>::ContractNotFound.into());
1520 };
1521
1522 let AccountType::Contract(ref mut contract) = account.account_type else {
1523 return Err(<Error<T>>::ContractNotFound.into());
1524 };
1525
1526 <CodeInfo<T>>::increment_refcount(code_hash)?;
1527 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1528 contract.code_hash = code_hash;
1529
1530 Ok(())
1531 })
1532 }
1533
1534 #[pallet::call_index(7)]
1539 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1540 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1541 Self::ensure_non_contract_if_signed(&origin)?;
1542 let origin = ensure_signed(origin)?;
1543 T::AddressMapper::map(&origin)
1544 }
1545
1546 #[pallet::call_index(8)]
1551 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1552 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1553 let origin = ensure_signed(origin)?;
1554 T::AddressMapper::unmap(&origin)
1555 }
1556
1557 #[pallet::call_index(9)]
1563 #[pallet::weight({
1564 let dispatch_info = call.get_dispatch_info();
1565 (
1566 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1567 dispatch_info.class
1568 )
1569 })]
1570 pub fn dispatch_as_fallback_account(
1571 origin: OriginFor<T>,
1572 call: Box<<T as Config>::RuntimeCall>,
1573 ) -> DispatchResultWithPostInfo {
1574 Self::ensure_non_contract_if_signed(&origin)?;
1575 let origin = ensure_signed(origin)?;
1576 let unmapped_account =
1577 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1578 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1579 }
1580 }
1581}
1582
1583fn dispatch_result<R>(
1585 result: Result<R, DispatchError>,
1586 weight_consumed: Weight,
1587 base_weight: Weight,
1588) -> DispatchResultWithPostInfo {
1589 let post_info = PostDispatchInfo {
1590 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1591 pays_fee: Default::default(),
1592 };
1593
1594 result
1595 .map(|_| post_info)
1596 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1597}
1598
1599impl<T: Config> Pallet<T> {
1600 pub fn bare_call(
1607 origin: OriginFor<T>,
1608 dest: H160,
1609 evm_value: U256,
1610 transaction_limits: TransactionLimits<T>,
1611 data: Vec<u8>,
1612 exec_config: &ExecConfig<T>,
1613 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1614 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1615 Ok(transaction_meter) => transaction_meter,
1616 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1617 };
1618 let mut storage_deposit = Default::default();
1619
1620 let try_call = || {
1621 let origin = ExecOrigin::from_runtime_origin(origin)?;
1622 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1623 origin.clone(),
1624 dest,
1625 &mut transaction_meter,
1626 evm_value,
1627 data,
1628 &exec_config,
1629 )?;
1630
1631 storage_deposit = transaction_meter
1632 .execute_postponed_deposits(&origin, &exec_config)
1633 .inspect_err(|err| {
1634 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1635 })?;
1636
1637 Ok(result)
1638 };
1639 let result = Self::run_guarded(try_call);
1640
1641 log::trace!(target: LOG_TARGET, "Bare call ends: \
1642 result={result:?}, \
1643 weight_consumed={:?}, \
1644 weight_required={:?}, \
1645 storage_deposit={:?}, \
1646 gas_consumed={:?}, \
1647 max_storage_deposit={:?}",
1648 transaction_meter.weight_consumed(),
1649 transaction_meter.weight_required(),
1650 storage_deposit,
1651 transaction_meter.total_consumed_gas(),
1652 transaction_meter.deposit_required()
1653 );
1654
1655 ContractResult {
1656 result: result.map_err(|r| r.error),
1657 weight_consumed: transaction_meter.weight_consumed(),
1658 weight_required: transaction_meter.weight_required(),
1659 storage_deposit,
1660 gas_consumed: transaction_meter.total_consumed_gas(),
1661 max_storage_deposit: transaction_meter.deposit_required(),
1662 }
1663 }
1664
1665 pub fn prepare_dry_run(account: &T::AccountId) {
1671 frame_system::Pallet::<T>::inc_account_nonce(account);
1674 }
1675
1676 pub fn bare_instantiate(
1682 origin: OriginFor<T>,
1683 evm_value: U256,
1684 transaction_limits: TransactionLimits<T>,
1685 code: Code,
1686 data: Vec<u8>,
1687 salt: Option<[u8; 32]>,
1688 exec_config: &ExecConfig<T>,
1689 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1690 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1691 Ok(transaction_meter) => transaction_meter,
1692 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1693 };
1694
1695 let mut storage_deposit = Default::default();
1696
1697 let try_instantiate = || {
1698 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1699
1700 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1701 let executable = match code {
1702 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1703 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1704 let executable = Self::try_upload_code(
1705 upload_account,
1706 code,
1707 BytecodeType::Pvm,
1708 &mut transaction_meter,
1709 &exec_config,
1710 )?;
1711 executable
1712 },
1713 Code::Upload(code) => {
1714 if T::AllowEVMBytecode::get() {
1715 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1716 let origin = T::UploadOrigin::ensure_origin(origin)?;
1717 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1718 executable
1719 } else {
1720 return Err(<Error<T>>::CodeRejected.into());
1721 }
1722 },
1723 Code::Existing(code_hash) => {
1724 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1725 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1726 executable
1727 },
1728 };
1729 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1730 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1731 instantiate_account,
1732 executable,
1733 &mut transaction_meter,
1734 evm_value,
1735 data,
1736 salt.as_ref(),
1737 &exec_config,
1738 );
1739
1740 storage_deposit = transaction_meter
1741 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1742 .inspect_err(|err| {
1743 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1744 })?;
1745 result
1746 };
1747 let output = Self::run_guarded(try_instantiate);
1748
1749 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1750 weight_required={:?} \
1751 storage_deposit={:?} \
1752 gas_consumed={:?} \
1753 max_storage_deposit={:?}",
1754 transaction_meter.weight_consumed(),
1755 transaction_meter.weight_required(),
1756 storage_deposit,
1757 transaction_meter.total_consumed_gas(),
1758 transaction_meter.deposit_required()
1759 );
1760
1761 ContractResult {
1762 result: output
1763 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1764 .map_err(|e| e.error),
1765 weight_consumed: transaction_meter.weight_consumed(),
1766 weight_required: transaction_meter.weight_required(),
1767 storage_deposit,
1768 gas_consumed: transaction_meter.total_consumed_gas(),
1769 max_storage_deposit: transaction_meter.deposit_required(),
1770 }
1771 }
1772
1773 pub fn eth_estimate_gas(
1785 tx: GenericTransaction,
1786 config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1787 ) -> Result<U256, EthTransactError>
1788 where
1789 T::Nonce: Into<U256>,
1790 CallOf<T>: SetWeightLimit,
1791 {
1792 log::debug!(target: LOG_TARGET, "eth_estimate_gas: {tx:?}");
1793
1794 let mut low = U256::zero();
1795 let mut high = Self::evm_block_gas_limit();
1796
1797 log::trace!(target: LOG_TARGET, "eth_estimate_gas starting with low={low}, high={high}");
1798
1799 let perform_balance_checks = if let Some(gas_limit) = tx.gas {
1803 high = gas_limit;
1804 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the gas limit high={high}");
1805 true
1806 } else {
1807 false
1808 };
1809
1810 let fee_cap = tx.max_fee_per_gas.or(tx.gas_price);
1812 if let (Some(fee_cap), Some(from), true) = (fee_cap, tx.from, perform_balance_checks) {
1813 let mut available_balance = Self::evm_balance(&from);
1814 if let Some(value) = tx.value {
1815 available_balance = available_balance.checked_sub(value).ok_or_else(|| {
1816 EthTransactError::Message("insufficient funds for value transfer".into())
1817 })?;
1818 }
1819 if let Some(allowance) = available_balance.checked_div(fee_cap) {
1820 if high > allowance && allowance != U256::zero() {
1821 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the user's allowance high={high} allowance={allowance}");
1822 high = allowance
1823 }
1824 }
1825 }
1826
1827 let dry_run_results = [high, Self::evm_max_extrinsic_weight_in_gas()].map(|gas_limit| {
1835 let mut transaction = tx.clone();
1836 transaction.gas = Some(gas_limit);
1837 let eth_transact_result = with_transaction(|| {
1838 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
1839 transaction,
1840 config.with_perform_balance_checks(perform_balance_checks),
1841 )))
1842 })
1843 .expect("Rollback shouldn't error out");
1844 (gas_limit, eth_transact_result)
1845 });
1846 let (gas_limit, first_dry_run_result) = match dry_run_results {
1847 [(gas_limit1, Ok(dry_run_result1)), (gas_limit2, Ok(dry_run_result2))] => {
1848 if dry_run_result2.eth_gas >= gas_limit2 {
1849 (gas_limit1, dry_run_result1)
1850 } else {
1851 (gas_limit2, dry_run_result2)
1852 }
1853 },
1854 [(gas_limit, Ok(dry_run_result)), (_, Err(_))] |
1855 [(_, Err(_)), (gas_limit, Ok(dry_run_result))] => (gas_limit, dry_run_result),
1856 [(_, Err(err)), (_, Err(..))] => return Err(err),
1857 };
1858 log::trace!(
1859 target: LOG_TARGET,
1860 "eth_estimate_gas first dry run succeeded with gas_limit={} consumed={}",
1861 gas_limit,
1862 first_dry_run_result.eth_gas
1863 );
1864 low = first_dry_run_result.eth_gas;
1865 high = gas_limit;
1866
1867 while low + U256::one() < high {
1868 log::trace!(target: LOG_TARGET, "eth_estimate_gas estimation iteration with low={low} high={high}");
1869 let error_ratio = high
1870 .checked_sub(low)
1871 .and_then(|value| value.checked_mul(U256::from(1000)))
1872 .and_then(|value| value.checked_div(high))
1873 .ok_or_else(|| {
1874 EthTransactError::Message(
1875 "failed to calculate error ratio in gas estimation".into(),
1876 )
1877 })?;
1878 if error_ratio <= U256::from(15) {
1879 log::trace!(
1880 target: LOG_TARGET,
1881 "eth_estimate_gas finished due to error ratio being less than 1.5% high={}",
1882 high
1883 );
1884 break;
1885 }
1886
1887 let mut midpoint = high
1888 .checked_sub(low)
1889 .and_then(|value| value.checked_div(U256::from(2)))
1890 .and_then(|value| value.checked_add(low))
1891 .ok_or_else(|| {
1892 EthTransactError::Message(
1893 "failed to calculate midpoint in gas estimation".into(),
1894 )
1895 })?;
1896
1897 if let Some(other_midpoint) = low.checked_mul(U256::from(2)) {
1898 if other_midpoint != U256::zero() {
1899 midpoint = midpoint.min(other_midpoint)
1900 }
1901 };
1902
1903 let mut transaction = tx.clone();
1904 transaction.gas = Some(midpoint);
1905 let dry_run_result = with_transaction(|| {
1906 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
1907 transaction,
1908 config.with_perform_balance_checks(perform_balance_checks),
1909 )))
1910 })
1911 .expect("Rollback shouldn't error out");
1912 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run result with midpoint={midpoint} is dry_run_result={dry_run_result:?}");
1913 match dry_run_result {
1914 Ok(..) => {
1915 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run succeeded, new high={midpoint}");
1916 high = midpoint
1917 },
1918 Err(..) => {
1919 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run failed, new low={midpoint}");
1920 low = midpoint
1921 },
1922 }
1923 }
1924
1925 log::trace!(target: LOG_TARGET, "eth_estimate_gas completed. high={high}");
1926 Ok(high)
1927 }
1928
1929 pub fn dry_run_eth_transact(
1935 mut tx: GenericTransaction,
1936 dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1937 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1938 where
1939 T::Nonce: Into<U256>,
1940 CallOf<T>: SetWeightLimit,
1941 {
1942 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1943
1944 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1945 Self::prepare_dry_run(&origin);
1946
1947 let base_fee = Self::evm_base_fee();
1948 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1949
1950 if effective_gas_price < base_fee {
1951 Err(EthTransactError::Message(format!(
1952 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1953 )))?;
1954 }
1955
1956 if tx.nonce.is_none() {
1957 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1958 }
1959 if tx.chain_id.is_none() {
1960 tx.chain_id = Some(T::ChainId::get().into());
1961 }
1962
1963 tx.gas_price = Some(effective_gas_price);
1965 tx.max_priority_fee_per_gas = Some(0.into());
1968 if tx.max_fee_per_gas.is_none() {
1969 tx.max_fee_per_gas = Some(effective_gas_price);
1970 }
1971
1972 let gas = tx.gas;
1973 if tx.gas.is_none() {
1974 tx.gas = Some(Self::evm_block_gas_limit());
1975 }
1976 if tx.r#type.is_none() {
1977 tx.r#type = Some(TYPE_EIP1559.into());
1978 }
1979
1980 let value = tx.value.unwrap_or_default();
1982 let input = tx.input.clone().to_vec();
1983 let from = tx.from;
1984 let to = tx.to;
1985
1986 let mut call_info = tx
1989 .into_call::<T>(CreateCallMode::DryRun)
1990 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1991
1992 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1996 let base_weight = base_info.total_weight();
1997 let exec_config =
1998 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
1999 .with_dry_run(dry_run_config);
2000
2001 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
2003 if let Some(from) = &from {
2004 let fees =
2005 if gas.is_some() && matches!(dry_run_config.perform_balance_checks, Some(true)) {
2006 fees
2007 } else {
2008 Zero::zero()
2009 };
2010 let balance = Self::evm_balance(from);
2011 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
2012 return Err(EthTransactError::Message(format!(
2013 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
2014 )));
2015 }
2016 }
2017
2018 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
2021
2022 let extract_error = |err| {
2023 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
2024 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
2025 } else {
2026 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
2027 }
2028 };
2029
2030 let transaction_limits = TransactionLimits::EthereumGas {
2031 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
2032 weight_limit: Self::evm_max_extrinsic_weight(),
2033 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
2034 };
2035
2036 let mut dry_run = match to {
2038 Some(dest) => {
2040 if dest == RUNTIME_PALLETS_ADDR {
2041 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
2042 return Err(EthTransactError::Message(format!(
2043 "Failed to decode pallet-call {input:?}"
2044 )));
2045 };
2046
2047 if let Err(result) =
2048 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
2049 {
2050 return Err(EthTransactError::Message(format!(
2051 "Failed to dispatch call: {:?}",
2052 result.error,
2053 )));
2054 };
2055
2056 Default::default()
2057 } else {
2058 let result = crate::Pallet::<T>::bare_call(
2060 OriginFor::<T>::signed(origin),
2061 dest,
2062 value,
2063 transaction_limits,
2064 input.clone(),
2065 &exec_config,
2066 );
2067
2068 let data = match result.result {
2069 Ok(return_value) => {
2070 if return_value.did_revert() {
2071 return Err(EthTransactError::Data(return_value.data));
2072 }
2073 return_value.data
2074 },
2075 Err(err) => {
2076 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
2077 return extract_error(err);
2078 },
2079 };
2080
2081 EthTransactInfo {
2082 weight_required: result.weight_required,
2083 storage_deposit: result.storage_deposit.charge_or_zero(),
2084 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2085 data,
2086 eth_gas: Default::default(),
2087 }
2088 }
2089 },
2090 None => {
2092 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2094 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
2095 } else {
2096 (input, vec![])
2097 };
2098
2099 let result = crate::Pallet::<T>::bare_instantiate(
2101 OriginFor::<T>::signed(origin),
2102 value,
2103 transaction_limits,
2104 Code::Upload(code.clone()),
2105 data.clone(),
2106 None,
2107 &exec_config,
2108 );
2109
2110 let returned_data = match result.result {
2111 Ok(return_value) => {
2112 if return_value.result.did_revert() {
2113 return Err(EthTransactError::Data(return_value.result.data));
2114 }
2115 return_value.result.data
2116 },
2117 Err(err) => {
2118 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
2119 return extract_error(err);
2120 },
2121 };
2122
2123 EthTransactInfo {
2124 weight_required: result.weight_required,
2125 storage_deposit: result.storage_deposit.charge_or_zero(),
2126 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2127 data: returned_data,
2128 eth_gas: Default::default(),
2129 }
2130 },
2131 };
2132
2133 call_info.call.set_weight_limit(dry_run.weight_required);
2135
2136 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
2138 let max_weight = Self::evm_max_extrinsic_weight();
2139 if total_weight.any_gt(max_weight) {
2140 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
2141 total_weight={total_weight:?} \
2142 max_weight={max_weight:?}",
2143 );
2144
2145 Err(EthTransactError::Message(format!(
2146 "\
2147 The transaction consumes more than the allowed weight. \
2148 needed={total_weight} \
2149 allowed={max_weight} \
2150 overweight_by={}\
2151 ",
2152 total_weight.saturating_sub(max_weight),
2153 )))?;
2154 }
2155
2156 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
2158 let available_fee = T::FeeInfo::remaining_txfee();
2159 if transaction_fee > available_fee {
2160 Err(EthTransactError::Message(format!(
2161 "Not enough gas supplied: Off by: {:?}",
2162 transaction_fee.saturating_sub(available_fee),
2163 )))?;
2164 }
2165
2166 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2167 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2168 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2169 if !rest.is_zero() {
2170 eth_gas = eth_gas.saturating_add(1_u32.into());
2171 }
2172
2173 log::debug!(target: LOG_TARGET, "\
2174 dry_run_eth_transact finished: \
2175 weight_limit={}, \
2176 total_weight={total_weight}, \
2177 max_weight={max_weight}, \
2178 weight_left={}, \
2179 eth_gas={eth_gas}, \
2180 encoded_len={}, \
2181 tx_fee={transaction_fee:?}, \
2182 storage_deposit={:?}, \
2183 max_storage_deposit={:?}\
2184 ",
2185 dry_run.weight_required,
2186 max_weight.saturating_sub(total_weight),
2187 call_info.encoded_len,
2188 dry_run.storage_deposit,
2189 dry_run.max_storage_deposit,
2190
2191 );
2192 dry_run.eth_gas = eth_gas;
2193 Ok(dry_run)
2194 }
2195
2196 pub fn evm_balance(address: &H160) -> U256 {
2200 let balance = AccountInfo::<T>::balance_of((*address).into());
2201 Self::convert_native_to_evm(balance)
2202 }
2203
2204 pub fn eth_block() -> EthBlock {
2206 EthereumBlock::<T>::get()
2207 }
2208
2209 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2216 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2217 let hash = <BlockHash<T>>::get(number);
2218 if hash == H256::zero() { None } else { Some(hash) }
2219 }
2220
2221 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2223 ReceiptInfoData::<T>::get()
2224 }
2225
2226 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2232 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2233 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2234 let account_id = T::AddressMapper::to_account_id(&address);
2235 T::Currency::set_balance(&account_id, balance);
2236 AccountInfoOf::<T>::mutate(&address, |account| {
2237 if let Some(account) = account {
2238 account.dust = dust;
2239 } else {
2240 *account = Some(AccountInfo { dust, ..Default::default() });
2241 }
2242 });
2243
2244 Ok(())
2245 }
2246
2247 pub fn new_balance_with_dust(
2251 evm_value: U256,
2252 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2253 let ed = T::Currency::minimum_balance();
2254 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2255 let (value, dust) = balance_with_dust.deconstruct();
2256
2257 Ok((ed.saturating_add(value), dust))
2258 }
2259
2260 pub fn evm_nonce(address: &H160) -> u32
2262 where
2263 T::Nonce: Into<u32>,
2264 {
2265 let account = T::AddressMapper::to_account_id(&address);
2266 System::<T>::account_nonce(account).into()
2267 }
2268
2269 pub fn evm_block_gas_limit() -> U256 {
2271 u64::MAX.into()
2278 }
2279
2280 pub fn evm_max_extrinsic_weight_in_gas() -> U256 {
2282 let max_extrinsic_fee = T::FeeInfo::weight_to_fee(&Self::evm_max_extrinsic_weight());
2283 let gas_scale: BalanceOf<T> = T::GasScale::get().into();
2284 (max_extrinsic_fee / gas_scale).into()
2285 }
2286
2287 pub fn evm_max_extrinsic_weight() -> Weight {
2289 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2290 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2291 .get(DispatchClass::Normal)
2292 .max_extrinsic
2293 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2294 Weight::from_parts(
2295 factor.saturating_mul_int(max_weight.ref_time()),
2296 factor.saturating_mul_int(max_weight.proof_size()),
2297 )
2298 }
2299
2300 pub fn evm_base_fee() -> U256 {
2302 let gas_scale = <T as Config>::GasScale::get();
2303 let multiplier = T::FeeInfo::next_fee_multiplier();
2304 multiplier
2305 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2306 .saturating_mul(gas_scale.saturated_into())
2307 .into()
2308 }
2309
2310 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2312 where
2313 T::Nonce: Into<u32>,
2314 {
2315 match tracer_type {
2316 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2317 TracerType::PrestateTracer(config) => {
2318 PrestateTracer::new(config.unwrap_or_default()).into()
2319 },
2320 TracerType::ExecutionTracer(config) => {
2321 ExecutionTracer::new(config.unwrap_or_default()).into()
2322 },
2323 }
2324 }
2325
2326 pub fn bare_upload_code(
2330 origin: OriginFor<T>,
2331 code: Vec<u8>,
2332 storage_deposit_limit: BalanceOf<T>,
2333 ) -> CodeUploadResult<BalanceOf<T>> {
2334 let origin = T::UploadOrigin::ensure_origin(origin)?;
2335
2336 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2337 BytecodeType::Pvm
2338 } else {
2339 if !T::AllowEVMBytecode::get() {
2340 return Err(<Error<T>>::CodeRejected.into());
2341 }
2342 BytecodeType::Evm
2343 };
2344
2345 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2346 weight_limit: Default::default(),
2347 deposit_limit: storage_deposit_limit,
2348 })?;
2349
2350 let module = Self::try_upload_code(
2351 origin,
2352 code,
2353 bytecode_type,
2354 &mut meter,
2355 &ExecConfig::new_substrate_tx(),
2356 )?;
2357 Ok(CodeUploadReturnValue {
2358 code_hash: *module.code_hash(),
2359 deposit: meter.deposit_consumed().charge_or_zero(),
2360 })
2361 }
2362
2363 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2365 let contract_info =
2366 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2367
2368 let maybe_value = contract_info.read(&Key::from_fixed(key));
2369 Ok(maybe_value)
2370 }
2371
2372 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2376 let immutable_data = <ImmutableDataOf<T>>::get(address);
2377 immutable_data
2378 }
2379
2380 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2388 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2389 <ImmutableDataOf<T>>::insert(address, data);
2390 Ok(())
2391 }
2392
2393 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2395 let contract_info =
2396 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2397
2398 let maybe_value = contract_info.read(
2399 &Key::try_from_var(key)
2400 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2401 .into(),
2402 );
2403 Ok(maybe_value)
2404 }
2405
2406 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2408 let (value, dust) = value.into().deconstruct();
2409 value
2410 .into()
2411 .saturating_mul(T::NativeToEthRatio::get().into())
2412 .saturating_add(dust.into())
2413 }
2414
2415 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2425 let contract_info =
2426 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2427
2428 contract_info
2429 .write(&Key::from_fixed(key), value, None, false)
2430 .map_err(ContractAccessError::StorageWriteFailed)
2431 }
2432
2433 pub fn set_storage_var_key(
2444 address: H160,
2445 key: Vec<u8>,
2446 value: Option<Vec<u8>>,
2447 ) -> SetStorageResult {
2448 let contract_info =
2449 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2450
2451 contract_info
2452 .write(
2453 &Key::try_from_var(key)
2454 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2455 .into(),
2456 value,
2457 None,
2458 false,
2459 )
2460 .map_err(ContractAccessError::StorageWriteFailed)
2461 }
2462
2463 pub fn account_id() -> T::AccountId {
2465 use frame_support::PalletId;
2466 use sp_runtime::traits::AccountIdConversion;
2467 PalletId(*b"py/reviv").into_account_truncating()
2468 }
2469
2470 pub fn block_author() -> H160 {
2472 use frame_support::traits::FindAuthor;
2473
2474 let digest = <frame_system::Pallet<T>>::digest();
2475 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2476
2477 T::FindAuthor::find_author(pre_runtime_digests)
2478 .map(|account_id| T::AddressMapper::to_address(&account_id))
2479 .unwrap_or_default()
2480 }
2481
2482 pub fn code(address: &H160) -> Vec<u8> {
2486 use precompiles::{All, Precompiles};
2487 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2488 return code.into();
2489 }
2490 AccountInfo::<T>::load_contract(&address)
2491 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2492 .map(|code| code.into())
2493 .unwrap_or_default()
2494 }
2495
2496 pub fn try_upload_code(
2498 origin: T::AccountId,
2499 code: Vec<u8>,
2500 code_type: BytecodeType,
2501 meter: &mut TransactionMeter<T>,
2502 exec_config: &ExecConfig<T>,
2503 ) -> Result<ContractBlob<T>, DispatchError> {
2504 let mut module = match code_type {
2505 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2506 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2507 };
2508 module.store_code(exec_config, meter)?;
2509 Ok(module)
2510 }
2511
2512 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2514 executing_contract::using_once(&mut false, || {
2515 executing_contract::with(|f| {
2516 if *f {
2518 return Err(())
2519 }
2520 *f = true;
2522 Ok(())
2523 })
2524 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2525 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2526 .map(|_| f())
2527 .and_then(|r| r)
2528 })
2529 }
2530
2531 fn charge_deposit(
2536 hold_reason: Option<HoldReason>,
2537 from: &T::AccountId,
2538 to: &T::AccountId,
2539 amount: BalanceOf<T>,
2540 exec_config: &ExecConfig<T>,
2541 ) -> DispatchResult {
2542 use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
2543
2544 if amount.is_zero() {
2545 return Ok(());
2546 }
2547
2548 match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2549 (true, hold_reason) => {
2550 T::FeeInfo::withdraw_txfee(amount)
2551 .ok_or(())
2552 .and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2553 .and_then(|_| {
2554 if let Some(hold_reason) = hold_reason {
2555 T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2556 }
2557 Ok(())
2558 })
2559 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2560 },
2561 (false, Some(hold_reason)) => {
2562 T::Currency::transfer_and_hold(
2563 &hold_reason.into(),
2564 from,
2565 to,
2566 amount,
2567 Precision::Exact,
2568 Preservation::Preserve,
2569 Fortitude::Polite,
2570 )
2571 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2572 },
2573 (false, None) => {
2574 T::Currency::transfer(from, to, amount, Preservation::Preserve)
2575 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2576 },
2577 }
2578 Ok(())
2579 }
2580
2581 fn refund_deposit(
2586 hold_reason: HoldReason,
2587 from: &T::AccountId,
2588 to: &T::AccountId,
2589 amount: BalanceOf<T>,
2590 exec_config: Option<&ExecConfig<T>>,
2591 ) -> Result<(), DispatchError> {
2592 use frame_support::traits::{
2593 fungible::InspectHold,
2594 tokens::{Fortitude, Precision, Preservation, Restriction},
2595 };
2596
2597 if amount.is_zero() {
2598 return Ok(());
2599 }
2600
2601 let hold_reason = hold_reason.into();
2602 let result = if exec_config.map(|c| c.collect_deposit_from_hold.is_some()).unwrap_or(false)
2603 {
2604 T::Currency::release(&hold_reason, from, amount, Precision::Exact)
2605 .and_then(|amount| {
2606 T::Currency::withdraw(
2607 from,
2608 amount,
2609 Precision::Exact,
2610 Preservation::Preserve,
2611 Fortitude::Polite,
2612 )
2613 })
2614 .map(T::FeeInfo::deposit_txfee)
2615 } else {
2616 T::Currency::transfer_on_hold(
2617 &hold_reason,
2618 from,
2619 to,
2620 amount,
2621 Precision::Exact,
2622 Restriction::Free,
2623 Fortitude::Polite,
2624 )
2625 .map(|_| ())
2626 };
2627
2628 result.map_err(|_| {
2629 let available = T::Currency::balance_on_hold(&hold_reason, from);
2630 if available < amount {
2631 log::error!(
2634 target: LOG_TARGET,
2635 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. Not enough deposit: {:?}. This is a bug.",
2636 amount, from, to, available,
2637 );
2638 Error::<T>::StorageRefundNotEnoughFunds.into()
2639 } else {
2640 log::warn!(
2645 target: LOG_TARGET,
2646 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. First remove locks (staking, governance) from the contracts account.",
2647 amount, from, to,
2648 );
2649 Error::<T>::StorageRefundLocked.into()
2650 }
2651 })
2652 }
2653
2654 fn has_dust(value: U256) -> bool {
2656 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2657 }
2658
2659 fn has_balance(value: U256) -> bool {
2661 value >= U256::from(<T>::NativeToEthRatio::get())
2662 }
2663
2664 fn min_balance() -> BalanceOf<T> {
2666 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2667 }
2668
2669 fn deposit_event(event: Event<T>) {
2674 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2675 }
2676
2677 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2679 match <T as Config>::RuntimeOrigin::from(origin).into() {
2680 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2681 _ => Err(BadOrigin.into()),
2682 }
2683 }
2684
2685 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2689 if DebugSettings::bypass_eip_3607::<T>() {
2690 return Ok(());
2691 }
2692 let Some(address) = origin
2693 .as_system_ref()
2694 .and_then(|o| o.as_signed())
2695 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2696 else {
2697 return Ok(());
2698 };
2699 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2700 <AccountInfo<T>>::is_contract(&address)
2701 {
2702 log::debug!(
2703 target: crate::LOG_TARGET,
2704 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2705 );
2706 Err(DispatchError::BadOrigin)
2707 } else {
2708 Ok(())
2709 }
2710 }
2711}
2712
2713pub const RUNTIME_PALLETS_ADDR: H160 =
2718 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2719
2720environmental!(executing_contract: bool);
2722
2723sp_api::decl_runtime_apis! {
2724 #[api_version(1)]
2726 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2727 AccountId: Codec,
2728 Balance: Codec,
2729 Nonce: Codec,
2730 BlockNumber: Codec,
2731 Moment: Codec,
2732 {
2733 fn eth_block() -> EthBlock;
2737
2738 fn eth_block_hash(number: U256) -> Option<H256>;
2740
2741 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2747
2748 fn block_gas_limit() -> U256;
2750
2751 fn max_extrinsic_weight_in_gas() -> U256;
2753
2754 fn balance(address: H160) -> U256;
2756
2757 fn gas_price() -> U256;
2759
2760 fn nonce(address: H160) -> Nonce;
2762
2763 fn call(
2767 origin: AccountId,
2768 dest: H160,
2769 value: Balance,
2770 gas_limit: Option<Weight>,
2771 storage_deposit_limit: Option<Balance>,
2772 input_data: Vec<u8>,
2773 ) -> ContractResult<ExecReturnValue, Balance>;
2774
2775 fn instantiate(
2779 origin: AccountId,
2780 value: Balance,
2781 gas_limit: Option<Weight>,
2782 storage_deposit_limit: Option<Balance>,
2783 code: Code,
2784 data: Vec<u8>,
2785 salt: Option<[u8; 32]>,
2786 ) -> ContractResult<InstantiateReturnValue, Balance>;
2787
2788
2789 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2794
2795 fn eth_transact_with_config(
2799 tx: GenericTransaction,
2800 config: DryRunConfig<Moment>,
2801 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2802
2803 fn eth_estimate_gas(
2809 tx: GenericTransaction,
2810 config: DryRunConfig<Moment>
2811 ) -> Result<U256, EthTransactError>;
2812
2813 fn upload_code(
2817 origin: AccountId,
2818 code: Vec<u8>,
2819 storage_deposit_limit: Option<Balance>,
2820 ) -> CodeUploadResult<Balance>;
2821
2822 fn get_storage(
2828 address: H160,
2829 key: [u8; 32],
2830 ) -> GetStorageResult;
2831
2832 fn get_storage_var_key(
2838 address: H160,
2839 key: Vec<u8>,
2840 ) -> GetStorageResult;
2841
2842 fn trace_block(
2849 block: Block,
2850 config: TracerType
2851 ) -> Vec<(u32, Trace)>;
2852
2853 fn trace_tx(
2860 block: Block,
2861 tx_index: u32,
2862 config: TracerType
2863 ) -> Option<Trace>;
2864
2865 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2869
2870 fn block_author() -> H160;
2872
2873 fn address(account_id: AccountId) -> H160;
2875
2876 fn account_id(address: H160) -> AccountId;
2878
2879 fn runtime_pallets_address() -> H160;
2881
2882 fn code(address: H160) -> Vec<u8>;
2884
2885 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2887 }
2888}
2889
2890#[macro_export]
2904macro_rules! impl_runtime_apis_plus_revive_traits {
2905 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2906
2907 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2908
2909 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2910 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
2911 use $crate::pallet::Call as ReviveCall;
2912 match self {
2913 Self::$Revive(
2914 ReviveCall::eth_call{ weight_limit, .. } |
2915 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
2916 ) => {
2917 let old = *weight_limit;
2918 *weight_limit = new_weight_limit;
2919 old
2920 },
2921 _ => Weight::default(),
2922 }
2923 }
2924 }
2925
2926 impl_runtime_apis! {
2927 $($rest)*
2928
2929
2930 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2931 {
2932 fn eth_block() -> $crate::EthBlock {
2933 $crate::Pallet::<Self>::eth_block()
2934 }
2935
2936 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2937 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
2938 }
2939
2940 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2941 $crate::Pallet::<Self>::eth_receipt_data()
2942 }
2943
2944 fn balance(address: $crate::H160) -> $crate::U256 {
2945 $crate::Pallet::<Self>::evm_balance(&address)
2946 }
2947
2948 fn block_author() -> $crate::H160 {
2949 $crate::Pallet::<Self>::block_author()
2950 }
2951
2952 fn block_gas_limit() -> $crate::U256 {
2953 $crate::Pallet::<Self>::evm_block_gas_limit()
2954 }
2955
2956 fn max_extrinsic_weight_in_gas() -> $crate::U256 {
2957 $crate::Pallet::<Self>::evm_max_extrinsic_weight_in_gas()
2958 }
2959
2960 fn gas_price() -> $crate::U256 {
2961 $crate::Pallet::<Self>::evm_base_fee()
2962 }
2963
2964 fn nonce(address: $crate::H160) -> Nonce {
2965 use $crate::AddressMapper;
2966 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2967 $crate::frame_system::Pallet::<Self>::account_nonce(account)
2968 }
2969
2970 fn address(account_id: AccountId) -> $crate::H160 {
2971 use $crate::AddressMapper;
2972 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2973 }
2974
2975 fn eth_transact(
2976 tx: $crate::evm::GenericTransaction,
2977 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2978 use $crate::{
2979 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2980 sp_runtime::traits::TransactionExtension,
2981 sp_runtime::traits::Block as BlockT
2982 };
2983 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
2984 }
2985
2986 fn eth_transact_with_config(
2987 tx: $crate::evm::GenericTransaction,
2988 config: $crate::DryRunConfig<__ReviveMacroMoment>,
2989 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2990 use $crate::{
2991 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2992 sp_runtime::traits::TransactionExtension,
2993 sp_runtime::traits::Block as BlockT
2994 };
2995 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
2996 }
2997
2998 fn eth_estimate_gas(
2999 tx: $crate::evm::GenericTransaction,
3000 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3001 ) -> Result<$crate::U256, $crate::EthTransactError> {
3002 use $crate::{
3003 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3004 sp_runtime::traits::TransactionExtension,
3005 sp_runtime::traits::Block as BlockT
3006 };
3007 $crate::Pallet::<Self>::eth_estimate_gas(tx, config)
3008 }
3009
3010 fn call(
3011 origin: AccountId,
3012 dest: $crate::H160,
3013 value: Balance,
3014 weight_limit: Option<$crate::Weight>,
3015 storage_deposit_limit: Option<Balance>,
3016 input_data: Vec<u8>,
3017 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
3018 use $crate::frame_support::traits::Get;
3019 let blockweights: $crate::BlockWeights =
3020 <Self as $crate::frame_system::Config>::BlockWeights::get();
3021
3022 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3023 $crate::Pallet::<Self>::bare_call(
3024 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3025 dest,
3026 $crate::Pallet::<Self>::convert_native_to_evm(value),
3027 $crate::TransactionLimits::WeightAndDeposit {
3028 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3029 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3030 },
3031 input_data,
3032 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3033 )
3034 }
3035
3036 fn instantiate(
3037 origin: AccountId,
3038 value: Balance,
3039 weight_limit: Option<$crate::Weight>,
3040 storage_deposit_limit: Option<Balance>,
3041 code: $crate::Code,
3042 data: Vec<u8>,
3043 salt: Option<[u8; 32]>,
3044 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
3045 use $crate::frame_support::traits::Get;
3046 let blockweights: $crate::BlockWeights =
3047 <Self as $crate::frame_system::Config>::BlockWeights::get();
3048
3049 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3050 $crate::Pallet::<Self>::bare_instantiate(
3051 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3052 $crate::Pallet::<Self>::convert_native_to_evm(value),
3053 $crate::TransactionLimits::WeightAndDeposit {
3054 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3055 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3056 },
3057 code,
3058 data,
3059 salt,
3060 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3061 )
3062 }
3063
3064 fn upload_code(
3065 origin: AccountId,
3066 code: Vec<u8>,
3067 storage_deposit_limit: Option<Balance>,
3068 ) -> $crate::CodeUploadResult<Balance> {
3069 let origin =
3070 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
3071 $crate::Pallet::<Self>::bare_upload_code(
3072 origin,
3073 code,
3074 storage_deposit_limit.unwrap_or(u128::MAX),
3075 )
3076 }
3077
3078 fn get_storage_var_key(
3079 address: $crate::H160,
3080 key: Vec<u8>,
3081 ) -> $crate::GetStorageResult {
3082 $crate::Pallet::<Self>::get_storage_var_key(address, key)
3083 }
3084
3085 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
3086 $crate::Pallet::<Self>::get_storage(address, key)
3087 }
3088
3089 fn trace_block(
3090 block: Block,
3091 tracer_type: $crate::evm::TracerType,
3092 ) -> Vec<(u32, $crate::evm::Trace)> {
3093 use $crate::{sp_runtime::traits::Block, tracing::trace};
3094
3095 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3096 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3097 {
3098 return Default::default()
3099 }
3100
3101 let mut traces = vec![];
3102 let (header, extrinsics) = block.deconstruct();
3103 <$Executive>::initialize_block(&header);
3104 for (index, ext) in extrinsics.into_iter().enumerate() {
3105 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3106 let t = tracer.as_tracing();
3107 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3108
3109 if let Some(tx_trace) = tracer.collect_trace() {
3110 traces.push((index as u32, tx_trace));
3111 }
3112 }
3113
3114 traces
3115 }
3116
3117 fn trace_tx(
3118 block: Block,
3119 tx_index: u32,
3120 tracer_type: $crate::evm::TracerType,
3121 ) -> Option<$crate::evm::Trace> {
3122 use $crate::{sp_runtime::traits::Block, tracing::trace};
3123
3124 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3125 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3126 {
3127 return None
3128 }
3129
3130 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
3131 let (header, extrinsics) = block.deconstruct();
3132
3133 <$Executive>::initialize_block(&header);
3134 for (index, ext) in extrinsics.into_iter().enumerate() {
3135 if index as u32 == tx_index {
3136 let t = tracer.as_tracing();
3137 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3138 break;
3139 } else {
3140 let _ = <$Executive>::apply_extrinsic(ext);
3141 }
3142 }
3143
3144 tracer.collect_trace()
3145 }
3146
3147 fn trace_call(
3148 tx: $crate::evm::GenericTransaction,
3149 tracer_type: $crate::evm::TracerType,
3150 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
3151 use $crate::tracing::trace;
3152
3153 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3154 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3155 {
3156 return Err($crate::EthTransactError::Message("Execution Tracing is disabled".into()))
3157 }
3158
3159 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3160 let t = tracer.as_tracing();
3161
3162 t.watch_address(&tx.from.unwrap_or_default());
3163 t.watch_address(&$crate::Pallet::<Self>::block_author());
3164 let result = trace(t, || Self::eth_transact(tx));
3165
3166 if let Some(trace) = tracer.collect_trace() {
3167 Ok(trace)
3168 } else if let Err(err) = result {
3169 Err(err)
3170 } else {
3171 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
3172 }
3173 }
3174
3175 fn runtime_pallets_address() -> $crate::H160 {
3176 $crate::RUNTIME_PALLETS_ADDR
3177 }
3178
3179 fn code(address: $crate::H160) -> Vec<u8> {
3180 $crate::Pallet::<Self>::code(&address)
3181 }
3182
3183 fn account_id(address: $crate::H160) -> AccountId {
3184 use $crate::AddressMapper;
3185 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
3186 }
3187
3188 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
3189 $crate::Pallet::<Self>::new_balance_with_dust(balance)
3190 }
3191 }
3192 }
3193 };
3194}