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 exec;
29mod gas;
30mod impl_fungibles;
31mod limits;
32mod primitives;
33mod storage;
34#[cfg(test)]
35mod tests;
36mod transient_storage;
37mod vm;
38
39pub mod evm;
40pub mod migrations;
41pub mod precompiles;
42pub mod test_utils;
43pub mod tracing;
44pub mod weights;
45
46use crate::{
47 evm::{
48 runtime::GAS_PRICE, CallTracer, GasEncoder, GenericTransaction, PrestateTracer, Trace,
49 Tracer, TracerType, TYPE_EIP1559,
50 },
51 exec::{AccountIdOf, ExecError, Executable, Stack as ExecStack},
52 gas::GasMeter,
53 storage::{meter::Meter as StorageMeter, AccountType, DeletionQueueManager},
54 tracing::if_tracing,
55 vm::{pvm::extract_code_and_data, CodeInfo, ContractBlob, RuntimeCosts},
56};
57use alloc::{boxed::Box, format, vec};
58use codec::{Codec, Decode, Encode};
59use environmental::*;
60use frame_support::{
61 dispatch::{
62 DispatchErrorWithPostInfo, DispatchResultWithPostInfo, GetDispatchInfo, Pays,
63 PostDispatchInfo, RawOrigin,
64 },
65 ensure,
66 pallet_prelude::DispatchClass,
67 traits::{
68 fungible::{Inspect, Mutate, MutateHold},
69 ConstU32, ConstU64, EnsureOrigin, Get, IsType, OriginTrait, Time,
70 },
71 weights::WeightMeter,
72 BoundedVec, RuntimeDebugNoBound,
73};
74use frame_system::{
75 ensure_signed,
76 pallet_prelude::{BlockNumberFor, OriginFor},
77 Pallet as System,
78};
79use pallet_transaction_payment::OnChargeTransaction;
80use scale_info::TypeInfo;
81use sp_runtime::{
82 traits::{Bounded, Convert, Dispatchable, Saturating},
83 AccountId32, DispatchError,
84};
85
86pub use crate::{
87 address::{
88 create1, create2, is_eth_derived, AccountId32Mapper, AddressMapper, TestAccountMapper,
89 },
90 exec::{Key, MomentOf, Origin},
91 pallet::{genesis, *},
92 storage::{AccountInfo, ContractInfo},
93};
94pub use codec;
95pub use frame_support::{self, dispatch::DispatchInfo, weights::Weight};
96pub use frame_system::{self, limits::BlockWeights};
97pub use pallet_transaction_payment;
98pub use primitives::*;
99pub use sp_core::{H160, H256, U256};
100pub use sp_runtime;
101pub use weights::WeightInfo;
102
103#[cfg(doc)]
104pub use crate::vm::pvm::SyscallDoc;
105
106pub type BalanceOf<T> =
107 <<T as Config>::Currency as Inspect<<T as frame_system::Config>::AccountId>>::Balance;
108type TrieId = BoundedVec<u8, ConstU32<128>>;
109type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
110pub(crate) type OnChargeTransactionBalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction<T>>::Balance;
111
112const SENTINEL: u32 = u32::MAX;
119
120const LOG_TARGET: &str = "runtime::revive";
126
127#[frame_support::pallet]
128pub mod pallet {
129 use super::*;
130 use frame_support::{pallet_prelude::*, traits::FindAuthor};
131 use frame_system::pallet_prelude::*;
132 use sp_core::U256;
133 use sp_runtime::Perbill;
134
135 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
137
138 #[pallet::pallet]
139 #[pallet::storage_version(STORAGE_VERSION)]
140 pub struct Pallet<T>(_);
141
142 #[pallet::config(with_default)]
143 pub trait Config: frame_system::Config {
144 type Time: Time;
146
147 #[pallet::no_default]
149 type Currency: Inspect<Self::AccountId>
150 + Mutate<Self::AccountId>
151 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>;
152
153 #[pallet::no_default_bounds]
155 #[allow(deprecated)]
156 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
157
158 #[pallet::no_default_bounds]
160 type RuntimeCall: Parameter
161 + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin, PostInfo = PostDispatchInfo>
162 + GetDispatchInfo;
163
164 #[pallet::no_default_bounds]
166 type RuntimeHoldReason: From<HoldReason>;
167
168 #[pallet::no_default_bounds]
171 type WeightPrice: Convert<Weight, BalanceOf<Self>>;
172
173 type WeightInfo: WeightInfo;
176
177 #[pallet::no_default_bounds]
181 #[allow(private_bounds)]
182 type Precompiles: precompiles::Precompiles<Self>;
183
184 type FindAuthor: FindAuthor<Self::AccountId>;
186
187 #[pallet::constant]
193 #[pallet::no_default_bounds]
194 type DepositPerByte: Get<BalanceOf<Self>>;
195
196 #[pallet::constant]
202 #[pallet::no_default_bounds]
203 type DepositPerItem: Get<BalanceOf<Self>>;
204
205 #[pallet::constant]
209 type CodeHashLockupDepositPercent: Get<Perbill>;
210
211 #[pallet::no_default]
213 type AddressMapper: AddressMapper<Self>;
214
215 #[pallet::constant]
225 type UnsafeUnstableInterface: Get<bool>;
226
227 #[pallet::constant]
229 type AllowEVMBytecode: Get<bool>;
230
231 #[pallet::no_default_bounds]
236 type UploadOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
237
238 #[pallet::no_default_bounds]
249 type InstantiateOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>;
250
251 type RuntimeMemory: Get<u32>;
256
257 type PVFMemory: Get<u32>;
265
266 #[pallet::constant]
271 type ChainId: Get<u64>;
272
273 #[pallet::constant]
275 type NativeToEthRatio: Get<u32>;
276
277 #[pallet::no_default_bounds]
280 type EthGasEncoder: GasEncoder<BalanceOf<Self>>;
281 }
282
283 pub mod config_preludes {
285 use super::*;
286 use frame_support::{
287 derive_impl,
288 traits::{ConstBool, ConstU32},
289 };
290 use frame_system::EnsureSigned;
291 use sp_core::parameter_types;
292
293 type Balance = u64;
294 const UNITS: Balance = 10_000_000_000;
295 const CENTS: Balance = UNITS / 100;
296
297 pub const fn deposit(items: u32, bytes: u32) -> Balance {
298 items as Balance * 1 * CENTS + (bytes as Balance) * 1 * CENTS
299 }
300
301 parameter_types! {
302 pub const DepositPerItem: Balance = deposit(1, 0);
303 pub const DepositPerByte: Balance = deposit(0, 1);
304 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
305 }
306
307 pub struct TestDefaultConfig;
309
310 impl Time for TestDefaultConfig {
311 type Moment = u64;
312 fn now() -> Self::Moment {
313 0u64
314 }
315 }
316
317 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
318 fn convert(w: Weight) -> T {
319 w.ref_time().into()
320 }
321 }
322
323 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
324 impl frame_system::DefaultConfig for TestDefaultConfig {}
325
326 #[frame_support::register_default_impl(TestDefaultConfig)]
327 impl DefaultConfig for TestDefaultConfig {
328 #[inject_runtime_type]
329 type RuntimeEvent = ();
330
331 #[inject_runtime_type]
332 type RuntimeHoldReason = ();
333
334 #[inject_runtime_type]
335 type RuntimeCall = ();
336 type Precompiles = ();
337 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
338 type DepositPerByte = DepositPerByte;
339 type DepositPerItem = DepositPerItem;
340 type Time = Self;
341 type UnsafeUnstableInterface = ConstBool<true>;
342 type AllowEVMBytecode = ConstBool<true>;
343 type UploadOrigin = EnsureSigned<Self::AccountId>;
344 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
345 type WeightInfo = ();
346 type WeightPrice = Self;
347 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
348 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
349 type ChainId = ConstU64<42>;
350 type NativeToEthRatio = ConstU32<1_000_000>;
351 type EthGasEncoder = ();
352 type FindAuthor = ();
353 }
354 }
355
356 #[pallet::event]
357 pub enum Event<T: Config> {
358 ContractEmitted {
360 contract: H160,
362 data: Vec<u8>,
365 topics: Vec<H256>,
368 },
369
370 Instantiated { deployer: H160, contract: H160 },
372 }
373
374 #[pallet::error]
375 #[repr(u8)]
376 pub enum Error<T> {
377 InvalidSchedule = 0x01,
379 InvalidCallFlags = 0x02,
381 OutOfGas = 0x03,
383 TransferFailed = 0x04,
386 MaxCallDepthReached = 0x05,
389 ContractNotFound = 0x06,
391 CodeNotFound = 0x07,
393 CodeInfoNotFound = 0x08,
395 OutOfBounds = 0x09,
397 DecodingFailed = 0x0A,
399 ContractTrapped = 0x0B,
401 ValueTooLarge = 0x0C,
403 TerminatedWhileReentrant = 0x0D,
406 InputForwarded = 0x0E,
408 TooManyTopics = 0x0F,
410 DuplicateContract = 0x12,
412 TerminatedInConstructor = 0x13,
416 ReentranceDenied = 0x14,
418 ReenteredPallet = 0x15,
420 StateChangeDenied = 0x16,
422 StorageDepositNotEnoughFunds = 0x17,
424 StorageDepositLimitExhausted = 0x18,
426 CodeInUse = 0x19,
428 ContractReverted = 0x1A,
433 CodeRejected = 0x1B,
438 BlobTooLarge = 0x1C,
440 StaticMemoryTooLarge = 0x1D,
442 BasicBlockTooLarge = 0x1E,
444 InvalidInstruction = 0x1F,
446 MaxDelegateDependenciesReached = 0x20,
448 DelegateDependencyNotFound = 0x21,
450 DelegateDependencyAlreadyExists = 0x22,
452 CannotAddSelfAsDelegateDependency = 0x23,
454 OutOfTransientStorage = 0x24,
456 InvalidSyscall = 0x25,
458 InvalidStorageFlags = 0x26,
460 ExecutionFailed = 0x27,
462 BalanceConversionFailed = 0x28,
464 InvalidImmutableAccess = 0x2A,
467 AccountUnmapped = 0x2B,
471 AccountAlreadyMapped = 0x2C,
473 InvalidGenericTransaction = 0x2D,
475 RefcountOverOrUnderflow = 0x2E,
477 UnsupportedPrecompileAddress = 0x2F,
479 CallDataTooLarge = 0x30,
481 ReturnDataTooLarge = 0x31,
483 }
484
485 #[pallet::composite_enum]
487 pub enum HoldReason {
488 CodeUploadDepositReserve,
490 StorageDepositReserve,
492 AddressMapping,
494 }
495
496 #[pallet::storage]
500 #[pallet::unbounded]
501 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
502
503 #[pallet::storage]
505 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
506
507 #[pallet::storage]
509 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
510
511 #[pallet::storage]
513 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
514
515 #[pallet::storage]
520 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
521
522 #[pallet::storage]
525 pub(crate) type DeletionQueueCounter<T: Config> =
526 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
527
528 #[pallet::storage]
535 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
536
537 pub mod genesis {
538 use super::*;
539 use crate::evm::Bytes32;
540
541 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
543 pub struct ContractData {
544 pub code: Vec<u8>,
546 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
548 }
549
550 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
552 pub struct Account<T: Config> {
553 pub address: H160,
555 #[serde(default)]
557 pub balance: U256,
558 #[serde(default)]
560 pub nonce: T::Nonce,
561 #[serde(flatten, skip_serializing_if = "Option::is_none")]
563 pub contract_data: Option<ContractData>,
564 }
565 }
566
567 #[pallet::genesis_config]
568 #[derive(Debug, PartialEq, frame_support::DefaultNoBound)]
569 pub struct GenesisConfig<T: Config> {
570 #[serde(default, skip_serializing_if = "Vec::is_empty")]
573 pub mapped_accounts: Vec<T::AccountId>,
574
575 #[serde(default, skip_serializing_if = "Vec::is_empty")]
577 pub accounts: Vec<genesis::Account<T>>,
578 }
579
580 #[pallet::genesis_build]
581 impl<T: Config> BuildGenesisConfig for GenesisConfig<T>
582 where
583 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
584 MomentOf<T>: Into<U256>,
585 T::Hash: frame_support::traits::IsType<H256>,
586 {
587 fn build(&self) {
588 use crate::{exec::Key, vm::ContractBlob};
589 use frame_support::traits::fungible::Mutate;
590
591 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
592 let _ = T::Currency::mint_into(
593 &Pallet::<T>::account_id(),
594 T::Currency::minimum_balance(),
595 );
596 }
597
598 for id in &self.mapped_accounts {
599 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
600 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
601 }
602 }
603
604 let owner = Pallet::<T>::account_id();
605
606 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
607 let account_id = T::AddressMapper::to_account_id(address);
608
609 frame_system::Account::<T>::mutate(&account_id, |info| {
610 info.nonce = (*nonce).into();
611 });
612
613 match contract_data {
614 None => {
615 AccountInfoOf::<T>::insert(
616 address,
617 AccountInfo { account_type: AccountType::EOA, dust: 0 },
618 );
619 },
620 Some(genesis::ContractData { code, storage }) => {
621 let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
622 ContractBlob::<T>::from_pvm_code( code.clone(), owner.clone()).inspect_err(|err| {
623 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
624 })
625 } else {
626 ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
627 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
628 })
629 };
630
631 let Ok(blob) = blob else {
632 continue;
633 };
634
635 let code_hash = *blob.code_hash();
636 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
637 .inspect_err(|err| {
638 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
639 })
640 else {
641 continue;
642 };
643
644 AccountInfoOf::<T>::insert(
645 address,
646 AccountInfo { account_type: info.clone().into(), dust: 0 },
647 );
648
649 <PristineCode<T>>::insert(blob.code_hash(), code);
650 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
651 for (k, v) in storage {
652 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
653 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
654 });
655 }
656 },
657 }
658
659 let _ = Pallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
660 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
661 });
662 }
663 }
664 }
665
666 #[pallet::hooks]
667 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
668 fn on_initialize(_block: BlockNumberFor<T>) -> Weight {
669 System::<T>::account_exists(&Pallet::<T>::account_id());
671 return T::DbWeight::get().reads(1)
672 }
673 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
674 let mut meter = WeightMeter::with_limit(limit);
675 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
676 meter.consumed()
677 }
678
679 fn integrity_test() {
680 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
681
682 let max_runtime_mem: u32 = T::RuntimeMemory::get();
684
685 const TOTAL_MEMORY_DEVIDER: u32 = 2;
688
689 let memory_left = i64::from(max_runtime_mem)
694 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
695 .saturating_sub(limits::MEMORY_REQUIRED.into());
696
697 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
698
699 assert!(
700 memory_left >= 0,
701 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
702 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
703 );
704
705 let max_block_ref_time = T::BlockWeights::get()
711 .get(DispatchClass::Normal)
712 .max_total
713 .unwrap_or_else(|| T::BlockWeights::get().max_block)
714 .ref_time();
715 let max_payload_size = limits::PAYLOAD_BYTES;
716 let max_key_size =
717 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
718 .expect("Key of maximal size shall be created")
719 .hash()
720 .len() as u32;
721
722 let max_immutable_key_size = T::AccountId::max_encoded_len() as u32;
723 let max_immutable_size: u32 = ((max_block_ref_time /
724 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetImmutableData(
725 limits::IMMUTABLE_BYTES,
726 ))
727 .ref_time()))
728 .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64))
729 .try_into()
730 .expect("Immutable data size too big");
731
732 let max_storage_size: u32 = ((max_block_ref_time /
735 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
736 new_bytes: max_payload_size,
737 old_bytes: 0,
738 })
739 .ref_time()))
740 .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64))
741 .saturating_add(max_immutable_size.into())
742 .try_into()
743 .expect("Storage size too big");
744
745 let max_pvf_mem: u32 = T::PVFMemory::get();
746 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
747
748 assert!(
749 max_storage_size < storage_size_limit,
750 "Maximal storage size {} exceeds the storage limit {}",
751 max_storage_size,
752 storage_size_limit
753 );
754
755 let max_events_size: u32 = ((max_block_ref_time /
759 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
760 num_topic: 0,
761 len: max_payload_size,
762 })
763 .saturating_add(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::HostFn))
764 .ref_time()))
765 .saturating_mul(max_payload_size as u64))
766 .try_into()
767 .expect("Events size too big");
768
769 assert!(
770 max_events_size < storage_size_limit,
771 "Maximal events size {} exceeds the events limit {}",
772 max_events_size,
773 storage_size_limit
774 );
775 }
776 }
777
778 #[pallet::call]
779 impl<T: Config> Pallet<T>
780 where
781 BalanceOf<T>: Into<U256> + TryFrom<U256>,
782 MomentOf<T>: Into<U256>,
783 T::Hash: frame_support::traits::IsType<H256>,
784 {
785 #[allow(unused_variables)]
801 #[pallet::call_index(0)]
802 #[pallet::weight(Weight::MAX)]
803 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
804 Err(frame_system::Error::CallFiltered::<T>.into())
805 }
806
807 #[pallet::call_index(1)]
824 #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))]
825 pub fn call(
826 origin: OriginFor<T>,
827 dest: H160,
828 #[pallet::compact] value: BalanceOf<T>,
829 gas_limit: Weight,
830 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
831 data: Vec<u8>,
832 ) -> DispatchResultWithPostInfo {
833 let mut output = Self::bare_call(
834 origin,
835 dest,
836 Pallet::<T>::convert_native_to_evm(value),
837 gas_limit,
838 DepositLimit::Balance(storage_deposit_limit),
839 data,
840 );
841
842 if let Ok(return_value) = &output.result {
843 if return_value.did_revert() {
844 output.result = Err(<Error<T>>::ContractReverted.into());
845 }
846 }
847 dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call())
848 }
849
850 #[pallet::call_index(2)]
856 #[pallet::weight(
857 T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
858 )]
859 pub fn instantiate(
860 origin: OriginFor<T>,
861 #[pallet::compact] value: BalanceOf<T>,
862 gas_limit: Weight,
863 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
864 code_hash: sp_core::H256,
865 data: Vec<u8>,
866 salt: Option<[u8; 32]>,
867 ) -> DispatchResultWithPostInfo {
868 let data_len = data.len() as u32;
869 let mut output = Self::bare_instantiate(
870 origin,
871 Pallet::<T>::convert_native_to_evm(value),
872 gas_limit,
873 DepositLimit::Balance(storage_deposit_limit),
874 Code::Existing(code_hash),
875 data,
876 salt,
877 BumpNonce::Yes,
878 );
879 if let Ok(retval) = &output.result {
880 if retval.result.did_revert() {
881 output.result = Err(<Error<T>>::ContractReverted.into());
882 }
883 }
884 dispatch_result(
885 output.result.map(|result| result.result),
886 output.gas_consumed,
887 T::WeightInfo::instantiate(data_len),
888 )
889 }
890
891 #[pallet::call_index(3)]
919 #[pallet::weight(
920 T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
921 .saturating_add(*gas_limit)
922 )]
923 pub fn instantiate_with_code(
924 origin: OriginFor<T>,
925 #[pallet::compact] value: BalanceOf<T>,
926 gas_limit: Weight,
927 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
928 code: Vec<u8>,
929 data: Vec<u8>,
930 salt: Option<[u8; 32]>,
931 ) -> DispatchResultWithPostInfo {
932 let code_len = code.len() as u32;
933 let data_len = data.len() as u32;
934 let mut output = Self::bare_instantiate(
935 origin,
936 Pallet::<T>::convert_native_to_evm(value),
937 gas_limit,
938 DepositLimit::Balance(storage_deposit_limit),
939 Code::Upload(code),
940 data,
941 salt,
942 BumpNonce::Yes,
943 );
944 if let Ok(retval) = &output.result {
945 if retval.result.did_revert() {
946 output.result = Err(<Error<T>>::ContractReverted.into());
947 }
948 }
949 dispatch_result(
950 output.result.map(|result| result.result),
951 output.gas_consumed,
952 T::WeightInfo::instantiate_with_code(code_len, data_len),
953 )
954 }
955
956 #[pallet::call_index(10)]
964 #[pallet::weight(
965 T::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
966 .saturating_add(*gas_limit)
967 )]
968 pub fn eth_instantiate_with_code(
969 origin: OriginFor<T>,
970 value: U256,
971 gas_limit: Weight,
972 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
973 code: Vec<u8>,
974 data: Vec<u8>,
975 ) -> DispatchResultWithPostInfo {
976 let code_len = code.len() as u32;
977 let data_len = data.len() as u32;
978 let mut output = Self::bare_instantiate(
979 origin,
980 value,
981 gas_limit,
982 DepositLimit::Balance(storage_deposit_limit),
983 Code::Upload(code),
984 data,
985 None,
986 BumpNonce::No,
987 );
988
989 if let Ok(retval) = &output.result {
990 if retval.result.did_revert() {
991 output.result = Err(<Error<T>>::ContractReverted.into());
992 }
993 }
994 dispatch_result(
995 output.result.map(|result| result.result),
996 output.gas_consumed,
997 T::WeightInfo::eth_instantiate_with_code(
998 code_len,
999 data_len,
1000 Pallet::<T>::has_dust(value).into(),
1001 ),
1002 )
1003 }
1004
1005 #[pallet::call_index(11)]
1008 #[pallet::weight(T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into()).saturating_add(*gas_limit))]
1009 pub fn eth_call(
1010 origin: OriginFor<T>,
1011 dest: H160,
1012 value: U256,
1013 gas_limit: Weight,
1014 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1015 data: Vec<u8>,
1016 ) -> DispatchResultWithPostInfo {
1017 let mut output = Self::bare_call(
1018 origin,
1019 dest,
1020 value,
1021 gas_limit,
1022 DepositLimit::Balance(storage_deposit_limit),
1023 data,
1024 );
1025
1026 if let Ok(return_value) = &output.result {
1027 if return_value.did_revert() {
1028 output.result = Err(<Error<T>>::ContractReverted.into());
1029 }
1030 }
1031 dispatch_result(
1032 output.result,
1033 output.gas_consumed,
1034 T::WeightInfo::eth_call(Pallet::<T>::has_dust(value).into()),
1035 )
1036 }
1037
1038 #[pallet::call_index(4)]
1053 #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))]
1054 pub fn upload_code(
1055 origin: OriginFor<T>,
1056 code: Vec<u8>,
1057 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1058 ) -> DispatchResult {
1059 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1060 }
1061
1062 #[pallet::call_index(5)]
1067 #[pallet::weight(T::WeightInfo::remove_code())]
1068 pub fn remove_code(
1069 origin: OriginFor<T>,
1070 code_hash: sp_core::H256,
1071 ) -> DispatchResultWithPostInfo {
1072 let origin = ensure_signed(origin)?;
1073 <ContractBlob<T>>::remove(&origin, code_hash)?;
1074 Ok(Pays::No.into())
1076 }
1077
1078 #[pallet::call_index(6)]
1089 #[pallet::weight(T::WeightInfo::set_code())]
1090 pub fn set_code(
1091 origin: OriginFor<T>,
1092 dest: H160,
1093 code_hash: sp_core::H256,
1094 ) -> DispatchResult {
1095 ensure_root(origin)?;
1096 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1097 let Some(account) = account else {
1098 return Err(<Error<T>>::ContractNotFound.into());
1099 };
1100
1101 let AccountType::Contract(ref mut contract) = account.account_type else {
1102 return Err(<Error<T>>::ContractNotFound.into());
1103 };
1104
1105 <CodeInfo<T>>::increment_refcount(code_hash)?;
1106 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1107 contract.code_hash = code_hash;
1108
1109 Ok(())
1110 })
1111 }
1112
1113 #[pallet::call_index(7)]
1118 #[pallet::weight(T::WeightInfo::map_account())]
1119 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1120 let origin = ensure_signed(origin)?;
1121 T::AddressMapper::map(&origin)
1122 }
1123
1124 #[pallet::call_index(8)]
1129 #[pallet::weight(T::WeightInfo::unmap_account())]
1130 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1131 let origin = ensure_signed(origin)?;
1132 T::AddressMapper::unmap(&origin)
1133 }
1134
1135 #[pallet::call_index(9)]
1141 #[pallet::weight({
1142 let dispatch_info = call.get_dispatch_info();
1143 (
1144 T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1145 dispatch_info.class
1146 )
1147 })]
1148 pub fn dispatch_as_fallback_account(
1149 origin: OriginFor<T>,
1150 call: Box<<T as Config>::RuntimeCall>,
1151 ) -> DispatchResultWithPostInfo {
1152 let origin = ensure_signed(origin)?;
1153 let unmapped_account =
1154 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1155 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1156 }
1157 }
1158}
1159
1160fn dispatch_result<R>(
1162 result: Result<R, DispatchError>,
1163 gas_consumed: Weight,
1164 base_weight: Weight,
1165) -> DispatchResultWithPostInfo {
1166 let post_info = PostDispatchInfo {
1167 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1168 pays_fee: Default::default(),
1169 };
1170
1171 result
1172 .map(|_| post_info)
1173 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1174}
1175
1176impl<T: Config> Pallet<T>
1177where
1178 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
1179 MomentOf<T>: Into<U256>,
1180 T::Hash: frame_support::traits::IsType<H256>,
1181{
1182 pub fn bare_call(
1189 origin: OriginFor<T>,
1190 dest: H160,
1191 evm_value: U256,
1192 gas_limit: Weight,
1193 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1194 data: Vec<u8>,
1195 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1196 if let Err(contract_result) = Self::ensure_non_contract_if_signed(&origin) {
1197 return contract_result;
1198 }
1199 let mut gas_meter = GasMeter::new(gas_limit);
1200 let mut storage_deposit = Default::default();
1201
1202 let try_call = || {
1203 let origin = Origin::from_runtime_origin(origin)?;
1204 let mut storage_meter = StorageMeter::new(storage_deposit_limit.limit());
1205 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1206 origin.clone(),
1207 dest,
1208 &mut gas_meter,
1209 &mut storage_meter,
1210 evm_value,
1211 data,
1212 storage_deposit_limit.is_unchecked(),
1213 )?;
1214 storage_deposit = storage_meter
1215 .try_into_deposit(&origin, storage_deposit_limit.is_unchecked())
1216 .inspect_err(|err| {
1217 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1218 })?;
1219 Ok(result)
1220 };
1221 let result = Self::run_guarded(try_call);
1222 ContractResult {
1223 result: result.map_err(|r| r.error),
1224 gas_consumed: gas_meter.gas_consumed(),
1225 gas_required: gas_meter.gas_required(),
1226 storage_deposit,
1227 }
1228 }
1229
1230 pub fn prepare_dry_run(account: &T::AccountId) {
1236 frame_system::Pallet::<T>::inc_account_nonce(account);
1239 }
1240
1241 pub fn bare_instantiate(
1247 origin: OriginFor<T>,
1248 evm_value: U256,
1249 gas_limit: Weight,
1250 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1251 code: Code,
1252 data: Vec<u8>,
1253 salt: Option<[u8; 32]>,
1254 bump_nonce: BumpNonce,
1255 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1256 if let Err(contract_result) = Self::ensure_non_contract_if_signed(&origin) {
1258 return contract_result;
1259 }
1260 let mut gas_meter = GasMeter::new(gas_limit);
1261 let mut storage_deposit = Default::default();
1262 let unchecked_deposit_limit = storage_deposit_limit.is_unchecked();
1263 let mut storage_deposit_limit = storage_deposit_limit.limit();
1264 let try_instantiate = || {
1265 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1266
1267 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1268 let (executable, upload_deposit) = match code {
1269 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1270 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1271 let (executable, upload_deposit) = Self::try_upload_pvm_code(
1272 upload_account,
1273 code,
1274 storage_deposit_limit,
1275 unchecked_deposit_limit,
1276 )?;
1277 storage_deposit_limit.saturating_reduce(upload_deposit);
1278 (executable, upload_deposit)
1279 },
1280 Code::Upload(code) =>
1281 if T::AllowEVMBytecode::get() {
1282 let origin = T::UploadOrigin::ensure_origin(origin)?;
1283 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1284 (executable, Default::default())
1285 } else {
1286 return Err(<Error<T>>::CodeRejected.into())
1287 },
1288 Code::Existing(code_hash) =>
1289 (ContractBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()),
1290 };
1291 let instantiate_origin = Origin::from_account_id(instantiate_account.clone());
1292 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1293 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1294 instantiate_account,
1295 executable,
1296 &mut gas_meter,
1297 &mut storage_meter,
1298 evm_value,
1299 data,
1300 salt.as_ref(),
1301 unchecked_deposit_limit,
1302 bump_nonce,
1303 );
1304 storage_deposit = storage_meter
1305 .try_into_deposit(&instantiate_origin, unchecked_deposit_limit)?
1306 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1307 result
1308 };
1309 let output = Self::run_guarded(try_instantiate);
1310 ContractResult {
1311 result: output
1312 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1313 .map_err(|e| e.error),
1314 gas_consumed: gas_meter.gas_consumed(),
1315 gas_required: gas_meter.gas_required(),
1316 storage_deposit,
1317 }
1318 }
1319
1320 pub fn dry_run_eth_transact(
1329 mut tx: GenericTransaction,
1330 gas_limit: Weight,
1331 tx_fee: impl Fn(<T as Config>::RuntimeCall, <T as Config>::RuntimeCall) -> BalanceOf<T>,
1332 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1333 where
1334 <T as frame_system::Config>::RuntimeCall:
1335 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1336 T: pallet_transaction_payment::Config,
1337 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1338 <T as Config>::RuntimeCall: From<crate::Call<T>>,
1339 <T as Config>::RuntimeCall: Encode,
1340 T::Nonce: Into<U256>,
1341 T::Hash: frame_support::traits::IsType<H256>,
1342 {
1343 log::trace!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?} gas_limit: {gas_limit:?}");
1344
1345 let from = tx.from.unwrap_or_default();
1346 let origin = T::AddressMapper::to_account_id(&from);
1347 Self::prepare_dry_run(&origin);
1348
1349 let storage_deposit_limit = if tx.gas.is_some() {
1350 DepositLimit::Balance(BalanceOf::<T>::max_value())
1351 } else {
1352 DepositLimit::UnsafeOnlyForDryRun
1353 };
1354
1355 if tx.nonce.is_none() {
1356 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1357 }
1358 if tx.chain_id.is_none() {
1359 tx.chain_id = Some(T::ChainId::get().into());
1360 }
1361 if tx.gas_price.is_none() {
1362 tx.gas_price = Some(GAS_PRICE.into());
1363 }
1364 if tx.max_priority_fee_per_gas.is_none() {
1365 tx.max_priority_fee_per_gas = Some(GAS_PRICE.into());
1366 }
1367 if tx.max_fee_per_gas.is_none() {
1368 tx.max_fee_per_gas = Some(GAS_PRICE.into());
1369 }
1370 if tx.gas.is_none() {
1371 tx.gas = Some(Self::evm_block_gas_limit());
1372 }
1373 if tx.r#type.is_none() {
1374 tx.r#type = Some(TYPE_EIP1559.into());
1375 }
1376
1377 let value = tx.value.unwrap_or_default();
1379 let input = tx.input.clone().to_vec();
1380
1381 let extract_error = |err| {
1382 if err == Error::<T>::TransferFailed.into() ||
1383 err == Error::<T>::StorageDepositNotEnoughFunds.into() ||
1384 err == Error::<T>::StorageDepositLimitExhausted.into()
1385 {
1386 let balance = Self::evm_balance(&from);
1387 return Err(EthTransactError::Message(format!(
1388 "insufficient funds for gas * price + value: address {from:?} have {balance} (supplied gas {})",
1389 tx.gas.unwrap_or_default()
1390 )));
1391 }
1392
1393 return Err(EthTransactError::Message(format!(
1394 "Failed to instantiate contract: {err:?}"
1395 )));
1396 };
1397
1398 let (mut result, dispatch_call) = match tx.to {
1400 Some(dest) => {
1402 if dest == RUNTIME_PALLETS_ADDR {
1403 let Ok(dispatch_call) = <T as Config>::RuntimeCall::decode(&mut &input[..])
1404 else {
1405 return Err(EthTransactError::Message(format!(
1406 "Failed to decode pallet-call {input:?}"
1407 )));
1408 };
1409
1410 if let Err(err) =
1411 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1412 {
1413 return Err(EthTransactError::Message(format!(
1414 "Failed to dispatch call: {err:?}"
1415 )));
1416 };
1417
1418 let result = EthTransactInfo {
1419 gas_required: dispatch_call.get_dispatch_info().total_weight(),
1420 ..Default::default()
1421 };
1422
1423 (result, dispatch_call)
1424 } else {
1425 let result = crate::Pallet::<T>::bare_call(
1427 T::RuntimeOrigin::signed(origin),
1428 dest,
1429 value,
1430 gas_limit,
1431 storage_deposit_limit,
1432 input.clone(),
1433 );
1434
1435 let data = match result.result {
1436 Ok(return_value) => {
1437 if return_value.did_revert() {
1438 return Err(EthTransactError::Data(return_value.data));
1439 }
1440 return_value.data
1441 },
1442 Err(err) => {
1443 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1444 return extract_error(err);
1445 },
1446 };
1447
1448 let result = EthTransactInfo {
1449 gas_required: result.gas_required,
1450 storage_deposit: result.storage_deposit.charge_or_zero(),
1451 data,
1452 eth_gas: Default::default(),
1453 };
1454
1455 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1456 result.gas_required,
1457 result.storage_deposit,
1458 );
1459 let dispatch_call: <T as Config>::RuntimeCall = crate::Call::<T>::eth_call {
1460 dest,
1461 value,
1462 gas_limit,
1463 storage_deposit_limit,
1464 data: input.clone(),
1465 }
1466 .into();
1467 (result, dispatch_call)
1468 }
1469 },
1470 None => {
1472 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1474 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1475 } else {
1476 (input, vec![])
1477 };
1478
1479 let result = crate::Pallet::<T>::bare_instantiate(
1481 T::RuntimeOrigin::signed(origin),
1482 value,
1483 gas_limit,
1484 storage_deposit_limit,
1485 Code::Upload(code.clone()),
1486 data.clone(),
1487 None,
1488 BumpNonce::No,
1489 );
1490
1491 let returned_data = match result.result {
1492 Ok(return_value) => {
1493 if return_value.result.did_revert() {
1494 return Err(EthTransactError::Data(return_value.result.data));
1495 }
1496 return_value.result.data
1497 },
1498 Err(err) => {
1499 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1500 return extract_error(err);
1501 },
1502 };
1503
1504 let result = EthTransactInfo {
1505 gas_required: result.gas_required,
1506 storage_deposit: result.storage_deposit.charge_or_zero(),
1507 data: returned_data,
1508 eth_gas: Default::default(),
1509 };
1510
1511 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1513 result.gas_required,
1514 result.storage_deposit,
1515 );
1516 let dispatch_call: <T as Config>::RuntimeCall =
1517 crate::Call::<T>::eth_instantiate_with_code {
1518 value,
1519 gas_limit,
1520 storage_deposit_limit,
1521 code,
1522 data,
1523 }
1524 .into();
1525 (result, dispatch_call)
1526 },
1527 };
1528
1529 let Ok(unsigned_tx) = tx.clone().try_into_unsigned() else {
1530 return Err(EthTransactError::Message("Invalid transaction".into()));
1531 };
1532
1533 let eth_transact_call =
1534 crate::Call::<T>::eth_transact { payload: unsigned_tx.dummy_signed_payload() };
1535 let fee = tx_fee(eth_transact_call.into(), dispatch_call);
1536 let raw_gas = Self::evm_fee_to_gas(fee);
1537 let eth_gas =
1538 T::EthGasEncoder::encode(raw_gas, result.gas_required, result.storage_deposit);
1539
1540 log::trace!(target: LOG_TARGET, "bare_eth_call: raw_gas: {raw_gas:?} eth_gas: {eth_gas:?}");
1541 result.eth_gas = eth_gas;
1542 Ok(result)
1543 }
1544
1545 pub fn evm_balance(address: &H160) -> U256 {
1549 let balance = AccountInfo::<T>::balance((*address).into());
1550 Self::convert_native_to_evm(balance)
1551 }
1552
1553 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
1559 let (balance, dust) = Self::new_balance_with_dust(evm_value)
1560 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
1561 let account_id = T::AddressMapper::to_account_id(&address);
1562 T::Currency::set_balance(&account_id, balance);
1563 AccountInfoOf::<T>::mutate(address, |account| {
1564 account.as_mut().map(|a| a.dust = dust);
1565 });
1566
1567 Ok(())
1568 }
1569
1570 pub fn new_balance_with_dust(
1574 evm_value: U256,
1575 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
1576 let ed = T::Currency::minimum_balance();
1577 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
1578 let (value, dust) = balance_with_dust.deconstruct();
1579
1580 Ok((ed.saturating_add(value), dust))
1581 }
1582
1583 pub fn evm_nonce(address: &H160) -> u32
1585 where
1586 T::Nonce: Into<u32>,
1587 {
1588 let account = T::AddressMapper::to_account_id(&address);
1589 System::<T>::account_nonce(account).into()
1590 }
1591
1592 pub fn evm_fee_to_gas(fee: BalanceOf<T>) -> U256 {
1595 let fee = Self::convert_native_to_evm(fee);
1596 let gas_price = GAS_PRICE.into();
1597 let (quotient, remainder) = fee.div_mod(gas_price);
1598 if remainder.is_zero() {
1599 quotient
1600 } else {
1601 quotient + U256::one()
1602 }
1603 }
1604
1605 fn evm_gas_to_fee(gas: U256, gas_price: U256) -> Result<BalanceOf<T>, Error<T>> {
1607 let fee = gas.saturating_mul(gas_price);
1608 let value = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(fee)
1609 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
1610 Ok(value.into_rounded_balance())
1611 }
1612
1613 pub fn evm_gas_from_weight(weight: Weight) -> U256 {
1615 let fee = T::WeightPrice::convert(weight);
1616 Self::evm_fee_to_gas(fee)
1617 }
1618
1619 pub fn evm_block_gas_limit() -> U256
1621 where
1622 <T as frame_system::Config>::RuntimeCall:
1623 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1624 T: pallet_transaction_payment::Config,
1625 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1626 {
1627 let max_block_weight = T::BlockWeights::get()
1628 .get(DispatchClass::Normal)
1629 .max_total
1630 .unwrap_or_else(|| T::BlockWeights::get().max_block);
1631
1632 let length_fee = pallet_transaction_payment::Pallet::<T>::length_to_fee(
1633 5 * 1024 * 1024, );
1635
1636 Self::evm_gas_from_weight(max_block_weight)
1637 .saturating_add(Self::evm_fee_to_gas(length_fee.into()))
1638 }
1639
1640 pub fn evm_gas_price() -> U256 {
1642 GAS_PRICE.into()
1643 }
1644
1645 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
1647 where
1648 T::Nonce: Into<u32>,
1649 {
1650 match tracer_type {
1651 TracerType::CallTracer(config) => CallTracer::new(
1652 config.unwrap_or_default(),
1653 Self::evm_gas_from_weight as fn(Weight) -> U256,
1654 )
1655 .into(),
1656 TracerType::PrestateTracer(config) =>
1657 PrestateTracer::new(config.unwrap_or_default()).into(),
1658 }
1659 }
1660
1661 pub fn bare_upload_code(
1665 origin: OriginFor<T>,
1666 code: Vec<u8>,
1667 storage_deposit_limit: BalanceOf<T>,
1668 ) -> CodeUploadResult<BalanceOf<T>> {
1669 let origin = T::UploadOrigin::ensure_origin(origin)?;
1670 let (module, deposit) =
1671 Self::try_upload_pvm_code(origin, code, storage_deposit_limit, false)?;
1672 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
1673 }
1674
1675 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
1677 let contract_info =
1678 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1679
1680 let maybe_value = contract_info.read(&Key::from_fixed(key));
1681 Ok(maybe_value)
1682 }
1683
1684 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
1686 let contract_info =
1687 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1688
1689 let maybe_value = contract_info.read(
1690 &Key::try_from_var(key)
1691 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
1692 .into(),
1693 );
1694 Ok(maybe_value)
1695 }
1696
1697 fn try_upload_pvm_code(
1699 origin: T::AccountId,
1700 code: Vec<u8>,
1701 storage_deposit_limit: BalanceOf<T>,
1702 skip_transfer: bool,
1703 ) -> Result<(ContractBlob<T>, BalanceOf<T>), DispatchError> {
1704 let mut module = ContractBlob::from_pvm_code(code, origin)?;
1705 let deposit = module.store_code(skip_transfer)?;
1706 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
1707 Ok((module, deposit))
1708 }
1709
1710 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
1712 executing_contract::using_once(&mut false, || {
1713 executing_contract::with(|f| {
1714 if *f {
1716 return Err(())
1717 }
1718 *f = true;
1720 Ok(())
1721 })
1722 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
1723 .map_err(|_| <Error<T>>::ReenteredPallet.into())
1724 .map(|_| f())
1725 .and_then(|r| r)
1726 })
1727 }
1728
1729 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
1731 let (value, dust) = value.into().deconstruct();
1732 value
1733 .into()
1734 .saturating_mul(T::NativeToEthRatio::get().into())
1735 .saturating_add(dust.into())
1736 }
1737
1738 fn ensure_non_contract_if_signed<ReturnValue>(
1740 origin: &OriginFor<T>,
1741 ) -> Result<(), ContractResult<ReturnValue, BalanceOf<T>>> {
1742 use crate::exec::is_precompile;
1743 let Ok(who) = ensure_signed(origin.clone()) else { return Ok(()) };
1744 let address = <T::AddressMapper as AddressMapper<T>>::to_address(&who);
1745
1746 if is_precompile::<T, ContractBlob<T>>(&address) {
1748 log::debug!(
1749 target: crate::LOG_TARGET,
1750 "EIP-3607: reject externally-signed tx from precompile account {:?}",
1751 address
1752 );
1753 return Err(ContractResult {
1754 result: Err(DispatchError::BadOrigin),
1755 gas_consumed: Weight::default(),
1756 gas_required: Weight::default(),
1757 storage_deposit: Default::default(),
1758 });
1759 }
1760
1761 if <AccountInfo<T>>::is_contract(&address) {
1764 log::debug!(
1765 target: crate::LOG_TARGET,
1766 "EIP-3607: reject externally-signed tx from contract account {:?}",
1767 address
1768 );
1769 return Err(ContractResult {
1770 result: Err(DispatchError::BadOrigin),
1771 gas_consumed: Weight::default(),
1772 gas_required: Weight::default(),
1773 storage_deposit: Default::default(),
1774 });
1775 }
1776 Ok(())
1777 }
1778}
1779
1780impl<T: Config> Pallet<T> {
1781 pub fn account_id() -> T::AccountId {
1783 use frame_support::PalletId;
1784 use sp_runtime::traits::AccountIdConversion;
1785 PalletId(*b"py/reviv").into_account_truncating()
1786 }
1787
1788 fn has_dust(value: U256) -> bool {
1790 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
1791 }
1792
1793 fn has_balance(value: U256) -> bool {
1795 value >= U256::from(<T>::NativeToEthRatio::get())
1796 }
1797
1798 fn min_balance() -> BalanceOf<T> {
1800 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
1801 }
1802
1803 fn deposit_event(event: Event<T>) {
1805 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
1806 }
1807
1808 pub fn block_author() -> Option<H160> {
1810 use frame_support::traits::FindAuthor;
1811
1812 let digest = <frame_system::Pallet<T>>::digest();
1813 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
1814
1815 let account_id = T::FindAuthor::find_author(pre_runtime_digests)?;
1816 Some(T::AddressMapper::to_address(&account_id))
1817 }
1818
1819 pub fn code(address: &H160) -> Vec<u8> {
1823 use precompiles::{All, Precompiles};
1824 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
1825 return code.into()
1826 }
1827 AccountInfo::<T>::load_contract(&address)
1828 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
1829 .map(|code| code.into())
1830 .unwrap_or_default()
1831 }
1832}
1833
1834pub const RUNTIME_PALLETS_ADDR: H160 =
1839 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
1840
1841environmental!(executing_contract: bool);
1843
1844sp_api::decl_runtime_apis! {
1845 #[api_version(1)]
1847 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber> where
1848 AccountId: Codec,
1849 Balance: Codec,
1850 Nonce: Codec,
1851 BlockNumber: Codec,
1852 {
1853 fn block_gas_limit() -> U256;
1855
1856 fn balance(address: H160) -> U256;
1858
1859 fn gas_price() -> U256;
1861
1862 fn nonce(address: H160) -> Nonce;
1864
1865 fn call(
1869 origin: AccountId,
1870 dest: H160,
1871 value: Balance,
1872 gas_limit: Option<Weight>,
1873 storage_deposit_limit: Option<Balance>,
1874 input_data: Vec<u8>,
1875 ) -> ContractResult<ExecReturnValue, Balance>;
1876
1877 fn instantiate(
1881 origin: AccountId,
1882 value: Balance,
1883 gas_limit: Option<Weight>,
1884 storage_deposit_limit: Option<Balance>,
1885 code: Code,
1886 data: Vec<u8>,
1887 salt: Option<[u8; 32]>,
1888 ) -> ContractResult<InstantiateReturnValue, Balance>;
1889
1890
1891 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
1895
1896 fn upload_code(
1900 origin: AccountId,
1901 code: Vec<u8>,
1902 storage_deposit_limit: Option<Balance>,
1903 ) -> CodeUploadResult<Balance>;
1904
1905 fn get_storage(
1911 address: H160,
1912 key: [u8; 32],
1913 ) -> GetStorageResult;
1914
1915 fn get_storage_var_key(
1921 address: H160,
1922 key: Vec<u8>,
1923 ) -> GetStorageResult;
1924
1925 fn trace_block(
1932 block: Block,
1933 config: TracerType
1934 ) -> Vec<(u32, Trace)>;
1935
1936 fn trace_tx(
1943 block: Block,
1944 tx_index: u32,
1945 config: TracerType
1946 ) -> Option<Trace>;
1947
1948 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
1952
1953 fn block_author() -> Option<H160>;
1955
1956 fn address(account_id: AccountId) -> H160;
1958
1959 fn account_id(address: H160) -> AccountId;
1961
1962 fn runtime_pallets_address() -> H160;
1964
1965 fn code(address: H160) -> Vec<u8>;
1967
1968 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
1970 }
1971}
1972
1973#[macro_export]
1981macro_rules! impl_runtime_apis_plus_revive {
1982 ($Runtime: ty, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
1983
1984 impl_runtime_apis! {
1985 $($rest)*
1986
1987 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for $Runtime {
1988 fn balance(address: $crate::H160) -> $crate::U256 {
1989 $crate::Pallet::<Self>::evm_balance(&address)
1990 }
1991
1992 fn block_author() -> Option<$crate::H160> {
1993 $crate::Pallet::<Self>::block_author()
1994 }
1995
1996 fn block_gas_limit() -> $crate::U256 {
1997 $crate::Pallet::<Self>::evm_block_gas_limit()
1998 }
1999
2000 fn gas_price() -> $crate::U256 {
2001 $crate::Pallet::<Self>::evm_gas_price()
2002 }
2003
2004 fn nonce(address: $crate::H160) -> Nonce {
2005 use $crate::AddressMapper;
2006 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2007 $crate::frame_system::Pallet::<Self>::account_nonce(account)
2008 }
2009
2010 fn address(account_id: AccountId) -> $crate::H160 {
2011 use $crate::AddressMapper;
2012 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2013 }
2014
2015 fn eth_transact(
2016 tx: $crate::evm::GenericTransaction,
2017 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2018 use $crate::{
2019 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
2020 sp_runtime::traits::TransactionExtension,
2021 sp_runtime::traits::Block as BlockT
2022 };
2023
2024 let tx_fee = |call: <Self as $crate::frame_system::Config>::RuntimeCall, dispatch_call: <Self as $crate::frame_system::Config>::RuntimeCall| {
2025 use $crate::frame_support::dispatch::GetDispatchInfo;
2026
2027 let mut dispatch_info = dispatch_call.get_dispatch_info();
2029 dispatch_info.extension_weight =
2030 <$EthExtra>::get_eth_extension(0, 0u32.into()).weight(&dispatch_call);
2031
2032 let uxt: <Block as BlockT>::Extrinsic =
2034 $crate::sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into();
2035
2036 $crate::pallet_transaction_payment::Pallet::<Self>::compute_fee(
2038 uxt.encoded_size() as u32,
2039 &dispatch_info,
2040 0u32.into(),
2041 )
2042 };
2043
2044 let blockweights: $crate::BlockWeights =
2045 <Self as $crate::frame_system::Config>::BlockWeights::get();
2046 $crate::Pallet::<Self>::dry_run_eth_transact(tx, blockweights.max_block, tx_fee)
2047 }
2048
2049 fn call(
2050 origin: AccountId,
2051 dest: $crate::H160,
2052 value: Balance,
2053 gas_limit: Option<$crate::Weight>,
2054 storage_deposit_limit: Option<Balance>,
2055 input_data: Vec<u8>,
2056 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2057 use $crate::frame_support::traits::Get;
2058 let blockweights: $crate::BlockWeights =
2059 <Self as $crate::frame_system::Config>::BlockWeights::get();
2060
2061 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2062 $crate::Pallet::<Self>::bare_call(
2063 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2064 dest,
2065 $crate::Pallet::<Self>::convert_native_to_evm(value),
2066 gas_limit.unwrap_or(blockweights.max_block),
2067 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
2068 input_data,
2069 )
2070 }
2071
2072 fn instantiate(
2073 origin: AccountId,
2074 value: Balance,
2075 gas_limit: Option<$crate::Weight>,
2076 storage_deposit_limit: Option<Balance>,
2077 code: $crate::Code,
2078 data: Vec<u8>,
2079 salt: Option<[u8; 32]>,
2080 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2081 use $crate::frame_support::traits::Get;
2082 let blockweights: $crate::BlockWeights =
2083 <Self as $crate::frame_system::Config>::BlockWeights::get();
2084
2085 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2086 $crate::Pallet::<Self>::bare_instantiate(
2087 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2088 $crate::Pallet::<Self>::convert_native_to_evm(value),
2089 gas_limit.unwrap_or(blockweights.max_block),
2090 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
2091 code,
2092 data,
2093 salt,
2094 $crate::BumpNonce::Yes,
2095 )
2096 }
2097
2098 fn upload_code(
2099 origin: AccountId,
2100 code: Vec<u8>,
2101 storage_deposit_limit: Option<Balance>,
2102 ) -> $crate::CodeUploadResult<Balance> {
2103 let origin =
2104 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
2105 $crate::Pallet::<Self>::bare_upload_code(
2106 origin,
2107 code,
2108 storage_deposit_limit.unwrap_or(u128::MAX),
2109 )
2110 }
2111
2112 fn get_storage_var_key(
2113 address: $crate::H160,
2114 key: Vec<u8>,
2115 ) -> $crate::GetStorageResult {
2116 $crate::Pallet::<Self>::get_storage_var_key(address, key)
2117 }
2118
2119 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2120 $crate::Pallet::<Self>::get_storage(address, key)
2121 }
2122
2123 fn trace_block(
2124 block: Block,
2125 tracer_type: $crate::evm::TracerType,
2126 ) -> Vec<(u32, $crate::evm::Trace)> {
2127 use $crate::{sp_runtime::traits::Block, tracing::trace};
2128 let mut traces = vec![];
2129 let (header, extrinsics) = block.deconstruct();
2130 <$Executive>::initialize_block(&header);
2131 for (index, ext) in extrinsics.into_iter().enumerate() {
2132 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2133 let t = tracer.as_tracing();
2134 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2135
2136 if let Some(tx_trace) = tracer.collect_trace() {
2137 traces.push((index as u32, tx_trace));
2138 }
2139 }
2140
2141 traces
2142 }
2143
2144 fn trace_tx(
2145 block: Block,
2146 tx_index: u32,
2147 tracer_type: $crate::evm::TracerType,
2148 ) -> Option<$crate::evm::Trace> {
2149 use $crate::{sp_runtime::traits::Block, tracing::trace};
2150
2151 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2152 let (header, extrinsics) = block.deconstruct();
2153
2154 <$Executive>::initialize_block(&header);
2155 for (index, ext) in extrinsics.into_iter().enumerate() {
2156 if index as u32 == tx_index {
2157 let t = tracer.as_tracing();
2158 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2159 break;
2160 } else {
2161 let _ = <$Executive>::apply_extrinsic(ext);
2162 }
2163 }
2164
2165 tracer.collect_trace()
2166 }
2167
2168 fn trace_call(
2169 tx: $crate::evm::GenericTransaction,
2170 tracer_type: $crate::evm::TracerType,
2171 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2172 use $crate::tracing::trace;
2173 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type.clone());
2174 let t = tracer.as_tracing();
2175
2176 t.watch_address(&tx.from.unwrap_or_default());
2177 t.watch_address(&$crate::Pallet::<Self>::block_author().unwrap_or_default());
2178 let result = trace(t, || Self::eth_transact(tx));
2179
2180 if let Some(trace) = tracer.collect_trace() {
2181 Ok(trace)
2182 } else if let Err(err) = result {
2183 Err(err)
2184 } else {
2185 Ok($crate::Pallet::<Self>::evm_tracer(tracer_type).empty_trace())
2186 }
2187 }
2188
2189 fn runtime_pallets_address() -> $crate::H160 {
2190 $crate::RUNTIME_PALLETS_ADDR
2191 }
2192
2193 fn code(address: $crate::H160) -> Vec<u8> {
2194 $crate::Pallet::<Self>::code(&address)
2195 }
2196
2197 fn account_id(address: $crate::H160) -> AccountId {
2198 use $crate::AddressMapper;
2199 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
2200 }
2201
2202 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2203 $crate::Pallet::<Self>::new_balance_with_dust(balance)
2204 }
2205 }
2206 }
2207 };
2208}