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;
119pub use frame_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
120pub use frame_system::{self, limits::BlockWeights};
121pub use primitives::*;
122pub use sp_core::{H160, H256, U256, keccak_256};
123pub use sp_runtime;
124pub use weights::WeightInfo;
125
126#[cfg(doc)]
127pub use crate::vm::pvm::SyscallDoc;
128
129pub type BalanceOf<T> = <T as Config>::Balance;
130pub type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
131type TrieId = BoundedVec<u8, ConstU32<128>>;
132type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
133type CallOf<T> = <T as Config>::RuntimeCall;
134
135const SENTINEL: u32 = u32::MAX;
142
143const LOG_TARGET: &str = "runtime::revive";
149
150#[frame_support::pallet]
151pub mod pallet {
152 use super::*;
153 use frame_support::{pallet_prelude::*, traits::FindAuthor};
154 use frame_system::pallet_prelude::*;
155 use sp_core::U256;
156 use sp_runtime::Perbill;
157
158 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
160
161 #[pallet::pallet]
162 #[pallet::storage_version(STORAGE_VERSION)]
163 pub struct Pallet<T>(_);
164
165 #[pallet::config(with_default)]
166 pub trait Config: frame_system::Config {
167 type Time: Time<Moment: Into<U256>>;
169
170 #[pallet::no_default]
174 type Balance: Balance
175 + TryFrom<U256>
176 + Into<U256>
177 + Bounded
178 + UniqueSaturatedInto<u64>
179 + UniqueSaturatedFrom<u64>
180 + UniqueSaturatedInto<u128>;
181
182 #[pallet::no_default]
184 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
185 + Mutate<Self::AccountId>
186 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
187 + Balanced<Self::AccountId>;
188
189 #[pallet::no_default_bounds]
196 type OnBurn: OnUnbalanced<CreditOf<Self>>;
197
198 #[pallet::no_default_bounds]
200 #[allow(deprecated)]
201 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
202
203 #[pallet::no_default_bounds]
205 type RuntimeCall: Parameter
206 + Dispatchable<
207 RuntimeOrigin = OriginFor<Self>,
208 Info = DispatchInfo,
209 PostInfo = PostDispatchInfo,
210 > + IsType<<Self as frame_system::Config>::RuntimeCall>
211 + From<Call<Self>>
212 + IsSubType<Call<Self>>
213 + GetDispatchInfo;
214
215 #[pallet::no_default_bounds]
217 type RuntimeOrigin: IsType<OriginFor<Self>>
218 + From<Origin<Self>>
219 + Into<Result<Origin<Self>, OriginFor<Self>>>;
220
221 #[pallet::no_default_bounds]
223 type RuntimeHoldReason: From<HoldReason>;
224
225 type WeightInfo: WeightInfo;
228
229 #[pallet::no_default_bounds]
233 #[allow(private_bounds)]
234 type Precompiles: precompiles::Precompiles<Self>;
235
236 type FindAuthor: FindAuthor<Self::AccountId>;
238
239 #[pallet::constant]
245 #[pallet::no_default_bounds]
246 type DepositPerByte: Get<BalanceOf<Self>>;
247
248 #[pallet::constant]
254 #[pallet::no_default_bounds]
255 type DepositPerItem: Get<BalanceOf<Self>>;
256
257 #[pallet::constant]
267 #[pallet::no_default_bounds]
268 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
269
270 #[pallet::constant]
274 type CodeHashLockupDepositPercent: Get<Perbill>;
275
276 #[pallet::no_default]
278 type AddressMapper: AddressMapper<Self>;
279
280 #[pallet::constant]
282 type AllowEVMBytecode: Get<bool>;
283
284 #[pallet::no_default_bounds]
289 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
290
291 #[pallet::no_default_bounds]
302 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
303
304 type RuntimeMemory: Get<u32>;
309
310 type PVFMemory: Get<u32>;
318
319 #[pallet::constant]
324 type ChainId: Get<u64>;
325
326 #[pallet::constant]
328 type NativeToEthRatio: Get<u32>;
329
330 #[pallet::no_default_bounds]
335 type FeeInfo: FeeInfo<Self>;
336
337 #[pallet::no_default_bounds]
340 type Deposit: Deposit<Self>;
341
342 #[pallet::constant]
355 type MaxEthExtrinsicWeight: Get<FixedU128>;
356
357 #[pallet::constant]
359 type DebugEnabled: Get<bool>;
360
361 #[pallet::constant]
368 type AutoMap: Get<bool>;
369
370 #[pallet::constant]
388 #[pallet::no_default_bounds]
389 type GasScale: Get<u32>;
390 }
391
392 pub mod config_preludes {
394 use super::*;
395 use frame_support::{
396 derive_impl,
397 traits::{ConstBool, ConstU32},
398 };
399 use frame_system::EnsureSigned;
400 use sp_core::parameter_types;
401
402 type Balance = u64;
403
404 pub const DOLLARS: Balance = 1_000_000_000_000;
405 pub const CENTS: Balance = DOLLARS / 100;
406 pub const MILLICENTS: Balance = CENTS / 1_000;
407
408 pub const fn deposit(items: u32, bytes: u32) -> Balance {
409 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
410 }
411
412 parameter_types! {
413 pub const DepositPerItem: Balance = deposit(1, 0);
414 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
415 pub const DepositPerByte: Balance = deposit(0, 1);
416 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
417 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
418 pub const GasScale: u32 = 10u32;
419 }
420
421 pub struct TestDefaultConfig;
423
424 impl Time for TestDefaultConfig {
425 type Moment = u64;
426 fn now() -> Self::Moment {
427 0u64
428 }
429 }
430
431 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
432 fn convert(w: Weight) -> T {
433 w.ref_time().into()
434 }
435 }
436
437 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
438 impl frame_system::DefaultConfig for TestDefaultConfig {}
439
440 #[frame_support::register_default_impl(TestDefaultConfig)]
441 impl DefaultConfig for TestDefaultConfig {
442 #[inject_runtime_type]
443 type RuntimeEvent = ();
444
445 #[inject_runtime_type]
446 type RuntimeHoldReason = ();
447
448 #[inject_runtime_type]
449 type RuntimeCall = ();
450
451 #[inject_runtime_type]
452 type RuntimeOrigin = ();
453
454 type Precompiles = ();
455 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
456 type DepositPerByte = DepositPerByte;
457 type DepositPerItem = DepositPerItem;
458 type DepositPerChildTrieItem = DepositPerChildTrieItem;
459 type Time = Self;
460 type AllowEVMBytecode = ConstBool<true>;
461 type UploadOrigin = EnsureSigned<Self::AccountId>;
462 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
463 type WeightInfo = ();
464 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
465 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
466 type ChainId = ConstU64<42>;
467 type NativeToEthRatio = ConstU32<1_000_000>;
468 type FindAuthor = ();
469 type FeeInfo = ();
470 type Deposit = ();
471 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
472 type DebugEnabled = ConstBool<false>;
473 type AutoMap = ConstBool<false>;
474 type GasScale = GasScale;
475 type OnBurn = ();
476 }
477 }
478
479 #[pallet::event]
480 pub enum Event<T: Config> {
481 ContractEmitted {
483 contract: H160,
485 data: Vec<u8>,
488 topics: Vec<H256>,
491 },
492
493 Instantiated { deployer: H160, contract: H160 },
495
496 EthExtrinsicRevert { dispatch_error: DispatchError },
503 }
504
505 #[pallet::error]
506 #[repr(u8)]
507 pub enum Error<T> {
508 InvalidSchedule = 0x01,
510 InvalidCallFlags = 0x02,
512 OutOfGas = 0x03,
514 TransferFailed = 0x04,
517 MaxCallDepthReached = 0x05,
520 ContractNotFound = 0x06,
522 CodeNotFound = 0x07,
524 CodeInfoNotFound = 0x08,
526 OutOfBounds = 0x09,
528 DecodingFailed = 0x0A,
530 ContractTrapped = 0x0B,
532 ValueTooLarge = 0x0C,
534 TerminatedWhileReentrant = 0x0D,
537 InputForwarded = 0x0E,
539 TooManyTopics = 0x0F,
541 DuplicateContract = 0x12,
543 TerminatedInConstructor = 0x13,
547 ReentranceDenied = 0x14,
549 ReenteredPallet = 0x15,
551 StateChangeDenied = 0x16,
553 StorageDepositNotEnoughFunds = 0x17,
555 StorageDepositLimitExhausted = 0x18,
557 CodeInUse = 0x19,
559 ContractReverted = 0x1A,
564 CodeRejected = 0x1B,
569 BlobTooLarge = 0x1C,
571 StaticMemoryTooLarge = 0x1D,
573 BasicBlockTooLarge = 0x1E,
575 InvalidInstruction = 0x1F,
577 MaxDelegateDependenciesReached = 0x20,
579 DelegateDependencyNotFound = 0x21,
581 DelegateDependencyAlreadyExists = 0x22,
583 CannotAddSelfAsDelegateDependency = 0x23,
585 OutOfTransientStorage = 0x24,
587 InvalidSyscall = 0x25,
589 InvalidStorageFlags = 0x26,
591 ExecutionFailed = 0x27,
593 BalanceConversionFailed = 0x28,
595 InvalidImmutableAccess = 0x2A,
598 AccountUnmapped = 0x2B,
602 AccountAlreadyMapped = 0x2C,
604 InvalidGenericTransaction = 0x2D,
606 RefcountOverOrUnderflow = 0x2E,
608 UnsupportedPrecompileAddress = 0x2F,
610 CallDataTooLarge = 0x30,
612 ReturnDataTooLarge = 0x31,
614 InvalidJump = 0x32,
616 StackUnderflow = 0x33,
618 StackOverflow = 0x34,
620 TxFeeOverdraw = 0x35,
624 EvmConstructorNonEmptyData = 0x36,
628 EvmConstructedFromHash = 0x37,
633 StorageRefundNotEnoughFunds = 0x38,
637 StorageRefundLocked = 0x39,
642 PrecompileDelegateDenied = 0x40,
647 EcdsaRecoveryFailed = 0x41,
649 AutoMappingEnabled = 0x42,
651 PendingDepositCleanup = 0x43,
655 #[cfg(feature = "runtime-benchmarks")]
657 BenchmarkingError = 0xFF,
658 }
659
660 #[pallet::composite_enum]
662 pub enum HoldReason {
663 CodeUploadDepositReserve,
665 StorageDepositReserve,
667 AddressMapping,
669 }
670
671 #[pallet::composite_enum]
673 pub enum FreezeReason {
674 PGasMinBalance,
679 }
680
681 #[derive(
682 PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug,
683 )]
684 #[pallet::origin]
685 pub enum Origin<T: Config> {
686 EthTransaction(T::AccountId),
687 }
688
689 #[pallet::storage]
693 #[pallet::unbounded]
694 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
695
696 #[pallet::storage]
698 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
699
700 #[pallet::storage]
702 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
703
704 #[pallet::storage]
715 pub(crate) type NativeDepositOf<T: Config> = StorageDoubleMap<
716 _,
717 Identity,
718 T::AccountId,
719 Identity,
720 T::AccountId,
721 BalanceOf<T>,
722 ValueQuery,
723 >;
724
725 #[pallet::storage]
727 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
728
729 #[pallet::storage]
735 pub(crate) type DeletionQueue<T: Config> =
736 StorageMap<_, Twox64Concat, u32, crate::storage::DeletionQueueItem<T>>;
737
738 #[pallet::storage]
741 pub(crate) type DeletionQueueCounter<T: Config> =
742 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
743
744 #[pallet::storage]
751 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
752
753 #[pallet::storage]
763 #[pallet::unbounded]
764 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
765
766 #[pallet::storage]
770 pub(crate) type BlockHash<T: Config> =
771 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
772
773 #[pallet::storage]
780 #[pallet::unbounded]
781 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
782
783 #[pallet::storage]
785 #[pallet::unbounded]
786 pub(crate) type EthBlockBuilderIR<T: Config> =
787 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
788
789 #[pallet::storage]
794 #[pallet::unbounded]
795 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
796 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
797
798 #[pallet::storage]
800 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
801
802 pub mod genesis {
803 use super::*;
804 use crate::evm::Bytes32;
805
806 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
808 pub struct ContractData {
809 pub code: crate::evm::Bytes,
811 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
813 }
814
815 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
817 pub struct Account<T: Config> {
818 pub address: H160,
820 #[serde(default)]
822 pub balance: U256,
823 #[serde(default)]
825 pub nonce: T::Nonce,
826 #[serde(flatten, skip_serializing_if = "Option::is_none")]
828 pub contract_data: Option<ContractData>,
829 }
830 }
831
832 #[pallet::genesis_config]
833 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
834 pub struct GenesisConfig<T: Config> {
835 #[serde(default, skip_serializing_if = "Vec::is_empty")]
838 pub mapped_accounts: Vec<T::AccountId>,
839
840 #[serde(default, skip_serializing_if = "Vec::is_empty")]
842 pub accounts: Vec<genesis::Account<T>>,
843
844 #[serde(default, skip_serializing_if = "Option::is_none")]
846 pub debug_settings: Option<DebugSettings>,
847 }
848
849 #[pallet::genesis_build]
850 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
851 fn build(&self) {
852 use crate::{exec::Key, vm::ContractBlob};
853 use frame_support::traits::fungible::Mutate;
854
855 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
856 let _ = T::Currency::mint_into(
857 &Pallet::<T>::account_id(),
858 T::Currency::minimum_balance(),
859 );
860 }
861
862 for id in &self.mapped_accounts {
863 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
864 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
865 }
866 }
867
868 let owner = Pallet::<T>::account_id();
869
870 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
871 let account_id = T::AddressMapper::to_account_id(address);
872
873 if !System::<T>::account_exists(&account_id) {
874 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
875 }
876
877 frame_system::Account::<T>::mutate(&account_id, |info| {
878 info.nonce = (*nonce).into();
879 });
880
881 match contract_data {
882 None => {
883 AccountInfoOf::<T>::insert(
884 address,
885 AccountInfo { account_type: AccountType::EOA, dust: 0 },
886 );
887 },
888 Some(genesis::ContractData { code, storage }) => {
889 let blob = if code.0.starts_with(&polkavm_common::program::BLOB_MAGIC) {
890 ContractBlob::<T>::from_pvm_code(code.0.clone(), owner.clone())
891 .inspect_err(|err| {
892 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
893 })
894 } else {
895 ContractBlob::<T>::from_evm_runtime_code(code.0.clone(), account_id)
896 .inspect_err(|err| {
897 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
898 })
899 };
900
901 let Ok(blob) = blob else {
902 continue;
903 };
904
905 let code_hash = *blob.code_hash();
906 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
907 .inspect_err(|err| {
908 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
909 })
910 else {
911 continue;
912 };
913
914 AccountInfoOf::<T>::insert(
915 address,
916 AccountInfo { account_type: info.clone().into(), dust: 0 },
917 );
918
919 <PristineCode<T>>::insert(blob.code_hash(), code.0.clone());
920 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
921 for (k, v) in storage {
922 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
923 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
924 });
925 }
926 },
927 }
928
929 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
930 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
931 });
932 }
933
934 block_storage::on_finalize_build_eth_block::<T>(
936 frame_system::Pallet::<T>::block_number(),
939 );
940
941 if let Some(settings) = self.debug_settings.as_ref() {
943 settings.write_to_storage::<T>()
944 }
945 }
946 }
947
948 #[pallet::hooks]
949 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
950 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
951 let mut meter = WeightMeter::with_limit(limit);
952 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
953 meter.consumed()
954 }
955
956 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
957 block_storage::on_initialize::<T>();
959
960 System::<T>::account_exists(&Pallet::<T>::account_id());
962 <T as Config>::WeightInfo::on_finalize_block_fixed()
964 }
965
966 fn on_finalize(block_number: BlockNumberFor<T>) {
967 block_storage::on_finalize_build_eth_block::<T>(block_number);
969 }
970
971 fn integrity_test() {
972 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
973
974 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
975
976 T::FeeInfo::integrity_test();
977
978 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
980
981 const TOTAL_MEMORY_DEVIDER: u64 = 2;
984
985 let max_block_weight = T::BlockWeights::get()
991 .get(DispatchClass::Normal)
992 .max_total
993 .unwrap_or_else(|| T::BlockWeights::get().max_block);
994 let max_key_size: u64 =
995 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
996 .expect("Key of maximal size shall be created")
997 .hash()
998 .len()
999 .try_into()
1000 .unwrap();
1001
1002 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
1003 let max_immutable_size: u64 = max_block_weight
1004 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
1005 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
1006 ))
1007 .unwrap()
1008 .saturating_mul(
1009 u64::from(limits::IMMUTABLE_BYTES)
1010 .saturating_add(max_immutable_key_size)
1011 .into(),
1012 );
1013
1014 let max_pvf_mem: u64 = T::PVFMemory::get().into();
1015 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
1016
1017 let max_events_size = max_block_weight
1021 .checked_div_per_component(
1022 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
1023 num_topic: 0,
1024 len: limits::EVENT_BYTES,
1025 })
1026 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
1027 &RuntimeCosts::HostFn,
1028 ))),
1029 )
1030 .unwrap()
1031 .saturating_mul(limits::EVENT_BYTES.into());
1032
1033 assert!(
1034 max_events_size <= storage_size_limit,
1035 "Maximal events size {} exceeds the events limit {}",
1036 max_events_size,
1037 storage_size_limit
1038 );
1039
1040 let max_eth_block_builder_bytes =
1075 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1076
1077 log::debug!(
1078 target: LOG_TARGET,
1079 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1080 max_eth_block_builder_bytes / 1024,
1081 max_events_size / 1024,
1082 );
1083
1084 let memory_left = i128::from(max_runtime_mem)
1089 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1090 .saturating_sub(limits::MEMORY_REQUIRED.into())
1091 .saturating_sub(max_eth_block_builder_bytes.into());
1092
1093 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1094
1095 assert!(
1096 memory_left >= 0,
1097 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1098 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1099 );
1100
1101 let max_storage_size = max_block_weight
1104 .checked_div_per_component(
1105 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1106 new_bytes: limits::STORAGE_BYTES,
1107 old_bytes: 0,
1108 })
1109 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1110 )
1111 .unwrap()
1112 .saturating_add(max_immutable_size.into())
1113 .saturating_add(max_eth_block_builder_bytes.into());
1114
1115 assert!(
1116 max_storage_size <= storage_size_limit,
1117 "Maximal storage size {} exceeds the storage limit {}",
1118 max_storage_size,
1119 storage_size_limit
1120 );
1121 }
1122 }
1123
1124 #[pallet::call]
1125 impl<T: Config> Pallet<T> {
1126 #[allow(unused_variables)]
1139 #[pallet::call_index(0)]
1140 #[pallet::weight(Weight::MAX)]
1141 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1142 Err(frame_system::Error::CallFiltered::<T>.into())
1143 }
1144
1145 #[pallet::call_index(1)]
1162 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1163 pub fn call(
1164 origin: OriginFor<T>,
1165 dest: H160,
1166 #[pallet::compact] value: BalanceOf<T>,
1167 weight_limit: Weight,
1168 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1169 data: Vec<u8>,
1170 ) -> DispatchResultWithPostInfo {
1171 Self::ensure_non_contract_if_signed(&origin)?;
1172 let mut output = Self::bare_call(
1173 origin,
1174 dest,
1175 Pallet::<T>::convert_native_to_evm(value),
1176 TransactionLimits::WeightAndDeposit {
1177 weight_limit,
1178 deposit_limit: storage_deposit_limit,
1179 },
1180 data,
1181 &ExecConfig::new_substrate_tx(),
1182 );
1183
1184 if let Ok(return_value) = &output.result &&
1185 return_value.did_revert()
1186 {
1187 output.result = Err(<Error<T>>::ContractReverted.into());
1188 }
1189 dispatch_result(
1190 output.result,
1191 output.weight_consumed,
1192 <T as Config>::WeightInfo::call(),
1193 )
1194 }
1195
1196 #[pallet::call_index(2)]
1202 #[pallet::weight(
1203 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1204 )]
1205 pub fn instantiate(
1206 origin: OriginFor<T>,
1207 #[pallet::compact] value: BalanceOf<T>,
1208 weight_limit: Weight,
1209 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1210 code_hash: sp_core::H256,
1211 data: Vec<u8>,
1212 salt: Option<[u8; 32]>,
1213 ) -> DispatchResultWithPostInfo {
1214 Self::ensure_non_contract_if_signed(&origin)?;
1215 let data_len = data.len() as u32;
1216 let mut output = Self::bare_instantiate(
1217 origin,
1218 Pallet::<T>::convert_native_to_evm(value),
1219 TransactionLimits::WeightAndDeposit {
1220 weight_limit,
1221 deposit_limit: storage_deposit_limit,
1222 },
1223 Code::Existing(code_hash),
1224 data,
1225 salt,
1226 &ExecConfig::new_substrate_tx(),
1227 );
1228 if let Ok(retval) = &output.result &&
1229 retval.result.did_revert()
1230 {
1231 output.result = Err(<Error<T>>::ContractReverted.into());
1232 }
1233 dispatch_result(
1234 output.result.map(|result| result.result),
1235 output.weight_consumed,
1236 <T as Config>::WeightInfo::instantiate(data_len),
1237 )
1238 }
1239
1240 #[pallet::call_index(3)]
1268 #[pallet::weight(
1269 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1270 .saturating_add(*weight_limit)
1271 )]
1272 pub fn instantiate_with_code(
1273 origin: OriginFor<T>,
1274 #[pallet::compact] value: BalanceOf<T>,
1275 weight_limit: Weight,
1276 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1277 code: Vec<u8>,
1278 data: Vec<u8>,
1279 salt: Option<[u8; 32]>,
1280 ) -> DispatchResultWithPostInfo {
1281 Self::ensure_non_contract_if_signed(&origin)?;
1282 let code_len = code.len() as u32;
1283 let data_len = data.len() as u32;
1284 let mut output = Self::bare_instantiate(
1285 origin,
1286 Pallet::<T>::convert_native_to_evm(value),
1287 TransactionLimits::WeightAndDeposit {
1288 weight_limit,
1289 deposit_limit: storage_deposit_limit,
1290 },
1291 Code::Upload(code),
1292 data,
1293 salt,
1294 &ExecConfig::new_substrate_tx(),
1295 );
1296 if let Ok(retval) = &output.result &&
1297 retval.result.did_revert()
1298 {
1299 output.result = Err(<Error<T>>::ContractReverted.into());
1300 }
1301 dispatch_result(
1302 output.result.map(|result| result.result),
1303 output.weight_consumed,
1304 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1305 )
1306 }
1307
1308 #[pallet::call_index(10)]
1330 #[pallet::weight(
1331 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1332 .saturating_add(*weight_limit)
1333 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1334 )]
1335 pub fn eth_instantiate_with_code(
1336 origin: OriginFor<T>,
1337 value: U256,
1338 weight_limit: Weight,
1339 eth_gas_limit: U256,
1340 code: Vec<u8>,
1341 data: Vec<u8>,
1342 transaction_encoded: Vec<u8>,
1343 effective_gas_price: U256,
1344 encoded_len: u32,
1345 ) -> DispatchResultWithPostInfo {
1346 let signer = Self::ensure_eth_signed(origin)?;
1347 let origin = OriginFor::<T>::signed(signer.clone());
1348 Self::ensure_non_contract_if_signed(&origin)?;
1349 let mut call = Call::<T>::eth_instantiate_with_code {
1350 value,
1351 weight_limit,
1352 eth_gas_limit,
1353 code: code.clone(),
1354 data: data.clone(),
1355 transaction_encoded: transaction_encoded.clone(),
1356 effective_gas_price,
1357 encoded_len,
1358 }
1359 .into();
1360 let info = T::FeeInfo::dispatch_info(&call);
1361 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1362 drop(call);
1363
1364 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1365 let extra_weight = base_info.total_weight();
1366 let output = Self::bare_instantiate(
1367 origin,
1368 value,
1369 TransactionLimits::EthereumGas {
1370 eth_gas_limit: eth_gas_limit.saturated_into(),
1371 weight_limit,
1372 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1373 },
1374 Code::Upload(code),
1375 data,
1376 None,
1377 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1378 );
1379
1380 block_storage::EthereumCallResult::new::<T>(
1381 signer,
1382 output.map_result(|r| r.result),
1383 base_info.call_weight,
1384 encoded_len,
1385 &info,
1386 effective_gas_price,
1387 )
1388 })
1389 }
1390
1391 #[pallet::call_index(11)]
1408 #[pallet::weight(
1409 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1410 .saturating_add(*weight_limit)
1411 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1412 )]
1413 pub fn eth_call(
1414 origin: OriginFor<T>,
1415 dest: H160,
1416 value: U256,
1417 weight_limit: Weight,
1418 eth_gas_limit: U256,
1419 data: Vec<u8>,
1420 transaction_encoded: Vec<u8>,
1421 effective_gas_price: U256,
1422 encoded_len: u32,
1423 ) -> DispatchResultWithPostInfo {
1424 let signer = Self::ensure_eth_signed(origin)?;
1425 let origin = OriginFor::<T>::signed(signer.clone());
1426
1427 Self::ensure_non_contract_if_signed(&origin)?;
1428 let mut call = Call::<T>::eth_call {
1429 dest,
1430 value,
1431 weight_limit,
1432 eth_gas_limit,
1433 data: data.clone(),
1434 transaction_encoded: transaction_encoded.clone(),
1435 effective_gas_price,
1436 encoded_len,
1437 }
1438 .into();
1439 let info = T::FeeInfo::dispatch_info(&call);
1440 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1441 drop(call);
1442
1443 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1444 let extra_weight = base_info.total_weight();
1445 let output = Self::bare_call(
1446 origin,
1447 dest,
1448 value,
1449 TransactionLimits::EthereumGas {
1450 eth_gas_limit: eth_gas_limit.saturated_into(),
1451 weight_limit,
1452 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1453 },
1454 data,
1455 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1456 );
1457
1458 block_storage::EthereumCallResult::new::<T>(
1459 signer,
1460 output,
1461 base_info.call_weight,
1462 encoded_len,
1463 &info,
1464 effective_gas_price,
1465 )
1466 })
1467 }
1468
1469 #[pallet::call_index(12)]
1480 #[pallet::weight(
1481 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32)
1482 .saturating_add(call.get_dispatch_info().call_weight)
1483 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1484 )]
1485 pub fn eth_substrate_call(
1486 origin: OriginFor<T>,
1487 call: Box<<T as Config>::RuntimeCall>,
1488 transaction_encoded: Vec<u8>,
1489 ) -> DispatchResultWithPostInfo {
1490 let signer = Self::ensure_eth_signed(origin)?;
1493 Self::ensure_non_contract_if_signed(&OriginFor::<T>::signed(signer.clone()))?;
1494 let tx_len = transaction_encoded.len() as u32;
1495 let weight_overhead = T::WeightInfo::eth_substrate_call(tx_len)
1496 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(tx_len));
1497
1498 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1499 let call_weight = call.get_dispatch_info().call_weight;
1500 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1501
1502 match &mut call_result {
1504 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1505 post_info.actual_weight = Some(
1506 post_info
1507 .actual_weight
1508 .unwrap_or_else(|| call_weight)
1509 .saturating_add(weight_overhead),
1510 );
1511 },
1512 }
1513
1514 block_storage::EthereumCallResult {
1517 receipt_gas_info: ReceiptGasInfo::default(),
1518 result: call_result,
1519 }
1520 })
1521 }
1522
1523 #[pallet::call_index(4)]
1538 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1539 pub fn upload_code(
1540 origin: OriginFor<T>,
1541 code: Vec<u8>,
1542 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1543 ) -> DispatchResult {
1544 Self::ensure_non_contract_if_signed(&origin)?;
1545 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1546 }
1547
1548 #[pallet::call_index(5)]
1553 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1554 pub fn remove_code(
1555 origin: OriginFor<T>,
1556 code_hash: sp_core::H256,
1557 ) -> DispatchResultWithPostInfo {
1558 let origin = ensure_signed(origin)?;
1559 <ContractBlob<T>>::remove(&origin, code_hash)?;
1560 Ok(Pays::No.into())
1562 }
1563
1564 #[pallet::call_index(6)]
1575 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1576 pub fn set_code(
1577 origin: OriginFor<T>,
1578 dest: H160,
1579 code_hash: sp_core::H256,
1580 ) -> DispatchResult {
1581 ensure_root(origin)?;
1582 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1583 let Some(account) = account else {
1584 return Err(<Error<T>>::ContractNotFound.into());
1585 };
1586
1587 let AccountType::Contract(ref mut contract) = account.account_type else {
1588 return Err(<Error<T>>::ContractNotFound.into());
1589 };
1590
1591 <CodeInfo<T>>::increment_refcount(code_hash)?;
1592 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1593 contract.code_hash = code_hash;
1594
1595 Ok(())
1596 })
1597 }
1598
1599 #[pallet::call_index(7)]
1607 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1608 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1609 #[cfg(not(feature = "runtime-benchmarks"))]
1610 if T::AutoMap::get() {
1611 return Ok(());
1612 }
1613
1614 Self::ensure_non_contract_if_signed(&origin)?;
1615 let origin = ensure_signed(origin)?;
1616 T::AddressMapper::map(&origin)
1617 }
1618
1619 #[pallet::call_index(8)]
1627 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1628 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1629 #[cfg(not(feature = "runtime-benchmarks"))]
1630 ensure!(!T::AutoMap::get(), <Error<T>>::AutoMappingEnabled);
1631 let origin = ensure_signed(origin)?;
1632 T::AddressMapper::unmap(&origin)
1633 }
1634
1635 #[pallet::call_index(9)]
1641 #[pallet::weight({
1642 let dispatch_info = call.get_dispatch_info();
1643 (
1644 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1645 dispatch_info.class
1646 )
1647 })]
1648 pub fn dispatch_as_fallback_account(
1649 mut origin: OriginFor<T>,
1650 call: Box<<T as Config>::RuntimeCall>,
1651 ) -> DispatchResultWithPostInfo {
1652 Self::ensure_non_contract_if_signed(&origin)?;
1653 let account_id = origin.as_signer().ok_or(DispatchError::BadOrigin)?;
1654 let unmapped_account = T::AddressMapper::to_fallback_account_id(
1655 &T::AddressMapper::to_address(&account_id),
1656 );
1657 origin.set_caller_from(RawOrigin::Signed(unmapped_account));
1658 call.dispatch(origin)
1659 }
1660 }
1661}
1662
1663fn dispatch_result<R>(
1665 result: Result<R, DispatchError>,
1666 weight_consumed: Weight,
1667 base_weight: Weight,
1668) -> DispatchResultWithPostInfo {
1669 let post_info = PostDispatchInfo {
1670 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1671 pays_fee: Default::default(),
1672 };
1673
1674 result
1675 .map(|_| post_info)
1676 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1677}
1678
1679impl<T: Config> Pallet<T> {
1680 pub fn bare_call(
1687 origin: OriginFor<T>,
1688 dest: H160,
1689 evm_value: U256,
1690 transaction_limits: TransactionLimits<T>,
1691 data: Vec<u8>,
1692 exec_config: &ExecConfig<T>,
1693 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1694 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1695 Ok(transaction_meter) => transaction_meter,
1696 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1697 };
1698 let mut storage_deposit = Default::default();
1699
1700 let try_call = || {
1701 let origin = ExecOrigin::from_runtime_origin(origin)?;
1702 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1703 origin.clone(),
1704 dest,
1705 &mut transaction_meter,
1706 evm_value,
1707 data,
1708 &exec_config,
1709 )?;
1710
1711 storage_deposit = transaction_meter
1712 .execute_postponed_deposits(&origin, &exec_config)
1713 .inspect_err(|err| {
1714 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1715 })?;
1716
1717 Ok(result)
1718 };
1719 let result = Self::run_guarded(try_call);
1720
1721 log::trace!(target: LOG_TARGET, "Bare call ends: \
1722 result={result:?}, \
1723 weight_consumed={:?}, \
1724 weight_required={:?}, \
1725 storage_deposit={:?}, \
1726 gas_consumed={:?}, \
1727 max_storage_deposit={:?}",
1728 transaction_meter.weight_consumed(),
1729 transaction_meter.weight_required(),
1730 storage_deposit,
1731 transaction_meter.total_consumed_gas(),
1732 transaction_meter.deposit_required()
1733 );
1734
1735 ContractResult {
1736 result: result.map_err(|r| r.error),
1737 weight_consumed: transaction_meter.weight_consumed(),
1738 weight_required: transaction_meter.weight_required(),
1739 storage_deposit,
1740 gas_consumed: transaction_meter.total_consumed_gas(),
1741 max_storage_deposit: transaction_meter.deposit_required(),
1742 }
1743 }
1744
1745 pub fn prepare_dry_run(account: &T::AccountId) {
1751 frame_system::Pallet::<T>::inc_account_nonce(account);
1754 }
1755
1756 pub fn bare_instantiate(
1762 origin: OriginFor<T>,
1763 evm_value: U256,
1764 transaction_limits: TransactionLimits<T>,
1765 code: Code,
1766 data: Vec<u8>,
1767 salt: Option<[u8; 32]>,
1768 exec_config: &ExecConfig<T>,
1769 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1770 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1771 Ok(transaction_meter) => transaction_meter,
1772 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1773 };
1774
1775 let mut storage_deposit = Default::default();
1776
1777 let try_instantiate = || {
1778 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1779
1780 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1781 let executable = match code {
1782 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1783 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1784 let executable = Self::try_upload_code(
1785 upload_account,
1786 code,
1787 BytecodeType::Pvm,
1788 &mut transaction_meter,
1789 &exec_config,
1790 )?;
1791 executable
1792 },
1793 Code::Upload(code) => {
1794 if T::AllowEVMBytecode::get() {
1795 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1796 let origin = T::UploadOrigin::ensure_origin(origin)?;
1797 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1798 executable
1799 } else {
1800 return Err(<Error<T>>::CodeRejected.into());
1801 }
1802 },
1803 Code::Existing(code_hash) => {
1804 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1805 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1806 executable
1807 },
1808 };
1809 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1810 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1811 instantiate_account,
1812 executable,
1813 &mut transaction_meter,
1814 evm_value,
1815 data,
1816 salt.as_ref(),
1817 &exec_config,
1818 );
1819
1820 storage_deposit = transaction_meter
1821 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1822 .inspect_err(|err| {
1823 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1824 })?;
1825 result
1826 };
1827 let output = Self::run_guarded(try_instantiate);
1828
1829 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1830 weight_required={:?} \
1831 storage_deposit={:?} \
1832 gas_consumed={:?} \
1833 max_storage_deposit={:?}",
1834 transaction_meter.weight_consumed(),
1835 transaction_meter.weight_required(),
1836 storage_deposit,
1837 transaction_meter.total_consumed_gas(),
1838 transaction_meter.deposit_required()
1839 );
1840
1841 ContractResult {
1842 result: output
1843 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1844 .map_err(|e| e.error),
1845 weight_consumed: transaction_meter.weight_consumed(),
1846 weight_required: transaction_meter.weight_required(),
1847 storage_deposit,
1848 gas_consumed: transaction_meter.total_consumed_gas(),
1849 max_storage_deposit: transaction_meter.deposit_required(),
1850 }
1851 }
1852
1853 pub fn eth_estimate_gas(
1865 tx: GenericTransaction,
1866 config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1867 ) -> Result<U256, EthTransactError>
1868 where
1869 T::Nonce: Into<U256> + TryFrom<U256>,
1870 CallOf<T>: SetWeightLimit,
1871 {
1872 log::debug!(target: LOG_TARGET, "eth_estimate_gas: {tx:?}");
1873
1874 let mut low = U256::zero();
1875 let mut high = Self::evm_block_gas_limit();
1876
1877 log::trace!(target: LOG_TARGET, "eth_estimate_gas starting with low={low}, high={high}");
1878
1879 let perform_balance_checks = if let Some(gas_limit) = tx.gas {
1883 high = gas_limit;
1884 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the gas limit high={high}");
1885 true
1886 } else {
1887 false
1888 };
1889
1890 let fee_cap = tx.max_fee_per_gas.or(tx.gas_price);
1892 if let (Some(fee_cap), Some(from), true) = (fee_cap, tx.from, perform_balance_checks) {
1893 let mut available_balance = Self::evm_balance(&from);
1894 if let Some(value) = tx.value {
1895 available_balance = available_balance.checked_sub(value).ok_or_else(|| {
1896 EthTransactError::Message("insufficient funds for value transfer".into())
1897 })?;
1898 }
1899 if let Some(allowance) = available_balance.checked_div(fee_cap) {
1900 if high > allowance && allowance != U256::zero() {
1901 log::trace!(target: LOG_TARGET, "eth_estimate_gas high limited by the user's allowance high={high} allowance={allowance}");
1902 high = allowance
1903 }
1904 }
1905 }
1906
1907 let dry_run_results = [high, Self::evm_max_extrinsic_weight_in_gas()].map(|gas_limit| {
1915 let mut transaction = tx.clone();
1916 transaction.gas = Some(gas_limit);
1917 let dry_run_config = config.clone().with_perform_balance_checks(perform_balance_checks);
1918 let eth_transact_result = with_transaction(|| {
1919 TransactionOutcome::Rollback(Ok::<_, DispatchError>(Self::dry_run_eth_transact(
1920 transaction,
1921 dry_run_config,
1922 )))
1923 })
1924 .expect("Rollback shouldn't error out");
1925 (gas_limit, eth_transact_result)
1926 });
1927 let (gas_limit, first_dry_run_result) = match dry_run_results {
1928 [(gas_limit1, Ok(dry_run_result1)), (gas_limit2, Ok(dry_run_result2))] => {
1929 if dry_run_result2.eth_gas >= gas_limit2 {
1930 (gas_limit1, dry_run_result1)
1931 } else {
1932 (gas_limit2, dry_run_result2)
1933 }
1934 },
1935 [(gas_limit, Ok(dry_run_result)), (_, Err(_))] |
1936 [(_, Err(_)), (gas_limit, Ok(dry_run_result))] => (gas_limit, dry_run_result),
1937 [(_, Err(err)), (_, Err(..))] => return Err(err),
1938 };
1939 log::trace!(
1940 target: LOG_TARGET,
1941 "eth_estimate_gas first dry run succeeded with gas_limit={} consumed={}",
1942 gas_limit,
1943 first_dry_run_result.eth_gas
1944 );
1945 low = first_dry_run_result.eth_gas;
1946 high = gas_limit;
1947
1948 while low + U256::one() < high {
1949 log::trace!(target: LOG_TARGET, "eth_estimate_gas estimation iteration with low={low} high={high}");
1950 let error_ratio = high
1951 .checked_sub(low)
1952 .and_then(|value| value.checked_mul(U256::from(1000)))
1953 .and_then(|value| value.checked_div(high))
1954 .ok_or_else(|| {
1955 EthTransactError::Message(
1956 "failed to calculate error ratio in gas estimation".into(),
1957 )
1958 })?;
1959 if error_ratio <= U256::from(15) {
1960 log::trace!(
1961 target: LOG_TARGET,
1962 "eth_estimate_gas finished due to error ratio being less than 1.5% high={}",
1963 high
1964 );
1965 break;
1966 }
1967
1968 let mut midpoint = high
1969 .checked_sub(low)
1970 .and_then(|value| value.checked_div(U256::from(2)))
1971 .and_then(|value| value.checked_add(low))
1972 .ok_or_else(|| {
1973 EthTransactError::Message(
1974 "failed to calculate midpoint in gas estimation".into(),
1975 )
1976 })?;
1977
1978 if let Some(other_midpoint) = low.checked_mul(U256::from(2)) {
1979 if other_midpoint != U256::zero() {
1980 midpoint = midpoint.min(other_midpoint)
1981 }
1982 };
1983
1984 let mut transaction = tx.clone();
1985 transaction.gas = Some(midpoint);
1986 let dry_run_config = config.clone().with_perform_balance_checks(perform_balance_checks);
1987 let dry_run_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 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run result with midpoint={midpoint} is dry_run_result={dry_run_result:?}");
1995 match dry_run_result {
1996 Ok(..) => {
1997 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run succeeded, new high={midpoint}");
1998 high = midpoint
1999 },
2000 Err(..) => {
2001 log::trace!(target: LOG_TARGET, "eth_estimate_gas dry run failed, new low={midpoint}");
2002 low = midpoint
2003 },
2004 }
2005 }
2006
2007 log::trace!(target: LOG_TARGET, "eth_estimate_gas completed. high={high}");
2008 Ok(high)
2009 }
2010
2011 pub fn eth_pre_dispatch_weight(transaction_encoded: Vec<u8>) -> Result<Weight, EthTransactError>
2019 where
2020 CallOf<T>: SetWeightLimit,
2021 {
2022 let signed_tx =
2023 crate::evm::TransactionSigned::decode(&transaction_encoded).map_err(|err| {
2024 EthTransactError::Message(format!("Failed to decode transaction: {err:?}"))
2025 })?;
2026 let signer_addr = signed_tx.recover_eth_address().map_err(|err| {
2027 EthTransactError::Message(format!("Failed to recover signer: {err:?}"))
2028 })?;
2029 let tx =
2030 GenericTransaction::from_signed(signed_tx, Self::evm_base_fee(), Some(signer_addr));
2031 let encoded_len = T::FeeInfo::encoded_len(
2032 crate::Call::<T>::eth_transact { payload: transaction_encoded.clone() }.into(),
2033 );
2034 let call_info = tx
2035 .into_call::<T>(CreateCallMode::ExtrinsicExecution(encoded_len, transaction_encoded))
2036 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2037 let info = T::FeeInfo::dispatch_info(&call_info.call);
2038
2039 Ok(frame_system::calculate_consumed_extrinsic_weight::<CallOf<T>>(
2040 &T::BlockWeights::get(),
2041 &info,
2042 call_info.encoded_len as usize,
2043 ))
2044 }
2045
2046 pub fn dry_run_eth_transact(
2052 mut tx: GenericTransaction,
2053 mut dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
2054 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
2055 where
2056 T::Nonce: Into<U256> + TryFrom<U256>,
2057 CallOf<T>: SetWeightLimit,
2058 {
2059 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
2060
2061 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
2062 Self::prepare_dry_run(&origin);
2063
2064 if let Some(overrides) = dry_run_config.state_overrides.take() {
2065 state_overrides::apply_state_overrides::<T>(overrides)?;
2066 }
2067
2068 let base_fee = Self::evm_base_fee();
2069 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
2070
2071 if effective_gas_price < base_fee {
2072 Err(EthTransactError::Message(format!(
2073 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
2074 )))?;
2075 }
2076
2077 if tx.nonce.is_none() {
2078 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
2079 }
2080 if tx.chain_id.is_none() {
2081 tx.chain_id = Some(T::ChainId::get().into());
2082 }
2083
2084 tx.gas_price = Some(effective_gas_price);
2086 tx.max_priority_fee_per_gas = Some(0.into());
2089 if tx.max_fee_per_gas.is_none() {
2090 tx.max_fee_per_gas = Some(effective_gas_price);
2091 }
2092
2093 let gas = tx.gas;
2094 if tx.gas.is_none() {
2095 tx.gas = Some(Self::evm_block_gas_limit());
2096 }
2097 if tx.r#type.is_none() {
2098 tx.r#type = Some(TYPE_EIP1559.into());
2099 }
2100
2101 let value = tx.value.unwrap_or_default();
2103 let input = tx.input.clone().to_vec();
2104 let from = tx.from;
2105 let to = tx.to;
2106
2107 let mut call_info = tx
2110 .into_call::<T>(CreateCallMode::DryRun)
2111 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
2112
2113 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
2117 let base_weight = base_info.total_weight();
2118 let perform_balance_checks = dry_run_config.perform_balance_checks;
2119 let exec_config =
2120 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
2121 .with_dry_run(dry_run_config);
2122
2123 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
2125 if let Some(from) = &from {
2126 let fees = if gas.is_some() && matches!(perform_balance_checks, Some(true)) {
2127 fees
2128 } else {
2129 Zero::zero()
2130 };
2131 let balance = Self::evm_balance(from);
2132 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
2133 return Err(EthTransactError::Message(format!(
2134 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
2135 )));
2136 }
2137 }
2138
2139 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
2142
2143 let extract_error = |err| {
2144 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
2145 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
2146 } else {
2147 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
2148 }
2149 };
2150
2151 let transaction_limits = TransactionLimits::EthereumGas {
2152 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
2153 weight_limit: Self::evm_max_extrinsic_weight(),
2154 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
2155 };
2156
2157 let mut dry_run = match to {
2159 Some(dest) => {
2161 if dest == RUNTIME_PALLETS_ADDR {
2162 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
2163 return Err(EthTransactError::Message(format!(
2164 "Failed to decode pallet-call {input:?}"
2165 )));
2166 };
2167
2168 if let Err(result) =
2169 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
2170 {
2171 return Err(EthTransactError::Message(format!(
2172 "Failed to dispatch call: {:?}",
2173 result.error,
2174 )));
2175 };
2176
2177 Default::default()
2178 } else {
2179 let result = crate::Pallet::<T>::bare_call(
2181 OriginFor::<T>::signed(origin),
2182 dest,
2183 value,
2184 transaction_limits,
2185 input.clone(),
2186 &exec_config,
2187 );
2188
2189 let data = match result.result {
2190 Ok(return_value) => {
2191 if return_value.did_revert() {
2192 return Err(EthTransactError::Data(return_value.data));
2193 }
2194 return_value.data
2195 },
2196 Err(err) => {
2197 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
2198 return extract_error(err);
2199 },
2200 };
2201
2202 EthTransactInfo {
2203 weight_required: result.weight_required,
2204 storage_deposit: result.storage_deposit.charge_or_zero(),
2205 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2206 data,
2207 eth_gas: Default::default(),
2208 }
2209 }
2210 },
2211 None => {
2213 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2215 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
2216 } else {
2217 (input, vec![])
2218 };
2219
2220 let result = crate::Pallet::<T>::bare_instantiate(
2222 OriginFor::<T>::signed(origin),
2223 value,
2224 transaction_limits,
2225 Code::Upload(code.clone()),
2226 data.clone(),
2227 None,
2228 &exec_config,
2229 );
2230
2231 let returned_data = match result.result {
2232 Ok(return_value) => {
2233 if return_value.result.did_revert() {
2234 return Err(EthTransactError::Data(return_value.result.data));
2235 }
2236 return_value.result.data
2237 },
2238 Err(err) => {
2239 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
2240 return extract_error(err);
2241 },
2242 };
2243
2244 EthTransactInfo {
2245 weight_required: result.weight_required,
2246 storage_deposit: result.storage_deposit.charge_or_zero(),
2247 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
2248 data: returned_data,
2249 eth_gas: Default::default(),
2250 }
2251 },
2252 };
2253
2254 call_info.call.set_weight_limit(dry_run.weight_required);
2256
2257 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
2259 let max_weight = Self::evm_max_extrinsic_weight();
2260 if total_weight.any_gt(max_weight) {
2261 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
2262 total_weight={total_weight:?} \
2263 max_weight={max_weight:?}",
2264 );
2265
2266 Err(EthTransactError::Message(format!(
2267 "\
2268 The transaction consumes more than the allowed weight. \
2269 needed={total_weight} \
2270 allowed={max_weight} \
2271 overweight_by={}\
2272 ",
2273 total_weight.saturating_sub(max_weight),
2274 )))?;
2275 }
2276
2277 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
2279 let available_fee = T::FeeInfo::remaining_txfee();
2280 if transaction_fee > available_fee {
2281 Err(EthTransactError::Message(format!(
2282 "Not enough gas supplied: Off by: {:?}",
2283 transaction_fee.saturating_sub(available_fee),
2284 )))?;
2285 }
2286
2287 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2288 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2289 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2290 if !rest.is_zero() {
2291 eth_gas = eth_gas.saturating_add(1_u32.into());
2292 }
2293
2294 log::debug!(target: LOG_TARGET, "\
2295 dry_run_eth_transact finished: \
2296 weight_limit={}, \
2297 total_weight={total_weight}, \
2298 max_weight={max_weight}, \
2299 weight_left={}, \
2300 eth_gas={eth_gas}, \
2301 encoded_len={}, \
2302 tx_fee={transaction_fee:?}, \
2303 storage_deposit={:?}, \
2304 max_storage_deposit={:?}\
2305 ",
2306 dry_run.weight_required,
2307 max_weight.saturating_sub(total_weight),
2308 call_info.encoded_len,
2309 dry_run.storage_deposit,
2310 dry_run.max_storage_deposit,
2311
2312 );
2313 dry_run.eth_gas = eth_gas;
2314 Ok(dry_run)
2315 }
2316
2317 pub fn evm_balance(address: &H160) -> U256 {
2321 let balance = AccountInfo::<T>::balance_of((*address).into());
2322 Self::convert_native_to_evm(balance)
2323 }
2324
2325 pub fn eth_block() -> EthBlock {
2327 EthereumBlock::<T>::get()
2328 }
2329
2330 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2337 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2338 let hash = <BlockHash<T>>::get(number);
2339 if hash == H256::zero() { None } else { Some(hash) }
2340 }
2341
2342 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2344 ReceiptInfoData::<T>::get()
2345 }
2346
2347 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2353 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2354 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2355 let account_id = T::AddressMapper::to_account_id(&address);
2356 T::Currency::set_balance(&account_id, balance);
2357 AccountInfoOf::<T>::mutate(&address, |account| {
2358 if let Some(account) = account {
2359 account.dust = dust;
2360 } else {
2361 *account = Some(AccountInfo { dust, ..Default::default() });
2362 }
2363 });
2364
2365 Ok(())
2366 }
2367
2368 pub fn new_balance_with_dust(
2372 evm_value: U256,
2373 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2374 let ed = T::Currency::minimum_balance();
2375 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2376 let (value, dust) = balance_with_dust.deconstruct();
2377
2378 Ok((ed.saturating_add(value), dust))
2379 }
2380
2381 pub fn evm_nonce(address: &H160) -> u32
2383 where
2384 T::Nonce: Into<u32>,
2385 {
2386 let account = T::AddressMapper::to_account_id(&address);
2387 System::<T>::account_nonce(account).into()
2388 }
2389
2390 pub fn evm_block_gas_limit() -> U256 {
2392 u64::MAX.into()
2399 }
2400
2401 pub fn evm_max_extrinsic_weight_in_gas() -> U256 {
2403 let max_extrinsic_fee = T::FeeInfo::weight_to_fee(&Self::evm_max_extrinsic_weight());
2404 let gas_scale: BalanceOf<T> = T::GasScale::get().into();
2405 (max_extrinsic_fee / gas_scale).into()
2406 }
2407
2408 pub fn evm_max_extrinsic_weight() -> Weight {
2410 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2411 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2412 .get(DispatchClass::Normal)
2413 .max_extrinsic
2414 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2415 Weight::from_parts(
2416 factor.saturating_mul_int(max_weight.ref_time()),
2417 factor.saturating_mul_int(max_weight.proof_size()),
2418 )
2419 }
2420
2421 pub fn evm_base_fee() -> U256 {
2423 let gas_scale = <T as Config>::GasScale::get();
2424 let multiplier = T::FeeInfo::next_fee_multiplier();
2425 multiplier
2426 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2427 .saturating_mul(gas_scale.saturated_into())
2428 .into()
2429 }
2430
2431 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2433 where
2434 T::Nonce: Into<u32>,
2435 {
2436 match tracer_type {
2437 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2438 TracerType::PrestateTracer(config) => {
2439 PrestateTracer::new(config.unwrap_or_default()).into()
2440 },
2441 TracerType::ExecutionTracer(config) => {
2442 ExecutionTracer::new(config.unwrap_or_default()).into()
2443 },
2444 }
2445 }
2446
2447 pub fn bare_upload_code(
2451 origin: OriginFor<T>,
2452 code: Vec<u8>,
2453 storage_deposit_limit: BalanceOf<T>,
2454 ) -> CodeUploadResult<BalanceOf<T>> {
2455 let origin = T::UploadOrigin::ensure_origin(origin)?;
2456
2457 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2458 BytecodeType::Pvm
2459 } else {
2460 if !T::AllowEVMBytecode::get() {
2461 return Err(<Error<T>>::CodeRejected.into());
2462 }
2463 BytecodeType::Evm
2464 };
2465
2466 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2467 weight_limit: Default::default(),
2468 deposit_limit: storage_deposit_limit,
2469 })?;
2470
2471 let module = Self::try_upload_code(
2472 origin,
2473 code,
2474 bytecode_type,
2475 &mut meter,
2476 &ExecConfig::new_substrate_tx(),
2477 )?;
2478 Ok(CodeUploadReturnValue {
2479 code_hash: *module.code_hash(),
2480 deposit: meter.deposit_consumed().charge_or_zero(),
2481 })
2482 }
2483
2484 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2486 let contract_info =
2487 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2488
2489 let maybe_value = contract_info.read(&Key::from_fixed(key));
2490 Ok(maybe_value)
2491 }
2492
2493 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2497 let immutable_data = <ImmutableDataOf<T>>::get(address);
2498 immutable_data
2499 }
2500
2501 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2509 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2510 <ImmutableDataOf<T>>::insert(address, data);
2511 Ok(())
2512 }
2513
2514 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2516 let contract_info =
2517 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2518
2519 let maybe_value = contract_info.read(
2520 &Key::try_from_var(key)
2521 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2522 .into(),
2523 );
2524 Ok(maybe_value)
2525 }
2526
2527 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2529 let (value, dust) = value.into().deconstruct();
2530 value
2531 .into()
2532 .saturating_mul(T::NativeToEthRatio::get().into())
2533 .saturating_add(dust.into())
2534 }
2535
2536 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2546 let contract_info =
2547 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2548
2549 contract_info
2550 .write(&Key::from_fixed(key), value, None, false)
2551 .map_err(ContractAccessError::StorageWriteFailed)
2552 }
2553
2554 pub fn set_storage_var_key(
2565 address: H160,
2566 key: Vec<u8>,
2567 value: Option<Vec<u8>>,
2568 ) -> SetStorageResult {
2569 let contract_info =
2570 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2571
2572 contract_info
2573 .write(
2574 &Key::try_from_var(key)
2575 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2576 .into(),
2577 value,
2578 None,
2579 false,
2580 )
2581 .map_err(ContractAccessError::StorageWriteFailed)
2582 }
2583
2584 pub fn account_id() -> T::AccountId {
2586 use frame_support::PalletId;
2587 use sp_runtime::traits::AccountIdConversion;
2588 PalletId(*b"py/reviv").into_account_truncating()
2589 }
2590
2591 pub fn block_author() -> H160 {
2593 use frame_support::traits::FindAuthor;
2594
2595 let digest = <frame_system::Pallet<T>>::digest();
2596 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2597
2598 T::FindAuthor::find_author(pre_runtime_digests)
2599 .map(|account_id| T::AddressMapper::to_address(&account_id))
2600 .unwrap_or_default()
2601 }
2602
2603 pub fn code(address: &H160) -> Vec<u8> {
2607 use precompiles::{All, Precompiles};
2608 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2609 return code.into();
2610 }
2611 AccountInfo::<T>::load_contract(&address)
2612 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2613 .map(|code| code.into())
2614 .unwrap_or_default()
2615 }
2616
2617 pub fn try_upload_code(
2619 origin: T::AccountId,
2620 code: Vec<u8>,
2621 code_type: BytecodeType,
2622 meter: &mut TransactionMeter<T>,
2623 exec_config: &ExecConfig<T>,
2624 ) -> Result<ContractBlob<T>, DispatchError> {
2625 let mut module = match code_type {
2626 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2627 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2628 };
2629 module.store_code(exec_config, meter)?;
2630 Ok(module)
2631 }
2632
2633 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2635 executing_contract::using_once(&mut false, || {
2636 executing_contract::with(|f| {
2637 if *f {
2639 return Err(())
2640 }
2641 *f = true;
2643 Ok(())
2644 })
2645 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2646 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2647 .map(|_| f())
2648 .and_then(|r| r)
2649 })
2650 }
2651
2652 fn charge_deposit(
2657 hold_reason: HoldReason,
2658 from: &T::AccountId,
2659 to: &T::AccountId,
2660 amount: BalanceOf<T>,
2661 exec_config: &ExecConfig<T>,
2662 ) -> DispatchResult {
2663 if amount.is_zero() {
2664 return Ok(());
2665 }
2666
2667 T::Deposit::charge_and_hold(hold_reason, exec_config.funds(from), to, amount)
2668 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2669 Ok(())
2670 }
2671
2672 fn refund_deposit(
2677 hold_reason: HoldReason,
2678 from: &T::AccountId,
2679 dst: deposit_payment::Funds<T::AccountId>,
2680 amount: BalanceOf<T>,
2681 ) -> Result<(), DispatchError> {
2682 if amount.is_zero() {
2683 return Ok(());
2684 }
2685
2686 let to = match &dst {
2687 deposit_payment::Funds::Balance(to) | deposit_payment::Funds::TxFee(to) => *to,
2688 };
2689 let result = T::Deposit::refund_on_hold(hold_reason, from, dst, amount);
2690
2691 result.defensive_map_err(|err| {
2692 let available = T::Deposit::total_on_hold(hold_reason, from);
2693 if available < amount {
2694 log::error!(
2697 target: LOG_TARGET,
2698 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}. Not enough deposit: {available:?}. This is a bug.",
2699 );
2700 Error::<T>::StorageRefundNotEnoughFunds.into()
2701 } else {
2702 log::warn!(
2707 target: LOG_TARGET,
2708 "Failed to refund storage deposit {amount:?} from contract {from:?} to origin {to:?}: {err:?}. First remove locks (staking, governance) from the contracts account.",
2709 );
2710 Error::<T>::StorageRefundLocked.into()
2711 }
2712 })
2713 }
2714
2715 fn has_dust(value: U256) -> bool {
2717 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2718 }
2719
2720 fn has_balance(value: U256) -> bool {
2722 value >= U256::from(<T>::NativeToEthRatio::get())
2723 }
2724
2725 #[cfg(any(feature = "runtime-benchmarks", feature = "try-runtime", test))]
2727 fn min_balance() -> BalanceOf<T> {
2728 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2729 }
2730
2731 fn deposit_event(event: Event<T>) {
2736 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2737 }
2738
2739 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2741 match <T as Config>::RuntimeOrigin::from(origin).into() {
2742 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2743 _ => Err(BadOrigin.into()),
2744 }
2745 }
2746
2747 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2751 if DebugSettings::bypass_eip_3607::<T>() {
2752 return Ok(());
2753 }
2754 let Some(address) = origin
2755 .as_system_ref()
2756 .and_then(|o| o.as_signed())
2757 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2758 else {
2759 return Ok(());
2760 };
2761 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2762 <AccountInfo<T>>::is_contract(&address)
2763 {
2764 log::debug!(
2765 target: crate::LOG_TARGET,
2766 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2767 );
2768 Err(DispatchError::BadOrigin)
2769 } else {
2770 Ok(())
2771 }
2772 }
2773}
2774
2775pub const RUNTIME_PALLETS_ADDR: H160 =
2780 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2781
2782environmental!(executing_contract: bool);
2784
2785sp_api::decl_runtime_apis! {
2786 #[api_version(1)]
2788 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2789 AccountId: Codec,
2790 Balance: Codec,
2791 Nonce: Codec,
2792 BlockNumber: Codec,
2793 Moment: Codec,
2794 {
2795 fn eth_block() -> EthBlock;
2799
2800 fn eth_block_hash(number: U256) -> Option<H256>;
2802
2803 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2809
2810 fn block_gas_limit() -> U256;
2812
2813 fn max_extrinsic_weight_in_gas() -> U256;
2815
2816 fn balance(address: H160) -> U256;
2818
2819 fn gas_price() -> U256;
2821
2822 fn nonce(address: H160) -> Nonce;
2824
2825 fn call(
2829 origin: AccountId,
2830 dest: H160,
2831 value: Balance,
2832 gas_limit: Option<Weight>,
2833 storage_deposit_limit: Option<Balance>,
2834 input_data: Vec<u8>,
2835 ) -> ContractResult<ExecReturnValue, Balance>;
2836
2837 fn instantiate(
2841 origin: AccountId,
2842 value: Balance,
2843 gas_limit: Option<Weight>,
2844 storage_deposit_limit: Option<Balance>,
2845 code: Code,
2846 data: Vec<u8>,
2847 salt: Option<[u8; 32]>,
2848 ) -> ContractResult<InstantiateReturnValue, Balance>;
2849
2850
2851 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2856
2857 fn eth_transact_with_config(
2861 tx: GenericTransaction,
2862 config: DryRunConfig<Moment>,
2863 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2864
2865 fn eth_estimate_gas(
2871 tx: GenericTransaction,
2872 config: DryRunConfig<Moment>
2873 ) -> Result<U256, EthTransactError>;
2874
2875 fn eth_pre_dispatch_weight(tx: Vec<u8>) -> Result<Weight, EthTransactError>;
2877
2878 fn upload_code(
2882 origin: AccountId,
2883 code: Vec<u8>,
2884 storage_deposit_limit: Option<Balance>,
2885 ) -> CodeUploadResult<Balance>;
2886
2887 fn get_storage(
2893 address: H160,
2894 key: [u8; 32],
2895 ) -> GetStorageResult;
2896
2897 fn get_storage_var_key(
2903 address: H160,
2904 key: Vec<u8>,
2905 ) -> GetStorageResult;
2906
2907 fn trace_block(
2914 block: Block,
2915 config: TracerType
2916 ) -> Vec<(u32, Trace)>;
2917
2918 fn trace_tx(
2925 block: Block,
2926 tx_index: u32,
2927 config: TracerType
2928 ) -> Option<Trace>;
2929
2930 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2934
2935 fn trace_call_with_config(
2941 tx: GenericTransaction,
2942 tracer_type: TracerType,
2943 config: TracingConfig,
2944 ) -> Result<Trace, EthTransactError>;
2945
2946 fn block_author() -> H160;
2948
2949 fn address(account_id: AccountId) -> H160;
2951
2952 fn account_id(address: H160) -> AccountId;
2954
2955 fn runtime_pallets_address() -> H160;
2957
2958 fn code(address: H160) -> Vec<u8>;
2960
2961 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2963 }
2964}
2965
2966#[macro_export]
2980macro_rules! impl_runtime_apis_plus_revive_traits {
2981 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2982
2983 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2984
2985 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2986 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
2987 use $crate::pallet::Call as ReviveCall;
2988 match self {
2989 Self::$Revive(
2990 ReviveCall::eth_call{ weight_limit, .. } |
2991 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
2992 ) => {
2993 let old = *weight_limit;
2994 *weight_limit = new_weight_limit;
2995 old
2996 },
2997 _ => Weight::default(),
2998 }
2999 }
3000 }
3001
3002 impl_runtime_apis! {
3003 $($rest)*
3004
3005
3006 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
3007 {
3008 fn eth_block() -> $crate::EthBlock {
3009 $crate::Pallet::<Self>::eth_block()
3010 }
3011
3012 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
3013 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
3014 }
3015
3016 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
3017 $crate::Pallet::<Self>::eth_receipt_data()
3018 }
3019
3020 fn balance(address: $crate::H160) -> $crate::U256 {
3021 $crate::Pallet::<Self>::evm_balance(&address)
3022 }
3023
3024 fn block_author() -> $crate::H160 {
3025 $crate::Pallet::<Self>::block_author()
3026 }
3027
3028 fn block_gas_limit() -> $crate::U256 {
3029 $crate::Pallet::<Self>::evm_block_gas_limit()
3030 }
3031
3032 fn max_extrinsic_weight_in_gas() -> $crate::U256 {
3033 $crate::Pallet::<Self>::evm_max_extrinsic_weight_in_gas()
3034 }
3035
3036 fn gas_price() -> $crate::U256 {
3037 $crate::Pallet::<Self>::evm_base_fee()
3038 }
3039
3040 fn nonce(address: $crate::H160) -> Nonce {
3041 use $crate::AddressMapper;
3042 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
3043 $crate::frame_system::Pallet::<Self>::account_nonce(account)
3044 }
3045
3046 fn address(account_id: AccountId) -> $crate::H160 {
3047 use $crate::AddressMapper;
3048 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
3049 }
3050
3051 fn eth_transact(
3052 tx: $crate::evm::GenericTransaction,
3053 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3054 use $crate::{
3055 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3056 sp_runtime::traits::TransactionExtension,
3057 sp_runtime::traits::Block as BlockT
3058 };
3059 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
3060 }
3061
3062 fn eth_transact_with_config(
3063 tx: $crate::evm::GenericTransaction,
3064 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3065 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
3066 use $crate::{
3067 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3068 sp_runtime::traits::TransactionExtension,
3069 sp_runtime::traits::Block as BlockT
3070 };
3071 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
3072 }
3073
3074 fn eth_estimate_gas(
3075 tx: $crate::evm::GenericTransaction,
3076 config: $crate::DryRunConfig<__ReviveMacroMoment>,
3077 ) -> Result<$crate::U256, $crate::EthTransactError> {
3078 use $crate::{
3079 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
3080 sp_runtime::traits::TransactionExtension,
3081 sp_runtime::traits::Block as BlockT
3082 };
3083 $crate::Pallet::<Self>::eth_estimate_gas(tx, config)
3084 }
3085
3086 fn eth_pre_dispatch_weight(
3087 tx: Vec<u8>,
3088 ) -> Result<$crate::Weight, $crate::EthTransactError> {
3089 $crate::Pallet::<Self>::eth_pre_dispatch_weight(tx)
3090 }
3091
3092 fn call(
3093 origin: AccountId,
3094 dest: $crate::H160,
3095 value: Balance,
3096 weight_limit: Option<$crate::Weight>,
3097 storage_deposit_limit: Option<Balance>,
3098 input_data: Vec<u8>,
3099 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
3100 use $crate::frame_support::traits::Get;
3101 let blockweights: $crate::BlockWeights =
3102 <Self as $crate::frame_system::Config>::BlockWeights::get();
3103
3104 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3105 $crate::Pallet::<Self>::bare_call(
3106 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3107 dest,
3108 $crate::Pallet::<Self>::convert_native_to_evm(value),
3109 $crate::TransactionLimits::WeightAndDeposit {
3110 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3111 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3112 },
3113 input_data,
3114 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3115 )
3116 }
3117
3118 fn instantiate(
3119 origin: AccountId,
3120 value: Balance,
3121 weight_limit: Option<$crate::Weight>,
3122 storage_deposit_limit: Option<Balance>,
3123 code: $crate::Code,
3124 data: Vec<u8>,
3125 salt: Option<[u8; 32]>,
3126 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
3127 use $crate::frame_support::traits::Get;
3128 let blockweights: $crate::BlockWeights =
3129 <Self as $crate::frame_system::Config>::BlockWeights::get();
3130
3131 $crate::Pallet::<Self>::prepare_dry_run(&origin);
3132 $crate::Pallet::<Self>::bare_instantiate(
3133 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
3134 $crate::Pallet::<Self>::convert_native_to_evm(value),
3135 $crate::TransactionLimits::WeightAndDeposit {
3136 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
3137 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
3138 },
3139 code,
3140 data,
3141 salt,
3142 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
3143 )
3144 }
3145
3146 fn upload_code(
3147 origin: AccountId,
3148 code: Vec<u8>,
3149 storage_deposit_limit: Option<Balance>,
3150 ) -> $crate::CodeUploadResult<Balance> {
3151 let origin =
3152 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
3153 $crate::Pallet::<Self>::bare_upload_code(
3154 origin,
3155 code,
3156 storage_deposit_limit.unwrap_or(u128::MAX),
3157 )
3158 }
3159
3160 fn get_storage_var_key(
3161 address: $crate::H160,
3162 key: Vec<u8>,
3163 ) -> $crate::GetStorageResult {
3164 $crate::Pallet::<Self>::get_storage_var_key(address, key)
3165 }
3166
3167 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
3168 $crate::Pallet::<Self>::get_storage(address, key)
3169 }
3170
3171 fn trace_block(
3172 block: Block,
3173 tracer_type: $crate::evm::TracerType,
3174 ) -> Vec<(u32, $crate::evm::Trace)> {
3175 use $crate::{sp_runtime::traits::Block, tracing::trace};
3176
3177 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3178 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3179 {
3180 return Default::default()
3181 }
3182
3183 let mut traces = vec![];
3184 let (header, extrinsics) = block.deconstruct();
3185 <$Executive>::initialize_block(&header);
3186 for (index, ext) in extrinsics.into_iter().enumerate() {
3187 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3188 let t = tracer.as_tracing();
3189 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3190
3191 if let Some(tx_trace) = tracer.collect_trace() {
3192 traces.push((index as u32, tx_trace));
3193 }
3194 }
3195
3196 traces
3197 }
3198
3199 fn trace_tx(
3200 block: Block,
3201 tx_index: u32,
3202 tracer_type: $crate::evm::TracerType,
3203 ) -> Option<$crate::evm::Trace> {
3204 use $crate::{sp_runtime::traits::Block, tracing::trace};
3205
3206 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3207 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3208 {
3209 return None
3210 }
3211
3212 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
3213 let (header, extrinsics) = block.deconstruct();
3214
3215 <$Executive>::initialize_block(&header);
3216 for (index, ext) in extrinsics.into_iter().enumerate() {
3217 if index as u32 == tx_index {
3218 let t = tracer.as_tracing();
3219 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
3220 break;
3221 } else {
3222 let _ = <$Executive>::apply_extrinsic(ext);
3223 }
3224 }
3225
3226 tracer.collect_trace()
3227 }
3228
3229 fn trace_call(
3230 tx: $crate::evm::GenericTransaction,
3231 tracer_type: $crate::evm::TracerType,
3232 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
3233 use $crate::tracing::trace;
3234
3235 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
3236 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
3237 {
3238 return Err($crate::EthTransactError::Message("Execution Tracing is disabled".into()))
3239 }
3240
3241 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
3242 let t = tracer.as_tracing();
3243
3244 t.watch_address(&tx.from.unwrap_or_default());
3245 t.watch_address(&$crate::Pallet::<Self>::block_author());
3246 let result = trace(t, || Self::eth_transact(tx));
3247
3248 if let Some(trace) = tracer.collect_trace() {
3249 Ok(trace)
3250 } else if let Err(err) = result {
3251 Err(err)
3252 } else {
3253 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
3254 }
3255 }
3256
3257 fn trace_call_with_config(
3258 tx: $crate::evm::GenericTransaction,
3259 tracer_type: $crate::evm::TracerType,
3260 config: $crate::evm::TracingConfig,
3261 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
3262 let $crate::evm::TracingConfig { state_overrides } = config;
3263
3264 if let Some(overrides) = state_overrides {
3265 $crate::state_overrides::apply_state_overrides::<Runtime>(overrides)?;
3266 }
3267
3268 Self::trace_call(tx, tracer_type)
3269 }
3270
3271 fn runtime_pallets_address() -> $crate::H160 {
3272 $crate::RUNTIME_PALLETS_ADDR
3273 }
3274
3275 fn code(address: $crate::H160) -> Vec<u8> {
3276 $crate::Pallet::<Self>::code(&address)
3277 }
3278
3279 fn account_id(address: $crate::H160) -> AccountId {
3280 use $crate::AddressMapper;
3281 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
3282 }
3283
3284 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
3285 $crate::Pallet::<Self>::new_balance_with_dust(balance)
3286 }
3287 }
3288 }
3289 };
3290}