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]
265 type AllowEVMBytecode: Get<bool>;
266
267 #[pallet::no_default_bounds]
272 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
273
274 #[pallet::no_default_bounds]
285 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
286
287 type RuntimeMemory: Get<u32>;
292
293 type PVFMemory: Get<u32>;
301
302 #[pallet::constant]
307 type ChainId: Get<u64>;
308
309 #[pallet::constant]
311 type NativeToEthRatio: Get<u32>;
312
313 #[pallet::no_default_bounds]
318 type FeeInfo: FeeInfo<Self>;
319
320 #[pallet::constant]
333 type MaxEthExtrinsicWeight: Get<FixedU128>;
334
335 #[pallet::constant]
337 type DebugEnabled: Get<bool>;
338
339 #[pallet::constant]
357 #[pallet::no_default_bounds]
358 type GasScale: Get<u32>;
359 }
360
361 pub mod config_preludes {
363 use super::*;
364 use frame_support::{
365 derive_impl,
366 traits::{ConstBool, ConstU32},
367 };
368 use frame_system::EnsureSigned;
369 use sp_core::parameter_types;
370
371 type Balance = u64;
372
373 pub const DOLLARS: Balance = 1_000_000_000_000;
374 pub const CENTS: Balance = DOLLARS / 100;
375 pub const MILLICENTS: Balance = CENTS / 1_000;
376
377 pub const fn deposit(items: u32, bytes: u32) -> Balance {
378 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
379 }
380
381 parameter_types! {
382 pub const DepositPerItem: Balance = deposit(1, 0);
383 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
384 pub const DepositPerByte: Balance = deposit(0, 1);
385 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
386 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
387 pub const GasScale: u32 = 10u32;
388 }
389
390 pub struct TestDefaultConfig;
392
393 impl Time for TestDefaultConfig {
394 type Moment = u64;
395 fn now() -> Self::Moment {
396 0u64
397 }
398 }
399
400 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
401 fn convert(w: Weight) -> T {
402 w.ref_time().into()
403 }
404 }
405
406 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
407 impl frame_system::DefaultConfig for TestDefaultConfig {}
408
409 #[frame_support::register_default_impl(TestDefaultConfig)]
410 impl DefaultConfig for TestDefaultConfig {
411 #[inject_runtime_type]
412 type RuntimeEvent = ();
413
414 #[inject_runtime_type]
415 type RuntimeHoldReason = ();
416
417 #[inject_runtime_type]
418 type RuntimeCall = ();
419
420 #[inject_runtime_type]
421 type RuntimeOrigin = ();
422
423 type Precompiles = ();
424 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
425 type DepositPerByte = DepositPerByte;
426 type DepositPerItem = DepositPerItem;
427 type DepositPerChildTrieItem = DepositPerChildTrieItem;
428 type Time = Self;
429 type AllowEVMBytecode = ConstBool<true>;
430 type UploadOrigin = EnsureSigned<Self::AccountId>;
431 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
432 type WeightInfo = ();
433 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
434 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
435 type ChainId = ConstU64<42>;
436 type NativeToEthRatio = ConstU32<1_000_000>;
437 type FindAuthor = ();
438 type FeeInfo = ();
439 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
440 type DebugEnabled = ConstBool<false>;
441 type GasScale = GasScale;
442 }
443 }
444
445 #[pallet::event]
446 pub enum Event<T: Config> {
447 ContractEmitted {
449 contract: H160,
451 data: Vec<u8>,
454 topics: Vec<H256>,
457 },
458
459 Instantiated { deployer: H160, contract: H160 },
461
462 EthExtrinsicRevert { dispatch_error: DispatchError },
469 }
470
471 #[pallet::error]
472 #[repr(u8)]
473 pub enum Error<T> {
474 InvalidSchedule = 0x01,
476 InvalidCallFlags = 0x02,
478 OutOfGas = 0x03,
480 TransferFailed = 0x04,
483 MaxCallDepthReached = 0x05,
486 ContractNotFound = 0x06,
488 CodeNotFound = 0x07,
490 CodeInfoNotFound = 0x08,
492 OutOfBounds = 0x09,
494 DecodingFailed = 0x0A,
496 ContractTrapped = 0x0B,
498 ValueTooLarge = 0x0C,
500 TerminatedWhileReentrant = 0x0D,
503 InputForwarded = 0x0E,
505 TooManyTopics = 0x0F,
507 DuplicateContract = 0x12,
509 TerminatedInConstructor = 0x13,
513 ReentranceDenied = 0x14,
515 ReenteredPallet = 0x15,
517 StateChangeDenied = 0x16,
519 StorageDepositNotEnoughFunds = 0x17,
521 StorageDepositLimitExhausted = 0x18,
523 CodeInUse = 0x19,
525 ContractReverted = 0x1A,
530 CodeRejected = 0x1B,
535 BlobTooLarge = 0x1C,
537 StaticMemoryTooLarge = 0x1D,
539 BasicBlockTooLarge = 0x1E,
541 InvalidInstruction = 0x1F,
543 MaxDelegateDependenciesReached = 0x20,
545 DelegateDependencyNotFound = 0x21,
547 DelegateDependencyAlreadyExists = 0x22,
549 CannotAddSelfAsDelegateDependency = 0x23,
551 OutOfTransientStorage = 0x24,
553 InvalidSyscall = 0x25,
555 InvalidStorageFlags = 0x26,
557 ExecutionFailed = 0x27,
559 BalanceConversionFailed = 0x28,
561 InvalidImmutableAccess = 0x2A,
564 AccountUnmapped = 0x2B,
568 AccountAlreadyMapped = 0x2C,
570 InvalidGenericTransaction = 0x2D,
572 RefcountOverOrUnderflow = 0x2E,
574 UnsupportedPrecompileAddress = 0x2F,
576 CallDataTooLarge = 0x30,
578 ReturnDataTooLarge = 0x31,
580 InvalidJump = 0x32,
582 StackUnderflow = 0x33,
584 StackOverflow = 0x34,
586 TxFeeOverdraw = 0x35,
590 EvmConstructorNonEmptyData = 0x36,
594 EvmConstructedFromHash = 0x37,
599 StorageRefundNotEnoughFunds = 0x38,
603 StorageRefundLocked = 0x39,
608 PrecompileDelegateDenied = 0x40,
613 EcdsaRecoveryFailed = 0x41,
615 #[cfg(feature = "runtime-benchmarks")]
617 BenchmarkingError = 0xFF,
618 }
619
620 #[pallet::composite_enum]
622 pub enum HoldReason {
623 CodeUploadDepositReserve,
625 StorageDepositReserve,
627 AddressMapping,
629 }
630
631 #[derive(
632 PartialEq,
633 Eq,
634 Clone,
635 MaxEncodedLen,
636 Encode,
637 Decode,
638 DecodeWithMemTracking,
639 TypeInfo,
640 RuntimeDebug,
641 )]
642 #[pallet::origin]
643 pub enum Origin<T: Config> {
644 EthTransaction(T::AccountId),
645 }
646
647 #[pallet::storage]
651 #[pallet::unbounded]
652 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
653
654 #[pallet::storage]
656 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
657
658 #[pallet::storage]
660 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
661
662 #[pallet::storage]
664 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
665
666 #[pallet::storage]
671 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
672
673 #[pallet::storage]
676 pub(crate) type DeletionQueueCounter<T: Config> =
677 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
678
679 #[pallet::storage]
686 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
687
688 #[pallet::storage]
698 #[pallet::unbounded]
699 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
700
701 #[pallet::storage]
705 pub(crate) type BlockHash<T: Config> =
706 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
707
708 #[pallet::storage]
715 #[pallet::unbounded]
716 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
717
718 #[pallet::storage]
720 #[pallet::unbounded]
721 pub(crate) type EthBlockBuilderIR<T: Config> =
722 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
723
724 #[pallet::storage]
729 #[pallet::unbounded]
730 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
731 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
732
733 #[pallet::storage]
735 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
736
737 pub mod genesis {
738 use super::*;
739 use crate::evm::Bytes32;
740
741 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
743 pub struct ContractData {
744 pub code: Vec<u8>,
746 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
748 }
749
750 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
752 pub struct Account<T: Config> {
753 pub address: H160,
755 #[serde(default)]
757 pub balance: U256,
758 #[serde(default)]
760 pub nonce: T::Nonce,
761 #[serde(flatten, skip_serializing_if = "Option::is_none")]
763 pub contract_data: Option<ContractData>,
764 }
765 }
766
767 #[pallet::genesis_config]
768 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
769 pub struct GenesisConfig<T: Config> {
770 #[serde(default, skip_serializing_if = "Vec::is_empty")]
773 pub mapped_accounts: Vec<T::AccountId>,
774
775 #[serde(default, skip_serializing_if = "Vec::is_empty")]
777 pub accounts: Vec<genesis::Account<T>>,
778
779 #[serde(default, skip_serializing_if = "Option::is_none")]
781 pub debug_settings: Option<DebugSettings>,
782 }
783
784 #[pallet::genesis_build]
785 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
786 fn build(&self) {
787 use crate::{exec::Key, vm::ContractBlob};
788 use frame_support::traits::fungible::Mutate;
789
790 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
791 let _ = T::Currency::mint_into(
792 &Pallet::<T>::account_id(),
793 T::Currency::minimum_balance(),
794 );
795 }
796
797 for id in &self.mapped_accounts {
798 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
799 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
800 }
801 }
802
803 let owner = Pallet::<T>::account_id();
804
805 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
806 let account_id = T::AddressMapper::to_account_id(address);
807
808 if !System::<T>::account_exists(&account_id) {
809 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
810 }
811
812 frame_system::Account::<T>::mutate(&account_id, |info| {
813 info.nonce = (*nonce).into();
814 });
815
816 match contract_data {
817 None => {
818 AccountInfoOf::<T>::insert(
819 address,
820 AccountInfo { account_type: AccountType::EOA, dust: 0 },
821 );
822 },
823 Some(genesis::ContractData { code, storage }) => {
824 let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
825 ContractBlob::<T>::from_pvm_code( code.clone(), owner.clone()).inspect_err(|err| {
826 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
827 })
828 } else {
829 ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
830 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
831 })
832 };
833
834 let Ok(blob) = blob else {
835 continue;
836 };
837
838 let code_hash = *blob.code_hash();
839 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
840 .inspect_err(|err| {
841 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
842 })
843 else {
844 continue;
845 };
846
847 AccountInfoOf::<T>::insert(
848 address,
849 AccountInfo { account_type: info.clone().into(), dust: 0 },
850 );
851
852 <PristineCode<T>>::insert(blob.code_hash(), code);
853 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
854 for (k, v) in storage {
855 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
856 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
857 });
858 }
859 },
860 }
861
862 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
863 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
864 });
865 }
866
867 block_storage::on_finalize_build_eth_block::<T>(
869 frame_system::Pallet::<T>::block_number(),
872 );
873
874 if let Some(settings) = self.debug_settings.as_ref() {
876 settings.write_to_storage::<T>()
877 }
878 }
879 }
880
881 #[pallet::hooks]
882 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
883 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
884 let mut meter = WeightMeter::with_limit(limit);
885 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
886 meter.consumed()
887 }
888
889 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
890 block_storage::on_initialize::<T>();
892
893 System::<T>::account_exists(&Pallet::<T>::account_id());
895 <T as Config>::WeightInfo::on_finalize_block_fixed()
897 }
898
899 fn on_finalize(block_number: BlockNumberFor<T>) {
900 block_storage::on_finalize_build_eth_block::<T>(block_number);
902 }
903
904 fn integrity_test() {
905 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
906
907 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
908
909 T::FeeInfo::integrity_test();
910
911 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
913
914 const TOTAL_MEMORY_DEVIDER: u64 = 2;
917
918 let max_block_weight = T::BlockWeights::get()
924 .get(DispatchClass::Normal)
925 .max_total
926 .unwrap_or_else(|| T::BlockWeights::get().max_block);
927 let max_key_size: u64 =
928 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
929 .expect("Key of maximal size shall be created")
930 .hash()
931 .len()
932 .try_into()
933 .unwrap();
934
935 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
936 let max_immutable_size: u64 = max_block_weight
937 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
938 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
939 ))
940 .unwrap()
941 .saturating_mul(
942 u64::from(limits::IMMUTABLE_BYTES)
943 .saturating_add(max_immutable_key_size)
944 .into(),
945 );
946
947 let max_pvf_mem: u64 = T::PVFMemory::get().into();
948 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
949
950 let max_events_size = max_block_weight
954 .checked_div_per_component(
955 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
956 num_topic: 0,
957 len: limits::EVENT_BYTES,
958 })
959 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
960 &RuntimeCosts::HostFn,
961 ))),
962 )
963 .unwrap()
964 .saturating_mul(limits::EVENT_BYTES.into());
965
966 assert!(
967 max_events_size <= storage_size_limit,
968 "Maximal events size {} exceeds the events limit {}",
969 max_events_size,
970 storage_size_limit
971 );
972
973 let max_eth_block_builder_bytes =
1008 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1009
1010 log::debug!(
1011 target: LOG_TARGET,
1012 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1013 max_eth_block_builder_bytes / 1024,
1014 max_events_size / 1024,
1015 );
1016
1017 let memory_left = i128::from(max_runtime_mem)
1022 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1023 .saturating_sub(limits::MEMORY_REQUIRED.into())
1024 .saturating_sub(max_eth_block_builder_bytes.into());
1025
1026 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1027
1028 assert!(
1029 memory_left >= 0,
1030 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1031 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1032 );
1033
1034 let max_storage_size = max_block_weight
1037 .checked_div_per_component(
1038 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1039 new_bytes: limits::STORAGE_BYTES,
1040 old_bytes: 0,
1041 })
1042 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1043 )
1044 .unwrap()
1045 .saturating_add(max_immutable_size.into())
1046 .saturating_add(max_eth_block_builder_bytes.into());
1047
1048 assert!(
1049 max_storage_size <= storage_size_limit,
1050 "Maximal storage size {} exceeds the storage limit {}",
1051 max_storage_size,
1052 storage_size_limit
1053 );
1054 }
1055 }
1056
1057 #[pallet::call]
1058 impl<T: Config> Pallet<T> {
1059 #[allow(unused_variables)]
1072 #[pallet::call_index(0)]
1073 #[pallet::weight(Weight::MAX)]
1074 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1075 Err(frame_system::Error::CallFiltered::<T>.into())
1076 }
1077
1078 #[pallet::call_index(1)]
1095 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1096 pub fn call(
1097 origin: OriginFor<T>,
1098 dest: H160,
1099 #[pallet::compact] value: BalanceOf<T>,
1100 weight_limit: Weight,
1101 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1102 data: Vec<u8>,
1103 ) -> DispatchResultWithPostInfo {
1104 Self::ensure_non_contract_if_signed(&origin)?;
1105 let mut output = Self::bare_call(
1106 origin,
1107 dest,
1108 Pallet::<T>::convert_native_to_evm(value),
1109 TransactionLimits::WeightAndDeposit {
1110 weight_limit,
1111 deposit_limit: storage_deposit_limit,
1112 },
1113 data,
1114 ExecConfig::new_substrate_tx(),
1115 );
1116
1117 if let Ok(return_value) = &output.result {
1118 if return_value.did_revert() {
1119 output.result = Err(<Error<T>>::ContractReverted.into());
1120 }
1121 }
1122 dispatch_result(
1123 output.result,
1124 output.weight_consumed,
1125 <T as Config>::WeightInfo::call(),
1126 )
1127 }
1128
1129 #[pallet::call_index(2)]
1135 #[pallet::weight(
1136 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1137 )]
1138 pub fn instantiate(
1139 origin: OriginFor<T>,
1140 #[pallet::compact] value: BalanceOf<T>,
1141 weight_limit: Weight,
1142 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1143 code_hash: sp_core::H256,
1144 data: Vec<u8>,
1145 salt: Option<[u8; 32]>,
1146 ) -> DispatchResultWithPostInfo {
1147 Self::ensure_non_contract_if_signed(&origin)?;
1148 let data_len = data.len() as u32;
1149 let mut output = Self::bare_instantiate(
1150 origin,
1151 Pallet::<T>::convert_native_to_evm(value),
1152 TransactionLimits::WeightAndDeposit {
1153 weight_limit,
1154 deposit_limit: storage_deposit_limit,
1155 },
1156 Code::Existing(code_hash),
1157 data,
1158 salt,
1159 ExecConfig::new_substrate_tx(),
1160 );
1161 if let Ok(retval) = &output.result {
1162 if retval.result.did_revert() {
1163 output.result = Err(<Error<T>>::ContractReverted.into());
1164 }
1165 }
1166 dispatch_result(
1167 output.result.map(|result| result.result),
1168 output.weight_consumed,
1169 <T as Config>::WeightInfo::instantiate(data_len),
1170 )
1171 }
1172
1173 #[pallet::call_index(3)]
1201 #[pallet::weight(
1202 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1203 .saturating_add(*weight_limit)
1204 )]
1205 pub fn instantiate_with_code(
1206 origin: OriginFor<T>,
1207 #[pallet::compact] value: BalanceOf<T>,
1208 weight_limit: Weight,
1209 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1210 code: Vec<u8>,
1211 data: Vec<u8>,
1212 salt: Option<[u8; 32]>,
1213 ) -> DispatchResultWithPostInfo {
1214 Self::ensure_non_contract_if_signed(&origin)?;
1215 let code_len = code.len() as u32;
1216 let data_len = data.len() as u32;
1217 let mut output = Self::bare_instantiate(
1218 origin,
1219 Pallet::<T>::convert_native_to_evm(value),
1220 TransactionLimits::WeightAndDeposit {
1221 weight_limit,
1222 deposit_limit: storage_deposit_limit,
1223 },
1224 Code::Upload(code),
1225 data,
1226 salt,
1227 ExecConfig::new_substrate_tx(),
1228 );
1229 if let Ok(retval) = &output.result {
1230 if retval.result.did_revert() {
1231 output.result = Err(<Error<T>>::ContractReverted.into());
1232 }
1233 }
1234 dispatch_result(
1235 output.result.map(|result| result.result),
1236 output.weight_consumed,
1237 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1238 )
1239 }
1240
1241 #[pallet::call_index(10)]
1263 #[pallet::weight(
1264 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1265 .saturating_add(*weight_limit)
1266 )]
1267 pub fn eth_instantiate_with_code(
1268 origin: OriginFor<T>,
1269 value: U256,
1270 weight_limit: Weight,
1271 eth_gas_limit: U256,
1272 code: Vec<u8>,
1273 data: Vec<u8>,
1274 transaction_encoded: Vec<u8>,
1275 effective_gas_price: U256,
1276 encoded_len: u32,
1277 ) -> DispatchResultWithPostInfo {
1278 let signer = Self::ensure_eth_signed(origin)?;
1279 let origin = OriginFor::<T>::signed(signer.clone());
1280 Self::ensure_non_contract_if_signed(&origin)?;
1281 let mut call = Call::<T>::eth_instantiate_with_code {
1282 value,
1283 weight_limit,
1284 eth_gas_limit,
1285 code: code.clone(),
1286 data: data.clone(),
1287 transaction_encoded: transaction_encoded.clone(),
1288 effective_gas_price,
1289 encoded_len,
1290 }
1291 .into();
1292 let info = T::FeeInfo::dispatch_info(&call);
1293 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1294 drop(call);
1295
1296 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1297 let extra_weight = base_info.total_weight();
1298 let output = Self::bare_instantiate(
1299 origin,
1300 value,
1301 TransactionLimits::EthereumGas {
1302 eth_gas_limit: eth_gas_limit.saturated_into(),
1303 maybe_weight_limit: Some(weight_limit),
1304 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1305 },
1306 Code::Upload(code),
1307 data,
1308 None,
1309 ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1310 );
1311
1312 block_storage::EthereumCallResult::new::<T>(
1313 signer,
1314 output.map_result(|r| r.result),
1315 base_info.call_weight,
1316 encoded_len,
1317 &info,
1318 effective_gas_price,
1319 )
1320 })
1321 }
1322
1323 #[pallet::call_index(11)]
1340 #[pallet::weight(
1341 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1342 .saturating_add(*weight_limit)
1343 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1344 )]
1345 pub fn eth_call(
1346 origin: OriginFor<T>,
1347 dest: H160,
1348 value: U256,
1349 weight_limit: Weight,
1350 eth_gas_limit: U256,
1351 data: Vec<u8>,
1352 transaction_encoded: Vec<u8>,
1353 effective_gas_price: U256,
1354 encoded_len: u32,
1355 ) -> DispatchResultWithPostInfo {
1356 let signer = Self::ensure_eth_signed(origin)?;
1357 let origin = OriginFor::<T>::signed(signer.clone());
1358
1359 Self::ensure_non_contract_if_signed(&origin)?;
1360 let mut call = Call::<T>::eth_call {
1361 dest,
1362 value,
1363 weight_limit,
1364 eth_gas_limit,
1365 data: data.clone(),
1366 transaction_encoded: transaction_encoded.clone(),
1367 effective_gas_price,
1368 encoded_len,
1369 }
1370 .into();
1371 let info = T::FeeInfo::dispatch_info(&call);
1372 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1373 drop(call);
1374
1375 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1376 let extra_weight = base_info.total_weight();
1377 let output = Self::bare_call(
1378 origin,
1379 dest,
1380 value,
1381 TransactionLimits::EthereumGas {
1382 eth_gas_limit: eth_gas_limit.saturated_into(),
1383 maybe_weight_limit: Some(weight_limit),
1384 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1385 },
1386 data,
1387 ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1388 );
1389
1390 block_storage::EthereumCallResult::new::<T>(
1391 signer,
1392 output,
1393 base_info.call_weight,
1394 encoded_len,
1395 &info,
1396 effective_gas_price,
1397 )
1398 })
1399 }
1400
1401 #[pallet::call_index(12)]
1412 #[pallet::weight(T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1413 pub fn eth_substrate_call(
1414 origin: OriginFor<T>,
1415 call: Box<<T as Config>::RuntimeCall>,
1416 transaction_encoded: Vec<u8>,
1417 ) -> DispatchResultWithPostInfo {
1418 let signer = Self::ensure_eth_signed(origin)?;
1421 let weight_overhead =
1422 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32);
1423
1424 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1425 let call_weight = call.get_dispatch_info().call_weight;
1426 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1427
1428 match &mut call_result {
1430 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1431 post_info.actual_weight = Some(
1432 post_info
1433 .actual_weight
1434 .unwrap_or_else(|| call_weight)
1435 .saturating_add(weight_overhead),
1436 );
1437 },
1438 }
1439
1440 block_storage::EthereumCallResult {
1443 receipt_gas_info: ReceiptGasInfo::default(),
1444 result: call_result,
1445 }
1446 })
1447 }
1448
1449 #[pallet::call_index(4)]
1464 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1465 pub fn upload_code(
1466 origin: OriginFor<T>,
1467 code: Vec<u8>,
1468 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1469 ) -> DispatchResult {
1470 Self::ensure_non_contract_if_signed(&origin)?;
1471 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1472 }
1473
1474 #[pallet::call_index(5)]
1479 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1480 pub fn remove_code(
1481 origin: OriginFor<T>,
1482 code_hash: sp_core::H256,
1483 ) -> DispatchResultWithPostInfo {
1484 let origin = ensure_signed(origin)?;
1485 <ContractBlob<T>>::remove(&origin, code_hash)?;
1486 Ok(Pays::No.into())
1488 }
1489
1490 #[pallet::call_index(6)]
1501 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1502 pub fn set_code(
1503 origin: OriginFor<T>,
1504 dest: H160,
1505 code_hash: sp_core::H256,
1506 ) -> DispatchResult {
1507 ensure_root(origin)?;
1508 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1509 let Some(account) = account else {
1510 return Err(<Error<T>>::ContractNotFound.into());
1511 };
1512
1513 let AccountType::Contract(ref mut contract) = account.account_type else {
1514 return Err(<Error<T>>::ContractNotFound.into());
1515 };
1516
1517 <CodeInfo<T>>::increment_refcount(code_hash)?;
1518 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1519 contract.code_hash = code_hash;
1520
1521 Ok(())
1522 })
1523 }
1524
1525 #[pallet::call_index(7)]
1530 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1531 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1532 Self::ensure_non_contract_if_signed(&origin)?;
1533 let origin = ensure_signed(origin)?;
1534 T::AddressMapper::map(&origin)
1535 }
1536
1537 #[pallet::call_index(8)]
1542 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1543 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1544 let origin = ensure_signed(origin)?;
1545 T::AddressMapper::unmap(&origin)
1546 }
1547
1548 #[pallet::call_index(9)]
1554 #[pallet::weight({
1555 let dispatch_info = call.get_dispatch_info();
1556 (
1557 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1558 dispatch_info.class
1559 )
1560 })]
1561 pub fn dispatch_as_fallback_account(
1562 origin: OriginFor<T>,
1563 call: Box<<T as Config>::RuntimeCall>,
1564 ) -> DispatchResultWithPostInfo {
1565 Self::ensure_non_contract_if_signed(&origin)?;
1566 let origin = ensure_signed(origin)?;
1567 let unmapped_account =
1568 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1569 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1570 }
1571 }
1572}
1573
1574fn dispatch_result<R>(
1576 result: Result<R, DispatchError>,
1577 weight_consumed: Weight,
1578 base_weight: Weight,
1579) -> DispatchResultWithPostInfo {
1580 let post_info = PostDispatchInfo {
1581 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1582 pays_fee: Default::default(),
1583 };
1584
1585 result
1586 .map(|_| post_info)
1587 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1588}
1589
1590impl<T: Config> Pallet<T> {
1591 pub fn bare_call(
1598 origin: OriginFor<T>,
1599 dest: H160,
1600 evm_value: U256,
1601 transaction_limits: TransactionLimits<T>,
1602 data: Vec<u8>,
1603 exec_config: ExecConfig<T>,
1604 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1605 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1606 Ok(transaction_meter) => transaction_meter,
1607 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1608 };
1609
1610 let mut storage_deposit = Default::default();
1611
1612 let try_call = || {
1613 let origin = ExecOrigin::from_runtime_origin(origin)?;
1614 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1615 origin.clone(),
1616 dest,
1617 &mut transaction_meter,
1618 evm_value,
1619 data,
1620 &exec_config,
1621 )?;
1622
1623 storage_deposit = transaction_meter
1624 .execute_postponed_deposits(&origin, &exec_config)
1625 .inspect_err(|err| {
1626 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1627 })?;
1628
1629 Ok(result)
1630 };
1631 let result = Self::run_guarded(try_call);
1632
1633 log::trace!(target: LOG_TARGET, "Bare call ends: \
1634 result={result:?}, \
1635 weight_consumed={:?}, \
1636 weight_required={:?}, \
1637 storage_deposit={:?}, \
1638 gas_consumed={:?}, \
1639 max_storage_deposit={:?}",
1640 transaction_meter.weight_consumed(),
1641 transaction_meter.weight_required(),
1642 storage_deposit,
1643 transaction_meter.total_consumed_gas(),
1644 transaction_meter.deposit_required()
1645 );
1646
1647 ContractResult {
1648 result: result.map_err(|r| r.error),
1649 weight_consumed: transaction_meter.weight_consumed(),
1650 weight_required: transaction_meter.weight_required(),
1651 storage_deposit,
1652 gas_consumed: transaction_meter.total_consumed_gas(),
1653 max_storage_deposit: transaction_meter.deposit_required(),
1654 }
1655 }
1656
1657 pub fn prepare_dry_run(account: &T::AccountId) {
1663 frame_system::Pallet::<T>::inc_account_nonce(account);
1666 }
1667
1668 pub fn bare_instantiate(
1674 origin: OriginFor<T>,
1675 evm_value: U256,
1676 transaction_limits: TransactionLimits<T>,
1677 code: Code,
1678 data: Vec<u8>,
1679 salt: Option<[u8; 32]>,
1680 exec_config: ExecConfig<T>,
1681 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1682 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1683 Ok(transaction_meter) => transaction_meter,
1684 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1685 };
1686
1687 let mut storage_deposit = Default::default();
1688
1689 let try_instantiate = || {
1690 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1691
1692 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1693 let executable = match code {
1694 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1695 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1696 let executable = Self::try_upload_code(
1697 upload_account,
1698 code,
1699 BytecodeType::Pvm,
1700 &mut transaction_meter,
1701 &exec_config,
1702 )?;
1703 executable
1704 },
1705 Code::Upload(code) =>
1706 if T::AllowEVMBytecode::get() {
1707 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1708 let origin = T::UploadOrigin::ensure_origin(origin)?;
1709 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1710 executable
1711 } else {
1712 return Err(<Error<T>>::CodeRejected.into())
1713 },
1714 Code::Existing(code_hash) => {
1715 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1716 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1717 executable
1718 },
1719 };
1720 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1721 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1722 instantiate_account,
1723 executable,
1724 &mut transaction_meter,
1725 evm_value,
1726 data,
1727 salt.as_ref(),
1728 &exec_config,
1729 );
1730
1731 storage_deposit = transaction_meter
1732 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1733 .inspect_err(|err| {
1734 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1735 })?;
1736 result
1737 };
1738 let output = Self::run_guarded(try_instantiate);
1739
1740 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1741 weight_required={:?} \
1742 storage_deposit={:?} \
1743 gas_consumed={:?} \
1744 max_storage_deposit={:?}",
1745 transaction_meter.weight_consumed(),
1746 transaction_meter.weight_required(),
1747 storage_deposit,
1748 transaction_meter.total_consumed_gas(),
1749 transaction_meter.deposit_required()
1750 );
1751
1752 ContractResult {
1753 result: output
1754 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1755 .map_err(|e| e.error),
1756 weight_consumed: transaction_meter.weight_consumed(),
1757 weight_required: transaction_meter.weight_required(),
1758 storage_deposit,
1759 gas_consumed: transaction_meter.total_consumed_gas(),
1760 max_storage_deposit: transaction_meter.deposit_required(),
1761 }
1762 }
1763
1764 pub fn dry_run_eth_transact(
1770 mut tx: GenericTransaction,
1771 dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1772 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1773 where
1774 T::Nonce: Into<U256>,
1775 CallOf<T>: SetWeightLimit,
1776 {
1777 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1778
1779 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1780 Self::prepare_dry_run(&origin);
1781
1782 let base_fee = Self::evm_base_fee();
1783 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1784
1785 if effective_gas_price < base_fee {
1786 Err(EthTransactError::Message(format!(
1787 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1788 )))?;
1789 }
1790
1791 if tx.nonce.is_none() {
1792 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1793 }
1794 if tx.chain_id.is_none() {
1795 tx.chain_id = Some(T::ChainId::get().into());
1796 }
1797
1798 tx.gas_price = Some(effective_gas_price);
1800 tx.max_priority_fee_per_gas = Some(0.into());
1803 if tx.max_fee_per_gas.is_none() {
1804 tx.max_fee_per_gas = Some(effective_gas_price);
1805 }
1806
1807 let gas = tx.gas;
1808 if tx.gas.is_none() {
1809 tx.gas = Some(Self::evm_block_gas_limit());
1810 }
1811 if tx.r#type.is_none() {
1812 tx.r#type = Some(TYPE_EIP1559.into());
1813 }
1814
1815 let value = tx.value.unwrap_or_default();
1817 let input = tx.input.clone().to_vec();
1818 let from = tx.from;
1819 let to = tx.to;
1820
1821 let mut call_info = tx
1824 .into_call::<T>(CreateCallMode::DryRun)
1825 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1826
1827 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1831 let base_weight = base_info.total_weight();
1832 let exec_config =
1833 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
1834 .with_dry_run(dry_run_config);
1835
1836 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
1838 if let Some(from) = &from {
1839 let fees = if gas.is_some() { fees } else { Zero::zero() };
1840 let balance = Self::evm_balance(from);
1841 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
1842 return Err(EthTransactError::Message(format!(
1843 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
1844 )));
1845 }
1846 }
1847
1848 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
1851
1852 let extract_error = |err| {
1853 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
1854 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
1855 } else {
1856 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
1857 }
1858 };
1859
1860 let transaction_limits = TransactionLimits::EthereumGas {
1861 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
1862 maybe_weight_limit: None,
1865 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
1866 };
1867
1868 let mut dry_run = match to {
1870 Some(dest) => {
1872 if dest == RUNTIME_PALLETS_ADDR {
1873 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
1874 return Err(EthTransactError::Message(format!(
1875 "Failed to decode pallet-call {input:?}"
1876 )));
1877 };
1878
1879 if let Err(result) =
1880 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1881 {
1882 return Err(EthTransactError::Message(format!(
1883 "Failed to dispatch call: {:?}",
1884 result.error,
1885 )));
1886 };
1887
1888 Default::default()
1889 } else {
1890 let result = crate::Pallet::<T>::bare_call(
1892 OriginFor::<T>::signed(origin),
1893 dest,
1894 value,
1895 transaction_limits,
1896 input.clone(),
1897 exec_config,
1898 );
1899
1900 let data = match result.result {
1901 Ok(return_value) => {
1902 if return_value.did_revert() {
1903 return Err(EthTransactError::Data(return_value.data));
1904 }
1905 return_value.data
1906 },
1907 Err(err) => {
1908 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1909 return extract_error(err);
1910 },
1911 };
1912
1913 EthTransactInfo {
1914 weight_required: result.weight_required,
1915 storage_deposit: result.storage_deposit.charge_or_zero(),
1916 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1917 data,
1918 eth_gas: Default::default(),
1919 }
1920 }
1921 },
1922 None => {
1924 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1926 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1927 } else {
1928 (input, vec![])
1929 };
1930
1931 let result = crate::Pallet::<T>::bare_instantiate(
1933 OriginFor::<T>::signed(origin),
1934 value,
1935 transaction_limits,
1936 Code::Upload(code.clone()),
1937 data.clone(),
1938 None,
1939 exec_config,
1940 );
1941
1942 let returned_data = match result.result {
1943 Ok(return_value) => {
1944 if return_value.result.did_revert() {
1945 return Err(EthTransactError::Data(return_value.result.data));
1946 }
1947 return_value.result.data
1948 },
1949 Err(err) => {
1950 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1951 return extract_error(err);
1952 },
1953 };
1954
1955 EthTransactInfo {
1956 weight_required: result.weight_required,
1957 storage_deposit: result.storage_deposit.charge_or_zero(),
1958 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1959 data: returned_data,
1960 eth_gas: Default::default(),
1961 }
1962 },
1963 };
1964
1965 call_info.call.set_weight_limit(dry_run.weight_required);
1967
1968 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
1970 let max_weight = Self::evm_max_extrinsic_weight();
1971 if total_weight.any_gt(max_weight) {
1972 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
1973 total_weight={total_weight:?} \
1974 max_weight={max_weight:?}",
1975 );
1976
1977 Err(EthTransactError::Message(format!(
1978 "\
1979 The transaction consumes more than the allowed weight. \
1980 needed={total_weight} \
1981 allowed={max_weight} \
1982 overweight_by={}\
1983 ",
1984 total_weight.saturating_sub(max_weight),
1985 )))?;
1986 }
1987
1988 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
1990 let available_fee = T::FeeInfo::remaining_txfee();
1991 if transaction_fee > available_fee {
1992 Err(EthTransactError::Message(format!(
1993 "Not enough gas supplied: Off by: {:?}",
1994 transaction_fee.saturating_sub(available_fee),
1995 )))?;
1996 }
1997
1998 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
1999 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2000 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2001 if !rest.is_zero() {
2002 eth_gas = eth_gas.saturating_add(1_u32.into());
2003 }
2004
2005 log::debug!(target: LOG_TARGET, "\
2006 dry_run_eth_transact finished: \
2007 weight_limit={}, \
2008 total_weight={total_weight}, \
2009 max_weight={max_weight}, \
2010 weight_left={}, \
2011 eth_gas={eth_gas}, \
2012 encoded_len={}, \
2013 tx_fee={transaction_fee:?}, \
2014 storage_deposit={:?}, \
2015 max_storage_deposit={:?}\
2016 ",
2017 dry_run.weight_required,
2018 max_weight.saturating_sub(total_weight),
2019 call_info.encoded_len,
2020 dry_run.storage_deposit,
2021 dry_run.max_storage_deposit,
2022
2023 );
2024 dry_run.eth_gas = eth_gas;
2025 Ok(dry_run)
2026 }
2027
2028 pub fn evm_balance(address: &H160) -> U256 {
2032 let balance = AccountInfo::<T>::balance_of((*address).into());
2033 Self::convert_native_to_evm(balance)
2034 }
2035
2036 pub fn eth_block() -> EthBlock {
2038 EthereumBlock::<T>::get()
2039 }
2040
2041 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2048 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2049 let hash = <BlockHash<T>>::get(number);
2050 if hash == H256::zero() {
2051 None
2052 } else {
2053 Some(hash)
2054 }
2055 }
2056
2057 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2059 ReceiptInfoData::<T>::get()
2060 }
2061
2062 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2068 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2069 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2070 let account_id = T::AddressMapper::to_account_id(&address);
2071 T::Currency::set_balance(&account_id, balance);
2072 AccountInfoOf::<T>::mutate(&address, |account| {
2073 if let Some(account) = account {
2074 account.dust = dust;
2075 } else {
2076 *account = Some(AccountInfo { dust, ..Default::default() });
2077 }
2078 });
2079
2080 Ok(())
2081 }
2082
2083 pub fn new_balance_with_dust(
2087 evm_value: U256,
2088 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2089 let ed = T::Currency::minimum_balance();
2090 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2091 let (value, dust) = balance_with_dust.deconstruct();
2092
2093 Ok((ed.saturating_add(value), dust))
2094 }
2095
2096 pub fn evm_nonce(address: &H160) -> u32
2098 where
2099 T::Nonce: Into<u32>,
2100 {
2101 let account = T::AddressMapper::to_account_id(&address);
2102 System::<T>::account_nonce(account).into()
2103 }
2104
2105 pub fn evm_block_gas_limit() -> U256 {
2107 u64::MAX.into()
2114 }
2115
2116 pub fn evm_max_extrinsic_weight() -> Weight {
2118 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2119 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2120 .get(DispatchClass::Normal)
2121 .max_extrinsic
2122 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2123 Weight::from_parts(
2124 factor.saturating_mul_int(max_weight.ref_time()),
2125 factor.saturating_mul_int(max_weight.proof_size()),
2126 )
2127 }
2128
2129 pub fn evm_base_fee() -> U256 {
2131 let gas_scale = <T as Config>::GasScale::get();
2132 let multiplier = T::FeeInfo::next_fee_multiplier();
2133 multiplier
2134 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2135 .saturating_mul(gas_scale.saturated_into())
2136 .into()
2137 }
2138
2139 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2141 where
2142 T::Nonce: Into<u32>,
2143 {
2144 match tracer_type {
2145 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2146 TracerType::PrestateTracer(config) =>
2147 PrestateTracer::new(config.unwrap_or_default()).into(),
2148 }
2149 }
2150
2151 pub fn bare_upload_code(
2155 origin: OriginFor<T>,
2156 code: Vec<u8>,
2157 storage_deposit_limit: BalanceOf<T>,
2158 ) -> CodeUploadResult<BalanceOf<T>> {
2159 let origin = T::UploadOrigin::ensure_origin(origin)?;
2160
2161 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2162 BytecodeType::Pvm
2163 } else {
2164 if !T::AllowEVMBytecode::get() {
2165 return Err(<Error<T>>::CodeRejected.into())
2166 }
2167 BytecodeType::Evm
2168 };
2169
2170 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2171 weight_limit: Default::default(),
2172 deposit_limit: storage_deposit_limit,
2173 })?;
2174
2175 let module = Self::try_upload_code(
2176 origin,
2177 code,
2178 bytecode_type,
2179 &mut meter,
2180 &ExecConfig::new_substrate_tx(),
2181 )?;
2182 Ok(CodeUploadReturnValue {
2183 code_hash: *module.code_hash(),
2184 deposit: meter.deposit_consumed().charge_or_zero(),
2185 })
2186 }
2187
2188 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2190 let contract_info =
2191 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2192
2193 let maybe_value = contract_info.read(&Key::from_fixed(key));
2194 Ok(maybe_value)
2195 }
2196
2197 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2201 let immutable_data = <ImmutableDataOf<T>>::get(address);
2202 immutable_data
2203 }
2204
2205 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2213 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2214 <ImmutableDataOf<T>>::insert(address, data);
2215 Ok(())
2216 }
2217
2218 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2220 let contract_info =
2221 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2222
2223 let maybe_value = contract_info.read(
2224 &Key::try_from_var(key)
2225 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2226 .into(),
2227 );
2228 Ok(maybe_value)
2229 }
2230
2231 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2233 let (value, dust) = value.into().deconstruct();
2234 value
2235 .into()
2236 .saturating_mul(T::NativeToEthRatio::get().into())
2237 .saturating_add(dust.into())
2238 }
2239
2240 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2250 let contract_info =
2251 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2252
2253 contract_info
2254 .write(&Key::from_fixed(key), value, None, false)
2255 .map_err(ContractAccessError::StorageWriteFailed)
2256 }
2257
2258 pub fn set_storage_var_key(
2269 address: H160,
2270 key: Vec<u8>,
2271 value: Option<Vec<u8>>,
2272 ) -> SetStorageResult {
2273 let contract_info =
2274 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2275
2276 contract_info
2277 .write(
2278 &Key::try_from_var(key)
2279 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2280 .into(),
2281 value,
2282 None,
2283 false,
2284 )
2285 .map_err(ContractAccessError::StorageWriteFailed)
2286 }
2287
2288 pub fn account_id() -> T::AccountId {
2290 use frame_support::PalletId;
2291 use sp_runtime::traits::AccountIdConversion;
2292 PalletId(*b"py/reviv").into_account_truncating()
2293 }
2294
2295 pub fn block_author() -> H160 {
2297 use frame_support::traits::FindAuthor;
2298
2299 let digest = <frame_system::Pallet<T>>::digest();
2300 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2301
2302 T::FindAuthor::find_author(pre_runtime_digests)
2303 .map(|account_id| T::AddressMapper::to_address(&account_id))
2304 .unwrap_or_default()
2305 }
2306
2307 pub fn code(address: &H160) -> Vec<u8> {
2311 use precompiles::{All, Precompiles};
2312 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2313 return code.into()
2314 }
2315 AccountInfo::<T>::load_contract(&address)
2316 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2317 .map(|code| code.into())
2318 .unwrap_or_default()
2319 }
2320
2321 pub fn try_upload_code(
2323 origin: T::AccountId,
2324 code: Vec<u8>,
2325 code_type: BytecodeType,
2326 meter: &mut TransactionMeter<T>,
2327 exec_config: &ExecConfig<T>,
2328 ) -> Result<ContractBlob<T>, DispatchError> {
2329 let mut module = match code_type {
2330 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2331 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2332 };
2333 module.store_code(exec_config, meter)?;
2334 Ok(module)
2335 }
2336
2337 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2339 executing_contract::using_once(&mut false, || {
2340 executing_contract::with(|f| {
2341 if *f {
2343 return Err(())
2344 }
2345 *f = true;
2347 Ok(())
2348 })
2349 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2350 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2351 .map(|_| f())
2352 .and_then(|r| r)
2353 })
2354 }
2355
2356 fn charge_deposit(
2361 hold_reason: Option<HoldReason>,
2362 from: &T::AccountId,
2363 to: &T::AccountId,
2364 amount: BalanceOf<T>,
2365 exec_config: &ExecConfig<T>,
2366 ) -> DispatchResult {
2367 use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
2368
2369 if amount.is_zero() {
2370 return Ok(());
2371 }
2372
2373 match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2374 (true, hold_reason) => {
2375 T::FeeInfo::withdraw_txfee(amount)
2376 .ok_or(())
2377 .and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2378 .and_then(|_| {
2379 if let Some(hold_reason) = hold_reason {
2380 T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2381 }
2382 Ok(())
2383 })
2384 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2385 },
2386 (false, Some(hold_reason)) => {
2387 T::Currency::transfer_and_hold(
2388 &hold_reason.into(),
2389 from,
2390 to,
2391 amount,
2392 Precision::Exact,
2393 Preservation::Preserve,
2394 Fortitude::Polite,
2395 )
2396 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2397 },
2398 (false, None) => {
2399 T::Currency::transfer(from, to, amount, Preservation::Preserve)
2400 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2401 },
2402 }
2403 Ok(())
2404 }
2405
2406 fn refund_deposit(
2411 hold_reason: HoldReason,
2412 from: &T::AccountId,
2413 to: &T::AccountId,
2414 amount: BalanceOf<T>,
2415 exec_config: Option<&ExecConfig<T>>,
2416 ) -> Result<(), DispatchError> {
2417 use frame_support::traits::{
2418 fungible::InspectHold,
2419 tokens::{Fortitude, Precision, Preservation, Restriction},
2420 };
2421
2422 if amount.is_zero() {
2423 return Ok(());
2424 }
2425
2426 let hold_reason = hold_reason.into();
2427 let result = if exec_config.map(|c| c.collect_deposit_from_hold.is_some()).unwrap_or(false)
2428 {
2429 T::Currency::release(&hold_reason, from, amount, Precision::Exact)
2430 .and_then(|amount| {
2431 T::Currency::withdraw(
2432 from,
2433 amount,
2434 Precision::Exact,
2435 Preservation::Preserve,
2436 Fortitude::Polite,
2437 )
2438 })
2439 .map(T::FeeInfo::deposit_txfee)
2440 } else {
2441 T::Currency::transfer_on_hold(
2442 &hold_reason,
2443 from,
2444 to,
2445 amount,
2446 Precision::Exact,
2447 Restriction::Free,
2448 Fortitude::Polite,
2449 )
2450 .map(|_| ())
2451 };
2452
2453 result.map_err(|_| {
2454 let available = T::Currency::balance_on_hold(&hold_reason, from);
2455 if available < amount {
2456 log::error!(
2459 target: LOG_TARGET,
2460 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. Not enough deposit: {:?}. This is a bug.",
2461 amount, from, to, available,
2462 );
2463 Error::<T>::StorageRefundNotEnoughFunds.into()
2464 } else {
2465 log::warn!(
2470 target: LOG_TARGET,
2471 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. First remove locks (staking, governance) from the contracts account.",
2472 amount, from, to,
2473 );
2474 Error::<T>::StorageRefundLocked.into()
2475 }
2476 })
2477 }
2478
2479 fn has_dust(value: U256) -> bool {
2481 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2482 }
2483
2484 fn has_balance(value: U256) -> bool {
2486 value >= U256::from(<T>::NativeToEthRatio::get())
2487 }
2488
2489 fn min_balance() -> BalanceOf<T> {
2491 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2492 }
2493
2494 fn deposit_event(event: Event<T>) {
2499 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2500 }
2501
2502 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2504 match <T as Config>::RuntimeOrigin::from(origin).into() {
2505 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2506 _ => Err(BadOrigin.into()),
2507 }
2508 }
2509
2510 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2514 if DebugSettings::bypass_eip_3607::<T>() {
2515 return Ok(())
2516 }
2517 let Some(address) = origin
2518 .as_system_ref()
2519 .and_then(|o| o.as_signed())
2520 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2521 else {
2522 return Ok(())
2523 };
2524 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2525 <AccountInfo<T>>::is_contract(&address)
2526 {
2527 log::debug!(
2528 target: crate::LOG_TARGET,
2529 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2530 );
2531 Err(DispatchError::BadOrigin)
2532 } else {
2533 Ok(())
2534 }
2535 }
2536}
2537
2538pub const RUNTIME_PALLETS_ADDR: H160 =
2543 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2544
2545environmental!(executing_contract: bool);
2547
2548sp_api::decl_runtime_apis! {
2549 #[api_version(1)]
2551 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2552 AccountId: Codec,
2553 Balance: Codec,
2554 Nonce: Codec,
2555 BlockNumber: Codec,
2556 Moment: Codec,
2557 {
2558 fn eth_block() -> EthBlock;
2562
2563 fn eth_block_hash(number: U256) -> Option<H256>;
2565
2566 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2572
2573 fn block_gas_limit() -> U256;
2575
2576 fn balance(address: H160) -> U256;
2578
2579 fn gas_price() -> U256;
2581
2582 fn nonce(address: H160) -> Nonce;
2584
2585 fn call(
2589 origin: AccountId,
2590 dest: H160,
2591 value: Balance,
2592 gas_limit: Option<Weight>,
2593 storage_deposit_limit: Option<Balance>,
2594 input_data: Vec<u8>,
2595 ) -> ContractResult<ExecReturnValue, Balance>;
2596
2597 fn instantiate(
2601 origin: AccountId,
2602 value: Balance,
2603 gas_limit: Option<Weight>,
2604 storage_deposit_limit: Option<Balance>,
2605 code: Code,
2606 data: Vec<u8>,
2607 salt: Option<[u8; 32]>,
2608 ) -> ContractResult<InstantiateReturnValue, Balance>;
2609
2610
2611 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2616
2617 fn eth_transact_with_config(
2621 tx: GenericTransaction,
2622 config: DryRunConfig<Moment>,
2623 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2624
2625 fn upload_code(
2629 origin: AccountId,
2630 code: Vec<u8>,
2631 storage_deposit_limit: Option<Balance>,
2632 ) -> CodeUploadResult<Balance>;
2633
2634 fn get_storage(
2640 address: H160,
2641 key: [u8; 32],
2642 ) -> GetStorageResult;
2643
2644 fn get_storage_var_key(
2650 address: H160,
2651 key: Vec<u8>,
2652 ) -> GetStorageResult;
2653
2654 fn trace_block(
2661 block: Block,
2662 config: TracerType
2663 ) -> Vec<(u32, Trace)>;
2664
2665 fn trace_tx(
2672 block: Block,
2673 tx_index: u32,
2674 config: TracerType
2675 ) -> Option<Trace>;
2676
2677 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2681
2682 fn block_author() -> H160;
2684
2685 fn address(account_id: AccountId) -> H160;
2687
2688 fn account_id(address: H160) -> AccountId;
2690
2691 fn runtime_pallets_address() -> H160;
2693
2694 fn code(address: H160) -> Vec<u8>;
2696
2697 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2699 }
2700}
2701
2702#[macro_export]
2716macro_rules! impl_runtime_apis_plus_revive_traits {
2717 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2718
2719 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2720
2721 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2722 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
2723 use $crate::pallet::Call as ReviveCall;
2724 match self {
2725 Self::$Revive(
2726 ReviveCall::eth_call{ weight_limit, .. } |
2727 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
2728 ) => {
2729 let old = *weight_limit;
2730 *weight_limit = new_weight_limit;
2731 old
2732 },
2733 _ => Weight::default(),
2734 }
2735 }
2736 }
2737
2738 impl_runtime_apis! {
2739 $($rest)*
2740
2741
2742 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2743 {
2744 fn eth_block() -> $crate::EthBlock {
2745 $crate::Pallet::<Self>::eth_block()
2746 }
2747
2748 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2749 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
2750 }
2751
2752 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2753 $crate::Pallet::<Self>::eth_receipt_data()
2754 }
2755
2756 fn balance(address: $crate::H160) -> $crate::U256 {
2757 $crate::Pallet::<Self>::evm_balance(&address)
2758 }
2759
2760 fn block_author() -> $crate::H160 {
2761 $crate::Pallet::<Self>::block_author()
2762 }
2763
2764 fn block_gas_limit() -> $crate::U256 {
2765 $crate::Pallet::<Self>::evm_block_gas_limit()
2766 }
2767
2768 fn gas_price() -> $crate::U256 {
2769 $crate::Pallet::<Self>::evm_base_fee()
2770 }
2771
2772 fn nonce(address: $crate::H160) -> Nonce {
2773 use $crate::AddressMapper;
2774 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2775 $crate::frame_system::Pallet::<Self>::account_nonce(account)
2776 }
2777
2778 fn address(account_id: AccountId) -> $crate::H160 {
2779 use $crate::AddressMapper;
2780 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2781 }
2782
2783 fn eth_transact(
2784 tx: $crate::evm::GenericTransaction,
2785 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2786 use $crate::{
2787 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2788 sp_runtime::traits::TransactionExtension,
2789 sp_runtime::traits::Block as BlockT
2790 };
2791 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
2792 }
2793
2794 fn eth_transact_with_config(
2795 tx: $crate::evm::GenericTransaction,
2796 config: $crate::DryRunConfig<__ReviveMacroMoment>,
2797 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2798 use $crate::{
2799 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2800 sp_runtime::traits::TransactionExtension,
2801 sp_runtime::traits::Block as BlockT
2802 };
2803 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
2804 }
2805
2806 fn call(
2807 origin: AccountId,
2808 dest: $crate::H160,
2809 value: Balance,
2810 weight_limit: Option<$crate::Weight>,
2811 storage_deposit_limit: Option<Balance>,
2812 input_data: Vec<u8>,
2813 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2814 use $crate::frame_support::traits::Get;
2815 let blockweights: $crate::BlockWeights =
2816 <Self as $crate::frame_system::Config>::BlockWeights::get();
2817
2818 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2819 $crate::Pallet::<Self>::bare_call(
2820 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2821 dest,
2822 $crate::Pallet::<Self>::convert_native_to_evm(value),
2823 $crate::TransactionLimits::WeightAndDeposit {
2824 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2825 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2826 },
2827 input_data,
2828 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2829 )
2830 }
2831
2832 fn instantiate(
2833 origin: AccountId,
2834 value: Balance,
2835 weight_limit: Option<$crate::Weight>,
2836 storage_deposit_limit: Option<Balance>,
2837 code: $crate::Code,
2838 data: Vec<u8>,
2839 salt: Option<[u8; 32]>,
2840 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2841 use $crate::frame_support::traits::Get;
2842 let blockweights: $crate::BlockWeights =
2843 <Self as $crate::frame_system::Config>::BlockWeights::get();
2844
2845 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2846 $crate::Pallet::<Self>::bare_instantiate(
2847 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2848 $crate::Pallet::<Self>::convert_native_to_evm(value),
2849 $crate::TransactionLimits::WeightAndDeposit {
2850 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2851 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2852 },
2853 code,
2854 data,
2855 salt,
2856 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2857 )
2858 }
2859
2860 fn upload_code(
2861 origin: AccountId,
2862 code: Vec<u8>,
2863 storage_deposit_limit: Option<Balance>,
2864 ) -> $crate::CodeUploadResult<Balance> {
2865 let origin =
2866 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
2867 $crate::Pallet::<Self>::bare_upload_code(
2868 origin,
2869 code,
2870 storage_deposit_limit.unwrap_or(u128::MAX),
2871 )
2872 }
2873
2874 fn get_storage_var_key(
2875 address: $crate::H160,
2876 key: Vec<u8>,
2877 ) -> $crate::GetStorageResult {
2878 $crate::Pallet::<Self>::get_storage_var_key(address, key)
2879 }
2880
2881 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2882 $crate::Pallet::<Self>::get_storage(address, key)
2883 }
2884
2885 fn trace_block(
2886 block: Block,
2887 tracer_type: $crate::evm::TracerType,
2888 ) -> Vec<(u32, $crate::evm::Trace)> {
2889 use $crate::{sp_runtime::traits::Block, tracing::trace};
2890 let mut traces = vec![];
2891 let (header, extrinsics) = block.deconstruct();
2892 <$Executive>::initialize_block(&header);
2893 for (index, ext) in extrinsics.into_iter().enumerate() {
2894 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2895 let t = tracer.as_tracing();
2896 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2897
2898 if let Some(tx_trace) = tracer.collect_trace() {
2899 traces.push((index as u32, tx_trace));
2900 }
2901 }
2902
2903 traces
2904 }
2905
2906 fn trace_tx(
2907 block: Block,
2908 tx_index: u32,
2909 tracer_type: $crate::evm::TracerType,
2910 ) -> Option<$crate::evm::Trace> {
2911 use $crate::{sp_runtime::traits::Block, tracing::trace};
2912
2913 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2914 let (header, extrinsics) = block.deconstruct();
2915
2916 <$Executive>::initialize_block(&header);
2917 for (index, ext) in extrinsics.into_iter().enumerate() {
2918 if index as u32 == tx_index {
2919 let t = tracer.as_tracing();
2920 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2921 break;
2922 } else {
2923 let _ = <$Executive>::apply_extrinsic(ext);
2924 }
2925 }
2926
2927 tracer.collect_trace()
2928 }
2929
2930 fn trace_call(
2931 tx: $crate::evm::GenericTransaction,
2932 tracer_type: $crate::evm::TracerType,
2933 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2934 use $crate::tracing::trace;
2935 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2936 let t = tracer.as_tracing();
2937
2938 t.watch_address(&tx.from.unwrap_or_default());
2939 t.watch_address(&$crate::Pallet::<Self>::block_author());
2940 let result = trace(t, || Self::eth_transact(tx));
2941
2942 if let Some(trace) = tracer.collect_trace() {
2943 Ok(trace)
2944 } else if let Err(err) = result {
2945 Err(err)
2946 } else {
2947 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
2948 }
2949 }
2950
2951 fn runtime_pallets_address() -> $crate::H160 {
2952 $crate::RUNTIME_PALLETS_ADDR
2953 }
2954
2955 fn code(address: $crate::H160) -> Vec<u8> {
2956 $crate::Pallet::<Self>::code(&address)
2957 }
2958
2959 fn account_id(address: $crate::H160) -> AccountId {
2960 use $crate::AddressMapper;
2961 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
2962 }
2963
2964 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2965 $crate::Pallet::<Self>::new_balance_with_dust(balance)
2966 }
2967 }
2968 }
2969 };
2970}