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 storage::{AccountType, DeletionQueueManager},
58 tracing::if_tracing,
59 vm::{CodeInfo, RuntimeCosts, pvm::extract_code_and_data},
60 weightinfo_extension::OnFinalizeBlockParts,
61};
62use alloc::{boxed::Box, format, vec};
63use codec::{Codec, Decode, Encode};
64use environmental::*;
65use frame_support::{
66 BoundedVec,
67 dispatch::{
68 DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
69 Pays, PostDispatchInfo, RawOrigin,
70 },
71 ensure,
72 pallet_prelude::DispatchClass,
73 traits::{
74 ConstU32, ConstU64, EnsureOrigin, Get, IsSubType, IsType, OnUnbalanced, OriginTrait,
75 fungible::{Balanced, Credit, Inspect, Mutate, MutateHold},
76 tokens::Balance,
77 },
78 weights::WeightMeter,
79};
80use frame_system::{
81 Pallet as System, ensure_signed,
82 pallet_prelude::{BlockNumberFor, OriginFor},
83};
84use scale_info::TypeInfo;
85use sp_runtime::{
86 AccountId32, DispatchError, FixedPointNumber, FixedU128, SaturatedConversion,
87 traits::{
88 BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedFrom,
89 UniqueSaturatedInto, Zero,
90 },
91};
92
93pub use crate::{
94 address::{
95 AccountId32Mapper, AddressMapper, TestAccountMapper, create1, create2, is_eth_derived,
96 },
97 debug::DebugSettings,
98 evm::{
99 Address as EthAddress, Block as EthBlock, DryRunConfig, ReceiptInfo,
100 block_hash::ReceiptGasInfo,
101 },
102 exec::{CallResources, DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
103 limits::TRANSIENT_STORAGE_BYTES as TRANSIENT_STORAGE_LIMIT,
104 metering::{
105 EthTxInfo, FrameMeter, ResourceMeter, Token as WeightToken, TransactionLimits,
106 TransactionMeter,
107 },
108 pallet::{genesis, *},
109 storage::{AccountInfo, ContractInfo},
110 transient_storage::{MeterEntry, StorageMeter as TransientStorageMeter, TransientStorage},
111 vm::{BytecodeType, ContractBlob},
112};
113pub use codec;
114pub use frame_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
115pub use frame_system::{self, limits::BlockWeights};
116pub use primitives::*;
117pub use sp_core::{H160, H256, U256, keccak_256};
118pub use sp_runtime;
119pub use weights::WeightInfo;
120
121#[cfg(doc)]
122pub use crate::vm::pvm::SyscallDoc;
123
124pub type BalanceOf<T> = <T as Config>::Balance;
125pub type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
126type TrieId = BoundedVec<u8, ConstU32<128>>;
127type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
128type CallOf<T> = <T as Config>::RuntimeCall;
129
130const SENTINEL: u32 = u32::MAX;
137
138const LOG_TARGET: &str = "runtime::revive";
144
145#[frame_support::pallet]
146pub mod pallet {
147 use super::*;
148 use frame_support::{pallet_prelude::*, traits::FindAuthor};
149 use frame_system::pallet_prelude::*;
150 use sp_core::U256;
151 use sp_runtime::Perbill;
152
153 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
155
156 #[pallet::pallet]
157 #[pallet::storage_version(STORAGE_VERSION)]
158 pub struct Pallet<T>(_);
159
160 #[pallet::config(with_default)]
161 pub trait Config: frame_system::Config {
162 type Time: Time<Moment: Into<U256>>;
164
165 #[pallet::no_default]
169 type Balance: Balance
170 + TryFrom<U256>
171 + Into<U256>
172 + Bounded
173 + UniqueSaturatedInto<u64>
174 + UniqueSaturatedFrom<u64>
175 + UniqueSaturatedInto<u128>;
176
177 #[pallet::no_default]
179 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
180 + Mutate<Self::AccountId>
181 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
182 + Balanced<Self::AccountId>;
183
184 #[pallet::no_default_bounds]
191 type OnBurn: OnUnbalanced<CreditOf<Self>>;
192
193 #[pallet::no_default_bounds]
195 #[allow(deprecated)]
196 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
197
198 #[pallet::no_default_bounds]
200 type RuntimeCall: Parameter
201 + Dispatchable<
202 RuntimeOrigin = OriginFor<Self>,
203 Info = DispatchInfo,
204 PostInfo = PostDispatchInfo,
205 > + IsType<<Self as frame_system::Config>::RuntimeCall>
206 + From<Call<Self>>
207 + IsSubType<Call<Self>>
208 + GetDispatchInfo;
209
210 #[pallet::no_default_bounds]
212 type RuntimeOrigin: IsType<OriginFor<Self>>
213 + From<Origin<Self>>
214 + Into<Result<Origin<Self>, OriginFor<Self>>>;
215
216 #[pallet::no_default_bounds]
218 type RuntimeHoldReason: From<HoldReason>;
219
220 type WeightInfo: WeightInfo;
223
224 #[pallet::no_default_bounds]
228 #[allow(private_bounds)]
229 type Precompiles: precompiles::Precompiles<Self>;
230
231 type FindAuthor: FindAuthor<Self::AccountId>;
233
234 #[pallet::constant]
240 #[pallet::no_default_bounds]
241 type DepositPerByte: Get<BalanceOf<Self>>;
242
243 #[pallet::constant]
249 #[pallet::no_default_bounds]
250 type DepositPerItem: Get<BalanceOf<Self>>;
251
252 #[pallet::constant]
262 #[pallet::no_default_bounds]
263 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
264
265 #[pallet::constant]
269 type CodeHashLockupDepositPercent: Get<Perbill>;
270
271 #[pallet::no_default]
273 type AddressMapper: AddressMapper<Self>;
274
275 #[pallet::constant]
277 type AllowEVMBytecode: Get<bool>;
278
279 #[pallet::no_default_bounds]
284 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
285
286 #[pallet::no_default_bounds]
297 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
298
299 type RuntimeMemory: Get<u32>;
304
305 type PVFMemory: Get<u32>;
313
314 #[pallet::constant]
319 type ChainId: Get<u64>;
320
321 #[pallet::constant]
323 type NativeToEthRatio: Get<u32>;
324
325 #[pallet::no_default_bounds]
330 type FeeInfo: FeeInfo<Self>;
331
332 #[pallet::constant]
345 type MaxEthExtrinsicWeight: Get<FixedU128>;
346
347 #[pallet::constant]
349 type DebugEnabled: Get<bool>;
350
351 #[pallet::constant]
369 #[pallet::no_default_bounds]
370 type GasScale: Get<u32>;
371 }
372
373 pub mod config_preludes {
375 use super::*;
376 use frame_support::{
377 derive_impl,
378 traits::{ConstBool, ConstU32},
379 };
380 use frame_system::EnsureSigned;
381 use sp_core::parameter_types;
382
383 type Balance = u64;
384
385 pub const DOLLARS: Balance = 1_000_000_000_000;
386 pub const CENTS: Balance = DOLLARS / 100;
387 pub const MILLICENTS: Balance = CENTS / 1_000;
388
389 pub const fn deposit(items: u32, bytes: u32) -> Balance {
390 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
391 }
392
393 parameter_types! {
394 pub const DepositPerItem: Balance = deposit(1, 0);
395 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
396 pub const DepositPerByte: Balance = deposit(0, 1);
397 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
398 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
399 pub const GasScale: u32 = 10u32;
400 }
401
402 pub struct TestDefaultConfig;
404
405 impl Time for TestDefaultConfig {
406 type Moment = u64;
407 fn now() -> Self::Moment {
408 0u64
409 }
410 }
411
412 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
413 fn convert(w: Weight) -> T {
414 w.ref_time().into()
415 }
416 }
417
418 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
419 impl frame_system::DefaultConfig for TestDefaultConfig {}
420
421 #[frame_support::register_default_impl(TestDefaultConfig)]
422 impl DefaultConfig for TestDefaultConfig {
423 #[inject_runtime_type]
424 type RuntimeEvent = ();
425
426 #[inject_runtime_type]
427 type RuntimeHoldReason = ();
428
429 #[inject_runtime_type]
430 type RuntimeCall = ();
431
432 #[inject_runtime_type]
433 type RuntimeOrigin = ();
434
435 type Precompiles = ();
436 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
437 type DepositPerByte = DepositPerByte;
438 type DepositPerItem = DepositPerItem;
439 type DepositPerChildTrieItem = DepositPerChildTrieItem;
440 type Time = Self;
441 type AllowEVMBytecode = ConstBool<true>;
442 type UploadOrigin = EnsureSigned<Self::AccountId>;
443 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
444 type WeightInfo = ();
445 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
446 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
447 type ChainId = ConstU64<42>;
448 type NativeToEthRatio = ConstU32<1_000_000>;
449 type FindAuthor = ();
450 type FeeInfo = ();
451 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
452 type DebugEnabled = ConstBool<false>;
453 type GasScale = GasScale;
454 type OnBurn = ();
455 }
456 }
457
458 #[pallet::event]
459 pub enum Event<T: Config> {
460 ContractEmitted {
462 contract: H160,
464 data: Vec<u8>,
467 topics: Vec<H256>,
470 },
471
472 Instantiated { deployer: H160, contract: H160 },
474
475 EthExtrinsicRevert { dispatch_error: DispatchError },
482 }
483
484 #[pallet::error]
485 #[repr(u8)]
486 pub enum Error<T> {
487 InvalidSchedule = 0x01,
489 InvalidCallFlags = 0x02,
491 OutOfGas = 0x03,
493 TransferFailed = 0x04,
496 MaxCallDepthReached = 0x05,
499 ContractNotFound = 0x06,
501 CodeNotFound = 0x07,
503 CodeInfoNotFound = 0x08,
505 OutOfBounds = 0x09,
507 DecodingFailed = 0x0A,
509 ContractTrapped = 0x0B,
511 ValueTooLarge = 0x0C,
513 TerminatedWhileReentrant = 0x0D,
516 InputForwarded = 0x0E,
518 TooManyTopics = 0x0F,
520 DuplicateContract = 0x12,
522 TerminatedInConstructor = 0x13,
526 ReentranceDenied = 0x14,
528 ReenteredPallet = 0x15,
530 StateChangeDenied = 0x16,
532 StorageDepositNotEnoughFunds = 0x17,
534 StorageDepositLimitExhausted = 0x18,
536 CodeInUse = 0x19,
538 ContractReverted = 0x1A,
543 CodeRejected = 0x1B,
548 BlobTooLarge = 0x1C,
550 StaticMemoryTooLarge = 0x1D,
552 BasicBlockTooLarge = 0x1E,
554 InvalidInstruction = 0x1F,
556 MaxDelegateDependenciesReached = 0x20,
558 DelegateDependencyNotFound = 0x21,
560 DelegateDependencyAlreadyExists = 0x22,
562 CannotAddSelfAsDelegateDependency = 0x23,
564 OutOfTransientStorage = 0x24,
566 InvalidSyscall = 0x25,
568 InvalidStorageFlags = 0x26,
570 ExecutionFailed = 0x27,
572 BalanceConversionFailed = 0x28,
574 InvalidImmutableAccess = 0x2A,
577 AccountUnmapped = 0x2B,
581 AccountAlreadyMapped = 0x2C,
583 InvalidGenericTransaction = 0x2D,
585 RefcountOverOrUnderflow = 0x2E,
587 UnsupportedPrecompileAddress = 0x2F,
589 CallDataTooLarge = 0x30,
591 ReturnDataTooLarge = 0x31,
593 InvalidJump = 0x32,
595 StackUnderflow = 0x33,
597 StackOverflow = 0x34,
599 TxFeeOverdraw = 0x35,
603 EvmConstructorNonEmptyData = 0x36,
607 EvmConstructedFromHash = 0x37,
612 StorageRefundNotEnoughFunds = 0x38,
616 StorageRefundLocked = 0x39,
621 PrecompileDelegateDenied = 0x40,
626 EcdsaRecoveryFailed = 0x41,
628 #[cfg(feature = "runtime-benchmarks")]
630 BenchmarkingError = 0xFF,
631 }
632
633 #[pallet::composite_enum]
635 pub enum HoldReason {
636 CodeUploadDepositReserve,
638 StorageDepositReserve,
640 AddressMapping,
642 }
643
644 #[derive(
645 PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug,
646 )]
647 #[pallet::origin]
648 pub enum Origin<T: Config> {
649 EthTransaction(T::AccountId),
650 }
651
652 #[pallet::storage]
656 #[pallet::unbounded]
657 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
658
659 #[pallet::storage]
661 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
662
663 #[pallet::storage]
665 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
666
667 #[pallet::storage]
669 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
670
671 #[pallet::storage]
676 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
677
678 #[pallet::storage]
681 pub(crate) type DeletionQueueCounter<T: Config> =
682 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
683
684 #[pallet::storage]
691 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
692
693 #[pallet::storage]
703 #[pallet::unbounded]
704 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
705
706 #[pallet::storage]
710 pub(crate) type BlockHash<T: Config> =
711 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
712
713 #[pallet::storage]
720 #[pallet::unbounded]
721 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
722
723 #[pallet::storage]
725 #[pallet::unbounded]
726 pub(crate) type EthBlockBuilderIR<T: Config> =
727 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
728
729 #[pallet::storage]
734 #[pallet::unbounded]
735 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
736 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
737
738 #[pallet::storage]
740 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
741
742 pub mod genesis {
743 use super::*;
744 use crate::evm::Bytes32;
745
746 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
748 pub struct ContractData {
749 pub code: crate::evm::Bytes,
751 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
753 }
754
755 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
757 pub struct Account<T: Config> {
758 pub address: H160,
760 #[serde(default)]
762 pub balance: U256,
763 #[serde(default)]
765 pub nonce: T::Nonce,
766 #[serde(flatten, skip_serializing_if = "Option::is_none")]
768 pub contract_data: Option<ContractData>,
769 }
770 }
771
772 #[pallet::genesis_config]
773 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
774 pub struct GenesisConfig<T: Config> {
775 #[serde(default, skip_serializing_if = "Vec::is_empty")]
778 pub mapped_accounts: Vec<T::AccountId>,
779
780 #[serde(default, skip_serializing_if = "Vec::is_empty")]
782 pub accounts: Vec<genesis::Account<T>>,
783
784 #[serde(default, skip_serializing_if = "Option::is_none")]
786 pub debug_settings: Option<DebugSettings>,
787 }
788
789 #[pallet::genesis_build]
790 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
791 fn build(&self) {
792 use crate::{exec::Key, vm::ContractBlob};
793 use frame_support::traits::fungible::Mutate;
794
795 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
796 let _ = T::Currency::mint_into(
797 &Pallet::<T>::account_id(),
798 T::Currency::minimum_balance(),
799 );
800 }
801
802 for id in &self.mapped_accounts {
803 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
804 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
805 }
806 }
807
808 let owner = Pallet::<T>::account_id();
809
810 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
811 let account_id = T::AddressMapper::to_account_id(address);
812
813 if !System::<T>::account_exists(&account_id) {
814 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
815 }
816
817 frame_system::Account::<T>::mutate(&account_id, |info| {
818 info.nonce = (*nonce).into();
819 });
820
821 match contract_data {
822 None => {
823 AccountInfoOf::<T>::insert(
824 address,
825 AccountInfo { account_type: AccountType::EOA, dust: 0 },
826 );
827 },
828 Some(genesis::ContractData { code, storage }) => {
829 let blob = if code.0.starts_with(&polkavm_common::program::BLOB_MAGIC) {
830 ContractBlob::<T>::from_pvm_code( code.0.clone(), owner.clone()).inspect_err(|err| {
831 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
832 })
833 } else {
834 ContractBlob::<T>::from_evm_runtime_code(code.0.clone(), account_id).inspect_err(|err| {
835 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
836 })
837 };
838
839 let Ok(blob) = blob else {
840 continue;
841 };
842
843 let code_hash = *blob.code_hash();
844 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
845 .inspect_err(|err| {
846 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
847 })
848 else {
849 continue;
850 };
851
852 AccountInfoOf::<T>::insert(
853 address,
854 AccountInfo { account_type: info.clone().into(), dust: 0 },
855 );
856
857 <PristineCode<T>>::insert(blob.code_hash(), code.0.clone());
858 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
859 for (k, v) in storage {
860 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
861 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
862 });
863 }
864 },
865 }
866
867 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
868 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
869 });
870 }
871
872 block_storage::on_finalize_build_eth_block::<T>(
874 frame_system::Pallet::<T>::block_number(),
877 );
878
879 if let Some(settings) = self.debug_settings.as_ref() {
881 settings.write_to_storage::<T>()
882 }
883 }
884 }
885
886 #[pallet::hooks]
887 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
888 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
889 let mut meter = WeightMeter::with_limit(limit);
890 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
891 meter.consumed()
892 }
893
894 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
895 block_storage::on_initialize::<T>();
897
898 System::<T>::account_exists(&Pallet::<T>::account_id());
900 <T as Config>::WeightInfo::on_finalize_block_fixed()
902 }
903
904 fn on_finalize(block_number: BlockNumberFor<T>) {
905 block_storage::on_finalize_build_eth_block::<T>(block_number);
907 }
908
909 fn integrity_test() {
910 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
911
912 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
913
914 T::FeeInfo::integrity_test();
915
916 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
918
919 const TOTAL_MEMORY_DEVIDER: u64 = 2;
922
923 let max_block_weight = T::BlockWeights::get()
929 .get(DispatchClass::Normal)
930 .max_total
931 .unwrap_or_else(|| T::BlockWeights::get().max_block);
932 let max_key_size: u64 =
933 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
934 .expect("Key of maximal size shall be created")
935 .hash()
936 .len()
937 .try_into()
938 .unwrap();
939
940 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
941 let max_immutable_size: u64 = max_block_weight
942 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
943 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
944 ))
945 .unwrap()
946 .saturating_mul(
947 u64::from(limits::IMMUTABLE_BYTES)
948 .saturating_add(max_immutable_key_size)
949 .into(),
950 );
951
952 let max_pvf_mem: u64 = T::PVFMemory::get().into();
953 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
954
955 let max_events_size = max_block_weight
959 .checked_div_per_component(
960 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
961 num_topic: 0,
962 len: limits::EVENT_BYTES,
963 })
964 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
965 &RuntimeCosts::HostFn,
966 ))),
967 )
968 .unwrap()
969 .saturating_mul(limits::EVENT_BYTES.into());
970
971 assert!(
972 max_events_size <= storage_size_limit,
973 "Maximal events size {} exceeds the events limit {}",
974 max_events_size,
975 storage_size_limit
976 );
977
978 let max_eth_block_builder_bytes =
1013 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1014
1015 log::debug!(
1016 target: LOG_TARGET,
1017 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1018 max_eth_block_builder_bytes / 1024,
1019 max_events_size / 1024,
1020 );
1021
1022 let memory_left = i128::from(max_runtime_mem)
1027 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1028 .saturating_sub(limits::MEMORY_REQUIRED.into())
1029 .saturating_sub(max_eth_block_builder_bytes.into());
1030
1031 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1032
1033 assert!(
1034 memory_left >= 0,
1035 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1036 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1037 );
1038
1039 let max_storage_size = max_block_weight
1042 .checked_div_per_component(
1043 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1044 new_bytes: limits::STORAGE_BYTES,
1045 old_bytes: 0,
1046 })
1047 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1048 )
1049 .unwrap()
1050 .saturating_add(max_immutable_size.into())
1051 .saturating_add(max_eth_block_builder_bytes.into());
1052
1053 assert!(
1054 max_storage_size <= storage_size_limit,
1055 "Maximal storage size {} exceeds the storage limit {}",
1056 max_storage_size,
1057 storage_size_limit
1058 );
1059 }
1060 }
1061
1062 #[pallet::call]
1063 impl<T: Config> Pallet<T> {
1064 #[allow(unused_variables)]
1077 #[pallet::call_index(0)]
1078 #[pallet::weight(Weight::MAX)]
1079 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1080 Err(frame_system::Error::CallFiltered::<T>.into())
1081 }
1082
1083 #[pallet::call_index(1)]
1100 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1101 pub fn call(
1102 origin: OriginFor<T>,
1103 dest: H160,
1104 #[pallet::compact] value: BalanceOf<T>,
1105 weight_limit: Weight,
1106 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1107 data: Vec<u8>,
1108 ) -> DispatchResultWithPostInfo {
1109 Self::ensure_non_contract_if_signed(&origin)?;
1110 let mut output = Self::bare_call(
1111 origin,
1112 dest,
1113 Pallet::<T>::convert_native_to_evm(value),
1114 TransactionLimits::WeightAndDeposit {
1115 weight_limit,
1116 deposit_limit: storage_deposit_limit,
1117 },
1118 data,
1119 &ExecConfig::new_substrate_tx(),
1120 );
1121
1122 if let Ok(return_value) = &output.result &&
1123 return_value.did_revert()
1124 {
1125 output.result = Err(<Error<T>>::ContractReverted.into());
1126 }
1127 dispatch_result(
1128 output.result,
1129 output.weight_consumed,
1130 <T as Config>::WeightInfo::call(),
1131 )
1132 }
1133
1134 #[pallet::call_index(2)]
1140 #[pallet::weight(
1141 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1142 )]
1143 pub fn instantiate(
1144 origin: OriginFor<T>,
1145 #[pallet::compact] value: BalanceOf<T>,
1146 weight_limit: Weight,
1147 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1148 code_hash: sp_core::H256,
1149 data: Vec<u8>,
1150 salt: Option<[u8; 32]>,
1151 ) -> DispatchResultWithPostInfo {
1152 Self::ensure_non_contract_if_signed(&origin)?;
1153 let data_len = data.len() as u32;
1154 let mut output = Self::bare_instantiate(
1155 origin,
1156 Pallet::<T>::convert_native_to_evm(value),
1157 TransactionLimits::WeightAndDeposit {
1158 weight_limit,
1159 deposit_limit: storage_deposit_limit,
1160 },
1161 Code::Existing(code_hash),
1162 data,
1163 salt,
1164 &ExecConfig::new_substrate_tx(),
1165 );
1166 if let Ok(retval) = &output.result &&
1167 retval.result.did_revert()
1168 {
1169 output.result = Err(<Error<T>>::ContractReverted.into());
1170 }
1171 dispatch_result(
1172 output.result.map(|result| result.result),
1173 output.weight_consumed,
1174 <T as Config>::WeightInfo::instantiate(data_len),
1175 )
1176 }
1177
1178 #[pallet::call_index(3)]
1206 #[pallet::weight(
1207 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1208 .saturating_add(*weight_limit)
1209 )]
1210 pub fn instantiate_with_code(
1211 origin: OriginFor<T>,
1212 #[pallet::compact] value: BalanceOf<T>,
1213 weight_limit: Weight,
1214 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1215 code: Vec<u8>,
1216 data: Vec<u8>,
1217 salt: Option<[u8; 32]>,
1218 ) -> DispatchResultWithPostInfo {
1219 Self::ensure_non_contract_if_signed(&origin)?;
1220 let code_len = code.len() as u32;
1221 let data_len = data.len() as u32;
1222 let mut output = Self::bare_instantiate(
1223 origin,
1224 Pallet::<T>::convert_native_to_evm(value),
1225 TransactionLimits::WeightAndDeposit {
1226 weight_limit,
1227 deposit_limit: storage_deposit_limit,
1228 },
1229 Code::Upload(code),
1230 data,
1231 salt,
1232 &ExecConfig::new_substrate_tx(),
1233 );
1234 if let Ok(retval) = &output.result &&
1235 retval.result.did_revert()
1236 {
1237 output.result = Err(<Error<T>>::ContractReverted.into());
1238 }
1239 dispatch_result(
1240 output.result.map(|result| result.result),
1241 output.weight_consumed,
1242 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1243 )
1244 }
1245
1246 #[pallet::call_index(10)]
1268 #[pallet::weight(
1269 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1270 .saturating_add(*weight_limit)
1271 )]
1272 pub fn eth_instantiate_with_code(
1273 origin: OriginFor<T>,
1274 value: U256,
1275 weight_limit: Weight,
1276 eth_gas_limit: U256,
1277 code: Vec<u8>,
1278 data: Vec<u8>,
1279 transaction_encoded: Vec<u8>,
1280 effective_gas_price: U256,
1281 encoded_len: u32,
1282 ) -> DispatchResultWithPostInfo {
1283 let signer = Self::ensure_eth_signed(origin)?;
1284 let origin = OriginFor::<T>::signed(signer.clone());
1285 Self::ensure_non_contract_if_signed(&origin)?;
1286 let mut call = Call::<T>::eth_instantiate_with_code {
1287 value,
1288 weight_limit,
1289 eth_gas_limit,
1290 code: code.clone(),
1291 data: data.clone(),
1292 transaction_encoded: transaction_encoded.clone(),
1293 effective_gas_price,
1294 encoded_len,
1295 }
1296 .into();
1297 let info = T::FeeInfo::dispatch_info(&call);
1298 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1299 drop(call);
1300
1301 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1302 let extra_weight = base_info.total_weight();
1303 let output = Self::bare_instantiate(
1304 origin,
1305 value,
1306 TransactionLimits::EthereumGas {
1307 eth_gas_limit: eth_gas_limit.saturated_into(),
1308 weight_limit,
1309 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1310 },
1311 Code::Upload(code),
1312 data,
1313 None,
1314 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1315 );
1316
1317 block_storage::EthereumCallResult::new::<T>(
1318 signer,
1319 output.map_result(|r| r.result),
1320 base_info.call_weight,
1321 encoded_len,
1322 &info,
1323 effective_gas_price,
1324 )
1325 })
1326 }
1327
1328 #[pallet::call_index(11)]
1345 #[pallet::weight(
1346 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1347 .saturating_add(*weight_limit)
1348 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1349 )]
1350 pub fn eth_call(
1351 origin: OriginFor<T>,
1352 dest: H160,
1353 value: U256,
1354 weight_limit: Weight,
1355 eth_gas_limit: U256,
1356 data: Vec<u8>,
1357 transaction_encoded: Vec<u8>,
1358 effective_gas_price: U256,
1359 encoded_len: u32,
1360 ) -> DispatchResultWithPostInfo {
1361 let signer = Self::ensure_eth_signed(origin)?;
1362 let origin = OriginFor::<T>::signed(signer.clone());
1363
1364 Self::ensure_non_contract_if_signed(&origin)?;
1365 let mut call = Call::<T>::eth_call {
1366 dest,
1367 value,
1368 weight_limit,
1369 eth_gas_limit,
1370 data: data.clone(),
1371 transaction_encoded: transaction_encoded.clone(),
1372 effective_gas_price,
1373 encoded_len,
1374 }
1375 .into();
1376 let info = T::FeeInfo::dispatch_info(&call);
1377 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1378 drop(call);
1379
1380 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1381 let extra_weight = base_info.total_weight();
1382 let output = Self::bare_call(
1383 origin,
1384 dest,
1385 value,
1386 TransactionLimits::EthereumGas {
1387 eth_gas_limit: eth_gas_limit.saturated_into(),
1388 weight_limit,
1389 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1390 },
1391 data,
1392 &ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1393 );
1394
1395 block_storage::EthereumCallResult::new::<T>(
1396 signer,
1397 output,
1398 base_info.call_weight,
1399 encoded_len,
1400 &info,
1401 effective_gas_price,
1402 )
1403 })
1404 }
1405
1406 #[pallet::call_index(12)]
1417 #[pallet::weight(T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1418 pub fn eth_substrate_call(
1419 origin: OriginFor<T>,
1420 call: Box<<T as Config>::RuntimeCall>,
1421 transaction_encoded: Vec<u8>,
1422 ) -> DispatchResultWithPostInfo {
1423 let signer = Self::ensure_eth_signed(origin)?;
1426 let weight_overhead =
1427 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32);
1428
1429 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1430 let call_weight = call.get_dispatch_info().call_weight;
1431 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1432
1433 match &mut call_result {
1435 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1436 post_info.actual_weight = Some(
1437 post_info
1438 .actual_weight
1439 .unwrap_or_else(|| call_weight)
1440 .saturating_add(weight_overhead),
1441 );
1442 },
1443 }
1444
1445 block_storage::EthereumCallResult {
1448 receipt_gas_info: ReceiptGasInfo::default(),
1449 result: call_result,
1450 }
1451 })
1452 }
1453
1454 #[pallet::call_index(4)]
1469 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1470 pub fn upload_code(
1471 origin: OriginFor<T>,
1472 code: Vec<u8>,
1473 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1474 ) -> DispatchResult {
1475 Self::ensure_non_contract_if_signed(&origin)?;
1476 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1477 }
1478
1479 #[pallet::call_index(5)]
1484 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1485 pub fn remove_code(
1486 origin: OriginFor<T>,
1487 code_hash: sp_core::H256,
1488 ) -> DispatchResultWithPostInfo {
1489 let origin = ensure_signed(origin)?;
1490 <ContractBlob<T>>::remove(&origin, code_hash)?;
1491 Ok(Pays::No.into())
1493 }
1494
1495 #[pallet::call_index(6)]
1506 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1507 pub fn set_code(
1508 origin: OriginFor<T>,
1509 dest: H160,
1510 code_hash: sp_core::H256,
1511 ) -> DispatchResult {
1512 ensure_root(origin)?;
1513 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1514 let Some(account) = account else {
1515 return Err(<Error<T>>::ContractNotFound.into());
1516 };
1517
1518 let AccountType::Contract(ref mut contract) = account.account_type else {
1519 return Err(<Error<T>>::ContractNotFound.into());
1520 };
1521
1522 <CodeInfo<T>>::increment_refcount(code_hash)?;
1523 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1524 contract.code_hash = code_hash;
1525
1526 Ok(())
1527 })
1528 }
1529
1530 #[pallet::call_index(7)]
1535 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1536 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1537 Self::ensure_non_contract_if_signed(&origin)?;
1538 let origin = ensure_signed(origin)?;
1539 T::AddressMapper::map(&origin)
1540 }
1541
1542 #[pallet::call_index(8)]
1547 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1548 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1549 let origin = ensure_signed(origin)?;
1550 T::AddressMapper::unmap(&origin)
1551 }
1552
1553 #[pallet::call_index(9)]
1559 #[pallet::weight({
1560 let dispatch_info = call.get_dispatch_info();
1561 (
1562 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1563 dispatch_info.class
1564 )
1565 })]
1566 pub fn dispatch_as_fallback_account(
1567 origin: OriginFor<T>,
1568 call: Box<<T as Config>::RuntimeCall>,
1569 ) -> DispatchResultWithPostInfo {
1570 Self::ensure_non_contract_if_signed(&origin)?;
1571 let origin = ensure_signed(origin)?;
1572 let unmapped_account =
1573 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1574 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1575 }
1576 }
1577}
1578
1579fn dispatch_result<R>(
1581 result: Result<R, DispatchError>,
1582 weight_consumed: Weight,
1583 base_weight: Weight,
1584) -> DispatchResultWithPostInfo {
1585 let post_info = PostDispatchInfo {
1586 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1587 pays_fee: Default::default(),
1588 };
1589
1590 result
1591 .map(|_| post_info)
1592 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1593}
1594
1595impl<T: Config> Pallet<T> {
1596 pub fn bare_call(
1603 origin: OriginFor<T>,
1604 dest: H160,
1605 evm_value: U256,
1606 transaction_limits: TransactionLimits<T>,
1607 data: Vec<u8>,
1608 exec_config: &ExecConfig<T>,
1609 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1610 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1611 Ok(transaction_meter) => transaction_meter,
1612 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1613 };
1614 let mut storage_deposit = Default::default();
1615
1616 let try_call = || {
1617 let origin = ExecOrigin::from_runtime_origin(origin)?;
1618 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1619 origin.clone(),
1620 dest,
1621 &mut transaction_meter,
1622 evm_value,
1623 data,
1624 &exec_config,
1625 )?;
1626
1627 storage_deposit = transaction_meter
1628 .execute_postponed_deposits(&origin, &exec_config)
1629 .inspect_err(|err| {
1630 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1631 })?;
1632
1633 Ok(result)
1634 };
1635 let result = Self::run_guarded(try_call);
1636
1637 log::trace!(target: LOG_TARGET, "Bare call ends: \
1638 result={result:?}, \
1639 weight_consumed={:?}, \
1640 weight_required={:?}, \
1641 storage_deposit={:?}, \
1642 gas_consumed={:?}, \
1643 max_storage_deposit={:?}",
1644 transaction_meter.weight_consumed(),
1645 transaction_meter.weight_required(),
1646 storage_deposit,
1647 transaction_meter.total_consumed_gas(),
1648 transaction_meter.deposit_required()
1649 );
1650
1651 ContractResult {
1652 result: result.map_err(|r| r.error),
1653 weight_consumed: transaction_meter.weight_consumed(),
1654 weight_required: transaction_meter.weight_required(),
1655 storage_deposit,
1656 gas_consumed: transaction_meter.total_consumed_gas(),
1657 max_storage_deposit: transaction_meter.deposit_required(),
1658 }
1659 }
1660
1661 pub fn prepare_dry_run(account: &T::AccountId) {
1667 frame_system::Pallet::<T>::inc_account_nonce(account);
1670 }
1671
1672 pub fn bare_instantiate(
1678 origin: OriginFor<T>,
1679 evm_value: U256,
1680 transaction_limits: TransactionLimits<T>,
1681 code: Code,
1682 data: Vec<u8>,
1683 salt: Option<[u8; 32]>,
1684 exec_config: &ExecConfig<T>,
1685 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1686 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1687 Ok(transaction_meter) => transaction_meter,
1688 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1689 };
1690
1691 let mut storage_deposit = Default::default();
1692
1693 let try_instantiate = || {
1694 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1695
1696 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1697 let executable = match code {
1698 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1699 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1700 let executable = Self::try_upload_code(
1701 upload_account,
1702 code,
1703 BytecodeType::Pvm,
1704 &mut transaction_meter,
1705 &exec_config,
1706 )?;
1707 executable
1708 },
1709 Code::Upload(code) => {
1710 if T::AllowEVMBytecode::get() {
1711 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1712 let origin = T::UploadOrigin::ensure_origin(origin)?;
1713 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1714 executable
1715 } else {
1716 return Err(<Error<T>>::CodeRejected.into());
1717 }
1718 },
1719 Code::Existing(code_hash) => {
1720 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1721 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1722 executable
1723 },
1724 };
1725 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1726 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1727 instantiate_account,
1728 executable,
1729 &mut transaction_meter,
1730 evm_value,
1731 data,
1732 salt.as_ref(),
1733 &exec_config,
1734 );
1735
1736 storage_deposit = transaction_meter
1737 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1738 .inspect_err(|err| {
1739 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1740 })?;
1741 result
1742 };
1743 let output = Self::run_guarded(try_instantiate);
1744
1745 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1746 weight_required={:?} \
1747 storage_deposit={:?} \
1748 gas_consumed={:?} \
1749 max_storage_deposit={:?}",
1750 transaction_meter.weight_consumed(),
1751 transaction_meter.weight_required(),
1752 storage_deposit,
1753 transaction_meter.total_consumed_gas(),
1754 transaction_meter.deposit_required()
1755 );
1756
1757 ContractResult {
1758 result: output
1759 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1760 .map_err(|e| e.error),
1761 weight_consumed: transaction_meter.weight_consumed(),
1762 weight_required: transaction_meter.weight_required(),
1763 storage_deposit,
1764 gas_consumed: transaction_meter.total_consumed_gas(),
1765 max_storage_deposit: transaction_meter.deposit_required(),
1766 }
1767 }
1768
1769 pub fn dry_run_eth_transact(
1775 mut tx: GenericTransaction,
1776 dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1777 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1778 where
1779 T::Nonce: Into<U256>,
1780 CallOf<T>: SetWeightLimit,
1781 {
1782 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1783
1784 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1785 Self::prepare_dry_run(&origin);
1786
1787 let base_fee = Self::evm_base_fee();
1788 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1789
1790 if effective_gas_price < base_fee {
1791 Err(EthTransactError::Message(format!(
1792 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1793 )))?;
1794 }
1795
1796 if tx.nonce.is_none() {
1797 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1798 }
1799 if tx.chain_id.is_none() {
1800 tx.chain_id = Some(T::ChainId::get().into());
1801 }
1802
1803 tx.gas_price = Some(effective_gas_price);
1805 tx.max_priority_fee_per_gas = Some(0.into());
1808 if tx.max_fee_per_gas.is_none() {
1809 tx.max_fee_per_gas = Some(effective_gas_price);
1810 }
1811
1812 let gas = tx.gas;
1813 if tx.gas.is_none() {
1814 tx.gas = Some(Self::evm_block_gas_limit());
1815 }
1816 if tx.r#type.is_none() {
1817 tx.r#type = Some(TYPE_EIP1559.into());
1818 }
1819
1820 let value = tx.value.unwrap_or_default();
1822 let input = tx.input.clone().to_vec();
1823 let from = tx.from;
1824 let to = tx.to;
1825
1826 let mut call_info = tx
1829 .into_call::<T>(CreateCallMode::DryRun)
1830 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1831
1832 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1836 let base_weight = base_info.total_weight();
1837 let exec_config =
1838 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
1839 .with_dry_run(dry_run_config);
1840
1841 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
1843 if let Some(from) = &from {
1844 let fees = if gas.is_some() { fees } else { Zero::zero() };
1845 let balance = Self::evm_balance(from);
1846 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
1847 return Err(EthTransactError::Message(format!(
1848 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
1849 )));
1850 }
1851 }
1852
1853 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
1856
1857 let extract_error = |err| {
1858 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
1859 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
1860 } else {
1861 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
1862 }
1863 };
1864
1865 let transaction_limits = TransactionLimits::EthereumGas {
1866 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
1867 weight_limit: Self::evm_max_extrinsic_weight(),
1868 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
1869 };
1870
1871 let mut dry_run = match to {
1873 Some(dest) => {
1875 if dest == RUNTIME_PALLETS_ADDR {
1876 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
1877 return Err(EthTransactError::Message(format!(
1878 "Failed to decode pallet-call {input:?}"
1879 )));
1880 };
1881
1882 if let Err(result) =
1883 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1884 {
1885 return Err(EthTransactError::Message(format!(
1886 "Failed to dispatch call: {:?}",
1887 result.error,
1888 )));
1889 };
1890
1891 Default::default()
1892 } else {
1893 let result = crate::Pallet::<T>::bare_call(
1895 OriginFor::<T>::signed(origin),
1896 dest,
1897 value,
1898 transaction_limits,
1899 input.clone(),
1900 &exec_config,
1901 );
1902
1903 let data = match result.result {
1904 Ok(return_value) => {
1905 if return_value.did_revert() {
1906 return Err(EthTransactError::Data(return_value.data));
1907 }
1908 return_value.data
1909 },
1910 Err(err) => {
1911 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1912 return extract_error(err);
1913 },
1914 };
1915
1916 EthTransactInfo {
1917 weight_required: result.weight_required,
1918 storage_deposit: result.storage_deposit.charge_or_zero(),
1919 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1920 data,
1921 eth_gas: Default::default(),
1922 }
1923 }
1924 },
1925 None => {
1927 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1929 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1930 } else {
1931 (input, vec![])
1932 };
1933
1934 let result = crate::Pallet::<T>::bare_instantiate(
1936 OriginFor::<T>::signed(origin),
1937 value,
1938 transaction_limits,
1939 Code::Upload(code.clone()),
1940 data.clone(),
1941 None,
1942 &exec_config,
1943 );
1944
1945 let returned_data = match result.result {
1946 Ok(return_value) => {
1947 if return_value.result.did_revert() {
1948 return Err(EthTransactError::Data(return_value.result.data));
1949 }
1950 return_value.result.data
1951 },
1952 Err(err) => {
1953 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1954 return extract_error(err);
1955 },
1956 };
1957
1958 EthTransactInfo {
1959 weight_required: result.weight_required,
1960 storage_deposit: result.storage_deposit.charge_or_zero(),
1961 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1962 data: returned_data,
1963 eth_gas: Default::default(),
1964 }
1965 },
1966 };
1967
1968 call_info.call.set_weight_limit(dry_run.weight_required);
1970
1971 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
1973 let max_weight = Self::evm_max_extrinsic_weight();
1974 if total_weight.any_gt(max_weight) {
1975 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
1976 total_weight={total_weight:?} \
1977 max_weight={max_weight:?}",
1978 );
1979
1980 Err(EthTransactError::Message(format!(
1981 "\
1982 The transaction consumes more than the allowed weight. \
1983 needed={total_weight} \
1984 allowed={max_weight} \
1985 overweight_by={}\
1986 ",
1987 total_weight.saturating_sub(max_weight),
1988 )))?;
1989 }
1990
1991 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
1993 let available_fee = T::FeeInfo::remaining_txfee();
1994 if transaction_fee > available_fee {
1995 Err(EthTransactError::Message(format!(
1996 "Not enough gas supplied: Off by: {:?}",
1997 transaction_fee.saturating_sub(available_fee),
1998 )))?;
1999 }
2000
2001 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2002 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2003 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2004 if !rest.is_zero() {
2005 eth_gas = eth_gas.saturating_add(1_u32.into());
2006 }
2007
2008 log::debug!(target: LOG_TARGET, "\
2009 dry_run_eth_transact finished: \
2010 weight_limit={}, \
2011 total_weight={total_weight}, \
2012 max_weight={max_weight}, \
2013 weight_left={}, \
2014 eth_gas={eth_gas}, \
2015 encoded_len={}, \
2016 tx_fee={transaction_fee:?}, \
2017 storage_deposit={:?}, \
2018 max_storage_deposit={:?}\
2019 ",
2020 dry_run.weight_required,
2021 max_weight.saturating_sub(total_weight),
2022 call_info.encoded_len,
2023 dry_run.storage_deposit,
2024 dry_run.max_storage_deposit,
2025
2026 );
2027 dry_run.eth_gas = eth_gas;
2028 Ok(dry_run)
2029 }
2030
2031 pub fn evm_balance(address: &H160) -> U256 {
2035 let balance = AccountInfo::<T>::balance_of((*address).into());
2036 Self::convert_native_to_evm(balance)
2037 }
2038
2039 pub fn eth_block() -> EthBlock {
2041 EthereumBlock::<T>::get()
2042 }
2043
2044 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2051 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2052 let hash = <BlockHash<T>>::get(number);
2053 if hash == H256::zero() { None } else { Some(hash) }
2054 }
2055
2056 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2058 ReceiptInfoData::<T>::get()
2059 }
2060
2061 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2067 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2068 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2069 let account_id = T::AddressMapper::to_account_id(&address);
2070 T::Currency::set_balance(&account_id, balance);
2071 AccountInfoOf::<T>::mutate(&address, |account| {
2072 if let Some(account) = account {
2073 account.dust = dust;
2074 } else {
2075 *account = Some(AccountInfo { dust, ..Default::default() });
2076 }
2077 });
2078
2079 Ok(())
2080 }
2081
2082 pub fn new_balance_with_dust(
2086 evm_value: U256,
2087 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2088 let ed = T::Currency::minimum_balance();
2089 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2090 let (value, dust) = balance_with_dust.deconstruct();
2091
2092 Ok((ed.saturating_add(value), dust))
2093 }
2094
2095 pub fn evm_nonce(address: &H160) -> u32
2097 where
2098 T::Nonce: Into<u32>,
2099 {
2100 let account = T::AddressMapper::to_account_id(&address);
2101 System::<T>::account_nonce(account).into()
2102 }
2103
2104 pub fn evm_block_gas_limit() -> U256 {
2106 u64::MAX.into()
2113 }
2114
2115 pub fn evm_max_extrinsic_weight() -> Weight {
2117 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2118 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2119 .get(DispatchClass::Normal)
2120 .max_extrinsic
2121 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2122 Weight::from_parts(
2123 factor.saturating_mul_int(max_weight.ref_time()),
2124 factor.saturating_mul_int(max_weight.proof_size()),
2125 )
2126 }
2127
2128 pub fn evm_base_fee() -> U256 {
2130 let gas_scale = <T as Config>::GasScale::get();
2131 let multiplier = T::FeeInfo::next_fee_multiplier();
2132 multiplier
2133 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2134 .saturating_mul(gas_scale.saturated_into())
2135 .into()
2136 }
2137
2138 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2140 where
2141 T::Nonce: Into<u32>,
2142 {
2143 match tracer_type {
2144 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2145 TracerType::PrestateTracer(config) => {
2146 PrestateTracer::new(config.unwrap_or_default()).into()
2147 },
2148 TracerType::ExecutionTracer(config) => {
2149 ExecutionTracer::new(config.unwrap_or_default()).into()
2150 },
2151 }
2152 }
2153
2154 pub fn bare_upload_code(
2158 origin: OriginFor<T>,
2159 code: Vec<u8>,
2160 storage_deposit_limit: BalanceOf<T>,
2161 ) -> CodeUploadResult<BalanceOf<T>> {
2162 let origin = T::UploadOrigin::ensure_origin(origin)?;
2163
2164 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2165 BytecodeType::Pvm
2166 } else {
2167 if !T::AllowEVMBytecode::get() {
2168 return Err(<Error<T>>::CodeRejected.into());
2169 }
2170 BytecodeType::Evm
2171 };
2172
2173 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2174 weight_limit: Default::default(),
2175 deposit_limit: storage_deposit_limit,
2176 })?;
2177
2178 let module = Self::try_upload_code(
2179 origin,
2180 code,
2181 bytecode_type,
2182 &mut meter,
2183 &ExecConfig::new_substrate_tx(),
2184 )?;
2185 Ok(CodeUploadReturnValue {
2186 code_hash: *module.code_hash(),
2187 deposit: meter.deposit_consumed().charge_or_zero(),
2188 })
2189 }
2190
2191 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2193 let contract_info =
2194 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2195
2196 let maybe_value = contract_info.read(&Key::from_fixed(key));
2197 Ok(maybe_value)
2198 }
2199
2200 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2204 let immutable_data = <ImmutableDataOf<T>>::get(address);
2205 immutable_data
2206 }
2207
2208 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2216 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2217 <ImmutableDataOf<T>>::insert(address, data);
2218 Ok(())
2219 }
2220
2221 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2223 let contract_info =
2224 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2225
2226 let maybe_value = contract_info.read(
2227 &Key::try_from_var(key)
2228 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2229 .into(),
2230 );
2231 Ok(maybe_value)
2232 }
2233
2234 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2236 let (value, dust) = value.into().deconstruct();
2237 value
2238 .into()
2239 .saturating_mul(T::NativeToEthRatio::get().into())
2240 .saturating_add(dust.into())
2241 }
2242
2243 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2253 let contract_info =
2254 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2255
2256 contract_info
2257 .write(&Key::from_fixed(key), value, None, false)
2258 .map_err(ContractAccessError::StorageWriteFailed)
2259 }
2260
2261 pub fn set_storage_var_key(
2272 address: H160,
2273 key: Vec<u8>,
2274 value: Option<Vec<u8>>,
2275 ) -> SetStorageResult {
2276 let contract_info =
2277 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2278
2279 contract_info
2280 .write(
2281 &Key::try_from_var(key)
2282 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2283 .into(),
2284 value,
2285 None,
2286 false,
2287 )
2288 .map_err(ContractAccessError::StorageWriteFailed)
2289 }
2290
2291 pub fn account_id() -> T::AccountId {
2293 use frame_support::PalletId;
2294 use sp_runtime::traits::AccountIdConversion;
2295 PalletId(*b"py/reviv").into_account_truncating()
2296 }
2297
2298 pub fn block_author() -> H160 {
2300 use frame_support::traits::FindAuthor;
2301
2302 let digest = <frame_system::Pallet<T>>::digest();
2303 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2304
2305 T::FindAuthor::find_author(pre_runtime_digests)
2306 .map(|account_id| T::AddressMapper::to_address(&account_id))
2307 .unwrap_or_default()
2308 }
2309
2310 pub fn code(address: &H160) -> Vec<u8> {
2314 use precompiles::{All, Precompiles};
2315 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2316 return code.into();
2317 }
2318 AccountInfo::<T>::load_contract(&address)
2319 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2320 .map(|code| code.into())
2321 .unwrap_or_default()
2322 }
2323
2324 pub fn try_upload_code(
2326 origin: T::AccountId,
2327 code: Vec<u8>,
2328 code_type: BytecodeType,
2329 meter: &mut TransactionMeter<T>,
2330 exec_config: &ExecConfig<T>,
2331 ) -> Result<ContractBlob<T>, DispatchError> {
2332 let mut module = match code_type {
2333 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2334 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2335 };
2336 module.store_code(exec_config, meter)?;
2337 Ok(module)
2338 }
2339
2340 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2342 executing_contract::using_once(&mut false, || {
2343 executing_contract::with(|f| {
2344 if *f {
2346 return Err(())
2347 }
2348 *f = true;
2350 Ok(())
2351 })
2352 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2353 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2354 .map(|_| f())
2355 .and_then(|r| r)
2356 })
2357 }
2358
2359 fn charge_deposit(
2364 hold_reason: Option<HoldReason>,
2365 from: &T::AccountId,
2366 to: &T::AccountId,
2367 amount: BalanceOf<T>,
2368 exec_config: &ExecConfig<T>,
2369 ) -> DispatchResult {
2370 use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
2371
2372 if amount.is_zero() {
2373 return Ok(());
2374 }
2375
2376 match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2377 (true, hold_reason) => {
2378 T::FeeInfo::withdraw_txfee(amount)
2379 .ok_or(())
2380 .and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2381 .and_then(|_| {
2382 if let Some(hold_reason) = hold_reason {
2383 T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2384 }
2385 Ok(())
2386 })
2387 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2388 },
2389 (false, Some(hold_reason)) => {
2390 T::Currency::transfer_and_hold(
2391 &hold_reason.into(),
2392 from,
2393 to,
2394 amount,
2395 Precision::Exact,
2396 Preservation::Preserve,
2397 Fortitude::Polite,
2398 )
2399 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2400 },
2401 (false, None) => {
2402 T::Currency::transfer(from, to, amount, Preservation::Preserve)
2403 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2404 },
2405 }
2406 Ok(())
2407 }
2408
2409 fn refund_deposit(
2414 hold_reason: HoldReason,
2415 from: &T::AccountId,
2416 to: &T::AccountId,
2417 amount: BalanceOf<T>,
2418 exec_config: Option<&ExecConfig<T>>,
2419 ) -> Result<(), DispatchError> {
2420 use frame_support::traits::{
2421 fungible::InspectHold,
2422 tokens::{Fortitude, Precision, Preservation, Restriction},
2423 };
2424
2425 if amount.is_zero() {
2426 return Ok(());
2427 }
2428
2429 let hold_reason = hold_reason.into();
2430 let result = if exec_config.map(|c| c.collect_deposit_from_hold.is_some()).unwrap_or(false)
2431 {
2432 T::Currency::release(&hold_reason, from, amount, Precision::Exact)
2433 .and_then(|amount| {
2434 T::Currency::withdraw(
2435 from,
2436 amount,
2437 Precision::Exact,
2438 Preservation::Preserve,
2439 Fortitude::Polite,
2440 )
2441 })
2442 .map(T::FeeInfo::deposit_txfee)
2443 } else {
2444 T::Currency::transfer_on_hold(
2445 &hold_reason,
2446 from,
2447 to,
2448 amount,
2449 Precision::Exact,
2450 Restriction::Free,
2451 Fortitude::Polite,
2452 )
2453 .map(|_| ())
2454 };
2455
2456 result.map_err(|_| {
2457 let available = T::Currency::balance_on_hold(&hold_reason, from);
2458 if available < amount {
2459 log::error!(
2462 target: LOG_TARGET,
2463 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. Not enough deposit: {:?}. This is a bug.",
2464 amount, from, to, available,
2465 );
2466 Error::<T>::StorageRefundNotEnoughFunds.into()
2467 } else {
2468 log::warn!(
2473 target: LOG_TARGET,
2474 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. First remove locks (staking, governance) from the contracts account.",
2475 amount, from, to,
2476 );
2477 Error::<T>::StorageRefundLocked.into()
2478 }
2479 })
2480 }
2481
2482 fn has_dust(value: U256) -> bool {
2484 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2485 }
2486
2487 fn has_balance(value: U256) -> bool {
2489 value >= U256::from(<T>::NativeToEthRatio::get())
2490 }
2491
2492 fn min_balance() -> BalanceOf<T> {
2494 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2495 }
2496
2497 fn deposit_event(event: Event<T>) {
2502 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2503 }
2504
2505 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2507 match <T as Config>::RuntimeOrigin::from(origin).into() {
2508 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2509 _ => Err(BadOrigin.into()),
2510 }
2511 }
2512
2513 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2517 if DebugSettings::bypass_eip_3607::<T>() {
2518 return Ok(());
2519 }
2520 let Some(address) = origin
2521 .as_system_ref()
2522 .and_then(|o| o.as_signed())
2523 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2524 else {
2525 return Ok(());
2526 };
2527 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2528 <AccountInfo<T>>::is_contract(&address)
2529 {
2530 log::debug!(
2531 target: crate::LOG_TARGET,
2532 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2533 );
2534 Err(DispatchError::BadOrigin)
2535 } else {
2536 Ok(())
2537 }
2538 }
2539}
2540
2541pub const RUNTIME_PALLETS_ADDR: H160 =
2546 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2547
2548environmental!(executing_contract: bool);
2550
2551sp_api::decl_runtime_apis! {
2552 #[api_version(1)]
2554 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2555 AccountId: Codec,
2556 Balance: Codec,
2557 Nonce: Codec,
2558 BlockNumber: Codec,
2559 Moment: Codec,
2560 {
2561 fn eth_block() -> EthBlock;
2565
2566 fn eth_block_hash(number: U256) -> Option<H256>;
2568
2569 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2575
2576 fn block_gas_limit() -> U256;
2578
2579 fn balance(address: H160) -> U256;
2581
2582 fn gas_price() -> U256;
2584
2585 fn nonce(address: H160) -> Nonce;
2587
2588 fn call(
2592 origin: AccountId,
2593 dest: H160,
2594 value: Balance,
2595 gas_limit: Option<Weight>,
2596 storage_deposit_limit: Option<Balance>,
2597 input_data: Vec<u8>,
2598 ) -> ContractResult<ExecReturnValue, Balance>;
2599
2600 fn instantiate(
2604 origin: AccountId,
2605 value: Balance,
2606 gas_limit: Option<Weight>,
2607 storage_deposit_limit: Option<Balance>,
2608 code: Code,
2609 data: Vec<u8>,
2610 salt: Option<[u8; 32]>,
2611 ) -> ContractResult<InstantiateReturnValue, Balance>;
2612
2613
2614 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2619
2620 fn eth_transact_with_config(
2624 tx: GenericTransaction,
2625 config: DryRunConfig<Moment>,
2626 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2627
2628 fn upload_code(
2632 origin: AccountId,
2633 code: Vec<u8>,
2634 storage_deposit_limit: Option<Balance>,
2635 ) -> CodeUploadResult<Balance>;
2636
2637 fn get_storage(
2643 address: H160,
2644 key: [u8; 32],
2645 ) -> GetStorageResult;
2646
2647 fn get_storage_var_key(
2653 address: H160,
2654 key: Vec<u8>,
2655 ) -> GetStorageResult;
2656
2657 fn trace_block(
2664 block: Block,
2665 config: TracerType
2666 ) -> Vec<(u32, Trace)>;
2667
2668 fn trace_tx(
2675 block: Block,
2676 tx_index: u32,
2677 config: TracerType
2678 ) -> Option<Trace>;
2679
2680 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2684
2685 fn block_author() -> H160;
2687
2688 fn address(account_id: AccountId) -> H160;
2690
2691 fn account_id(address: H160) -> AccountId;
2693
2694 fn runtime_pallets_address() -> H160;
2696
2697 fn code(address: H160) -> Vec<u8>;
2699
2700 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2702 }
2703}
2704
2705#[macro_export]
2719macro_rules! impl_runtime_apis_plus_revive_traits {
2720 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2721
2722 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2723
2724 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2725 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
2726 use $crate::pallet::Call as ReviveCall;
2727 match self {
2728 Self::$Revive(
2729 ReviveCall::eth_call{ weight_limit, .. } |
2730 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
2731 ) => {
2732 let old = *weight_limit;
2733 *weight_limit = new_weight_limit;
2734 old
2735 },
2736 _ => Weight::default(),
2737 }
2738 }
2739 }
2740
2741 impl_runtime_apis! {
2742 $($rest)*
2743
2744
2745 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2746 {
2747 fn eth_block() -> $crate::EthBlock {
2748 $crate::Pallet::<Self>::eth_block()
2749 }
2750
2751 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2752 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
2753 }
2754
2755 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2756 $crate::Pallet::<Self>::eth_receipt_data()
2757 }
2758
2759 fn balance(address: $crate::H160) -> $crate::U256 {
2760 $crate::Pallet::<Self>::evm_balance(&address)
2761 }
2762
2763 fn block_author() -> $crate::H160 {
2764 $crate::Pallet::<Self>::block_author()
2765 }
2766
2767 fn block_gas_limit() -> $crate::U256 {
2768 $crate::Pallet::<Self>::evm_block_gas_limit()
2769 }
2770
2771 fn gas_price() -> $crate::U256 {
2772 $crate::Pallet::<Self>::evm_base_fee()
2773 }
2774
2775 fn nonce(address: $crate::H160) -> Nonce {
2776 use $crate::AddressMapper;
2777 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2778 $crate::frame_system::Pallet::<Self>::account_nonce(account)
2779 }
2780
2781 fn address(account_id: AccountId) -> $crate::H160 {
2782 use $crate::AddressMapper;
2783 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2784 }
2785
2786 fn eth_transact(
2787 tx: $crate::evm::GenericTransaction,
2788 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2789 use $crate::{
2790 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2791 sp_runtime::traits::TransactionExtension,
2792 sp_runtime::traits::Block as BlockT
2793 };
2794 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
2795 }
2796
2797 fn eth_transact_with_config(
2798 tx: $crate::evm::GenericTransaction,
2799 config: $crate::DryRunConfig<__ReviveMacroMoment>,
2800 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2801 use $crate::{
2802 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2803 sp_runtime::traits::TransactionExtension,
2804 sp_runtime::traits::Block as BlockT
2805 };
2806 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
2807 }
2808
2809 fn call(
2810 origin: AccountId,
2811 dest: $crate::H160,
2812 value: Balance,
2813 weight_limit: Option<$crate::Weight>,
2814 storage_deposit_limit: Option<Balance>,
2815 input_data: Vec<u8>,
2816 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2817 use $crate::frame_support::traits::Get;
2818 let blockweights: $crate::BlockWeights =
2819 <Self as $crate::frame_system::Config>::BlockWeights::get();
2820
2821 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2822 $crate::Pallet::<Self>::bare_call(
2823 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2824 dest,
2825 $crate::Pallet::<Self>::convert_native_to_evm(value),
2826 $crate::TransactionLimits::WeightAndDeposit {
2827 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2828 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2829 },
2830 input_data,
2831 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2832 )
2833 }
2834
2835 fn instantiate(
2836 origin: AccountId,
2837 value: Balance,
2838 weight_limit: Option<$crate::Weight>,
2839 storage_deposit_limit: Option<Balance>,
2840 code: $crate::Code,
2841 data: Vec<u8>,
2842 salt: Option<[u8; 32]>,
2843 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2844 use $crate::frame_support::traits::Get;
2845 let blockweights: $crate::BlockWeights =
2846 <Self as $crate::frame_system::Config>::BlockWeights::get();
2847
2848 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2849 $crate::Pallet::<Self>::bare_instantiate(
2850 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2851 $crate::Pallet::<Self>::convert_native_to_evm(value),
2852 $crate::TransactionLimits::WeightAndDeposit {
2853 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2854 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2855 },
2856 code,
2857 data,
2858 salt,
2859 &$crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2860 )
2861 }
2862
2863 fn upload_code(
2864 origin: AccountId,
2865 code: Vec<u8>,
2866 storage_deposit_limit: Option<Balance>,
2867 ) -> $crate::CodeUploadResult<Balance> {
2868 let origin =
2869 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
2870 $crate::Pallet::<Self>::bare_upload_code(
2871 origin,
2872 code,
2873 storage_deposit_limit.unwrap_or(u128::MAX),
2874 )
2875 }
2876
2877 fn get_storage_var_key(
2878 address: $crate::H160,
2879 key: Vec<u8>,
2880 ) -> $crate::GetStorageResult {
2881 $crate::Pallet::<Self>::get_storage_var_key(address, key)
2882 }
2883
2884 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2885 $crate::Pallet::<Self>::get_storage(address, key)
2886 }
2887
2888 fn trace_block(
2889 block: Block,
2890 tracer_type: $crate::evm::TracerType,
2891 ) -> Vec<(u32, $crate::evm::Trace)> {
2892 use $crate::{sp_runtime::traits::Block, tracing::trace};
2893
2894 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
2895 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
2896 {
2897 return Default::default()
2898 }
2899
2900 let mut traces = vec![];
2901 let (header, extrinsics) = block.deconstruct();
2902 <$Executive>::initialize_block(&header);
2903 for (index, ext) in extrinsics.into_iter().enumerate() {
2904 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2905 let t = tracer.as_tracing();
2906 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2907
2908 if let Some(tx_trace) = tracer.collect_trace() {
2909 traces.push((index as u32, tx_trace));
2910 }
2911 }
2912
2913 traces
2914 }
2915
2916 fn trace_tx(
2917 block: Block,
2918 tx_index: u32,
2919 tracer_type: $crate::evm::TracerType,
2920 ) -> Option<$crate::evm::Trace> {
2921 use $crate::{sp_runtime::traits::Block, tracing::trace};
2922
2923 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
2924 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
2925 {
2926 return None
2927 }
2928
2929 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2930 let (header, extrinsics) = block.deconstruct();
2931
2932 <$Executive>::initialize_block(&header);
2933 for (index, ext) in extrinsics.into_iter().enumerate() {
2934 if index as u32 == tx_index {
2935 let t = tracer.as_tracing();
2936 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2937 break;
2938 } else {
2939 let _ = <$Executive>::apply_extrinsic(ext);
2940 }
2941 }
2942
2943 tracer.collect_trace()
2944 }
2945
2946 fn trace_call(
2947 tx: $crate::evm::GenericTransaction,
2948 tracer_type: $crate::evm::TracerType,
2949 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2950 use $crate::tracing::trace;
2951
2952 if matches!(tracer_type, $crate::evm::TracerType::ExecutionTracer(_)) &&
2953 !$crate::DebugSettings::is_execution_tracing_enabled::<Runtime>()
2954 {
2955 return Err($crate::EthTransactError::Message("Execution Tracing is disabled".into()))
2956 }
2957
2958 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2959 let t = tracer.as_tracing();
2960
2961 t.watch_address(&tx.from.unwrap_or_default());
2962 t.watch_address(&$crate::Pallet::<Self>::block_author());
2963 let result = trace(t, || Self::eth_transact(tx));
2964
2965 if let Some(trace) = tracer.collect_trace() {
2966 Ok(trace)
2967 } else if let Err(err) = result {
2968 Err(err)
2969 } else {
2970 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
2971 }
2972 }
2973
2974 fn runtime_pallets_address() -> $crate::H160 {
2975 $crate::RUNTIME_PALLETS_ADDR
2976 }
2977
2978 fn code(address: $crate::H160) -> Vec<u8> {
2979 $crate::Pallet::<Self>::code(&address)
2980 }
2981
2982 fn account_id(address: $crate::H160) -> AccountId {
2983 use $crate::AddressMapper;
2984 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
2985 }
2986
2987 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2988 $crate::Pallet::<Self>::new_balance_with_dust(balance)
2989 }
2990 }
2991 }
2992 };
2993}