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;
27mod call_builder;
28mod debug;
29mod exec;
30mod impl_fungibles;
31mod limits;
32mod metering;
33mod primitives;
34mod storage;
35#[cfg(test)]
36mod tests;
37mod transient_storage;
38mod vm;
39mod weightinfo_extension;
40
41pub mod evm;
42pub mod migrations;
43pub mod mock;
44pub mod precompiles;
45pub mod test_utils;
46pub mod tracing;
47pub mod weights;
48
49use crate::{
50 evm::{
51 block_hash::EthereumBlockBuilderIR, block_storage, fees::InfoT as FeeInfo,
52 runtime::SetWeightLimit, CallTracer, CreateCallMode, GenericTransaction, PrestateTracer,
53 Trace, Tracer, TracerType, TYPE_EIP1559,
54 },
55 exec::{AccountIdOf, ExecError, ReentrancyProtection, Stack as ExecStack},
56 storage::{AccountType, DeletionQueueManager},
57 tracing::if_tracing,
58 vm::{pvm::extract_code_and_data, CodeInfo, RuntimeCosts},
59 weightinfo_extension::OnFinalizeBlockParts,
60};
61use alloc::{boxed::Box, format, vec};
62use codec::{Codec, Decode, Encode};
63use environmental::*;
64use frame_support::{
65 dispatch::{
66 DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
67 Pays, PostDispatchInfo, RawOrigin,
68 },
69 ensure,
70 pallet_prelude::DispatchClass,
71 traits::{
72 fungible::{Balanced, Inspect, Mutate, MutateHold},
73 tokens::Balance,
74 ConstU32, ConstU64, EnsureOrigin, Get, IsSubType, IsType, OriginTrait,
75 },
76 weights::WeightMeter,
77 BoundedVec, RuntimeDebugNoBound,
78};
79use frame_system::{
80 ensure_signed,
81 pallet_prelude::{BlockNumberFor, OriginFor},
82 Pallet as System,
83};
84use scale_info::TypeInfo;
85use sp_runtime::{
86 traits::{
87 BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedFrom,
88 UniqueSaturatedInto, Zero,
89 },
90 AccountId32, DispatchError, FixedPointNumber, FixedU128, SaturatedConversion,
91};
92
93pub use crate::{
94 address::{
95 create1, create2, is_eth_derived, AccountId32Mapper, AddressMapper, TestAccountMapper,
96 },
97 debug::DebugSettings,
98 evm::{
99 block_hash::ReceiptGasInfo, Address as EthAddress, Block as EthBlock, DryRunConfig,
100 ReceiptInfo,
101 },
102 exec::{CallResources, DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
103 metering::{
104 EthTxInfo, FrameMeter, ResourceMeter, Token as WeightToken, TransactionLimits,
105 TransactionMeter,
106 },
107 pallet::{genesis, *},
108 storage::{AccountInfo, ContractInfo},
109 vm::{BytecodeType, ContractBlob},
110};
111pub use codec;
112pub use frame_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
113pub use frame_system::{self, limits::BlockWeights};
114pub use primitives::*;
115pub use sp_core::{keccak_256, H160, H256, U256};
116pub use sp_runtime;
117pub use weights::WeightInfo;
118
119#[cfg(doc)]
120pub use crate::vm::pvm::SyscallDoc;
121
122pub type BalanceOf<T> = <T as Config>::Balance;
123type TrieId = BoundedVec<u8, ConstU32<128>>;
124type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
125type CallOf<T> = <T as Config>::RuntimeCall;
126
127const SENTINEL: u32 = u32::MAX;
134
135const LOG_TARGET: &str = "runtime::revive";
141
142#[frame_support::pallet]
143pub mod pallet {
144 use super::*;
145 use frame_support::{pallet_prelude::*, traits::FindAuthor};
146 use frame_system::pallet_prelude::*;
147 use sp_core::U256;
148 use sp_runtime::Perbill;
149
150 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
152
153 #[pallet::pallet]
154 #[pallet::storage_version(STORAGE_VERSION)]
155 pub struct Pallet<T>(_);
156
157 #[pallet::config(with_default)]
158 pub trait Config: frame_system::Config {
159 type Time: Time<Moment: Into<U256>>;
161
162 #[pallet::no_default]
166 type Balance: Balance
167 + TryFrom<U256>
168 + Into<U256>
169 + Bounded
170 + UniqueSaturatedInto<u64>
171 + UniqueSaturatedFrom<u64>
172 + UniqueSaturatedInto<u128>;
173
174 #[pallet::no_default]
176 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
177 + Mutate<Self::AccountId>
178 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
179 + Balanced<Self::AccountId>;
180
181 #[pallet::no_default_bounds]
183 #[allow(deprecated)]
184 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
185
186 #[pallet::no_default_bounds]
188 type RuntimeCall: Parameter
189 + Dispatchable<
190 RuntimeOrigin = OriginFor<Self>,
191 Info = DispatchInfo,
192 PostInfo = PostDispatchInfo,
193 > + IsType<<Self as frame_system::Config>::RuntimeCall>
194 + From<Call<Self>>
195 + IsSubType<Call<Self>>
196 + GetDispatchInfo;
197
198 #[pallet::no_default_bounds]
200 type RuntimeOrigin: IsType<OriginFor<Self>>
201 + From<Origin<Self>>
202 + Into<Result<Origin<Self>, OriginFor<Self>>>;
203
204 #[pallet::no_default_bounds]
206 type RuntimeHoldReason: From<HoldReason>;
207
208 type WeightInfo: WeightInfo;
211
212 #[pallet::no_default_bounds]
216 #[allow(private_bounds)]
217 type Precompiles: precompiles::Precompiles<Self>;
218
219 type FindAuthor: FindAuthor<Self::AccountId>;
221
222 #[pallet::constant]
228 #[pallet::no_default_bounds]
229 type DepositPerByte: Get<BalanceOf<Self>>;
230
231 #[pallet::constant]
237 #[pallet::no_default_bounds]
238 type DepositPerItem: Get<BalanceOf<Self>>;
239
240 #[pallet::constant]
250 #[pallet::no_default_bounds]
251 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
252
253 #[pallet::constant]
257 type CodeHashLockupDepositPercent: Get<Perbill>;
258
259 #[pallet::no_default]
261 type AddressMapper: AddressMapper<Self>;
262
263 #[pallet::constant]
273 type UnsafeUnstableInterface: Get<bool>;
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 UnsafeUnstableInterface = ConstBool<true>;
442 type AllowEVMBytecode = ConstBool<true>;
443 type UploadOrigin = EnsureSigned<Self::AccountId>;
444 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
445 type WeightInfo = ();
446 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
447 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
448 type ChainId = ConstU64<42>;
449 type NativeToEthRatio = ConstU32<1_000_000>;
450 type FindAuthor = ();
451 type FeeInfo = ();
452 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
453 type DebugEnabled = ConstBool<false>;
454 type GasScale = GasScale;
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 #[cfg(feature = "runtime-benchmarks")]
628 BenchmarkingError = 0xFF,
629 }
630
631 #[pallet::composite_enum]
633 pub enum HoldReason {
634 CodeUploadDepositReserve,
636 StorageDepositReserve,
638 AddressMapping,
640 }
641
642 #[derive(
643 PartialEq,
644 Eq,
645 Clone,
646 MaxEncodedLen,
647 Encode,
648 Decode,
649 DecodeWithMemTracking,
650 TypeInfo,
651 RuntimeDebug,
652 )]
653 #[pallet::origin]
654 pub enum Origin<T: Config> {
655 EthTransaction(T::AccountId),
656 }
657
658 #[pallet::storage]
662 #[pallet::unbounded]
663 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
664
665 #[pallet::storage]
667 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
668
669 #[pallet::storage]
671 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
672
673 #[pallet::storage]
675 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
676
677 #[pallet::storage]
682 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
683
684 #[pallet::storage]
687 pub(crate) type DeletionQueueCounter<T: Config> =
688 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
689
690 #[pallet::storage]
697 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
698
699 #[pallet::storage]
709 #[pallet::unbounded]
710 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
711
712 #[pallet::storage]
716 pub(crate) type BlockHash<T: Config> =
717 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
718
719 #[pallet::storage]
726 #[pallet::unbounded]
727 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
728
729 #[pallet::storage]
731 #[pallet::unbounded]
732 pub(crate) type EthBlockBuilderIR<T: Config> =
733 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
734
735 #[pallet::storage]
740 #[pallet::unbounded]
741 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
742 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
743
744 #[pallet::storage]
746 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
747
748 pub mod genesis {
749 use super::*;
750 use crate::evm::Bytes32;
751
752 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
754 pub struct ContractData {
755 pub code: Vec<u8>,
757 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
759 }
760
761 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
763 pub struct Account<T: Config> {
764 pub address: H160,
766 #[serde(default)]
768 pub balance: U256,
769 #[serde(default)]
771 pub nonce: T::Nonce,
772 #[serde(flatten, skip_serializing_if = "Option::is_none")]
774 pub contract_data: Option<ContractData>,
775 }
776 }
777
778 #[pallet::genesis_config]
779 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
780 pub struct GenesisConfig<T: Config> {
781 #[serde(default, skip_serializing_if = "Vec::is_empty")]
784 pub mapped_accounts: Vec<T::AccountId>,
785
786 #[serde(default, skip_serializing_if = "Vec::is_empty")]
788 pub accounts: Vec<genesis::Account<T>>,
789
790 #[serde(default, skip_serializing_if = "Option::is_none")]
792 pub debug_settings: Option<DebugSettings>,
793 }
794
795 #[pallet::genesis_build]
796 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
797 fn build(&self) {
798 use crate::{exec::Key, vm::ContractBlob};
799 use frame_support::traits::fungible::Mutate;
800
801 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
802 let _ = T::Currency::mint_into(
803 &Pallet::<T>::account_id(),
804 T::Currency::minimum_balance(),
805 );
806 }
807
808 for id in &self.mapped_accounts {
809 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
810 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
811 }
812 }
813
814 let owner = Pallet::<T>::account_id();
815
816 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
817 let account_id = T::AddressMapper::to_account_id(address);
818
819 if !System::<T>::account_exists(&account_id) {
820 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
821 }
822
823 frame_system::Account::<T>::mutate(&account_id, |info| {
824 info.nonce = (*nonce).into();
825 });
826
827 match contract_data {
828 None => {
829 AccountInfoOf::<T>::insert(
830 address,
831 AccountInfo { account_type: AccountType::EOA, dust: 0 },
832 );
833 },
834 Some(genesis::ContractData { code, storage }) => {
835 let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
836 ContractBlob::<T>::from_pvm_code( code.clone(), owner.clone()).inspect_err(|err| {
837 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
838 })
839 } else {
840 ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
841 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
842 })
843 };
844
845 let Ok(blob) = blob else {
846 continue;
847 };
848
849 let code_hash = *blob.code_hash();
850 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
851 .inspect_err(|err| {
852 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
853 })
854 else {
855 continue;
856 };
857
858 AccountInfoOf::<T>::insert(
859 address,
860 AccountInfo { account_type: info.clone().into(), dust: 0 },
861 );
862
863 <PristineCode<T>>::insert(blob.code_hash(), code);
864 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
865 for (k, v) in storage {
866 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
867 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
868 });
869 }
870 },
871 }
872
873 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
874 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
875 });
876 }
877
878 block_storage::on_finalize_build_eth_block::<T>(
880 frame_system::Pallet::<T>::block_number(),
883 );
884
885 if let Some(settings) = self.debug_settings.as_ref() {
887 settings.write_to_storage::<T>()
888 }
889 }
890 }
891
892 #[pallet::hooks]
893 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
894 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
895 let mut meter = WeightMeter::with_limit(limit);
896 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
897 meter.consumed()
898 }
899
900 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
901 block_storage::on_initialize::<T>();
903
904 System::<T>::account_exists(&Pallet::<T>::account_id());
906 <T as Config>::WeightInfo::on_finalize_block_fixed()
908 }
909
910 fn on_finalize(block_number: BlockNumberFor<T>) {
911 block_storage::on_finalize_build_eth_block::<T>(block_number);
913 }
914
915 fn integrity_test() {
916 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
917
918 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
919
920 T::FeeInfo::integrity_test();
921
922 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
924
925 const TOTAL_MEMORY_DEVIDER: u64 = 2;
928
929 let max_block_weight = T::BlockWeights::get()
935 .get(DispatchClass::Normal)
936 .max_total
937 .unwrap_or_else(|| T::BlockWeights::get().max_block);
938 let max_key_size: u64 =
939 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
940 .expect("Key of maximal size shall be created")
941 .hash()
942 .len()
943 .try_into()
944 .unwrap();
945
946 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
947 let max_immutable_size: u64 = max_block_weight
948 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
949 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
950 ))
951 .unwrap()
952 .saturating_mul(
953 u64::from(limits::IMMUTABLE_BYTES)
954 .saturating_add(max_immutable_key_size)
955 .into(),
956 );
957
958 let max_pvf_mem: u64 = T::PVFMemory::get().into();
959 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
960
961 let max_events_size = max_block_weight
965 .checked_div_per_component(
966 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
967 num_topic: 0,
968 len: limits::EVENT_BYTES,
969 })
970 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
971 &RuntimeCosts::HostFn,
972 ))),
973 )
974 .unwrap()
975 .saturating_mul(limits::EVENT_BYTES.into());
976
977 assert!(
978 max_events_size <= storage_size_limit,
979 "Maximal events size {} exceeds the events limit {}",
980 max_events_size,
981 storage_size_limit
982 );
983
984 let max_eth_block_builder_bytes =
1019 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1020
1021 log::debug!(
1022 target: LOG_TARGET,
1023 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1024 max_eth_block_builder_bytes / 1024,
1025 max_events_size / 1024,
1026 );
1027
1028 let memory_left = i128::from(max_runtime_mem)
1033 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1034 .saturating_sub(limits::MEMORY_REQUIRED.into())
1035 .saturating_sub(max_eth_block_builder_bytes.into());
1036
1037 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1038
1039 assert!(
1040 memory_left >= 0,
1041 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1042 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1043 );
1044
1045 let max_storage_size = max_block_weight
1048 .checked_div_per_component(
1049 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1050 new_bytes: limits::STORAGE_BYTES,
1051 old_bytes: 0,
1052 })
1053 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1054 )
1055 .unwrap()
1056 .saturating_add(max_immutable_size.into())
1057 .saturating_add(max_eth_block_builder_bytes.into());
1058
1059 assert!(
1060 max_storage_size <= storage_size_limit,
1061 "Maximal storage size {} exceeds the storage limit {}",
1062 max_storage_size,
1063 storage_size_limit
1064 );
1065 }
1066 }
1067
1068 #[pallet::call]
1069 impl<T: Config> Pallet<T> {
1070 #[allow(unused_variables)]
1083 #[pallet::call_index(0)]
1084 #[pallet::weight(Weight::MAX)]
1085 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1086 Err(frame_system::Error::CallFiltered::<T>.into())
1087 }
1088
1089 #[pallet::call_index(1)]
1106 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1107 pub fn call(
1108 origin: OriginFor<T>,
1109 dest: H160,
1110 #[pallet::compact] value: BalanceOf<T>,
1111 weight_limit: Weight,
1112 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1113 data: Vec<u8>,
1114 ) -> DispatchResultWithPostInfo {
1115 Self::ensure_non_contract_if_signed(&origin)?;
1116 let mut output = Self::bare_call(
1117 origin,
1118 dest,
1119 Pallet::<T>::convert_native_to_evm(value),
1120 TransactionLimits::WeightAndDeposit {
1121 weight_limit,
1122 deposit_limit: storage_deposit_limit,
1123 },
1124 data,
1125 ExecConfig::new_substrate_tx(),
1126 );
1127
1128 if let Ok(return_value) = &output.result {
1129 if return_value.did_revert() {
1130 output.result = Err(<Error<T>>::ContractReverted.into());
1131 }
1132 }
1133 dispatch_result(
1134 output.result,
1135 output.weight_consumed,
1136 <T as Config>::WeightInfo::call(),
1137 )
1138 }
1139
1140 #[pallet::call_index(2)]
1146 #[pallet::weight(
1147 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1148 )]
1149 pub fn instantiate(
1150 origin: OriginFor<T>,
1151 #[pallet::compact] value: BalanceOf<T>,
1152 weight_limit: Weight,
1153 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1154 code_hash: sp_core::H256,
1155 data: Vec<u8>,
1156 salt: Option<[u8; 32]>,
1157 ) -> DispatchResultWithPostInfo {
1158 Self::ensure_non_contract_if_signed(&origin)?;
1159 let data_len = data.len() as u32;
1160 let mut output = Self::bare_instantiate(
1161 origin,
1162 Pallet::<T>::convert_native_to_evm(value),
1163 TransactionLimits::WeightAndDeposit {
1164 weight_limit,
1165 deposit_limit: storage_deposit_limit,
1166 },
1167 Code::Existing(code_hash),
1168 data,
1169 salt,
1170 ExecConfig::new_substrate_tx(),
1171 );
1172 if let Ok(retval) = &output.result {
1173 if retval.result.did_revert() {
1174 output.result = Err(<Error<T>>::ContractReverted.into());
1175 }
1176 }
1177 dispatch_result(
1178 output.result.map(|result| result.result),
1179 output.weight_consumed,
1180 <T as Config>::WeightInfo::instantiate(data_len),
1181 )
1182 }
1183
1184 #[pallet::call_index(3)]
1212 #[pallet::weight(
1213 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1214 .saturating_add(*weight_limit)
1215 )]
1216 pub fn instantiate_with_code(
1217 origin: OriginFor<T>,
1218 #[pallet::compact] value: BalanceOf<T>,
1219 weight_limit: Weight,
1220 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1221 code: Vec<u8>,
1222 data: Vec<u8>,
1223 salt: Option<[u8; 32]>,
1224 ) -> DispatchResultWithPostInfo {
1225 Self::ensure_non_contract_if_signed(&origin)?;
1226 let code_len = code.len() as u32;
1227 let data_len = data.len() as u32;
1228 let mut output = Self::bare_instantiate(
1229 origin,
1230 Pallet::<T>::convert_native_to_evm(value),
1231 TransactionLimits::WeightAndDeposit {
1232 weight_limit,
1233 deposit_limit: storage_deposit_limit,
1234 },
1235 Code::Upload(code),
1236 data,
1237 salt,
1238 ExecConfig::new_substrate_tx(),
1239 );
1240 if let Ok(retval) = &output.result {
1241 if retval.result.did_revert() {
1242 output.result = Err(<Error<T>>::ContractReverted.into());
1243 }
1244 }
1245 dispatch_result(
1246 output.result.map(|result| result.result),
1247 output.weight_consumed,
1248 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1249 )
1250 }
1251
1252 #[pallet::call_index(10)]
1274 #[pallet::weight(
1275 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1276 .saturating_add(*weight_limit)
1277 )]
1278 pub fn eth_instantiate_with_code(
1279 origin: OriginFor<T>,
1280 value: U256,
1281 weight_limit: Weight,
1282 eth_gas_limit: U256,
1283 code: Vec<u8>,
1284 data: Vec<u8>,
1285 transaction_encoded: Vec<u8>,
1286 effective_gas_price: U256,
1287 encoded_len: u32,
1288 ) -> DispatchResultWithPostInfo {
1289 let signer = Self::ensure_eth_signed(origin)?;
1290 let origin = OriginFor::<T>::signed(signer.clone());
1291 Self::ensure_non_contract_if_signed(&origin)?;
1292 let mut call = Call::<T>::eth_instantiate_with_code {
1293 value,
1294 weight_limit,
1295 eth_gas_limit,
1296 code: code.clone(),
1297 data: data.clone(),
1298 transaction_encoded: transaction_encoded.clone(),
1299 effective_gas_price,
1300 encoded_len,
1301 }
1302 .into();
1303 let info = T::FeeInfo::dispatch_info(&call);
1304 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1305 drop(call);
1306
1307 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1308 let extra_weight = base_info.total_weight();
1309 let output = Self::bare_instantiate(
1310 origin,
1311 value,
1312 TransactionLimits::EthereumGas {
1313 eth_gas_limit: eth_gas_limit.saturated_into(),
1314 maybe_weight_limit: Some(weight_limit),
1315 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1316 },
1317 Code::Upload(code),
1318 data,
1319 None,
1320 ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1321 );
1322
1323 block_storage::EthereumCallResult::new::<T>(
1324 signer,
1325 output.map_result(|r| r.result),
1326 base_info.call_weight,
1327 encoded_len,
1328 &info,
1329 effective_gas_price,
1330 )
1331 })
1332 }
1333
1334 #[pallet::call_index(11)]
1351 #[pallet::weight(
1352 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1353 .saturating_add(*weight_limit)
1354 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1355 )]
1356 pub fn eth_call(
1357 origin: OriginFor<T>,
1358 dest: H160,
1359 value: U256,
1360 weight_limit: Weight,
1361 eth_gas_limit: U256,
1362 data: Vec<u8>,
1363 transaction_encoded: Vec<u8>,
1364 effective_gas_price: U256,
1365 encoded_len: u32,
1366 ) -> DispatchResultWithPostInfo {
1367 let signer = Self::ensure_eth_signed(origin)?;
1368 let origin = OriginFor::<T>::signed(signer.clone());
1369
1370 Self::ensure_non_contract_if_signed(&origin)?;
1371 let mut call = Call::<T>::eth_call {
1372 dest,
1373 value,
1374 weight_limit,
1375 eth_gas_limit,
1376 data: data.clone(),
1377 transaction_encoded: transaction_encoded.clone(),
1378 effective_gas_price,
1379 encoded_len,
1380 }
1381 .into();
1382 let info = T::FeeInfo::dispatch_info(&call);
1383 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1384 drop(call);
1385
1386 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1387 let extra_weight = base_info.total_weight();
1388 let output = Self::bare_call(
1389 origin,
1390 dest,
1391 value,
1392 TransactionLimits::EthereumGas {
1393 eth_gas_limit: eth_gas_limit.saturated_into(),
1394 maybe_weight_limit: Some(weight_limit),
1395 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1396 },
1397 data,
1398 ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1399 );
1400
1401 block_storage::EthereumCallResult::new::<T>(
1402 signer,
1403 output,
1404 base_info.call_weight,
1405 encoded_len,
1406 &info,
1407 effective_gas_price,
1408 )
1409 })
1410 }
1411
1412 #[pallet::call_index(12)]
1423 #[pallet::weight(T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1424 pub fn eth_substrate_call(
1425 origin: OriginFor<T>,
1426 call: Box<<T as Config>::RuntimeCall>,
1427 transaction_encoded: Vec<u8>,
1428 ) -> DispatchResultWithPostInfo {
1429 let signer = Self::ensure_eth_signed(origin)?;
1432 let weight_overhead =
1433 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32);
1434
1435 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1436 let call_weight = call.get_dispatch_info().call_weight;
1437 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1438
1439 match &mut call_result {
1441 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1442 post_info.actual_weight = Some(
1443 post_info
1444 .actual_weight
1445 .unwrap_or_else(|| call_weight)
1446 .saturating_add(weight_overhead),
1447 );
1448 },
1449 }
1450
1451 block_storage::EthereumCallResult {
1454 receipt_gas_info: ReceiptGasInfo::default(),
1455 result: call_result,
1456 }
1457 })
1458 }
1459
1460 #[pallet::call_index(4)]
1475 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1476 pub fn upload_code(
1477 origin: OriginFor<T>,
1478 code: Vec<u8>,
1479 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1480 ) -> DispatchResult {
1481 Self::ensure_non_contract_if_signed(&origin)?;
1482 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1483 }
1484
1485 #[pallet::call_index(5)]
1490 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1491 pub fn remove_code(
1492 origin: OriginFor<T>,
1493 code_hash: sp_core::H256,
1494 ) -> DispatchResultWithPostInfo {
1495 let origin = ensure_signed(origin)?;
1496 <ContractBlob<T>>::remove(&origin, code_hash)?;
1497 Ok(Pays::No.into())
1499 }
1500
1501 #[pallet::call_index(6)]
1512 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1513 pub fn set_code(
1514 origin: OriginFor<T>,
1515 dest: H160,
1516 code_hash: sp_core::H256,
1517 ) -> DispatchResult {
1518 ensure_root(origin)?;
1519 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1520 let Some(account) = account else {
1521 return Err(<Error<T>>::ContractNotFound.into());
1522 };
1523
1524 let AccountType::Contract(ref mut contract) = account.account_type else {
1525 return Err(<Error<T>>::ContractNotFound.into());
1526 };
1527
1528 <CodeInfo<T>>::increment_refcount(code_hash)?;
1529 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1530 contract.code_hash = code_hash;
1531
1532 Ok(())
1533 })
1534 }
1535
1536 #[pallet::call_index(7)]
1541 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1542 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1543 Self::ensure_non_contract_if_signed(&origin)?;
1544 let origin = ensure_signed(origin)?;
1545 T::AddressMapper::map(&origin)
1546 }
1547
1548 #[pallet::call_index(8)]
1553 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1554 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1555 let origin = ensure_signed(origin)?;
1556 T::AddressMapper::unmap(&origin)
1557 }
1558
1559 #[pallet::call_index(9)]
1565 #[pallet::weight({
1566 let dispatch_info = call.get_dispatch_info();
1567 (
1568 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1569 dispatch_info.class
1570 )
1571 })]
1572 pub fn dispatch_as_fallback_account(
1573 origin: OriginFor<T>,
1574 call: Box<<T as Config>::RuntimeCall>,
1575 ) -> DispatchResultWithPostInfo {
1576 Self::ensure_non_contract_if_signed(&origin)?;
1577 let origin = ensure_signed(origin)?;
1578 let unmapped_account =
1579 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1580 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1581 }
1582 }
1583}
1584
1585fn dispatch_result<R>(
1587 result: Result<R, DispatchError>,
1588 weight_consumed: Weight,
1589 base_weight: Weight,
1590) -> DispatchResultWithPostInfo {
1591 let post_info = PostDispatchInfo {
1592 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1593 pays_fee: Default::default(),
1594 };
1595
1596 result
1597 .map(|_| post_info)
1598 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1599}
1600
1601impl<T: Config> Pallet<T> {
1602 pub fn bare_call(
1609 origin: OriginFor<T>,
1610 dest: H160,
1611 evm_value: U256,
1612 transaction_limits: TransactionLimits<T>,
1613 data: Vec<u8>,
1614 exec_config: ExecConfig<T>,
1615 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1616 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1617 Ok(transaction_meter) => transaction_meter,
1618 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1619 };
1620
1621 let mut storage_deposit = Default::default();
1622
1623 let try_call = || {
1624 let origin = ExecOrigin::from_runtime_origin(origin)?;
1625 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1626 origin.clone(),
1627 dest,
1628 &mut transaction_meter,
1629 evm_value,
1630 data,
1631 &exec_config,
1632 )?;
1633
1634 storage_deposit = transaction_meter
1635 .execute_postponed_deposits(&origin, &exec_config)
1636 .inspect_err(|err| {
1637 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1638 })?;
1639
1640 Ok(result)
1641 };
1642 let result = Self::run_guarded(try_call);
1643
1644 log::trace!(target: LOG_TARGET, "Bare call ends: \
1645 result={result:?}, \
1646 weight_consumed={:?}, \
1647 weight_required={:?}, \
1648 storage_deposit={:?}, \
1649 gas_consumed={:?}, \
1650 max_storage_deposit={:?}",
1651 transaction_meter.weight_consumed(),
1652 transaction_meter.weight_required(),
1653 storage_deposit,
1654 transaction_meter.total_consumed_gas(),
1655 transaction_meter.deposit_required()
1656 );
1657
1658 ContractResult {
1659 result: result.map_err(|r| r.error),
1660 weight_consumed: transaction_meter.weight_consumed(),
1661 weight_required: transaction_meter.weight_required(),
1662 storage_deposit,
1663 gas_consumed: transaction_meter.total_consumed_gas(),
1664 max_storage_deposit: transaction_meter.deposit_required(),
1665 }
1666 }
1667
1668 pub fn prepare_dry_run(account: &T::AccountId) {
1674 frame_system::Pallet::<T>::inc_account_nonce(account);
1677 }
1678
1679 pub fn bare_instantiate(
1685 origin: OriginFor<T>,
1686 evm_value: U256,
1687 transaction_limits: TransactionLimits<T>,
1688 code: Code,
1689 data: Vec<u8>,
1690 salt: Option<[u8; 32]>,
1691 exec_config: ExecConfig<T>,
1692 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1693 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1694 Ok(transaction_meter) => transaction_meter,
1695 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1696 };
1697
1698 let mut storage_deposit = Default::default();
1699
1700 let try_instantiate = || {
1701 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1702
1703 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1704 let executable = match code {
1705 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1706 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1707 let executable = Self::try_upload_code(
1708 upload_account,
1709 code,
1710 BytecodeType::Pvm,
1711 &mut transaction_meter,
1712 &exec_config,
1713 )?;
1714 executable
1715 },
1716 Code::Upload(code) =>
1717 if T::AllowEVMBytecode::get() {
1718 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1719 let origin = T::UploadOrigin::ensure_origin(origin)?;
1720 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1721 executable
1722 } else {
1723 return Err(<Error<T>>::CodeRejected.into())
1724 },
1725 Code::Existing(code_hash) => {
1726 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1727 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1728 executable
1729 },
1730 };
1731 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1732 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1733 instantiate_account,
1734 executable,
1735 &mut transaction_meter,
1736 evm_value,
1737 data,
1738 salt.as_ref(),
1739 &exec_config,
1740 );
1741
1742 storage_deposit = transaction_meter
1743 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1744 .inspect_err(|err| {
1745 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1746 })?;
1747 result
1748 };
1749 let output = Self::run_guarded(try_instantiate);
1750
1751 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1752 weight_required={:?} \
1753 storage_deposit={:?} \
1754 gas_consumed={:?} \
1755 max_storage_deposit={:?}",
1756 transaction_meter.weight_consumed(),
1757 transaction_meter.weight_required(),
1758 storage_deposit,
1759 transaction_meter.total_consumed_gas(),
1760 transaction_meter.deposit_required()
1761 );
1762
1763 ContractResult {
1764 result: output
1765 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1766 .map_err(|e| e.error),
1767 weight_consumed: transaction_meter.weight_consumed(),
1768 weight_required: transaction_meter.weight_required(),
1769 storage_deposit,
1770 gas_consumed: transaction_meter.total_consumed_gas(),
1771 max_storage_deposit: transaction_meter.deposit_required(),
1772 }
1773 }
1774
1775 pub fn dry_run_eth_transact(
1781 mut tx: GenericTransaction,
1782 dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1783 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1784 where
1785 T::Nonce: Into<U256>,
1786 CallOf<T>: SetWeightLimit,
1787 {
1788 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1789
1790 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1791 Self::prepare_dry_run(&origin);
1792
1793 let base_fee = Self::evm_base_fee();
1794 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1795
1796 if effective_gas_price < base_fee {
1797 Err(EthTransactError::Message(format!(
1798 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1799 )))?;
1800 }
1801
1802 if tx.nonce.is_none() {
1803 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1804 }
1805 if tx.chain_id.is_none() {
1806 tx.chain_id = Some(T::ChainId::get().into());
1807 }
1808
1809 tx.gas_price = Some(effective_gas_price);
1811 tx.max_priority_fee_per_gas = Some(0.into());
1814 if tx.max_fee_per_gas.is_none() {
1815 tx.max_fee_per_gas = Some(effective_gas_price);
1816 }
1817
1818 let gas = tx.gas;
1819 if tx.gas.is_none() {
1820 tx.gas = Some(Self::evm_block_gas_limit());
1821 }
1822 if tx.r#type.is_none() {
1823 tx.r#type = Some(TYPE_EIP1559.into());
1824 }
1825
1826 let value = tx.value.unwrap_or_default();
1828 let input = tx.input.clone().to_vec();
1829 let from = tx.from;
1830 let to = tx.to;
1831
1832 let mut call_info = tx
1835 .into_call::<T>(CreateCallMode::DryRun)
1836 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1837
1838 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1842 let base_weight = base_info.total_weight();
1843 let exec_config =
1844 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
1845 .with_dry_run(dry_run_config);
1846
1847 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
1849 if let Some(from) = &from {
1850 let fees = if gas.is_some() { fees } else { Zero::zero() };
1851 let balance = Self::evm_balance(from);
1852 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
1853 return Err(EthTransactError::Message(format!(
1854 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
1855 )));
1856 }
1857 }
1858
1859 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
1862
1863 let extract_error = |err| {
1864 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
1865 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
1866 } else {
1867 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
1868 }
1869 };
1870
1871 let transaction_limits = TransactionLimits::EthereumGas {
1872 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
1873 maybe_weight_limit: None,
1876 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
1877 };
1878
1879 let mut dry_run = match to {
1881 Some(dest) => {
1883 if dest == RUNTIME_PALLETS_ADDR {
1884 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
1885 return Err(EthTransactError::Message(format!(
1886 "Failed to decode pallet-call {input:?}"
1887 )));
1888 };
1889
1890 if let Err(result) =
1891 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1892 {
1893 return Err(EthTransactError::Message(format!(
1894 "Failed to dispatch call: {:?}",
1895 result.error,
1896 )));
1897 };
1898
1899 Default::default()
1900 } else {
1901 let result = crate::Pallet::<T>::bare_call(
1903 OriginFor::<T>::signed(origin),
1904 dest,
1905 value,
1906 transaction_limits,
1907 input.clone(),
1908 exec_config,
1909 );
1910
1911 let data = match result.result {
1912 Ok(return_value) => {
1913 if return_value.did_revert() {
1914 return Err(EthTransactError::Data(return_value.data));
1915 }
1916 return_value.data
1917 },
1918 Err(err) => {
1919 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1920 return extract_error(err);
1921 },
1922 };
1923
1924 EthTransactInfo {
1925 weight_required: result.weight_required,
1926 storage_deposit: result.storage_deposit.charge_or_zero(),
1927 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1928 data,
1929 eth_gas: Default::default(),
1930 }
1931 }
1932 },
1933 None => {
1935 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1937 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1938 } else {
1939 (input, vec![])
1940 };
1941
1942 let result = crate::Pallet::<T>::bare_instantiate(
1944 OriginFor::<T>::signed(origin),
1945 value,
1946 transaction_limits,
1947 Code::Upload(code.clone()),
1948 data.clone(),
1949 None,
1950 exec_config,
1951 );
1952
1953 let returned_data = match result.result {
1954 Ok(return_value) => {
1955 if return_value.result.did_revert() {
1956 return Err(EthTransactError::Data(return_value.result.data));
1957 }
1958 return_value.result.data
1959 },
1960 Err(err) => {
1961 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1962 return extract_error(err);
1963 },
1964 };
1965
1966 EthTransactInfo {
1967 weight_required: result.weight_required,
1968 storage_deposit: result.storage_deposit.charge_or_zero(),
1969 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1970 data: returned_data,
1971 eth_gas: Default::default(),
1972 }
1973 },
1974 };
1975
1976 call_info.call.set_weight_limit(dry_run.weight_required);
1978
1979 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
1981 let max_weight = Self::evm_max_extrinsic_weight();
1982 if total_weight.any_gt(max_weight) {
1983 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
1984 total_weight={total_weight:?} \
1985 max_weight={max_weight:?}",
1986 );
1987
1988 Err(EthTransactError::Message(format!(
1989 "\
1990 The transaction consumes more than the allowed weight. \
1991 needed={total_weight} \
1992 allowed={max_weight} \
1993 overweight_by={}\
1994 ",
1995 total_weight.saturating_sub(max_weight),
1996 )))?;
1997 }
1998
1999 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
2001 let available_fee = T::FeeInfo::remaining_txfee();
2002 if transaction_fee > available_fee {
2003 Err(EthTransactError::Message(format!(
2004 "Not enough gas supplied: Off by: {:?}",
2005 transaction_fee.saturating_sub(available_fee),
2006 )))?;
2007 }
2008
2009 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2010 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2011 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2012 if !rest.is_zero() {
2013 eth_gas = eth_gas.saturating_add(1_u32.into());
2014 }
2015
2016 log::debug!(target: LOG_TARGET, "\
2017 dry_run_eth_transact finished: \
2018 weight_limit={}, \
2019 total_weight={total_weight}, \
2020 max_weight={max_weight}, \
2021 weight_left={}, \
2022 eth_gas={eth_gas}, \
2023 encoded_len={}, \
2024 tx_fee={transaction_fee:?}, \
2025 storage_deposit={:?}, \
2026 max_storage_deposit={:?}\
2027 ",
2028 dry_run.weight_required,
2029 max_weight.saturating_sub(total_weight),
2030 call_info.encoded_len,
2031 dry_run.storage_deposit,
2032 dry_run.max_storage_deposit,
2033
2034 );
2035 dry_run.eth_gas = eth_gas;
2036 Ok(dry_run)
2037 }
2038
2039 pub fn evm_balance(address: &H160) -> U256 {
2043 let balance = AccountInfo::<T>::balance_of((*address).into());
2044 Self::convert_native_to_evm(balance)
2045 }
2046
2047 pub fn eth_block() -> EthBlock {
2049 EthereumBlock::<T>::get()
2050 }
2051
2052 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2059 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2060 let hash = <BlockHash<T>>::get(number);
2061 if hash == H256::zero() {
2062 None
2063 } else {
2064 Some(hash)
2065 }
2066 }
2067
2068 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2070 ReceiptInfoData::<T>::get()
2071 }
2072
2073 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2079 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2080 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2081 let account_id = T::AddressMapper::to_account_id(&address);
2082 T::Currency::set_balance(&account_id, balance);
2083 AccountInfoOf::<T>::mutate(&address, |account| {
2084 if let Some(account) = account {
2085 account.dust = dust;
2086 } else {
2087 *account = Some(AccountInfo { dust, ..Default::default() });
2088 }
2089 });
2090
2091 Ok(())
2092 }
2093
2094 pub fn new_balance_with_dust(
2098 evm_value: U256,
2099 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2100 let ed = T::Currency::minimum_balance();
2101 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2102 let (value, dust) = balance_with_dust.deconstruct();
2103
2104 Ok((ed.saturating_add(value), dust))
2105 }
2106
2107 pub fn evm_nonce(address: &H160) -> u32
2109 where
2110 T::Nonce: Into<u32>,
2111 {
2112 let account = T::AddressMapper::to_account_id(&address);
2113 System::<T>::account_nonce(account).into()
2114 }
2115
2116 pub fn evm_block_gas_limit() -> U256 {
2118 u64::MAX.into()
2125 }
2126
2127 pub fn evm_max_extrinsic_weight() -> Weight {
2129 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2130 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2131 .get(DispatchClass::Normal)
2132 .max_extrinsic
2133 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2134 Weight::from_parts(
2135 factor.saturating_mul_int(max_weight.ref_time()),
2136 factor.saturating_mul_int(max_weight.proof_size()),
2137 )
2138 }
2139
2140 pub fn evm_base_fee() -> U256 {
2142 let gas_scale = <T as Config>::GasScale::get();
2143 let multiplier = T::FeeInfo::next_fee_multiplier();
2144 multiplier
2145 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2146 .saturating_mul(gas_scale.saturated_into())
2147 .into()
2148 }
2149
2150 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2152 where
2153 T::Nonce: Into<u32>,
2154 {
2155 match tracer_type {
2156 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2157 TracerType::PrestateTracer(config) =>
2158 PrestateTracer::new(config.unwrap_or_default()).into(),
2159 }
2160 }
2161
2162 pub fn bare_upload_code(
2166 origin: OriginFor<T>,
2167 code: Vec<u8>,
2168 storage_deposit_limit: BalanceOf<T>,
2169 ) -> CodeUploadResult<BalanceOf<T>> {
2170 let origin = T::UploadOrigin::ensure_origin(origin)?;
2171
2172 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2173 BytecodeType::Pvm
2174 } else {
2175 if !T::AllowEVMBytecode::get() {
2176 return Err(<Error<T>>::CodeRejected.into())
2177 }
2178 BytecodeType::Evm
2179 };
2180
2181 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2182 weight_limit: Default::default(),
2183 deposit_limit: storage_deposit_limit,
2184 })?;
2185
2186 let module = Self::try_upload_code(
2187 origin,
2188 code,
2189 bytecode_type,
2190 &mut meter,
2191 &ExecConfig::new_substrate_tx(),
2192 )?;
2193 Ok(CodeUploadReturnValue {
2194 code_hash: *module.code_hash(),
2195 deposit: meter.deposit_consumed().charge_or_zero(),
2196 })
2197 }
2198
2199 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2201 let contract_info =
2202 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2203
2204 let maybe_value = contract_info.read(&Key::from_fixed(key));
2205 Ok(maybe_value)
2206 }
2207
2208 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2212 let immutable_data = <ImmutableDataOf<T>>::get(address);
2213 immutable_data
2214 }
2215
2216 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2224 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2225 <ImmutableDataOf<T>>::insert(address, data);
2226 Ok(())
2227 }
2228
2229 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2231 let contract_info =
2232 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2233
2234 let maybe_value = contract_info.read(
2235 &Key::try_from_var(key)
2236 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2237 .into(),
2238 );
2239 Ok(maybe_value)
2240 }
2241
2242 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2244 let (value, dust) = value.into().deconstruct();
2245 value
2246 .into()
2247 .saturating_mul(T::NativeToEthRatio::get().into())
2248 .saturating_add(dust.into())
2249 }
2250
2251 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2261 let contract_info =
2262 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2263
2264 contract_info
2265 .write(&Key::from_fixed(key), value, None, false)
2266 .map_err(ContractAccessError::StorageWriteFailed)
2267 }
2268
2269 pub fn set_storage_var_key(
2280 address: H160,
2281 key: Vec<u8>,
2282 value: Option<Vec<u8>>,
2283 ) -> SetStorageResult {
2284 let contract_info =
2285 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2286
2287 contract_info
2288 .write(
2289 &Key::try_from_var(key)
2290 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2291 .into(),
2292 value,
2293 None,
2294 false,
2295 )
2296 .map_err(ContractAccessError::StorageWriteFailed)
2297 }
2298
2299 pub fn account_id() -> T::AccountId {
2301 use frame_support::PalletId;
2302 use sp_runtime::traits::AccountIdConversion;
2303 PalletId(*b"py/reviv").into_account_truncating()
2304 }
2305
2306 pub fn block_author() -> H160 {
2308 use frame_support::traits::FindAuthor;
2309
2310 let digest = <frame_system::Pallet<T>>::digest();
2311 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2312
2313 T::FindAuthor::find_author(pre_runtime_digests)
2314 .map(|account_id| T::AddressMapper::to_address(&account_id))
2315 .unwrap_or_default()
2316 }
2317
2318 pub fn code(address: &H160) -> Vec<u8> {
2322 use precompiles::{All, Precompiles};
2323 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2324 return code.into()
2325 }
2326 AccountInfo::<T>::load_contract(&address)
2327 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2328 .map(|code| code.into())
2329 .unwrap_or_default()
2330 }
2331
2332 pub fn try_upload_code(
2334 origin: T::AccountId,
2335 code: Vec<u8>,
2336 code_type: BytecodeType,
2337 meter: &mut TransactionMeter<T>,
2338 exec_config: &ExecConfig<T>,
2339 ) -> Result<ContractBlob<T>, DispatchError> {
2340 let mut module = match code_type {
2341 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2342 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2343 };
2344 module.store_code(exec_config, meter)?;
2345 Ok(module)
2346 }
2347
2348 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2350 executing_contract::using_once(&mut false, || {
2351 executing_contract::with(|f| {
2352 if *f {
2354 return Err(())
2355 }
2356 *f = true;
2358 Ok(())
2359 })
2360 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2361 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2362 .map(|_| f())
2363 .and_then(|r| r)
2364 })
2365 }
2366
2367 fn charge_deposit(
2372 hold_reason: Option<HoldReason>,
2373 from: &T::AccountId,
2374 to: &T::AccountId,
2375 amount: BalanceOf<T>,
2376 exec_config: &ExecConfig<T>,
2377 ) -> DispatchResult {
2378 use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
2379
2380 if amount.is_zero() {
2381 return Ok(());
2382 }
2383
2384 match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2385 (true, hold_reason) => {
2386 T::FeeInfo::withdraw_txfee(amount)
2387 .ok_or(())
2388 .and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2389 .and_then(|_| {
2390 if let Some(hold_reason) = hold_reason {
2391 T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2392 }
2393 Ok(())
2394 })
2395 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2396 },
2397 (false, Some(hold_reason)) => {
2398 T::Currency::transfer_and_hold(
2399 &hold_reason.into(),
2400 from,
2401 to,
2402 amount,
2403 Precision::Exact,
2404 Preservation::Preserve,
2405 Fortitude::Polite,
2406 )
2407 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2408 },
2409 (false, None) => {
2410 T::Currency::transfer(from, to, amount, Preservation::Preserve)
2411 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2412 },
2413 }
2414 Ok(())
2415 }
2416
2417 fn refund_deposit(
2422 hold_reason: HoldReason,
2423 from: &T::AccountId,
2424 to: &T::AccountId,
2425 amount: BalanceOf<T>,
2426 exec_config: Option<&ExecConfig<T>>,
2427 ) -> Result<(), DispatchError> {
2428 use frame_support::traits::{
2429 fungible::InspectHold,
2430 tokens::{Fortitude, Precision, Preservation, Restriction},
2431 };
2432
2433 if amount.is_zero() {
2434 return Ok(());
2435 }
2436
2437 let hold_reason = hold_reason.into();
2438 let result = if exec_config.map(|c| c.collect_deposit_from_hold.is_some()).unwrap_or(false)
2439 {
2440 T::Currency::release(&hold_reason, from, amount, Precision::Exact)
2441 .and_then(|amount| {
2442 T::Currency::withdraw(
2443 from,
2444 amount,
2445 Precision::Exact,
2446 Preservation::Preserve,
2447 Fortitude::Polite,
2448 )
2449 })
2450 .map(T::FeeInfo::deposit_txfee)
2451 } else {
2452 T::Currency::transfer_on_hold(
2453 &hold_reason,
2454 from,
2455 to,
2456 amount,
2457 Precision::Exact,
2458 Restriction::Free,
2459 Fortitude::Polite,
2460 )
2461 .map(|_| ())
2462 };
2463
2464 result.map_err(|_| {
2465 let available = T::Currency::balance_on_hold(&hold_reason, from);
2466 if available < amount {
2467 log::error!(
2470 target: LOG_TARGET,
2471 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. Not enough deposit: {:?}. This is a bug.",
2472 amount, from, to, available,
2473 );
2474 Error::<T>::StorageRefundNotEnoughFunds.into()
2475 } else {
2476 log::warn!(
2481 target: LOG_TARGET,
2482 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. First remove locks (staking, governance) from the contracts account.",
2483 amount, from, to,
2484 );
2485 Error::<T>::StorageRefundLocked.into()
2486 }
2487 })
2488 }
2489
2490 fn has_dust(value: U256) -> bool {
2492 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2493 }
2494
2495 fn has_balance(value: U256) -> bool {
2497 value >= U256::from(<T>::NativeToEthRatio::get())
2498 }
2499
2500 fn min_balance() -> BalanceOf<T> {
2502 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2503 }
2504
2505 fn deposit_event(event: Event<T>) {
2510 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2511 }
2512
2513 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2515 match <T as Config>::RuntimeOrigin::from(origin).into() {
2516 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2517 _ => Err(BadOrigin.into()),
2518 }
2519 }
2520
2521 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2525 if DebugSettings::bypass_eip_3607::<T>() {
2526 return Ok(())
2527 }
2528 let Some(address) = origin
2529 .as_system_ref()
2530 .and_then(|o| o.as_signed())
2531 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2532 else {
2533 return Ok(())
2534 };
2535 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2536 <AccountInfo<T>>::is_contract(&address)
2537 {
2538 log::debug!(
2539 target: crate::LOG_TARGET,
2540 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2541 );
2542 Err(DispatchError::BadOrigin)
2543 } else {
2544 Ok(())
2545 }
2546 }
2547}
2548
2549pub const RUNTIME_PALLETS_ADDR: H160 =
2554 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2555
2556environmental!(executing_contract: bool);
2558
2559sp_api::decl_runtime_apis! {
2560 #[api_version(1)]
2562 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2563 AccountId: Codec,
2564 Balance: Codec,
2565 Nonce: Codec,
2566 BlockNumber: Codec,
2567 Moment: Codec,
2568 {
2569 fn eth_block() -> EthBlock;
2573
2574 fn eth_block_hash(number: U256) -> Option<H256>;
2576
2577 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2583
2584 fn block_gas_limit() -> U256;
2586
2587 fn balance(address: H160) -> U256;
2589
2590 fn gas_price() -> U256;
2592
2593 fn nonce(address: H160) -> Nonce;
2595
2596 fn call(
2600 origin: AccountId,
2601 dest: H160,
2602 value: Balance,
2603 gas_limit: Option<Weight>,
2604 storage_deposit_limit: Option<Balance>,
2605 input_data: Vec<u8>,
2606 ) -> ContractResult<ExecReturnValue, Balance>;
2607
2608 fn instantiate(
2612 origin: AccountId,
2613 value: Balance,
2614 gas_limit: Option<Weight>,
2615 storage_deposit_limit: Option<Balance>,
2616 code: Code,
2617 data: Vec<u8>,
2618 salt: Option<[u8; 32]>,
2619 ) -> ContractResult<InstantiateReturnValue, Balance>;
2620
2621
2622 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2627
2628 fn eth_transact_with_config(
2632 tx: GenericTransaction,
2633 config: DryRunConfig<Moment>,
2634 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2635
2636 fn upload_code(
2640 origin: AccountId,
2641 code: Vec<u8>,
2642 storage_deposit_limit: Option<Balance>,
2643 ) -> CodeUploadResult<Balance>;
2644
2645 fn get_storage(
2651 address: H160,
2652 key: [u8; 32],
2653 ) -> GetStorageResult;
2654
2655 fn get_storage_var_key(
2661 address: H160,
2662 key: Vec<u8>,
2663 ) -> GetStorageResult;
2664
2665 fn trace_block(
2672 block: Block,
2673 config: TracerType
2674 ) -> Vec<(u32, Trace)>;
2675
2676 fn trace_tx(
2683 block: Block,
2684 tx_index: u32,
2685 config: TracerType
2686 ) -> Option<Trace>;
2687
2688 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2692
2693 fn block_author() -> H160;
2695
2696 fn address(account_id: AccountId) -> H160;
2698
2699 fn account_id(address: H160) -> AccountId;
2701
2702 fn runtime_pallets_address() -> H160;
2704
2705 fn code(address: H160) -> Vec<u8>;
2707
2708 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2710 }
2711}
2712
2713#[macro_export]
2727macro_rules! impl_runtime_apis_plus_revive_traits {
2728 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2729
2730 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2731
2732 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2733 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
2734 use $crate::pallet::Call as ReviveCall;
2735 match self {
2736 Self::$Revive(
2737 ReviveCall::eth_call{ weight_limit, .. } |
2738 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
2739 ) => {
2740 let old = *weight_limit;
2741 *weight_limit = new_weight_limit;
2742 old
2743 },
2744 _ => Weight::default(),
2745 }
2746 }
2747 }
2748
2749 impl_runtime_apis! {
2750 $($rest)*
2751
2752
2753 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2754 {
2755 fn eth_block() -> $crate::EthBlock {
2756 $crate::Pallet::<Self>::eth_block()
2757 }
2758
2759 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2760 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
2761 }
2762
2763 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2764 $crate::Pallet::<Self>::eth_receipt_data()
2765 }
2766
2767 fn balance(address: $crate::H160) -> $crate::U256 {
2768 $crate::Pallet::<Self>::evm_balance(&address)
2769 }
2770
2771 fn block_author() -> $crate::H160 {
2772 $crate::Pallet::<Self>::block_author()
2773 }
2774
2775 fn block_gas_limit() -> $crate::U256 {
2776 $crate::Pallet::<Self>::evm_block_gas_limit()
2777 }
2778
2779 fn gas_price() -> $crate::U256 {
2780 $crate::Pallet::<Self>::evm_base_fee()
2781 }
2782
2783 fn nonce(address: $crate::H160) -> Nonce {
2784 use $crate::AddressMapper;
2785 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2786 $crate::frame_system::Pallet::<Self>::account_nonce(account)
2787 }
2788
2789 fn address(account_id: AccountId) -> $crate::H160 {
2790 use $crate::AddressMapper;
2791 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2792 }
2793
2794 fn eth_transact(
2795 tx: $crate::evm::GenericTransaction,
2796 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2797 use $crate::{
2798 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2799 sp_runtime::traits::TransactionExtension,
2800 sp_runtime::traits::Block as BlockT
2801 };
2802 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
2803 }
2804
2805 fn eth_transact_with_config(
2806 tx: $crate::evm::GenericTransaction,
2807 config: $crate::DryRunConfig<__ReviveMacroMoment>,
2808 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2809 use $crate::{
2810 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2811 sp_runtime::traits::TransactionExtension,
2812 sp_runtime::traits::Block as BlockT
2813 };
2814 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
2815 }
2816
2817 fn call(
2818 origin: AccountId,
2819 dest: $crate::H160,
2820 value: Balance,
2821 weight_limit: Option<$crate::Weight>,
2822 storage_deposit_limit: Option<Balance>,
2823 input_data: Vec<u8>,
2824 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2825 use $crate::frame_support::traits::Get;
2826 let blockweights: $crate::BlockWeights =
2827 <Self as $crate::frame_system::Config>::BlockWeights::get();
2828
2829 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2830 $crate::Pallet::<Self>::bare_call(
2831 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2832 dest,
2833 $crate::Pallet::<Self>::convert_native_to_evm(value),
2834 $crate::TransactionLimits::WeightAndDeposit {
2835 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2836 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2837 },
2838 input_data,
2839 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2840 )
2841 }
2842
2843 fn instantiate(
2844 origin: AccountId,
2845 value: Balance,
2846 weight_limit: Option<$crate::Weight>,
2847 storage_deposit_limit: Option<Balance>,
2848 code: $crate::Code,
2849 data: Vec<u8>,
2850 salt: Option<[u8; 32]>,
2851 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2852 use $crate::frame_support::traits::Get;
2853 let blockweights: $crate::BlockWeights =
2854 <Self as $crate::frame_system::Config>::BlockWeights::get();
2855
2856 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2857 $crate::Pallet::<Self>::bare_instantiate(
2858 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2859 $crate::Pallet::<Self>::convert_native_to_evm(value),
2860 $crate::TransactionLimits::WeightAndDeposit {
2861 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2862 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2863 },
2864 code,
2865 data,
2866 salt,
2867 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2868 )
2869 }
2870
2871 fn upload_code(
2872 origin: AccountId,
2873 code: Vec<u8>,
2874 storage_deposit_limit: Option<Balance>,
2875 ) -> $crate::CodeUploadResult<Balance> {
2876 let origin =
2877 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
2878 $crate::Pallet::<Self>::bare_upload_code(
2879 origin,
2880 code,
2881 storage_deposit_limit.unwrap_or(u128::MAX),
2882 )
2883 }
2884
2885 fn get_storage_var_key(
2886 address: $crate::H160,
2887 key: Vec<u8>,
2888 ) -> $crate::GetStorageResult {
2889 $crate::Pallet::<Self>::get_storage_var_key(address, key)
2890 }
2891
2892 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2893 $crate::Pallet::<Self>::get_storage(address, key)
2894 }
2895
2896 fn trace_block(
2897 block: Block,
2898 tracer_type: $crate::evm::TracerType,
2899 ) -> Vec<(u32, $crate::evm::Trace)> {
2900 use $crate::{sp_runtime::traits::Block, tracing::trace};
2901 let mut traces = vec![];
2902 let (header, extrinsics) = block.deconstruct();
2903 <$Executive>::initialize_block(&header);
2904 for (index, ext) in extrinsics.into_iter().enumerate() {
2905 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2906 let t = tracer.as_tracing();
2907 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2908
2909 if let Some(tx_trace) = tracer.collect_trace() {
2910 traces.push((index as u32, tx_trace));
2911 }
2912 }
2913
2914 traces
2915 }
2916
2917 fn trace_tx(
2918 block: Block,
2919 tx_index: u32,
2920 tracer_type: $crate::evm::TracerType,
2921 ) -> Option<$crate::evm::Trace> {
2922 use $crate::{sp_runtime::traits::Block, tracing::trace};
2923
2924 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2925 let (header, extrinsics) = block.deconstruct();
2926
2927 <$Executive>::initialize_block(&header);
2928 for (index, ext) in extrinsics.into_iter().enumerate() {
2929 if index as u32 == tx_index {
2930 let t = tracer.as_tracing();
2931 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2932 break;
2933 } else {
2934 let _ = <$Executive>::apply_extrinsic(ext);
2935 }
2936 }
2937
2938 tracer.collect_trace()
2939 }
2940
2941 fn trace_call(
2942 tx: $crate::evm::GenericTransaction,
2943 tracer_type: $crate::evm::TracerType,
2944 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2945 use $crate::tracing::trace;
2946 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2947 let t = tracer.as_tracing();
2948
2949 t.watch_address(&tx.from.unwrap_or_default());
2950 t.watch_address(&$crate::Pallet::<Self>::block_author());
2951 let result = trace(t, || Self::eth_transact(tx));
2952
2953 if let Some(trace) = tracer.collect_trace() {
2954 Ok(trace)
2955 } else if let Err(err) = result {
2956 Err(err)
2957 } else {
2958 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
2959 }
2960 }
2961
2962 fn runtime_pallets_address() -> $crate::H160 {
2963 $crate::RUNTIME_PALLETS_ADDR
2964 }
2965
2966 fn code(address: $crate::H160) -> Vec<u8> {
2967 $crate::Pallet::<Self>::code(&address)
2968 }
2969
2970 fn account_id(address: $crate::H160) -> AccountId {
2971 use $crate::AddressMapper;
2972 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
2973 }
2974
2975 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2976 $crate::Pallet::<Self>::new_balance_with_dust(balance)
2977 }
2978 }
2979 }
2980 };
2981}