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>,
584 {
585 fn build(&self) {
586 use crate::{exec::Key, vm::ContractBlob};
587 use frame_support::traits::fungible::Mutate;
588
589 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
590 let _ = T::Currency::mint_into(
591 &Pallet::<T>::account_id(),
592 T::Currency::minimum_balance(),
593 );
594 }
595
596 for id in &self.mapped_accounts {
597 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
598 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
599 }
600 }
601
602 let owner = Pallet::<T>::account_id();
603
604 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
605 let Ok(balance_with_dust) =
606 BalanceWithDust::<BalanceOf<T>>::from_value::<T>(*balance).inspect_err(|err| {
607 log::error!(target: LOG_TARGET, "Failed to convert balance for {address:?}: {err:?}");
608 })
609 else {
610 continue;
611 };
612 let account_id = T::AddressMapper::to_account_id(address);
613 let (value, dust) = balance_with_dust.deconstruct();
614
615 let _ = T::Currency::set_balance(&account_id, value);
616 frame_system::Account::<T>::mutate(&account_id, |info| {
617 info.nonce = (*nonce).into();
618 });
619
620 match contract_data {
621 None => {
622 AccountInfoOf::<T>::insert(
623 address,
624 AccountInfo { account_type: AccountType::EOA, dust },
625 );
626 },
627 Some(genesis::ContractData { code, storage }) => {
628 let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
629 ContractBlob::<T>::from_pvm_code( code.clone(), owner.clone()).inspect_err(|err| {
630 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
631 })
632 } else {
633 ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
634 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
635 })
636 };
637
638 let Ok(blob) = blob else {
639 continue;
640 };
641
642 let code_hash = *blob.code_hash();
643 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
644 .inspect_err(|err| {
645 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
646 })
647 else {
648 continue;
649 };
650
651 AccountInfoOf::<T>::insert(
652 address,
653 AccountInfo { account_type: info.clone().into(), dust },
654 );
655
656 <PristineCode<T>>::insert(blob.code_hash(), code);
657 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
658 for (k, v) in storage {
659 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
660 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
661 });
662 }
663 },
664 }
665 }
666 }
667 }
668
669 #[pallet::hooks]
670 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
671 fn on_initialize(_block: BlockNumberFor<T>) -> Weight {
672 System::<T>::account_exists(&Pallet::<T>::account_id());
674 return T::DbWeight::get().reads(1)
675 }
676 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
677 let mut meter = WeightMeter::with_limit(limit);
678 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
679 meter.consumed()
680 }
681
682 fn integrity_test() {
683 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
684
685 let max_runtime_mem: u32 = T::RuntimeMemory::get();
687
688 const TOTAL_MEMORY_DEVIDER: u32 = 2;
691
692 let memory_left = i64::from(max_runtime_mem)
697 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
698 .saturating_sub(limits::MEMORY_REQUIRED.into());
699
700 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
701
702 assert!(
703 memory_left >= 0,
704 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
705 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
706 );
707
708 let max_block_ref_time = T::BlockWeights::get()
714 .get(DispatchClass::Normal)
715 .max_total
716 .unwrap_or_else(|| T::BlockWeights::get().max_block)
717 .ref_time();
718 let max_payload_size = limits::PAYLOAD_BYTES;
719 let max_key_size =
720 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
721 .expect("Key of maximal size shall be created")
722 .hash()
723 .len() as u32;
724
725 let max_immutable_key_size = T::AccountId::max_encoded_len() as u32;
726 let max_immutable_size: u32 = ((max_block_ref_time /
727 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetImmutableData(
728 limits::IMMUTABLE_BYTES,
729 ))
730 .ref_time()))
731 .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64))
732 .try_into()
733 .expect("Immutable data size too big");
734
735 let max_storage_size: u32 = ((max_block_ref_time /
738 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
739 new_bytes: max_payload_size,
740 old_bytes: 0,
741 })
742 .ref_time()))
743 .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64))
744 .saturating_add(max_immutable_size.into())
745 .try_into()
746 .expect("Storage size too big");
747
748 let max_pvf_mem: u32 = T::PVFMemory::get();
749 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
750
751 assert!(
752 max_storage_size < storage_size_limit,
753 "Maximal storage size {} exceeds the storage limit {}",
754 max_storage_size,
755 storage_size_limit
756 );
757
758 let max_events_size: u32 = ((max_block_ref_time /
762 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
763 num_topic: 0,
764 len: max_payload_size,
765 })
766 .saturating_add(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::HostFn))
767 .ref_time()))
768 .saturating_mul(max_payload_size as u64))
769 .try_into()
770 .expect("Events size too big");
771
772 assert!(
773 max_events_size < storage_size_limit,
774 "Maximal events size {} exceeds the events limit {}",
775 max_events_size,
776 storage_size_limit
777 );
778 }
779 }
780
781 #[pallet::call]
782 impl<T: Config> Pallet<T>
783 where
784 BalanceOf<T>: Into<U256> + TryFrom<U256>,
785 MomentOf<T>: Into<U256>,
786 T::Hash: frame_support::traits::IsType<H256>,
787 {
788 #[allow(unused_variables)]
804 #[pallet::call_index(0)]
805 #[pallet::weight(Weight::MAX)]
806 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
807 Err(frame_system::Error::CallFiltered::<T>.into())
808 }
809
810 #[pallet::call_index(1)]
827 #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))]
828 pub fn call(
829 origin: OriginFor<T>,
830 dest: H160,
831 #[pallet::compact] value: BalanceOf<T>,
832 gas_limit: Weight,
833 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
834 data: Vec<u8>,
835 ) -> DispatchResultWithPostInfo {
836 let mut output = Self::bare_call(
837 origin,
838 dest,
839 Pallet::<T>::convert_native_to_evm(value),
840 gas_limit,
841 DepositLimit::Balance(storage_deposit_limit),
842 data,
843 );
844
845 if let Ok(return_value) = &output.result {
846 if return_value.did_revert() {
847 output.result = Err(<Error<T>>::ContractReverted.into());
848 }
849 }
850 dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call())
851 }
852
853 #[pallet::call_index(2)]
859 #[pallet::weight(
860 T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
861 )]
862 pub fn instantiate(
863 origin: OriginFor<T>,
864 #[pallet::compact] value: BalanceOf<T>,
865 gas_limit: Weight,
866 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
867 code_hash: sp_core::H256,
868 data: Vec<u8>,
869 salt: Option<[u8; 32]>,
870 ) -> DispatchResultWithPostInfo {
871 let data_len = data.len() as u32;
872 let mut output = Self::bare_instantiate(
873 origin,
874 Pallet::<T>::convert_native_to_evm(value),
875 gas_limit,
876 DepositLimit::Balance(storage_deposit_limit),
877 Code::Existing(code_hash),
878 data,
879 salt,
880 BumpNonce::Yes,
881 );
882 if let Ok(retval) = &output.result {
883 if retval.result.did_revert() {
884 output.result = Err(<Error<T>>::ContractReverted.into());
885 }
886 }
887 dispatch_result(
888 output.result.map(|result| result.result),
889 output.gas_consumed,
890 T::WeightInfo::instantiate(data_len),
891 )
892 }
893
894 #[pallet::call_index(3)]
922 #[pallet::weight(
923 T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
924 .saturating_add(*gas_limit)
925 )]
926 pub fn instantiate_with_code(
927 origin: OriginFor<T>,
928 #[pallet::compact] value: BalanceOf<T>,
929 gas_limit: Weight,
930 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
931 code: Vec<u8>,
932 data: Vec<u8>,
933 salt: Option<[u8; 32]>,
934 ) -> DispatchResultWithPostInfo {
935 let code_len = code.len() as u32;
936 let data_len = data.len() as u32;
937 let mut output = Self::bare_instantiate(
938 origin,
939 Pallet::<T>::convert_native_to_evm(value),
940 gas_limit,
941 DepositLimit::Balance(storage_deposit_limit),
942 Code::Upload(code),
943 data,
944 salt,
945 BumpNonce::Yes,
946 );
947 if let Ok(retval) = &output.result {
948 if retval.result.did_revert() {
949 output.result = Err(<Error<T>>::ContractReverted.into());
950 }
951 }
952 dispatch_result(
953 output.result.map(|result| result.result),
954 output.gas_consumed,
955 T::WeightInfo::instantiate_with_code(code_len, data_len),
956 )
957 }
958
959 #[pallet::call_index(10)]
967 #[pallet::weight(
968 T::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
969 .saturating_add(*gas_limit)
970 )]
971 pub fn eth_instantiate_with_code(
972 origin: OriginFor<T>,
973 value: U256,
974 gas_limit: Weight,
975 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
976 code: Vec<u8>,
977 data: Vec<u8>,
978 ) -> DispatchResultWithPostInfo {
979 let code_len = code.len() as u32;
980 let data_len = data.len() as u32;
981 let mut output = Self::bare_instantiate(
982 origin,
983 value,
984 gas_limit,
985 DepositLimit::Balance(storage_deposit_limit),
986 Code::Upload(code),
987 data,
988 None,
989 BumpNonce::No,
990 );
991
992 if let Ok(retval) = &output.result {
993 if retval.result.did_revert() {
994 output.result = Err(<Error<T>>::ContractReverted.into());
995 }
996 }
997 dispatch_result(
998 output.result.map(|result| result.result),
999 output.gas_consumed,
1000 T::WeightInfo::eth_instantiate_with_code(
1001 code_len,
1002 data_len,
1003 Pallet::<T>::has_dust(value).into(),
1004 ),
1005 )
1006 }
1007
1008 #[pallet::call_index(11)]
1011 #[pallet::weight(T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into()).saturating_add(*gas_limit))]
1012 pub fn eth_call(
1013 origin: OriginFor<T>,
1014 dest: H160,
1015 value: U256,
1016 gas_limit: Weight,
1017 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1018 data: Vec<u8>,
1019 ) -> DispatchResultWithPostInfo {
1020 let mut output = Self::bare_call(
1021 origin,
1022 dest,
1023 value,
1024 gas_limit,
1025 DepositLimit::Balance(storage_deposit_limit),
1026 data,
1027 );
1028
1029 if let Ok(return_value) = &output.result {
1030 if return_value.did_revert() {
1031 output.result = Err(<Error<T>>::ContractReverted.into());
1032 }
1033 }
1034 dispatch_result(
1035 output.result,
1036 output.gas_consumed,
1037 T::WeightInfo::eth_call(Pallet::<T>::has_dust(value).into()),
1038 )
1039 }
1040
1041 #[pallet::call_index(4)]
1056 #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))]
1057 pub fn upload_code(
1058 origin: OriginFor<T>,
1059 code: Vec<u8>,
1060 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
1061 ) -> DispatchResult {
1062 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1063 }
1064
1065 #[pallet::call_index(5)]
1070 #[pallet::weight(T::WeightInfo::remove_code())]
1071 pub fn remove_code(
1072 origin: OriginFor<T>,
1073 code_hash: sp_core::H256,
1074 ) -> DispatchResultWithPostInfo {
1075 let origin = ensure_signed(origin)?;
1076 <ContractBlob<T>>::remove(&origin, code_hash)?;
1077 Ok(Pays::No.into())
1079 }
1080
1081 #[pallet::call_index(6)]
1092 #[pallet::weight(T::WeightInfo::set_code())]
1093 pub fn set_code(
1094 origin: OriginFor<T>,
1095 dest: H160,
1096 code_hash: sp_core::H256,
1097 ) -> DispatchResult {
1098 ensure_root(origin)?;
1099 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1100 let Some(account) = account else {
1101 return Err(<Error<T>>::ContractNotFound.into());
1102 };
1103
1104 let AccountType::Contract(ref mut contract) = account.account_type else {
1105 return Err(<Error<T>>::ContractNotFound.into());
1106 };
1107
1108 <CodeInfo<T>>::increment_refcount(code_hash)?;
1109 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1110 contract.code_hash = code_hash;
1111
1112 Ok(())
1113 })
1114 }
1115
1116 #[pallet::call_index(7)]
1121 #[pallet::weight(T::WeightInfo::map_account())]
1122 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1123 let origin = ensure_signed(origin)?;
1124 T::AddressMapper::map(&origin)
1125 }
1126
1127 #[pallet::call_index(8)]
1132 #[pallet::weight(T::WeightInfo::unmap_account())]
1133 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1134 let origin = ensure_signed(origin)?;
1135 T::AddressMapper::unmap(&origin)
1136 }
1137
1138 #[pallet::call_index(9)]
1144 #[pallet::weight({
1145 let dispatch_info = call.get_dispatch_info();
1146 (
1147 T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1148 dispatch_info.class
1149 )
1150 })]
1151 pub fn dispatch_as_fallback_account(
1152 origin: OriginFor<T>,
1153 call: Box<<T as Config>::RuntimeCall>,
1154 ) -> DispatchResultWithPostInfo {
1155 let origin = ensure_signed(origin)?;
1156 let unmapped_account =
1157 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1158 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1159 }
1160 }
1161}
1162
1163fn dispatch_result<R>(
1165 result: Result<R, DispatchError>,
1166 gas_consumed: Weight,
1167 base_weight: Weight,
1168) -> DispatchResultWithPostInfo {
1169 let post_info = PostDispatchInfo {
1170 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1171 pays_fee: Default::default(),
1172 };
1173
1174 result
1175 .map(|_| post_info)
1176 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1177}
1178
1179impl<T: Config> Pallet<T>
1180where
1181 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
1182 MomentOf<T>: Into<U256>,
1183 T::Hash: frame_support::traits::IsType<H256>,
1184{
1185 pub fn bare_call(
1192 origin: OriginFor<T>,
1193 dest: H160,
1194 evm_value: U256,
1195 gas_limit: Weight,
1196 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1197 data: Vec<u8>,
1198 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
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 let mut gas_meter = GasMeter::new(gas_limit);
1257 let mut storage_deposit = Default::default();
1258 let unchecked_deposit_limit = storage_deposit_limit.is_unchecked();
1259 let mut storage_deposit_limit = storage_deposit_limit.limit();
1260 let try_instantiate = || {
1261 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1262
1263 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1264 let (executable, upload_deposit) = match code {
1265 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1266 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1267 let (executable, upload_deposit) = Self::try_upload_pvm_code(
1268 upload_account,
1269 code,
1270 storage_deposit_limit,
1271 unchecked_deposit_limit,
1272 )?;
1273 storage_deposit_limit.saturating_reduce(upload_deposit);
1274 (executable, upload_deposit)
1275 },
1276 Code::Upload(code) =>
1277 if T::AllowEVMBytecode::get() {
1278 let origin = T::UploadOrigin::ensure_origin(origin)?;
1279 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1280 (executable, Default::default())
1281 } else {
1282 return Err(<Error<T>>::CodeRejected.into())
1283 },
1284 Code::Existing(code_hash) =>
1285 (ContractBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()),
1286 };
1287 let instantiate_origin = Origin::from_account_id(instantiate_account.clone());
1288 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1289 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1290 instantiate_account,
1291 executable,
1292 &mut gas_meter,
1293 &mut storage_meter,
1294 evm_value,
1295 data,
1296 salt.as_ref(),
1297 unchecked_deposit_limit,
1298 bump_nonce,
1299 );
1300 storage_deposit = storage_meter
1301 .try_into_deposit(&instantiate_origin, unchecked_deposit_limit)?
1302 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1303 result
1304 };
1305 let output = Self::run_guarded(try_instantiate);
1306 ContractResult {
1307 result: output
1308 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1309 .map_err(|e| e.error),
1310 gas_consumed: gas_meter.gas_consumed(),
1311 gas_required: gas_meter.gas_required(),
1312 storage_deposit,
1313 }
1314 }
1315
1316 pub fn dry_run_eth_transact(
1325 mut tx: GenericTransaction,
1326 gas_limit: Weight,
1327 tx_fee: impl Fn(<T as Config>::RuntimeCall, <T as Config>::RuntimeCall) -> BalanceOf<T>,
1328 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1329 where
1330 <T as frame_system::Config>::RuntimeCall:
1331 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1332 T: pallet_transaction_payment::Config,
1333 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1334 <T as Config>::RuntimeCall: From<crate::Call<T>>,
1335 <T as Config>::RuntimeCall: Encode,
1336 T::Nonce: Into<U256>,
1337 T::Hash: frame_support::traits::IsType<H256>,
1338 {
1339 log::trace!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?} gas_limit: {gas_limit:?}");
1340
1341 let from = tx.from.unwrap_or_default();
1342 let origin = T::AddressMapper::to_account_id(&from);
1343 Self::prepare_dry_run(&origin);
1344
1345 let storage_deposit_limit = if tx.gas.is_some() {
1346 DepositLimit::Balance(BalanceOf::<T>::max_value())
1347 } else {
1348 DepositLimit::UnsafeOnlyForDryRun
1349 };
1350
1351 if tx.nonce.is_none() {
1352 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1353 }
1354 if tx.chain_id.is_none() {
1355 tx.chain_id = Some(T::ChainId::get().into());
1356 }
1357 if tx.gas_price.is_none() {
1358 tx.gas_price = Some(GAS_PRICE.into());
1359 }
1360 if tx.max_priority_fee_per_gas.is_none() {
1361 tx.max_priority_fee_per_gas = Some(GAS_PRICE.into());
1362 }
1363 if tx.max_fee_per_gas.is_none() {
1364 tx.max_fee_per_gas = Some(GAS_PRICE.into());
1365 }
1366 if tx.gas.is_none() {
1367 tx.gas = Some(Self::evm_block_gas_limit());
1368 }
1369 if tx.r#type.is_none() {
1370 tx.r#type = Some(TYPE_EIP1559.into());
1371 }
1372
1373 let value = tx.value.unwrap_or_default();
1375 let input = tx.input.clone().to_vec();
1376
1377 let extract_error = |err| {
1378 if err == Error::<T>::TransferFailed.into() ||
1379 err == Error::<T>::StorageDepositNotEnoughFunds.into() ||
1380 err == Error::<T>::StorageDepositLimitExhausted.into()
1381 {
1382 let balance = Self::evm_balance(&from);
1383 return Err(EthTransactError::Message(format!(
1384 "insufficient funds for gas * price + value: address {from:?} have {balance} (supplied gas {})",
1385 tx.gas.unwrap_or_default()
1386 )));
1387 }
1388
1389 return Err(EthTransactError::Message(format!(
1390 "Failed to instantiate contract: {err:?}"
1391 )));
1392 };
1393
1394 let (mut result, dispatch_call) = match tx.to {
1396 Some(dest) => {
1398 if dest == RUNTIME_PALLETS_ADDR {
1399 let Ok(dispatch_call) = <T as Config>::RuntimeCall::decode(&mut &input[..])
1400 else {
1401 return Err(EthTransactError::Message(format!(
1402 "Failed to decode pallet-call {input:?}"
1403 )));
1404 };
1405
1406 if let Err(err) =
1407 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1408 {
1409 return Err(EthTransactError::Message(format!(
1410 "Failed to dispatch call: {err:?}"
1411 )));
1412 };
1413
1414 let result = EthTransactInfo {
1415 gas_required: dispatch_call.get_dispatch_info().total_weight(),
1416 ..Default::default()
1417 };
1418
1419 (result, dispatch_call)
1420 } else {
1421 let result = crate::Pallet::<T>::bare_call(
1423 T::RuntimeOrigin::signed(origin),
1424 dest,
1425 value,
1426 gas_limit,
1427 storage_deposit_limit,
1428 input.clone(),
1429 );
1430
1431 let data = match result.result {
1432 Ok(return_value) => {
1433 if return_value.did_revert() {
1434 return Err(EthTransactError::Data(return_value.data));
1435 }
1436 return_value.data
1437 },
1438 Err(err) => {
1439 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1440 return extract_error(err);
1441 },
1442 };
1443
1444 let result = EthTransactInfo {
1445 gas_required: result.gas_required,
1446 storage_deposit: result.storage_deposit.charge_or_zero(),
1447 data,
1448 eth_gas: Default::default(),
1449 };
1450
1451 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1452 result.gas_required,
1453 result.storage_deposit,
1454 );
1455 let dispatch_call: <T as Config>::RuntimeCall = crate::Call::<T>::eth_call {
1456 dest,
1457 value,
1458 gas_limit,
1459 storage_deposit_limit,
1460 data: input.clone(),
1461 }
1462 .into();
1463 (result, dispatch_call)
1464 }
1465 },
1466 None => {
1468 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1470 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1471 } else {
1472 (input, vec![])
1473 };
1474
1475 let result = crate::Pallet::<T>::bare_instantiate(
1477 T::RuntimeOrigin::signed(origin),
1478 value,
1479 gas_limit,
1480 storage_deposit_limit,
1481 Code::Upload(code.clone()),
1482 data.clone(),
1483 None,
1484 BumpNonce::No,
1485 );
1486
1487 let returned_data = match result.result {
1488 Ok(return_value) => {
1489 if return_value.result.did_revert() {
1490 return Err(EthTransactError::Data(return_value.result.data));
1491 }
1492 return_value.result.data
1493 },
1494 Err(err) => {
1495 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1496 return extract_error(err);
1497 },
1498 };
1499
1500 let result = EthTransactInfo {
1501 gas_required: result.gas_required,
1502 storage_deposit: result.storage_deposit.charge_or_zero(),
1503 data: returned_data,
1504 eth_gas: Default::default(),
1505 };
1506
1507 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1509 result.gas_required,
1510 result.storage_deposit,
1511 );
1512 let dispatch_call: <T as Config>::RuntimeCall =
1513 crate::Call::<T>::eth_instantiate_with_code {
1514 value,
1515 gas_limit,
1516 storage_deposit_limit,
1517 code,
1518 data,
1519 }
1520 .into();
1521 (result, dispatch_call)
1522 },
1523 };
1524
1525 let Ok(unsigned_tx) = tx.clone().try_into_unsigned() else {
1526 return Err(EthTransactError::Message("Invalid transaction".into()));
1527 };
1528
1529 let eth_transact_call =
1530 crate::Call::<T>::eth_transact { payload: unsigned_tx.dummy_signed_payload() };
1531 let fee = tx_fee(eth_transact_call.into(), dispatch_call);
1532 let raw_gas = Self::evm_fee_to_gas(fee);
1533 let eth_gas =
1534 T::EthGasEncoder::encode(raw_gas, result.gas_required, result.storage_deposit);
1535
1536 log::trace!(target: LOG_TARGET, "bare_eth_call: raw_gas: {raw_gas:?} eth_gas: {eth_gas:?}");
1537 result.eth_gas = eth_gas;
1538 Ok(result)
1539 }
1540
1541 pub fn evm_balance(address: &H160) -> U256 {
1543 let balance = AccountInfo::<T>::balance((*address).into());
1544 Self::convert_native_to_evm(balance)
1545 }
1546
1547 pub fn evm_nonce(address: &H160) -> u32
1549 where
1550 T::Nonce: Into<u32>,
1551 {
1552 let account = T::AddressMapper::to_account_id(&address);
1553 System::<T>::account_nonce(account).into()
1554 }
1555
1556 pub fn evm_fee_to_gas(fee: BalanceOf<T>) -> U256 {
1559 let fee = Self::convert_native_to_evm(fee);
1560 let gas_price = GAS_PRICE.into();
1561 let (quotient, remainder) = fee.div_mod(gas_price);
1562 if remainder.is_zero() {
1563 quotient
1564 } else {
1565 quotient + U256::one()
1566 }
1567 }
1568
1569 fn evm_gas_to_fee(gas: U256, gas_price: U256) -> Result<BalanceOf<T>, Error<T>> {
1571 let fee = gas.saturating_mul(gas_price);
1572 let value = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(fee)?;
1573 Ok(value.into_rounded_balance())
1574 }
1575
1576 pub fn evm_gas_from_weight(weight: Weight) -> U256 {
1578 let fee = T::WeightPrice::convert(weight);
1579 Self::evm_fee_to_gas(fee)
1580 }
1581
1582 pub fn evm_block_gas_limit() -> U256
1584 where
1585 <T as frame_system::Config>::RuntimeCall:
1586 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1587 T: pallet_transaction_payment::Config,
1588 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1589 {
1590 let max_block_weight = T::BlockWeights::get()
1591 .get(DispatchClass::Normal)
1592 .max_total
1593 .unwrap_or_else(|| T::BlockWeights::get().max_block);
1594
1595 let length_fee = pallet_transaction_payment::Pallet::<T>::length_to_fee(
1596 5 * 1024 * 1024, );
1598
1599 Self::evm_gas_from_weight(max_block_weight)
1600 .saturating_add(Self::evm_fee_to_gas(length_fee.into()))
1601 }
1602
1603 pub fn evm_gas_price() -> U256 {
1605 GAS_PRICE.into()
1606 }
1607
1608 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
1610 where
1611 T::Nonce: Into<u32>,
1612 {
1613 match tracer_type {
1614 TracerType::CallTracer(config) => CallTracer::new(
1615 config.unwrap_or_default(),
1616 Self::evm_gas_from_weight as fn(Weight) -> U256,
1617 )
1618 .into(),
1619 TracerType::PrestateTracer(config) =>
1620 PrestateTracer::new(config.unwrap_or_default()).into(),
1621 }
1622 }
1623
1624 pub fn bare_upload_code(
1628 origin: OriginFor<T>,
1629 code: Vec<u8>,
1630 storage_deposit_limit: BalanceOf<T>,
1631 ) -> CodeUploadResult<BalanceOf<T>> {
1632 let origin = T::UploadOrigin::ensure_origin(origin)?;
1633 let (module, deposit) =
1634 Self::try_upload_pvm_code(origin, code, storage_deposit_limit, false)?;
1635 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
1636 }
1637
1638 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
1640 let contract_info =
1641 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1642
1643 let maybe_value = contract_info.read(&Key::from_fixed(key));
1644 Ok(maybe_value)
1645 }
1646
1647 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
1649 let contract_info =
1650 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1651
1652 let maybe_value = contract_info.read(
1653 &Key::try_from_var(key)
1654 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
1655 .into(),
1656 );
1657 Ok(maybe_value)
1658 }
1659
1660 fn try_upload_pvm_code(
1662 origin: T::AccountId,
1663 code: Vec<u8>,
1664 storage_deposit_limit: BalanceOf<T>,
1665 skip_transfer: bool,
1666 ) -> Result<(ContractBlob<T>, BalanceOf<T>), DispatchError> {
1667 let mut module = ContractBlob::from_pvm_code(code, origin)?;
1668 let deposit = module.store_code(skip_transfer)?;
1669 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
1670 Ok((module, deposit))
1671 }
1672
1673 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
1675 executing_contract::using_once(&mut false, || {
1676 executing_contract::with(|f| {
1677 if *f {
1679 return Err(())
1680 }
1681 *f = true;
1683 Ok(())
1684 })
1685 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
1686 .map_err(|_| <Error<T>>::ReenteredPallet.into())
1687 .map(|_| f())
1688 .and_then(|r| r)
1689 })
1690 }
1691
1692 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
1694 let (value, dust) = value.into().deconstruct();
1695 value
1696 .into()
1697 .saturating_mul(T::NativeToEthRatio::get().into())
1698 .saturating_add(dust.into())
1699 }
1700}
1701
1702impl<T: Config> Pallet<T> {
1703 pub fn account_id() -> T::AccountId {
1705 use frame_support::PalletId;
1706 use sp_runtime::traits::AccountIdConversion;
1707 PalletId(*b"py/reviv").into_account_truncating()
1708 }
1709
1710 fn has_dust(value: U256) -> bool {
1712 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
1713 }
1714
1715 fn has_balance(value: U256) -> bool {
1717 value >= U256::from(<T>::NativeToEthRatio::get())
1718 }
1719
1720 fn min_balance() -> BalanceOf<T> {
1722 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
1723 }
1724
1725 fn deposit_event(event: Event<T>) {
1727 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
1728 }
1729
1730 pub fn block_author() -> Option<H160> {
1732 use frame_support::traits::FindAuthor;
1733
1734 let digest = <frame_system::Pallet<T>>::digest();
1735 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
1736
1737 let account_id = T::FindAuthor::find_author(pre_runtime_digests)?;
1738 Some(T::AddressMapper::to_address(&account_id))
1739 }
1740
1741 pub fn code(address: &H160) -> Vec<u8> {
1745 use precompiles::{All, Precompiles};
1746 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
1747 return code.into()
1748 }
1749 AccountInfo::<T>::load_contract(&address)
1750 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
1751 .map(|code| code.into())
1752 .unwrap_or_default()
1753 }
1754}
1755
1756pub const RUNTIME_PALLETS_ADDR: H160 =
1761 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
1762
1763environmental!(executing_contract: bool);
1765
1766sp_api::decl_runtime_apis! {
1767 #[api_version(1)]
1769 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber> where
1770 AccountId: Codec,
1771 Balance: Codec,
1772 Nonce: Codec,
1773 BlockNumber: Codec,
1774 {
1775 fn block_gas_limit() -> U256;
1777
1778 fn balance(address: H160) -> U256;
1780
1781 fn gas_price() -> U256;
1783
1784 fn nonce(address: H160) -> Nonce;
1786
1787 fn call(
1791 origin: AccountId,
1792 dest: H160,
1793 value: Balance,
1794 gas_limit: Option<Weight>,
1795 storage_deposit_limit: Option<Balance>,
1796 input_data: Vec<u8>,
1797 ) -> ContractResult<ExecReturnValue, Balance>;
1798
1799 fn instantiate(
1803 origin: AccountId,
1804 value: Balance,
1805 gas_limit: Option<Weight>,
1806 storage_deposit_limit: Option<Balance>,
1807 code: Code,
1808 data: Vec<u8>,
1809 salt: Option<[u8; 32]>,
1810 ) -> ContractResult<InstantiateReturnValue, Balance>;
1811
1812
1813 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
1817
1818 fn upload_code(
1822 origin: AccountId,
1823 code: Vec<u8>,
1824 storage_deposit_limit: Option<Balance>,
1825 ) -> CodeUploadResult<Balance>;
1826
1827 fn get_storage(
1833 address: H160,
1834 key: [u8; 32],
1835 ) -> GetStorageResult;
1836
1837 fn get_storage_var_key(
1843 address: H160,
1844 key: Vec<u8>,
1845 ) -> GetStorageResult;
1846
1847 fn trace_block(
1854 block: Block,
1855 config: TracerType
1856 ) -> Vec<(u32, Trace)>;
1857
1858 fn trace_tx(
1865 block: Block,
1866 tx_index: u32,
1867 config: TracerType
1868 ) -> Option<Trace>;
1869
1870 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
1874
1875 fn block_author() -> Option<H160>;
1877
1878 fn address(account_id: AccountId) -> H160;
1880
1881 fn runtime_pallets_address() -> H160;
1883
1884 fn code(address: H160) -> Vec<u8>;
1886 }
1887}
1888
1889#[macro_export]
1897macro_rules! impl_runtime_apis_plus_revive {
1898 ($Runtime: ty, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
1899
1900 impl_runtime_apis! {
1901 $($rest)*
1902
1903 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for $Runtime {
1904 fn balance(address: $crate::H160) -> $crate::U256 {
1905 $crate::Pallet::<Self>::evm_balance(&address)
1906 }
1907
1908 fn block_author() -> Option<$crate::H160> {
1909 $crate::Pallet::<Self>::block_author()
1910 }
1911
1912 fn block_gas_limit() -> $crate::U256 {
1913 $crate::Pallet::<Self>::evm_block_gas_limit()
1914 }
1915
1916 fn gas_price() -> $crate::U256 {
1917 $crate::Pallet::<Self>::evm_gas_price()
1918 }
1919
1920 fn nonce(address: $crate::H160) -> Nonce {
1921 use $crate::AddressMapper;
1922 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
1923 $crate::frame_system::Pallet::<Self>::account_nonce(account)
1924 }
1925
1926 fn address(account_id: AccountId) -> $crate::H160 {
1927 use $crate::AddressMapper;
1928 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
1929 }
1930
1931 fn eth_transact(
1932 tx: $crate::evm::GenericTransaction,
1933 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
1934 use $crate::{
1935 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
1936 sp_runtime::traits::TransactionExtension,
1937 sp_runtime::traits::Block as BlockT
1938 };
1939
1940 let tx_fee = |call: <Self as $crate::frame_system::Config>::RuntimeCall, dispatch_call: <Self as $crate::frame_system::Config>::RuntimeCall| {
1941 use $crate::frame_support::dispatch::GetDispatchInfo;
1942
1943 let mut dispatch_info = dispatch_call.get_dispatch_info();
1945 dispatch_info.extension_weight =
1946 <$EthExtra>::get_eth_extension(0, 0u32.into()).weight(&dispatch_call);
1947
1948 let uxt: <Block as BlockT>::Extrinsic =
1950 $crate::sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into();
1951
1952 $crate::pallet_transaction_payment::Pallet::<Self>::compute_fee(
1954 uxt.encoded_size() as u32,
1955 &dispatch_info,
1956 0u32.into(),
1957 )
1958 };
1959
1960 let blockweights: $crate::BlockWeights =
1961 <Self as $crate::frame_system::Config>::BlockWeights::get();
1962 $crate::Pallet::<Self>::dry_run_eth_transact(tx, blockweights.max_block, tx_fee)
1963 }
1964
1965 fn call(
1966 origin: AccountId,
1967 dest: $crate::H160,
1968 value: Balance,
1969 gas_limit: Option<$crate::Weight>,
1970 storage_deposit_limit: Option<Balance>,
1971 input_data: Vec<u8>,
1972 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
1973 use $crate::frame_support::traits::Get;
1974 let blockweights: $crate::BlockWeights =
1975 <Self as $crate::frame_system::Config>::BlockWeights::get();
1976
1977 $crate::Pallet::<Self>::prepare_dry_run(&origin);
1978 $crate::Pallet::<Self>::bare_call(
1979 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
1980 dest,
1981 $crate::Pallet::<Self>::convert_native_to_evm(value),
1982 gas_limit.unwrap_or(blockweights.max_block),
1983 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
1984 input_data,
1985 )
1986 }
1987
1988 fn instantiate(
1989 origin: AccountId,
1990 value: Balance,
1991 gas_limit: Option<$crate::Weight>,
1992 storage_deposit_limit: Option<Balance>,
1993 code: $crate::Code,
1994 data: Vec<u8>,
1995 salt: Option<[u8; 32]>,
1996 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
1997 use $crate::frame_support::traits::Get;
1998 let blockweights: $crate::BlockWeights =
1999 <Self as $crate::frame_system::Config>::BlockWeights::get();
2000
2001 $crate::Pallet::<Self>::prepare_dry_run(&origin);
2002 $crate::Pallet::<Self>::bare_instantiate(
2003 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
2004 $crate::Pallet::<Self>::convert_native_to_evm(value),
2005 gas_limit.unwrap_or(blockweights.max_block),
2006 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
2007 code,
2008 data,
2009 salt,
2010 $crate::BumpNonce::Yes,
2011 )
2012 }
2013
2014 fn upload_code(
2015 origin: AccountId,
2016 code: Vec<u8>,
2017 storage_deposit_limit: Option<Balance>,
2018 ) -> $crate::CodeUploadResult<Balance> {
2019 let origin =
2020 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
2021 $crate::Pallet::<Self>::bare_upload_code(
2022 origin,
2023 code,
2024 storage_deposit_limit.unwrap_or(u128::MAX),
2025 )
2026 }
2027
2028 fn get_storage_var_key(
2029 address: $crate::H160,
2030 key: Vec<u8>,
2031 ) -> $crate::GetStorageResult {
2032 $crate::Pallet::<Self>::get_storage_var_key(address, key)
2033 }
2034
2035 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2036 $crate::Pallet::<Self>::get_storage(address, key)
2037 }
2038
2039 fn trace_block(
2040 block: Block,
2041 tracer_type: $crate::evm::TracerType,
2042 ) -> Vec<(u32, $crate::evm::Trace)> {
2043 use $crate::{sp_runtime::traits::Block, tracing::trace};
2044 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2045 let mut traces = vec![];
2046 let (header, extrinsics) = block.deconstruct();
2047 <$Executive>::initialize_block(&header);
2048 for (index, ext) in extrinsics.into_iter().enumerate() {
2049 let t = tracer.as_tracing();
2050 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2051
2052 if let Some(tx_trace) = tracer.collect_trace() {
2053 traces.push((index as u32, tx_trace));
2054 }
2055 }
2056
2057 traces
2058 }
2059
2060 fn trace_tx(
2061 block: Block,
2062 tx_index: u32,
2063 tracer_type: $crate::evm::TracerType,
2064 ) -> Option<$crate::evm::Trace> {
2065 use $crate::{sp_runtime::traits::Block, tracing::trace};
2066
2067 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2068 let (header, extrinsics) = block.deconstruct();
2069
2070 <$Executive>::initialize_block(&header);
2071 for (index, ext) in extrinsics.into_iter().enumerate() {
2072 if index as u32 == tx_index {
2073 let t = tracer.as_tracing();
2074 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2075 break;
2076 } else {
2077 let _ = <$Executive>::apply_extrinsic(ext);
2078 }
2079 }
2080
2081 tracer.collect_trace()
2082 }
2083
2084 fn trace_call(
2085 tx: $crate::evm::GenericTransaction,
2086 tracer_type: $crate::evm::TracerType,
2087 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2088 use $crate::tracing::trace;
2089 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
2090 let t = tracer.as_tracing();
2091
2092 t.watch_address(&tx.from.unwrap_or_default());
2093 t.watch_address(&$crate::Pallet::<Self>::block_author().unwrap_or_default());
2094 let result = trace(t, || Self::eth_transact(tx));
2095
2096 if let Some(trace) = tracer.collect_trace() {
2097 Ok(trace)
2098 } else if let Err(err) = result {
2099 Err(err)
2100 } else {
2101 Ok(tracer.empty_trace())
2102 }
2103 }
2104
2105 fn runtime_pallets_address() -> $crate::H160 {
2106 $crate::RUNTIME_PALLETS_ADDR
2107 }
2108
2109 fn code(address: $crate::H160) -> Vec<u8> {
2110 $crate::Pallet::<Self>::code(&address)
2111 }
2112 }
2113 }
2114 };
2115}