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 EcdsaRecoveryFailed = 0x41,
628 #[cfg(feature = "runtime-benchmarks")]
630 BenchmarkingError = 0xFF,
631 }
632
633 #[pallet::composite_enum]
635 pub enum HoldReason {
636 CodeUploadDepositReserve,
638 StorageDepositReserve,
640 AddressMapping,
642 }
643
644 #[derive(
645 PartialEq,
646 Eq,
647 Clone,
648 MaxEncodedLen,
649 Encode,
650 Decode,
651 DecodeWithMemTracking,
652 TypeInfo,
653 RuntimeDebug,
654 )]
655 #[pallet::origin]
656 pub enum Origin<T: Config> {
657 EthTransaction(T::AccountId),
658 }
659
660 #[pallet::storage]
664 #[pallet::unbounded]
665 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
666
667 #[pallet::storage]
669 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
670
671 #[pallet::storage]
673 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
674
675 #[pallet::storage]
677 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
678
679 #[pallet::storage]
684 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
685
686 #[pallet::storage]
689 pub(crate) type DeletionQueueCounter<T: Config> =
690 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
691
692 #[pallet::storage]
699 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
700
701 #[pallet::storage]
711 #[pallet::unbounded]
712 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
713
714 #[pallet::storage]
718 pub(crate) type BlockHash<T: Config> =
719 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
720
721 #[pallet::storage]
728 #[pallet::unbounded]
729 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
730
731 #[pallet::storage]
733 #[pallet::unbounded]
734 pub(crate) type EthBlockBuilderIR<T: Config> =
735 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
736
737 #[pallet::storage]
742 #[pallet::unbounded]
743 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
744 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
745
746 #[pallet::storage]
748 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
749
750 pub mod genesis {
751 use super::*;
752 use crate::evm::Bytes32;
753
754 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
756 pub struct ContractData {
757 pub code: Vec<u8>,
759 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
761 }
762
763 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
765 pub struct Account<T: Config> {
766 pub address: H160,
768 #[serde(default)]
770 pub balance: U256,
771 #[serde(default)]
773 pub nonce: T::Nonce,
774 #[serde(flatten, skip_serializing_if = "Option::is_none")]
776 pub contract_data: Option<ContractData>,
777 }
778 }
779
780 #[pallet::genesis_config]
781 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
782 pub struct GenesisConfig<T: Config> {
783 #[serde(default, skip_serializing_if = "Vec::is_empty")]
786 pub mapped_accounts: Vec<T::AccountId>,
787
788 #[serde(default, skip_serializing_if = "Vec::is_empty")]
790 pub accounts: Vec<genesis::Account<T>>,
791
792 #[serde(default, skip_serializing_if = "Option::is_none")]
794 pub debug_settings: Option<DebugSettings>,
795 }
796
797 #[pallet::genesis_build]
798 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
799 fn build(&self) {
800 use crate::{exec::Key, vm::ContractBlob};
801 use frame_support::traits::fungible::Mutate;
802
803 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
804 let _ = T::Currency::mint_into(
805 &Pallet::<T>::account_id(),
806 T::Currency::minimum_balance(),
807 );
808 }
809
810 for id in &self.mapped_accounts {
811 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
812 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
813 }
814 }
815
816 let owner = Pallet::<T>::account_id();
817
818 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
819 let account_id = T::AddressMapper::to_account_id(address);
820
821 if !System::<T>::account_exists(&account_id) {
822 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
823 }
824
825 frame_system::Account::<T>::mutate(&account_id, |info| {
826 info.nonce = (*nonce).into();
827 });
828
829 match contract_data {
830 None => {
831 AccountInfoOf::<T>::insert(
832 address,
833 AccountInfo { account_type: AccountType::EOA, dust: 0 },
834 );
835 },
836 Some(genesis::ContractData { code, storage }) => {
837 let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
838 ContractBlob::<T>::from_pvm_code( code.clone(), owner.clone()).inspect_err(|err| {
839 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
840 })
841 } else {
842 ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
843 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
844 })
845 };
846
847 let Ok(blob) = blob else {
848 continue;
849 };
850
851 let code_hash = *blob.code_hash();
852 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
853 .inspect_err(|err| {
854 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
855 })
856 else {
857 continue;
858 };
859
860 AccountInfoOf::<T>::insert(
861 address,
862 AccountInfo { account_type: info.clone().into(), dust: 0 },
863 );
864
865 <PristineCode<T>>::insert(blob.code_hash(), code);
866 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
867 for (k, v) in storage {
868 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
869 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
870 });
871 }
872 },
873 }
874
875 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
876 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
877 });
878 }
879
880 block_storage::on_finalize_build_eth_block::<T>(
882 frame_system::Pallet::<T>::block_number(),
885 );
886
887 if let Some(settings) = self.debug_settings.as_ref() {
889 settings.write_to_storage::<T>()
890 }
891 }
892 }
893
894 #[pallet::hooks]
895 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
896 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
897 let mut meter = WeightMeter::with_limit(limit);
898 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
899 meter.consumed()
900 }
901
902 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
903 block_storage::on_initialize::<T>();
905
906 System::<T>::account_exists(&Pallet::<T>::account_id());
908 <T as Config>::WeightInfo::on_finalize_block_fixed()
910 }
911
912 fn on_finalize(block_number: BlockNumberFor<T>) {
913 block_storage::on_finalize_build_eth_block::<T>(block_number);
915 }
916
917 fn integrity_test() {
918 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
919
920 assert!(T::GasScale::get() > 0u32.into(), "GasScale must not be 0");
921
922 T::FeeInfo::integrity_test();
923
924 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
926
927 const TOTAL_MEMORY_DEVIDER: u64 = 2;
930
931 let max_block_weight = T::BlockWeights::get()
937 .get(DispatchClass::Normal)
938 .max_total
939 .unwrap_or_else(|| T::BlockWeights::get().max_block);
940 let max_key_size: u64 =
941 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
942 .expect("Key of maximal size shall be created")
943 .hash()
944 .len()
945 .try_into()
946 .unwrap();
947
948 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
949 let max_immutable_size: u64 = max_block_weight
950 .checked_div_per_component(&<RuntimeCosts as WeightToken<T>>::weight(
951 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
952 ))
953 .unwrap()
954 .saturating_mul(
955 u64::from(limits::IMMUTABLE_BYTES)
956 .saturating_add(max_immutable_key_size)
957 .into(),
958 );
959
960 let max_pvf_mem: u64 = T::PVFMemory::get().into();
961 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
962
963 let max_events_size = max_block_weight
967 .checked_div_per_component(
968 &(<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::DepositEvent {
969 num_topic: 0,
970 len: limits::EVENT_BYTES,
971 })
972 .saturating_add(<RuntimeCosts as WeightToken<T>>::weight(
973 &RuntimeCosts::HostFn,
974 ))),
975 )
976 .unwrap()
977 .saturating_mul(limits::EVENT_BYTES.into());
978
979 assert!(
980 max_events_size <= storage_size_limit,
981 "Maximal events size {} exceeds the events limit {}",
982 max_events_size,
983 storage_size_limit
984 );
985
986 let max_eth_block_builder_bytes =
1021 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
1022
1023 log::debug!(
1024 target: LOG_TARGET,
1025 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
1026 max_eth_block_builder_bytes / 1024,
1027 max_events_size / 1024,
1028 );
1029
1030 let memory_left = i128::from(max_runtime_mem)
1035 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1036 .saturating_sub(limits::MEMORY_REQUIRED.into())
1037 .saturating_sub(max_eth_block_builder_bytes.into());
1038
1039 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1040
1041 assert!(
1042 memory_left >= 0,
1043 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1044 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1045 );
1046
1047 let max_storage_size = max_block_weight
1050 .checked_div_per_component(
1051 &<RuntimeCosts as WeightToken<T>>::weight(&RuntimeCosts::SetStorage {
1052 new_bytes: limits::STORAGE_BYTES,
1053 old_bytes: 0,
1054 })
1055 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1056 )
1057 .unwrap()
1058 .saturating_add(max_immutable_size.into())
1059 .saturating_add(max_eth_block_builder_bytes.into());
1060
1061 assert!(
1062 max_storage_size <= storage_size_limit,
1063 "Maximal storage size {} exceeds the storage limit {}",
1064 max_storage_size,
1065 storage_size_limit
1066 );
1067 }
1068 }
1069
1070 #[pallet::call]
1071 impl<T: Config> Pallet<T> {
1072 #[allow(unused_variables)]
1085 #[pallet::call_index(0)]
1086 #[pallet::weight(Weight::MAX)]
1087 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1088 Err(frame_system::Error::CallFiltered::<T>.into())
1089 }
1090
1091 #[pallet::call_index(1)]
1108 #[pallet::weight(<T as Config>::WeightInfo::call().saturating_add(*weight_limit))]
1109 pub fn call(
1110 origin: OriginFor<T>,
1111 dest: H160,
1112 #[pallet::compact] value: BalanceOf<T>,
1113 weight_limit: Weight,
1114 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1115 data: Vec<u8>,
1116 ) -> DispatchResultWithPostInfo {
1117 Self::ensure_non_contract_if_signed(&origin)?;
1118 let mut output = Self::bare_call(
1119 origin,
1120 dest,
1121 Pallet::<T>::convert_native_to_evm(value),
1122 TransactionLimits::WeightAndDeposit {
1123 weight_limit,
1124 deposit_limit: storage_deposit_limit,
1125 },
1126 data,
1127 ExecConfig::new_substrate_tx(),
1128 );
1129
1130 if let Ok(return_value) = &output.result {
1131 if return_value.did_revert() {
1132 output.result = Err(<Error<T>>::ContractReverted.into());
1133 }
1134 }
1135 dispatch_result(
1136 output.result,
1137 output.weight_consumed,
1138 <T as Config>::WeightInfo::call(),
1139 )
1140 }
1141
1142 #[pallet::call_index(2)]
1148 #[pallet::weight(
1149 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*weight_limit)
1150 )]
1151 pub fn instantiate(
1152 origin: OriginFor<T>,
1153 #[pallet::compact] value: BalanceOf<T>,
1154 weight_limit: Weight,
1155 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1156 code_hash: sp_core::H256,
1157 data: Vec<u8>,
1158 salt: Option<[u8; 32]>,
1159 ) -> DispatchResultWithPostInfo {
1160 Self::ensure_non_contract_if_signed(&origin)?;
1161 let data_len = data.len() as u32;
1162 let mut output = Self::bare_instantiate(
1163 origin,
1164 Pallet::<T>::convert_native_to_evm(value),
1165 TransactionLimits::WeightAndDeposit {
1166 weight_limit,
1167 deposit_limit: storage_deposit_limit,
1168 },
1169 Code::Existing(code_hash),
1170 data,
1171 salt,
1172 ExecConfig::new_substrate_tx(),
1173 );
1174 if let Ok(retval) = &output.result {
1175 if retval.result.did_revert() {
1176 output.result = Err(<Error<T>>::ContractReverted.into());
1177 }
1178 }
1179 dispatch_result(
1180 output.result.map(|result| result.result),
1181 output.weight_consumed,
1182 <T as Config>::WeightInfo::instantiate(data_len),
1183 )
1184 }
1185
1186 #[pallet::call_index(3)]
1214 #[pallet::weight(
1215 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1216 .saturating_add(*weight_limit)
1217 )]
1218 pub fn instantiate_with_code(
1219 origin: OriginFor<T>,
1220 #[pallet::compact] value: BalanceOf<T>,
1221 weight_limit: Weight,
1222 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1223 code: Vec<u8>,
1224 data: Vec<u8>,
1225 salt: Option<[u8; 32]>,
1226 ) -> DispatchResultWithPostInfo {
1227 Self::ensure_non_contract_if_signed(&origin)?;
1228 let code_len = code.len() as u32;
1229 let data_len = data.len() as u32;
1230 let mut output = Self::bare_instantiate(
1231 origin,
1232 Pallet::<T>::convert_native_to_evm(value),
1233 TransactionLimits::WeightAndDeposit {
1234 weight_limit,
1235 deposit_limit: storage_deposit_limit,
1236 },
1237 Code::Upload(code),
1238 data,
1239 salt,
1240 ExecConfig::new_substrate_tx(),
1241 );
1242 if let Ok(retval) = &output.result {
1243 if retval.result.did_revert() {
1244 output.result = Err(<Error<T>>::ContractReverted.into());
1245 }
1246 }
1247 dispatch_result(
1248 output.result.map(|result| result.result),
1249 output.weight_consumed,
1250 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1251 )
1252 }
1253
1254 #[pallet::call_index(10)]
1276 #[pallet::weight(
1277 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
1278 .saturating_add(*weight_limit)
1279 )]
1280 pub fn eth_instantiate_with_code(
1281 origin: OriginFor<T>,
1282 value: U256,
1283 weight_limit: Weight,
1284 eth_gas_limit: U256,
1285 code: Vec<u8>,
1286 data: Vec<u8>,
1287 transaction_encoded: Vec<u8>,
1288 effective_gas_price: U256,
1289 encoded_len: u32,
1290 ) -> DispatchResultWithPostInfo {
1291 let signer = Self::ensure_eth_signed(origin)?;
1292 let origin = OriginFor::<T>::signed(signer.clone());
1293 Self::ensure_non_contract_if_signed(&origin)?;
1294 let mut call = Call::<T>::eth_instantiate_with_code {
1295 value,
1296 weight_limit,
1297 eth_gas_limit,
1298 code: code.clone(),
1299 data: data.clone(),
1300 transaction_encoded: transaction_encoded.clone(),
1301 effective_gas_price,
1302 encoded_len,
1303 }
1304 .into();
1305 let info = T::FeeInfo::dispatch_info(&call);
1306 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1307 drop(call);
1308
1309 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1310 let extra_weight = base_info.total_weight();
1311 let output = Self::bare_instantiate(
1312 origin,
1313 value,
1314 TransactionLimits::EthereumGas {
1315 eth_gas_limit: eth_gas_limit.saturated_into(),
1316 maybe_weight_limit: Some(weight_limit),
1317 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1318 },
1319 Code::Upload(code),
1320 data,
1321 None,
1322 ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1323 );
1324
1325 block_storage::EthereumCallResult::new::<T>(
1326 signer,
1327 output.map_result(|r| r.result),
1328 base_info.call_weight,
1329 encoded_len,
1330 &info,
1331 effective_gas_price,
1332 )
1333 })
1334 }
1335
1336 #[pallet::call_index(11)]
1353 #[pallet::weight(
1354 T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into())
1355 .saturating_add(*weight_limit)
1356 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1357 )]
1358 pub fn eth_call(
1359 origin: OriginFor<T>,
1360 dest: H160,
1361 value: U256,
1362 weight_limit: Weight,
1363 eth_gas_limit: U256,
1364 data: Vec<u8>,
1365 transaction_encoded: Vec<u8>,
1366 effective_gas_price: U256,
1367 encoded_len: u32,
1368 ) -> DispatchResultWithPostInfo {
1369 let signer = Self::ensure_eth_signed(origin)?;
1370 let origin = OriginFor::<T>::signed(signer.clone());
1371
1372 Self::ensure_non_contract_if_signed(&origin)?;
1373 let mut call = Call::<T>::eth_call {
1374 dest,
1375 value,
1376 weight_limit,
1377 eth_gas_limit,
1378 data: data.clone(),
1379 transaction_encoded: transaction_encoded.clone(),
1380 effective_gas_price,
1381 encoded_len,
1382 }
1383 .into();
1384 let info = T::FeeInfo::dispatch_info(&call);
1385 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1386 drop(call);
1387
1388 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1389 let extra_weight = base_info.total_weight();
1390 let output = Self::bare_call(
1391 origin,
1392 dest,
1393 value,
1394 TransactionLimits::EthereumGas {
1395 eth_gas_limit: eth_gas_limit.saturated_into(),
1396 maybe_weight_limit: Some(weight_limit),
1397 eth_tx_info: EthTxInfo::new(encoded_len, extra_weight),
1398 },
1399 data,
1400 ExecConfig::new_eth_tx(effective_gas_price, encoded_len, extra_weight),
1401 );
1402
1403 block_storage::EthereumCallResult::new::<T>(
1404 signer,
1405 output,
1406 base_info.call_weight,
1407 encoded_len,
1408 &info,
1409 effective_gas_price,
1410 )
1411 })
1412 }
1413
1414 #[pallet::call_index(12)]
1425 #[pallet::weight(T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1426 pub fn eth_substrate_call(
1427 origin: OriginFor<T>,
1428 call: Box<<T as Config>::RuntimeCall>,
1429 transaction_encoded: Vec<u8>,
1430 ) -> DispatchResultWithPostInfo {
1431 let signer = Self::ensure_eth_signed(origin)?;
1434 let weight_overhead =
1435 T::WeightInfo::eth_substrate_call(transaction_encoded.len() as u32);
1436
1437 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1438 let call_weight = call.get_dispatch_info().call_weight;
1439 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1440
1441 match &mut call_result {
1443 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1444 post_info.actual_weight = Some(
1445 post_info
1446 .actual_weight
1447 .unwrap_or_else(|| call_weight)
1448 .saturating_add(weight_overhead),
1449 );
1450 },
1451 }
1452
1453 block_storage::EthereumCallResult {
1456 receipt_gas_info: ReceiptGasInfo::default(),
1457 result: call_result,
1458 }
1459 })
1460 }
1461
1462 #[pallet::call_index(4)]
1477 #[pallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1478 pub fn upload_code(
1479 origin: OriginFor<T>,
1480 code: Vec<u8>,
1481 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1482 ) -> DispatchResult {
1483 Self::ensure_non_contract_if_signed(&origin)?;
1484 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1485 }
1486
1487 #[pallet::call_index(5)]
1492 #[pallet::weight(<T as Config>::WeightInfo::remove_code())]
1493 pub fn remove_code(
1494 origin: OriginFor<T>,
1495 code_hash: sp_core::H256,
1496 ) -> DispatchResultWithPostInfo {
1497 let origin = ensure_signed(origin)?;
1498 <ContractBlob<T>>::remove(&origin, code_hash)?;
1499 Ok(Pays::No.into())
1501 }
1502
1503 #[pallet::call_index(6)]
1514 #[pallet::weight(<T as Config>::WeightInfo::set_code())]
1515 pub fn set_code(
1516 origin: OriginFor<T>,
1517 dest: H160,
1518 code_hash: sp_core::H256,
1519 ) -> DispatchResult {
1520 ensure_root(origin)?;
1521 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1522 let Some(account) = account else {
1523 return Err(<Error<T>>::ContractNotFound.into());
1524 };
1525
1526 let AccountType::Contract(ref mut contract) = account.account_type else {
1527 return Err(<Error<T>>::ContractNotFound.into());
1528 };
1529
1530 <CodeInfo<T>>::increment_refcount(code_hash)?;
1531 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1532 contract.code_hash = code_hash;
1533
1534 Ok(())
1535 })
1536 }
1537
1538 #[pallet::call_index(7)]
1543 #[pallet::weight(<T as Config>::WeightInfo::map_account())]
1544 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1545 Self::ensure_non_contract_if_signed(&origin)?;
1546 let origin = ensure_signed(origin)?;
1547 T::AddressMapper::map(&origin)
1548 }
1549
1550 #[pallet::call_index(8)]
1555 #[pallet::weight(<T as Config>::WeightInfo::unmap_account())]
1556 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1557 let origin = ensure_signed(origin)?;
1558 T::AddressMapper::unmap(&origin)
1559 }
1560
1561 #[pallet::call_index(9)]
1567 #[pallet::weight({
1568 let dispatch_info = call.get_dispatch_info();
1569 (
1570 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1571 dispatch_info.class
1572 )
1573 })]
1574 pub fn dispatch_as_fallback_account(
1575 origin: OriginFor<T>,
1576 call: Box<<T as Config>::RuntimeCall>,
1577 ) -> DispatchResultWithPostInfo {
1578 Self::ensure_non_contract_if_signed(&origin)?;
1579 let origin = ensure_signed(origin)?;
1580 let unmapped_account =
1581 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1582 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1583 }
1584 }
1585}
1586
1587fn dispatch_result<R>(
1589 result: Result<R, DispatchError>,
1590 weight_consumed: Weight,
1591 base_weight: Weight,
1592) -> DispatchResultWithPostInfo {
1593 let post_info = PostDispatchInfo {
1594 actual_weight: Some(weight_consumed.saturating_add(base_weight)),
1595 pays_fee: Default::default(),
1596 };
1597
1598 result
1599 .map(|_| post_info)
1600 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1601}
1602
1603impl<T: Config> Pallet<T> {
1604 pub fn bare_call(
1611 origin: OriginFor<T>,
1612 dest: H160,
1613 evm_value: U256,
1614 transaction_limits: TransactionLimits<T>,
1615 data: Vec<u8>,
1616 exec_config: ExecConfig<T>,
1617 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1618 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1619 Ok(transaction_meter) => transaction_meter,
1620 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1621 };
1622
1623 let mut storage_deposit = Default::default();
1624
1625 let try_call = || {
1626 let origin = ExecOrigin::from_runtime_origin(origin)?;
1627 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1628 origin.clone(),
1629 dest,
1630 &mut transaction_meter,
1631 evm_value,
1632 data,
1633 &exec_config,
1634 )?;
1635
1636 storage_deposit = transaction_meter
1637 .execute_postponed_deposits(&origin, &exec_config)
1638 .inspect_err(|err| {
1639 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1640 })?;
1641
1642 Ok(result)
1643 };
1644 let result = Self::run_guarded(try_call);
1645
1646 log::trace!(target: LOG_TARGET, "Bare call ends: \
1647 result={result:?}, \
1648 weight_consumed={:?}, \
1649 weight_required={:?}, \
1650 storage_deposit={:?}, \
1651 gas_consumed={:?}, \
1652 max_storage_deposit={:?}",
1653 transaction_meter.weight_consumed(),
1654 transaction_meter.weight_required(),
1655 storage_deposit,
1656 transaction_meter.total_consumed_gas(),
1657 transaction_meter.deposit_required()
1658 );
1659
1660 ContractResult {
1661 result: result.map_err(|r| r.error),
1662 weight_consumed: transaction_meter.weight_consumed(),
1663 weight_required: transaction_meter.weight_required(),
1664 storage_deposit,
1665 gas_consumed: transaction_meter.total_consumed_gas(),
1666 max_storage_deposit: transaction_meter.deposit_required(),
1667 }
1668 }
1669
1670 pub fn prepare_dry_run(account: &T::AccountId) {
1676 frame_system::Pallet::<T>::inc_account_nonce(account);
1679 }
1680
1681 pub fn bare_instantiate(
1687 origin: OriginFor<T>,
1688 evm_value: U256,
1689 transaction_limits: TransactionLimits<T>,
1690 code: Code,
1691 data: Vec<u8>,
1692 salt: Option<[u8; 32]>,
1693 exec_config: ExecConfig<T>,
1694 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1695 let mut transaction_meter = match TransactionMeter::new(transaction_limits) {
1696 Ok(transaction_meter) => transaction_meter,
1697 Err(error) => return ContractResult { result: Err(error), ..Default::default() },
1698 };
1699
1700 let mut storage_deposit = Default::default();
1701
1702 let try_instantiate = || {
1703 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1704
1705 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1706 let executable = match code {
1707 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1708 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1709 let executable = Self::try_upload_code(
1710 upload_account,
1711 code,
1712 BytecodeType::Pvm,
1713 &mut transaction_meter,
1714 &exec_config,
1715 )?;
1716 executable
1717 },
1718 Code::Upload(code) =>
1719 if T::AllowEVMBytecode::get() {
1720 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1721 let origin = T::UploadOrigin::ensure_origin(origin)?;
1722 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1723 executable
1724 } else {
1725 return Err(<Error<T>>::CodeRejected.into())
1726 },
1727 Code::Existing(code_hash) => {
1728 let executable = ContractBlob::from_storage(code_hash, &mut transaction_meter)?;
1729 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1730 executable
1731 },
1732 };
1733 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1734 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1735 instantiate_account,
1736 executable,
1737 &mut transaction_meter,
1738 evm_value,
1739 data,
1740 salt.as_ref(),
1741 &exec_config,
1742 );
1743
1744 storage_deposit = transaction_meter
1745 .execute_postponed_deposits(&instantiate_origin, &exec_config)
1746 .inspect_err(|err| {
1747 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1748 })?;
1749 result
1750 };
1751 let output = Self::run_guarded(try_instantiate);
1752
1753 log::trace!(target: LOG_TARGET, "Bare instantiate ends: weight_consumed={:?}\
1754 weight_required={:?} \
1755 storage_deposit={:?} \
1756 gas_consumed={:?} \
1757 max_storage_deposit={:?}",
1758 transaction_meter.weight_consumed(),
1759 transaction_meter.weight_required(),
1760 storage_deposit,
1761 transaction_meter.total_consumed_gas(),
1762 transaction_meter.deposit_required()
1763 );
1764
1765 ContractResult {
1766 result: output
1767 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1768 .map_err(|e| e.error),
1769 weight_consumed: transaction_meter.weight_consumed(),
1770 weight_required: transaction_meter.weight_required(),
1771 storage_deposit,
1772 gas_consumed: transaction_meter.total_consumed_gas(),
1773 max_storage_deposit: transaction_meter.deposit_required(),
1774 }
1775 }
1776
1777 pub fn dry_run_eth_transact(
1783 mut tx: GenericTransaction,
1784 dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1785 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1786 where
1787 T::Nonce: Into<U256>,
1788 CallOf<T>: SetWeightLimit,
1789 {
1790 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1791
1792 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1793 Self::prepare_dry_run(&origin);
1794
1795 let base_fee = Self::evm_base_fee();
1796 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1797
1798 if effective_gas_price < base_fee {
1799 Err(EthTransactError::Message(format!(
1800 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1801 )))?;
1802 }
1803
1804 if tx.nonce.is_none() {
1805 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1806 }
1807 if tx.chain_id.is_none() {
1808 tx.chain_id = Some(T::ChainId::get().into());
1809 }
1810
1811 tx.gas_price = Some(effective_gas_price);
1813 tx.max_priority_fee_per_gas = Some(0.into());
1816 if tx.max_fee_per_gas.is_none() {
1817 tx.max_fee_per_gas = Some(effective_gas_price);
1818 }
1819
1820 let gas = tx.gas;
1821 if tx.gas.is_none() {
1822 tx.gas = Some(Self::evm_block_gas_limit());
1823 }
1824 if tx.r#type.is_none() {
1825 tx.r#type = Some(TYPE_EIP1559.into());
1826 }
1827
1828 let value = tx.value.unwrap_or_default();
1830 let input = tx.input.clone().to_vec();
1831 let from = tx.from;
1832 let to = tx.to;
1833
1834 let mut call_info = tx
1837 .into_call::<T>(CreateCallMode::DryRun)
1838 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1839
1840 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1844 let base_weight = base_info.total_weight();
1845 let exec_config =
1846 ExecConfig::new_eth_tx(effective_gas_price, call_info.encoded_len, base_weight)
1847 .with_dry_run(dry_run_config);
1848
1849 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
1851 if let Some(from) = &from {
1852 let fees = if gas.is_some() { fees } else { Zero::zero() };
1853 let balance = Self::evm_balance(from);
1854 if balance < Pallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
1855 return Err(EthTransactError::Message(format!(
1856 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
1857 )));
1858 }
1859 }
1860
1861 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
1864
1865 let extract_error = |err| {
1866 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
1867 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
1868 } else {
1869 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
1870 }
1871 };
1872
1873 let transaction_limits = TransactionLimits::EthereumGas {
1874 eth_gas_limit: call_info.eth_gas_limit.saturated_into(),
1875 maybe_weight_limit: None,
1878 eth_tx_info: EthTxInfo::new(call_info.encoded_len, base_weight),
1879 };
1880
1881 let mut dry_run = match to {
1883 Some(dest) => {
1885 if dest == RUNTIME_PALLETS_ADDR {
1886 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
1887 return Err(EthTransactError::Message(format!(
1888 "Failed to decode pallet-call {input:?}"
1889 )));
1890 };
1891
1892 if let Err(result) =
1893 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1894 {
1895 return Err(EthTransactError::Message(format!(
1896 "Failed to dispatch call: {:?}",
1897 result.error,
1898 )));
1899 };
1900
1901 Default::default()
1902 } else {
1903 let result = crate::Pallet::<T>::bare_call(
1905 OriginFor::<T>::signed(origin),
1906 dest,
1907 value,
1908 transaction_limits,
1909 input.clone(),
1910 exec_config,
1911 );
1912
1913 let data = match result.result {
1914 Ok(return_value) => {
1915 if return_value.did_revert() {
1916 return Err(EthTransactError::Data(return_value.data));
1917 }
1918 return_value.data
1919 },
1920 Err(err) => {
1921 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1922 return extract_error(err);
1923 },
1924 };
1925
1926 EthTransactInfo {
1927 weight_required: result.weight_required,
1928 storage_deposit: result.storage_deposit.charge_or_zero(),
1929 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1930 data,
1931 eth_gas: Default::default(),
1932 }
1933 }
1934 },
1935 None => {
1937 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1939 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1940 } else {
1941 (input, vec![])
1942 };
1943
1944 let result = crate::Pallet::<T>::bare_instantiate(
1946 OriginFor::<T>::signed(origin),
1947 value,
1948 transaction_limits,
1949 Code::Upload(code.clone()),
1950 data.clone(),
1951 None,
1952 exec_config,
1953 );
1954
1955 let returned_data = match result.result {
1956 Ok(return_value) => {
1957 if return_value.result.did_revert() {
1958 return Err(EthTransactError::Data(return_value.result.data));
1959 }
1960 return_value.result.data
1961 },
1962 Err(err) => {
1963 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1964 return extract_error(err);
1965 },
1966 };
1967
1968 EthTransactInfo {
1969 weight_required: result.weight_required,
1970 storage_deposit: result.storage_deposit.charge_or_zero(),
1971 max_storage_deposit: result.max_storage_deposit.charge_or_zero(),
1972 data: returned_data,
1973 eth_gas: Default::default(),
1974 }
1975 },
1976 };
1977
1978 call_info.call.set_weight_limit(dry_run.weight_required);
1980
1981 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
1983 let max_weight = Self::evm_max_extrinsic_weight();
1984 if total_weight.any_gt(max_weight) {
1985 log::debug!(target: LOG_TARGET, "Transaction weight estimate exceeds extrinsic maximum: \
1986 total_weight={total_weight:?} \
1987 max_weight={max_weight:?}",
1988 );
1989
1990 Err(EthTransactError::Message(format!(
1991 "\
1992 The transaction consumes more than the allowed weight. \
1993 needed={total_weight} \
1994 allowed={max_weight} \
1995 overweight_by={}\
1996 ",
1997 total_weight.saturating_sub(max_weight),
1998 )))?;
1999 }
2000
2001 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
2003 let available_fee = T::FeeInfo::remaining_txfee();
2004 if transaction_fee > available_fee {
2005 Err(EthTransactError::Message(format!(
2006 "Not enough gas supplied: Off by: {:?}",
2007 transaction_fee.saturating_sub(available_fee),
2008 )))?;
2009 }
2010
2011 let total_cost = transaction_fee.saturating_add(dry_run.max_storage_deposit);
2012 let total_cost_wei = Pallet::<T>::convert_native_to_evm(total_cost);
2013 let (mut eth_gas, rest) = total_cost_wei.div_mod(base_fee);
2014 if !rest.is_zero() {
2015 eth_gas = eth_gas.saturating_add(1_u32.into());
2016 }
2017
2018 log::debug!(target: LOG_TARGET, "\
2019 dry_run_eth_transact finished: \
2020 weight_limit={}, \
2021 total_weight={total_weight}, \
2022 max_weight={max_weight}, \
2023 weight_left={}, \
2024 eth_gas={eth_gas}, \
2025 encoded_len={}, \
2026 tx_fee={transaction_fee:?}, \
2027 storage_deposit={:?}, \
2028 max_storage_deposit={:?}\
2029 ",
2030 dry_run.weight_required,
2031 max_weight.saturating_sub(total_weight),
2032 call_info.encoded_len,
2033 dry_run.storage_deposit,
2034 dry_run.max_storage_deposit,
2035
2036 );
2037 dry_run.eth_gas = eth_gas;
2038 Ok(dry_run)
2039 }
2040
2041 pub fn evm_balance(address: &H160) -> U256 {
2045 let balance = AccountInfo::<T>::balance_of((*address).into());
2046 Self::convert_native_to_evm(balance)
2047 }
2048
2049 pub fn eth_block() -> EthBlock {
2051 EthereumBlock::<T>::get()
2052 }
2053
2054 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
2061 let number = BlockNumberFor::<T>::try_from(number).ok()?;
2062 let hash = <BlockHash<T>>::get(number);
2063 if hash == H256::zero() {
2064 None
2065 } else {
2066 Some(hash)
2067 }
2068 }
2069
2070 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
2072 ReceiptInfoData::<T>::get()
2073 }
2074
2075 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
2081 let (balance, dust) = Self::new_balance_with_dust(evm_value)
2082 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
2083 let account_id = T::AddressMapper::to_account_id(&address);
2084 T::Currency::set_balance(&account_id, balance);
2085 AccountInfoOf::<T>::mutate(&address, |account| {
2086 if let Some(account) = account {
2087 account.dust = dust;
2088 } else {
2089 *account = Some(AccountInfo { dust, ..Default::default() });
2090 }
2091 });
2092
2093 Ok(())
2094 }
2095
2096 pub fn new_balance_with_dust(
2100 evm_value: U256,
2101 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
2102 let ed = T::Currency::minimum_balance();
2103 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
2104 let (value, dust) = balance_with_dust.deconstruct();
2105
2106 Ok((ed.saturating_add(value), dust))
2107 }
2108
2109 pub fn evm_nonce(address: &H160) -> u32
2111 where
2112 T::Nonce: Into<u32>,
2113 {
2114 let account = T::AddressMapper::to_account_id(&address);
2115 System::<T>::account_nonce(account).into()
2116 }
2117
2118 pub fn evm_block_gas_limit() -> U256 {
2120 u64::MAX.into()
2127 }
2128
2129 pub fn evm_max_extrinsic_weight() -> Weight {
2131 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2132 let max_weight = <T as frame_system::Config>::BlockWeights::get()
2133 .get(DispatchClass::Normal)
2134 .max_extrinsic
2135 .unwrap_or_else(|| <T as frame_system::Config>::BlockWeights::get().max_block);
2136 Weight::from_parts(
2137 factor.saturating_mul_int(max_weight.ref_time()),
2138 factor.saturating_mul_int(max_weight.proof_size()),
2139 )
2140 }
2141
2142 pub fn evm_base_fee() -> U256 {
2144 let gas_scale = <T as Config>::GasScale::get();
2145 let multiplier = T::FeeInfo::next_fee_multiplier();
2146 multiplier
2147 .saturating_mul_int::<u128>(T::NativeToEthRatio::get().into())
2148 .saturating_mul(gas_scale.saturated_into())
2149 .into()
2150 }
2151
2152 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2154 where
2155 T::Nonce: Into<u32>,
2156 {
2157 match tracer_type {
2158 TracerType::CallTracer(config) => CallTracer::new(config.unwrap_or_default()).into(),
2159 TracerType::PrestateTracer(config) =>
2160 PrestateTracer::new(config.unwrap_or_default()).into(),
2161 }
2162 }
2163
2164 pub fn bare_upload_code(
2168 origin: OriginFor<T>,
2169 code: Vec<u8>,
2170 storage_deposit_limit: BalanceOf<T>,
2171 ) -> CodeUploadResult<BalanceOf<T>> {
2172 let origin = T::UploadOrigin::ensure_origin(origin)?;
2173
2174 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2175 BytecodeType::Pvm
2176 } else {
2177 if !T::AllowEVMBytecode::get() {
2178 return Err(<Error<T>>::CodeRejected.into())
2179 }
2180 BytecodeType::Evm
2181 };
2182
2183 let mut meter = TransactionMeter::new(TransactionLimits::WeightAndDeposit {
2184 weight_limit: Default::default(),
2185 deposit_limit: storage_deposit_limit,
2186 })?;
2187
2188 let module = Self::try_upload_code(
2189 origin,
2190 code,
2191 bytecode_type,
2192 &mut meter,
2193 &ExecConfig::new_substrate_tx(),
2194 )?;
2195 Ok(CodeUploadReturnValue {
2196 code_hash: *module.code_hash(),
2197 deposit: meter.deposit_consumed().charge_or_zero(),
2198 })
2199 }
2200
2201 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2203 let contract_info =
2204 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2205
2206 let maybe_value = contract_info.read(&Key::from_fixed(key));
2207 Ok(maybe_value)
2208 }
2209
2210 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2214 let immutable_data = <ImmutableDataOf<T>>::get(address);
2215 immutable_data
2216 }
2217
2218 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2226 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2227 <ImmutableDataOf<T>>::insert(address, data);
2228 Ok(())
2229 }
2230
2231 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2233 let contract_info =
2234 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2235
2236 let maybe_value = contract_info.read(
2237 &Key::try_from_var(key)
2238 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2239 .into(),
2240 );
2241 Ok(maybe_value)
2242 }
2243
2244 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2246 let (value, dust) = value.into().deconstruct();
2247 value
2248 .into()
2249 .saturating_mul(T::NativeToEthRatio::get().into())
2250 .saturating_add(dust.into())
2251 }
2252
2253 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2263 let contract_info =
2264 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2265
2266 contract_info
2267 .write(&Key::from_fixed(key), value, None, false)
2268 .map_err(ContractAccessError::StorageWriteFailed)
2269 }
2270
2271 pub fn set_storage_var_key(
2282 address: H160,
2283 key: Vec<u8>,
2284 value: Option<Vec<u8>>,
2285 ) -> SetStorageResult {
2286 let contract_info =
2287 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2288
2289 contract_info
2290 .write(
2291 &Key::try_from_var(key)
2292 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2293 .into(),
2294 value,
2295 None,
2296 false,
2297 )
2298 .map_err(ContractAccessError::StorageWriteFailed)
2299 }
2300
2301 pub fn account_id() -> T::AccountId {
2303 use frame_support::PalletId;
2304 use sp_runtime::traits::AccountIdConversion;
2305 PalletId(*b"py/reviv").into_account_truncating()
2306 }
2307
2308 pub fn block_author() -> H160 {
2310 use frame_support::traits::FindAuthor;
2311
2312 let digest = <frame_system::Pallet<T>>::digest();
2313 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2314
2315 T::FindAuthor::find_author(pre_runtime_digests)
2316 .map(|account_id| T::AddressMapper::to_address(&account_id))
2317 .unwrap_or_default()
2318 }
2319
2320 pub fn code(address: &H160) -> Vec<u8> {
2324 use precompiles::{All, Precompiles};
2325 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2326 return code.into()
2327 }
2328 AccountInfo::<T>::load_contract(&address)
2329 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2330 .map(|code| code.into())
2331 .unwrap_or_default()
2332 }
2333
2334 pub fn try_upload_code(
2336 origin: T::AccountId,
2337 code: Vec<u8>,
2338 code_type: BytecodeType,
2339 meter: &mut TransactionMeter<T>,
2340 exec_config: &ExecConfig<T>,
2341 ) -> Result<ContractBlob<T>, DispatchError> {
2342 let mut module = match code_type {
2343 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2344 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2345 };
2346 module.store_code(exec_config, meter)?;
2347 Ok(module)
2348 }
2349
2350 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2352 executing_contract::using_once(&mut false, || {
2353 executing_contract::with(|f| {
2354 if *f {
2356 return Err(())
2357 }
2358 *f = true;
2360 Ok(())
2361 })
2362 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2363 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2364 .map(|_| f())
2365 .and_then(|r| r)
2366 })
2367 }
2368
2369 fn charge_deposit(
2374 hold_reason: Option<HoldReason>,
2375 from: &T::AccountId,
2376 to: &T::AccountId,
2377 amount: BalanceOf<T>,
2378 exec_config: &ExecConfig<T>,
2379 ) -> DispatchResult {
2380 use frame_support::traits::tokens::{Fortitude, Precision, Preservation};
2381
2382 if amount.is_zero() {
2383 return Ok(());
2384 }
2385
2386 match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2387 (true, hold_reason) => {
2388 T::FeeInfo::withdraw_txfee(amount)
2389 .ok_or(())
2390 .and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2391 .and_then(|_| {
2392 if let Some(hold_reason) = hold_reason {
2393 T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2394 }
2395 Ok(())
2396 })
2397 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2398 },
2399 (false, Some(hold_reason)) => {
2400 T::Currency::transfer_and_hold(
2401 &hold_reason.into(),
2402 from,
2403 to,
2404 amount,
2405 Precision::Exact,
2406 Preservation::Preserve,
2407 Fortitude::Polite,
2408 )
2409 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2410 },
2411 (false, None) => {
2412 T::Currency::transfer(from, to, amount, Preservation::Preserve)
2413 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2414 },
2415 }
2416 Ok(())
2417 }
2418
2419 fn refund_deposit(
2424 hold_reason: HoldReason,
2425 from: &T::AccountId,
2426 to: &T::AccountId,
2427 amount: BalanceOf<T>,
2428 exec_config: Option<&ExecConfig<T>>,
2429 ) -> Result<(), DispatchError> {
2430 use frame_support::traits::{
2431 fungible::InspectHold,
2432 tokens::{Fortitude, Precision, Preservation, Restriction},
2433 };
2434
2435 if amount.is_zero() {
2436 return Ok(());
2437 }
2438
2439 let hold_reason = hold_reason.into();
2440 let result = if exec_config.map(|c| c.collect_deposit_from_hold.is_some()).unwrap_or(false)
2441 {
2442 T::Currency::release(&hold_reason, from, amount, Precision::Exact)
2443 .and_then(|amount| {
2444 T::Currency::withdraw(
2445 from,
2446 amount,
2447 Precision::Exact,
2448 Preservation::Preserve,
2449 Fortitude::Polite,
2450 )
2451 })
2452 .map(T::FeeInfo::deposit_txfee)
2453 } else {
2454 T::Currency::transfer_on_hold(
2455 &hold_reason,
2456 from,
2457 to,
2458 amount,
2459 Precision::Exact,
2460 Restriction::Free,
2461 Fortitude::Polite,
2462 )
2463 .map(|_| ())
2464 };
2465
2466 result.map_err(|_| {
2467 let available = T::Currency::balance_on_hold(&hold_reason, from);
2468 if available < amount {
2469 log::error!(
2472 target: LOG_TARGET,
2473 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. Not enough deposit: {:?}. This is a bug.",
2474 amount, from, to, available,
2475 );
2476 Error::<T>::StorageRefundNotEnoughFunds.into()
2477 } else {
2478 log::warn!(
2483 target: LOG_TARGET,
2484 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. First remove locks (staking, governance) from the contracts account.",
2485 amount, from, to,
2486 );
2487 Error::<T>::StorageRefundLocked.into()
2488 }
2489 })
2490 }
2491
2492 fn has_dust(value: U256) -> bool {
2494 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2495 }
2496
2497 fn has_balance(value: U256) -> bool {
2499 value >= U256::from(<T>::NativeToEthRatio::get())
2500 }
2501
2502 fn min_balance() -> BalanceOf<T> {
2504 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2505 }
2506
2507 fn deposit_event(event: Event<T>) {
2512 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2513 }
2514
2515 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2517 match <T as Config>::RuntimeOrigin::from(origin).into() {
2518 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2519 _ => Err(BadOrigin.into()),
2520 }
2521 }
2522
2523 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2527 if DebugSettings::bypass_eip_3607::<T>() {
2528 return Ok(())
2529 }
2530 let Some(address) = origin
2531 .as_system_ref()
2532 .and_then(|o| o.as_signed())
2533 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2534 else {
2535 return Ok(())
2536 };
2537 if exec::is_precompile::<T, ContractBlob<T>>(&address) ||
2538 <AccountInfo<T>>::is_contract(&address)
2539 {
2540 log::debug!(
2541 target: crate::LOG_TARGET,
2542 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2543 );
2544 Err(DispatchError::BadOrigin)
2545 } else {
2546 Ok(())
2547 }
2548 }
2549}
2550
2551pub const RUNTIME_PALLETS_ADDR: H160 =
2556 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2557
2558environmental!(executing_contract: bool);
2560
2561sp_api::decl_runtime_apis! {
2562 #[api_version(1)]
2564 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2565 AccountId: Codec,
2566 Balance: Codec,
2567 Nonce: Codec,
2568 BlockNumber: Codec,
2569 Moment: Codec,
2570 {
2571 fn eth_block() -> EthBlock;
2575
2576 fn eth_block_hash(number: U256) -> Option<H256>;
2578
2579 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2585
2586 fn block_gas_limit() -> U256;
2588
2589 fn balance(address: H160) -> U256;
2591
2592 fn gas_price() -> U256;
2594
2595 fn nonce(address: H160) -> Nonce;
2597
2598 fn call(
2602 origin: AccountId,
2603 dest: H160,
2604 value: Balance,
2605 gas_limit: Option<Weight>,
2606 storage_deposit_limit: Option<Balance>,
2607 input_data: Vec<u8>,
2608 ) -> ContractResult<ExecReturnValue, Balance>;
2609
2610 fn instantiate(
2614 origin: AccountId,
2615 value: Balance,
2616 gas_limit: Option<Weight>,
2617 storage_deposit_limit: Option<Balance>,
2618 code: Code,
2619 data: Vec<u8>,
2620 salt: Option<[u8; 32]>,
2621 ) -> ContractResult<InstantiateReturnValue, Balance>;
2622
2623
2624 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2629
2630 fn eth_transact_with_config(
2634 tx: GenericTransaction,
2635 config: DryRunConfig<Moment>,
2636 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2637
2638 fn upload_code(
2642 origin: AccountId,
2643 code: Vec<u8>,
2644 storage_deposit_limit: Option<Balance>,
2645 ) -> CodeUploadResult<Balance>;
2646
2647 fn get_storage(
2653 address: H160,
2654 key: [u8; 32],
2655 ) -> GetStorageResult;
2656
2657 fn get_storage_var_key(
2663 address: H160,
2664 key: Vec<u8>,
2665 ) -> GetStorageResult;
2666
2667 fn trace_block(
2674 block: Block,
2675 config: TracerType
2676 ) -> Vec<(u32, Trace)>;
2677
2678 fn trace_tx(
2685 block: Block,
2686 tx_index: u32,
2687 config: TracerType
2688 ) -> Option<Trace>;
2689
2690 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2694
2695 fn block_author() -> H160;
2697
2698 fn address(account_id: AccountId) -> H160;
2700
2701 fn account_id(address: H160) -> AccountId;
2703
2704 fn runtime_pallets_address() -> H160;
2706
2707 fn code(address: H160) -> Vec<u8>;
2709
2710 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2712 }
2713}
2714
2715#[macro_export]
2729macro_rules! impl_runtime_apis_plus_revive_traits {
2730 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2731
2732 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2733
2734 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2735 fn set_weight_limit(&mut self, new_weight_limit: Weight) -> Weight {
2736 use $crate::pallet::Call as ReviveCall;
2737 match self {
2738 Self::$Revive(
2739 ReviveCall::eth_call{ weight_limit, .. } |
2740 ReviveCall::eth_instantiate_with_code{ weight_limit, .. }
2741 ) => {
2742 let old = *weight_limit;
2743 *weight_limit = new_weight_limit;
2744 old
2745 },
2746 _ => Weight::default(),
2747 }
2748 }
2749 }
2750
2751 impl_runtime_apis! {
2752 $($rest)*
2753
2754
2755 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2756 {
2757 fn eth_block() -> $crate::EthBlock {
2758 $crate::Pallet::<Self>::eth_block()
2759 }
2760
2761 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2762 $crate::Pallet::<Self>::eth_block_hash_from_number(number)
2763 }
2764
2765 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2766 $crate::Pallet::<Self>::eth_receipt_data()
2767 }
2768
2769 fn balance(address: $crate::H160) -> $crate::U256 {
2770 $crate::Pallet::<Self>::evm_balance(&address)
2771 }
2772
2773 fn block_author() -> $crate::H160 {
2774 $crate::Pallet::<Self>::block_author()
2775 }
2776
2777 fn block_gas_limit() -> $crate::U256 {
2778 $crate::Pallet::<Self>::evm_block_gas_limit()
2779 }
2780
2781 fn gas_price() -> $crate::U256 {
2782 $crate::Pallet::<Self>::evm_base_fee()
2783 }
2784
2785 fn nonce(address: $crate::H160) -> Nonce {
2786 use $crate::AddressMapper;
2787 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2788 $crate::frame_system::Pallet::<Self>::account_nonce(account)
2789 }
2790
2791 fn address(account_id: AccountId) -> $crate::H160 {
2792 use $crate::AddressMapper;
2793 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2794 }
2795
2796 fn eth_transact(
2797 tx: $crate::evm::GenericTransaction,
2798 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2799 use $crate::{
2800 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2801 sp_runtime::traits::TransactionExtension,
2802 sp_runtime::traits::Block as BlockT
2803 };
2804 $crate::Pallet::<Self>::dry_run_eth_transact(tx, Default::default())
2805 }
2806
2807 fn eth_transact_with_config(
2808 tx: $crate::evm::GenericTransaction,
2809 config: $crate::DryRunConfig<__ReviveMacroMoment>,
2810 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2811 use $crate::{
2812 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2813 sp_runtime::traits::TransactionExtension,
2814 sp_runtime::traits::Block as BlockT
2815 };
2816 $crate::Pallet::<Self>::dry_run_eth_transact(tx, config)
2817 }
2818
2819 fn call(
2820 origin: AccountId,
2821 dest: $crate::H160,
2822 value: Balance,
2823 weight_limit: Option<$crate::Weight>,
2824 storage_deposit_limit: Option<Balance>,
2825 input_data: Vec<u8>,
2826 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2827 use $crate::frame_support::traits::Get;
2828 let blockweights: $crate::BlockWeights =
2829 <Self as $crate::frame_system::Config>::BlockWeights::get();
2830
2831 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2832 $crate::Pallet::<Self>::bare_call(
2833 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2834 dest,
2835 $crate::Pallet::<Self>::convert_native_to_evm(value),
2836 $crate::TransactionLimits::WeightAndDeposit {
2837 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2838 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2839 },
2840 input_data,
2841 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2842 )
2843 }
2844
2845 fn instantiate(
2846 origin: AccountId,
2847 value: Balance,
2848 weight_limit: Option<$crate::Weight>,
2849 storage_deposit_limit: Option<Balance>,
2850 code: $crate::Code,
2851 data: Vec<u8>,
2852 salt: Option<[u8; 32]>,
2853 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2854 use $crate::frame_support::traits::Get;
2855 let blockweights: $crate::BlockWeights =
2856 <Self as $crate::frame_system::Config>::BlockWeights::get();
2857
2858 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2859 $crate::Pallet::<Self>::bare_instantiate(
2860 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2861 $crate::Pallet::<Self>::convert_native_to_evm(value),
2862 $crate::TransactionLimits::WeightAndDeposit {
2863 weight_limit: weight_limit.unwrap_or(blockweights.max_block),
2864 deposit_limit: storage_deposit_limit.unwrap_or(u128::MAX),
2865 },
2866 code,
2867 data,
2868 salt,
2869 $crate::ExecConfig::new_substrate_tx().with_dry_run(Default::default()),
2870 )
2871 }
2872
2873 fn upload_code(
2874 origin: AccountId,
2875 code: Vec<u8>,
2876 storage_deposit_limit: Option<Balance>,
2877 ) -> $crate::CodeUploadResult<Balance> {
2878 let origin =
2879 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
2880 $crate::Pallet::<Self>::bare_upload_code(
2881 origin,
2882 code,
2883 storage_deposit_limit.unwrap_or(u128::MAX),
2884 )
2885 }
2886
2887 fn get_storage_var_key(
2888 address: $crate::H160,
2889 key: Vec<u8>,
2890 ) -> $crate::GetStorageResult {
2891 $crate::Pallet::<Self>::get_storage_var_key(address, key)
2892 }
2893
2894 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2895 $crate::Pallet::<Self>::get_storage(address, key)
2896 }
2897
2898 fn trace_block(
2899 block: Block,
2900 tracer_type: $crate::evm::TracerType,
2901 ) -> Vec<(u32, $crate::evm::Trace)> {
2902 use $crate::{sp_runtime::traits::Block, tracing::trace};
2903 let mut traces = vec![];
2904 let (header, extrinsics) = block.deconstruct();
2905 <$Executive>::initialize_block(&header);
2906 for (index, ext) in extrinsics.into_iter().enumerate() {
2907 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2908 let t = tracer.as_tracing();
2909 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2910
2911 if let Some(tx_trace) = tracer.collect_trace() {
2912 traces.push((index as u32, tx_trace));
2913 }
2914 }
2915
2916 traces
2917 }
2918
2919 fn trace_tx(
2920 block: Block,
2921 tx_index: u32,
2922 tracer_type: $crate::evm::TracerType,
2923 ) -> Option<$crate::evm::Trace> {
2924 use $crate::{sp_runtime::traits::Block, tracing::trace};
2925
2926 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2927 let (header, extrinsics) = block.deconstruct();
2928
2929 <$Executive>::initialize_block(&header);
2930 for (index, ext) in extrinsics.into_iter().enumerate() {
2931 if index as u32 == tx_index {
2932 let t = tracer.as_tracing();
2933 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2934 break;
2935 } else {
2936 let _ = <$Executive>::apply_extrinsic(ext);
2937 }
2938 }
2939
2940 tracer.collect_trace()
2941 }
2942
2943 fn trace_call(
2944 tx: $crate::evm::GenericTransaction,
2945 tracer_type: $crate::evm::TracerType,
2946 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2947 use $crate::tracing::trace;
2948 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2949 let t = tracer.as_tracing();
2950
2951 t.watch_address(&tx.from.unwrap_or_default());
2952 t.watch_address(&$crate::Pallet::<Self>::block_author());
2953 let result = trace(t, || Self::eth_transact(tx));
2954
2955 if let Some(trace) = tracer.collect_trace() {
2956 Ok(trace)
2957 } else if let Err(err) = result {
2958 Err(err)
2959 } else {
2960 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
2961 }
2962 }
2963
2964 fn runtime_pallets_address() -> $crate::H160 {
2965 $crate::RUNTIME_PALLETS_ADDR
2966 }
2967
2968 fn code(address: $crate::H160) -> Vec<u8> {
2969 $crate::Pallet::<Self>::code(&address)
2970 }
2971
2972 fn account_id(address: $crate::H160) -> AccountId {
2973 use $crate::AddressMapper;
2974 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
2975 }
2976
2977 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2978 $crate::Pallet::<Self>::new_balance_with_dust(balance)
2979 }
2980 }
2981 }
2982 };
2983}