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::*,
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 #[pallet::genesis_config]
538 #[derive(frame_support::DefaultNoBound)]
539 pub struct GenesisConfig<T: Config> {
540 pub mapped_accounts: Vec<T::AccountId>,
542 }
543
544 #[pallet::genesis_build]
545 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
546 fn build(&self) {
547 if !System::<T>::account_exists(&Pallet::<T>::account_id()) {
548 let _ = T::Currency::mint_into(
549 &Pallet::<T>::account_id(),
550 T::Currency::minimum_balance(),
551 );
552 }
553
554 for id in &self.mapped_accounts {
555 if let Err(err) = T::AddressMapper::map(id) {
556 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
557 }
558 }
559 }
560 }
561
562 #[pallet::hooks]
563 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
564 fn on_initialize(_block: BlockNumberFor<T>) -> Weight {
565 System::<T>::account_exists(&Pallet::<T>::account_id());
567 return T::DbWeight::get().reads(1)
568 }
569 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
570 let mut meter = WeightMeter::with_limit(limit);
571 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
572 meter.consumed()
573 }
574
575 fn integrity_test() {
576 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
577
578 let max_runtime_mem: u32 = T::RuntimeMemory::get();
580
581 const TOTAL_MEMORY_DEVIDER: u32 = 2;
584
585 let memory_left = i64::from(max_runtime_mem)
590 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
591 .saturating_sub(limits::MEMORY_REQUIRED.into());
592
593 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
594
595 assert!(
596 memory_left >= 0,
597 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
598 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
599 );
600
601 let max_block_ref_time = T::BlockWeights::get()
607 .get(DispatchClass::Normal)
608 .max_total
609 .unwrap_or_else(|| T::BlockWeights::get().max_block)
610 .ref_time();
611 let max_payload_size = limits::PAYLOAD_BYTES;
612 let max_key_size =
613 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
614 .expect("Key of maximal size shall be created")
615 .hash()
616 .len() as u32;
617
618 let max_immutable_key_size = T::AccountId::max_encoded_len() as u32;
619 let max_immutable_size: u32 = ((max_block_ref_time /
620 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetImmutableData(
621 limits::IMMUTABLE_BYTES,
622 ))
623 .ref_time()))
624 .saturating_mul(limits::IMMUTABLE_BYTES.saturating_add(max_immutable_key_size) as u64))
625 .try_into()
626 .expect("Immutable data size too big");
627
628 let max_storage_size: u32 = ((max_block_ref_time /
631 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
632 new_bytes: max_payload_size,
633 old_bytes: 0,
634 })
635 .ref_time()))
636 .saturating_mul(max_payload_size.saturating_add(max_key_size) as u64))
637 .saturating_add(max_immutable_size.into())
638 .try_into()
639 .expect("Storage size too big");
640
641 let max_pvf_mem: u32 = T::PVFMemory::get();
642 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
643
644 assert!(
645 max_storage_size < storage_size_limit,
646 "Maximal storage size {} exceeds the storage limit {}",
647 max_storage_size,
648 storage_size_limit
649 );
650
651 let max_events_size: u32 = ((max_block_ref_time /
655 (<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
656 num_topic: 0,
657 len: max_payload_size,
658 })
659 .saturating_add(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::HostFn))
660 .ref_time()))
661 .saturating_mul(max_payload_size as u64))
662 .try_into()
663 .expect("Events size too big");
664
665 assert!(
666 max_events_size < storage_size_limit,
667 "Maximal events size {} exceeds the events limit {}",
668 max_events_size,
669 storage_size_limit
670 );
671 }
672 }
673
674 #[pallet::call]
675 impl<T: Config> Pallet<T>
676 where
677 BalanceOf<T>: Into<U256> + TryFrom<U256>,
678 MomentOf<T>: Into<U256>,
679 T::Hash: frame_support::traits::IsType<H256>,
680 {
681 #[allow(unused_variables)]
697 #[pallet::call_index(0)]
698 #[pallet::weight(Weight::MAX)]
699 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
700 Err(frame_system::Error::CallFiltered::<T>.into())
701 }
702
703 #[pallet::call_index(1)]
720 #[pallet::weight(T::WeightInfo::call().saturating_add(*gas_limit))]
721 pub fn call(
722 origin: OriginFor<T>,
723 dest: H160,
724 #[pallet::compact] value: BalanceOf<T>,
725 gas_limit: Weight,
726 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
727 data: Vec<u8>,
728 ) -> DispatchResultWithPostInfo {
729 let mut output = Self::bare_call(
730 origin,
731 dest,
732 Pallet::<T>::convert_native_to_evm(value),
733 gas_limit,
734 DepositLimit::Balance(storage_deposit_limit),
735 data,
736 );
737
738 if let Ok(return_value) = &output.result {
739 if return_value.did_revert() {
740 output.result = Err(<Error<T>>::ContractReverted.into());
741 }
742 }
743 dispatch_result(output.result, output.gas_consumed, T::WeightInfo::call())
744 }
745
746 #[pallet::call_index(2)]
752 #[pallet::weight(
753 T::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
754 )]
755 pub fn instantiate(
756 origin: OriginFor<T>,
757 #[pallet::compact] value: BalanceOf<T>,
758 gas_limit: Weight,
759 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
760 code_hash: sp_core::H256,
761 data: Vec<u8>,
762 salt: Option<[u8; 32]>,
763 ) -> DispatchResultWithPostInfo {
764 let data_len = data.len() as u32;
765 let mut output = Self::bare_instantiate(
766 origin,
767 Pallet::<T>::convert_native_to_evm(value),
768 gas_limit,
769 DepositLimit::Balance(storage_deposit_limit),
770 Code::Existing(code_hash),
771 data,
772 salt,
773 BumpNonce::Yes,
774 );
775 if let Ok(retval) = &output.result {
776 if retval.result.did_revert() {
777 output.result = Err(<Error<T>>::ContractReverted.into());
778 }
779 }
780 dispatch_result(
781 output.result.map(|result| result.result),
782 output.gas_consumed,
783 T::WeightInfo::instantiate(data_len),
784 )
785 }
786
787 #[pallet::call_index(3)]
815 #[pallet::weight(
816 T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
817 .saturating_add(*gas_limit)
818 )]
819 pub fn instantiate_with_code(
820 origin: OriginFor<T>,
821 #[pallet::compact] value: BalanceOf<T>,
822 gas_limit: Weight,
823 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
824 code: Vec<u8>,
825 data: Vec<u8>,
826 salt: Option<[u8; 32]>,
827 ) -> DispatchResultWithPostInfo {
828 let code_len = code.len() as u32;
829 let data_len = data.len() as u32;
830 let mut output = Self::bare_instantiate(
831 origin,
832 Pallet::<T>::convert_native_to_evm(value),
833 gas_limit,
834 DepositLimit::Balance(storage_deposit_limit),
835 Code::Upload(code),
836 data,
837 salt,
838 BumpNonce::Yes,
839 );
840 if let Ok(retval) = &output.result {
841 if retval.result.did_revert() {
842 output.result = Err(<Error<T>>::ContractReverted.into());
843 }
844 }
845 dispatch_result(
846 output.result.map(|result| result.result),
847 output.gas_consumed,
848 T::WeightInfo::instantiate_with_code(code_len, data_len),
849 )
850 }
851
852 #[pallet::call_index(10)]
860 #[pallet::weight(
861 T::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pallet::<T>::has_dust(*value).into())
862 .saturating_add(*gas_limit)
863 )]
864 pub fn eth_instantiate_with_code(
865 origin: OriginFor<T>,
866 value: U256,
867 gas_limit: Weight,
868 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
869 code: Vec<u8>,
870 data: Vec<u8>,
871 ) -> DispatchResultWithPostInfo {
872 let code_len = code.len() as u32;
873 let data_len = data.len() as u32;
874 let mut output = Self::bare_instantiate(
875 origin,
876 value,
877 gas_limit,
878 DepositLimit::Balance(storage_deposit_limit),
879 Code::Upload(code),
880 data,
881 None,
882 BumpNonce::No,
883 );
884
885 if let Ok(retval) = &output.result {
886 if retval.result.did_revert() {
887 output.result = Err(<Error<T>>::ContractReverted.into());
888 }
889 }
890 dispatch_result(
891 output.result.map(|result| result.result),
892 output.gas_consumed,
893 T::WeightInfo::eth_instantiate_with_code(
894 code_len,
895 data_len,
896 Pallet::<T>::has_dust(value).into(),
897 ),
898 )
899 }
900
901 #[pallet::call_index(11)]
904 #[pallet::weight(T::WeightInfo::eth_call(Pallet::<T>::has_dust(*value).into()).saturating_add(*gas_limit))]
905 pub fn eth_call(
906 origin: OriginFor<T>,
907 dest: H160,
908 value: U256,
909 gas_limit: Weight,
910 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
911 data: Vec<u8>,
912 ) -> DispatchResultWithPostInfo {
913 let mut output = Self::bare_call(
914 origin,
915 dest,
916 value,
917 gas_limit,
918 DepositLimit::Balance(storage_deposit_limit),
919 data,
920 );
921
922 if let Ok(return_value) = &output.result {
923 if return_value.did_revert() {
924 output.result = Err(<Error<T>>::ContractReverted.into());
925 }
926 }
927 dispatch_result(
928 output.result,
929 output.gas_consumed,
930 T::WeightInfo::eth_call(Pallet::<T>::has_dust(value).into()),
931 )
932 }
933
934 #[pallet::call_index(4)]
949 #[pallet::weight(T::WeightInfo::upload_code(code.len() as u32))]
950 pub fn upload_code(
951 origin: OriginFor<T>,
952 code: Vec<u8>,
953 #[pallet::compact] storage_deposit_limit: BalanceOf<T>,
954 ) -> DispatchResult {
955 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
956 }
957
958 #[pallet::call_index(5)]
963 #[pallet::weight(T::WeightInfo::remove_code())]
964 pub fn remove_code(
965 origin: OriginFor<T>,
966 code_hash: sp_core::H256,
967 ) -> DispatchResultWithPostInfo {
968 let origin = ensure_signed(origin)?;
969 <ContractBlob<T>>::remove(&origin, code_hash)?;
970 Ok(Pays::No.into())
972 }
973
974 #[pallet::call_index(6)]
985 #[pallet::weight(T::WeightInfo::set_code())]
986 pub fn set_code(
987 origin: OriginFor<T>,
988 dest: H160,
989 code_hash: sp_core::H256,
990 ) -> DispatchResult {
991 ensure_root(origin)?;
992 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
993 let Some(account) = account else {
994 return Err(<Error<T>>::ContractNotFound.into());
995 };
996
997 let AccountType::Contract(ref mut contract) = account.account_type else {
998 return Err(<Error<T>>::ContractNotFound.into());
999 };
1000
1001 <CodeInfo<T>>::increment_refcount(code_hash)?;
1002 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1003 contract.code_hash = code_hash;
1004
1005 Ok(())
1006 })
1007 }
1008
1009 #[pallet::call_index(7)]
1014 #[pallet::weight(T::WeightInfo::map_account())]
1015 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1016 let origin = ensure_signed(origin)?;
1017 T::AddressMapper::map(&origin)
1018 }
1019
1020 #[pallet::call_index(8)]
1025 #[pallet::weight(T::WeightInfo::unmap_account())]
1026 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1027 let origin = ensure_signed(origin)?;
1028 T::AddressMapper::unmap(&origin)
1029 }
1030
1031 #[pallet::call_index(9)]
1037 #[pallet::weight({
1038 let dispatch_info = call.get_dispatch_info();
1039 (
1040 T::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1041 dispatch_info.class
1042 )
1043 })]
1044 pub fn dispatch_as_fallback_account(
1045 origin: OriginFor<T>,
1046 call: Box<<T as Config>::RuntimeCall>,
1047 ) -> DispatchResultWithPostInfo {
1048 let origin = ensure_signed(origin)?;
1049 let unmapped_account =
1050 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1051 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1052 }
1053 }
1054}
1055
1056fn dispatch_result<R>(
1058 result: Result<R, DispatchError>,
1059 gas_consumed: Weight,
1060 base_weight: Weight,
1061) -> DispatchResultWithPostInfo {
1062 let post_info = PostDispatchInfo {
1063 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1064 pays_fee: Default::default(),
1065 };
1066
1067 result
1068 .map(|_| post_info)
1069 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1070}
1071
1072impl<T: Config> Pallet<T>
1073where
1074 BalanceOf<T>: Into<U256> + TryFrom<U256> + Bounded,
1075 MomentOf<T>: Into<U256>,
1076 T::Hash: frame_support::traits::IsType<H256>,
1077{
1078 pub fn bare_call(
1085 origin: OriginFor<T>,
1086 dest: H160,
1087 evm_value: U256,
1088 gas_limit: Weight,
1089 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1090 data: Vec<u8>,
1091 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1092 let mut gas_meter = GasMeter::new(gas_limit);
1093 let mut storage_deposit = Default::default();
1094
1095 let try_call = || {
1096 let origin = Origin::from_runtime_origin(origin)?;
1097 let mut storage_meter = StorageMeter::new(storage_deposit_limit.limit());
1098 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1099 origin.clone(),
1100 dest,
1101 &mut gas_meter,
1102 &mut storage_meter,
1103 evm_value,
1104 data,
1105 storage_deposit_limit.is_unchecked(),
1106 )?;
1107 storage_deposit = storage_meter
1108 .try_into_deposit(&origin, storage_deposit_limit.is_unchecked())
1109 .inspect_err(|err| {
1110 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1111 })?;
1112 Ok(result)
1113 };
1114 let result = Self::run_guarded(try_call);
1115 ContractResult {
1116 result: result.map_err(|r| r.error),
1117 gas_consumed: gas_meter.gas_consumed(),
1118 gas_required: gas_meter.gas_required(),
1119 storage_deposit,
1120 }
1121 }
1122
1123 pub fn prepare_dry_run(account: &T::AccountId) {
1129 frame_system::Pallet::<T>::inc_account_nonce(account);
1132 }
1133
1134 pub fn bare_instantiate(
1140 origin: OriginFor<T>,
1141 evm_value: U256,
1142 gas_limit: Weight,
1143 storage_deposit_limit: DepositLimit<BalanceOf<T>>,
1144 code: Code,
1145 data: Vec<u8>,
1146 salt: Option<[u8; 32]>,
1147 bump_nonce: BumpNonce,
1148 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1149 let mut gas_meter = GasMeter::new(gas_limit);
1150 let mut storage_deposit = Default::default();
1151 let unchecked_deposit_limit = storage_deposit_limit.is_unchecked();
1152 let mut storage_deposit_limit = storage_deposit_limit.limit();
1153 let try_instantiate = || {
1154 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1155
1156 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1157 let (executable, upload_deposit) = match code {
1158 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1159 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1160 let (executable, upload_deposit) = Self::try_upload_pvm_code(
1161 upload_account,
1162 code,
1163 storage_deposit_limit,
1164 unchecked_deposit_limit,
1165 )?;
1166 storage_deposit_limit.saturating_reduce(upload_deposit);
1167 (executable, upload_deposit)
1168 },
1169 Code::Upload(code) =>
1170 if T::AllowEVMBytecode::get() {
1171 let origin = T::UploadOrigin::ensure_origin(origin)?;
1172 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1173 (executable, Default::default())
1174 } else {
1175 return Err(<Error<T>>::CodeRejected.into())
1176 },
1177 Code::Existing(code_hash) =>
1178 (ContractBlob::from_storage(code_hash, &mut gas_meter)?, Default::default()),
1179 };
1180 let instantiate_origin = Origin::from_account_id(instantiate_account.clone());
1181 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1182 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1183 instantiate_account,
1184 executable,
1185 &mut gas_meter,
1186 &mut storage_meter,
1187 evm_value,
1188 data,
1189 salt.as_ref(),
1190 unchecked_deposit_limit,
1191 bump_nonce,
1192 );
1193 storage_deposit = storage_meter
1194 .try_into_deposit(&instantiate_origin, unchecked_deposit_limit)?
1195 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1196 result
1197 };
1198 let output = Self::run_guarded(try_instantiate);
1199 ContractResult {
1200 result: output
1201 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1202 .map_err(|e| e.error),
1203 gas_consumed: gas_meter.gas_consumed(),
1204 gas_required: gas_meter.gas_required(),
1205 storage_deposit,
1206 }
1207 }
1208
1209 pub fn dry_run_eth_transact(
1218 mut tx: GenericTransaction,
1219 gas_limit: Weight,
1220 tx_fee: impl Fn(<T as Config>::RuntimeCall, <T as Config>::RuntimeCall) -> BalanceOf<T>,
1221 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1222 where
1223 <T as frame_system::Config>::RuntimeCall:
1224 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1225 T: pallet_transaction_payment::Config,
1226 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1227 <T as Config>::RuntimeCall: From<crate::Call<T>>,
1228 <T as Config>::RuntimeCall: Encode,
1229 T::Nonce: Into<U256>,
1230 T::Hash: frame_support::traits::IsType<H256>,
1231 {
1232 log::trace!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?} gas_limit: {gas_limit:?}");
1233
1234 let from = tx.from.unwrap_or_default();
1235 let origin = T::AddressMapper::to_account_id(&from);
1236 Self::prepare_dry_run(&origin);
1237
1238 let storage_deposit_limit = if tx.gas.is_some() {
1239 DepositLimit::Balance(BalanceOf::<T>::max_value())
1240 } else {
1241 DepositLimit::UnsafeOnlyForDryRun
1242 };
1243
1244 if tx.nonce.is_none() {
1245 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1246 }
1247 if tx.chain_id.is_none() {
1248 tx.chain_id = Some(T::ChainId::get().into());
1249 }
1250 if tx.gas_price.is_none() {
1251 tx.gas_price = Some(GAS_PRICE.into());
1252 }
1253 if tx.max_priority_fee_per_gas.is_none() {
1254 tx.max_priority_fee_per_gas = Some(GAS_PRICE.into());
1255 }
1256 if tx.max_fee_per_gas.is_none() {
1257 tx.max_fee_per_gas = Some(GAS_PRICE.into());
1258 }
1259 if tx.gas.is_none() {
1260 tx.gas = Some(Self::evm_block_gas_limit());
1261 }
1262 if tx.r#type.is_none() {
1263 tx.r#type = Some(TYPE_EIP1559.into());
1264 }
1265
1266 let value = tx.value.unwrap_or_default();
1268 let input = tx.input.clone().to_vec();
1269
1270 let extract_error = |err| {
1271 if err == Error::<T>::TransferFailed.into() ||
1272 err == Error::<T>::StorageDepositNotEnoughFunds.into() ||
1273 err == Error::<T>::StorageDepositLimitExhausted.into()
1274 {
1275 let balance = Self::evm_balance(&from);
1276 return Err(EthTransactError::Message(format!(
1277 "insufficient funds for gas * price + value: address {from:?} have {balance} (supplied gas {})",
1278 tx.gas.unwrap_or_default()
1279 )));
1280 }
1281
1282 return Err(EthTransactError::Message(format!(
1283 "Failed to instantiate contract: {err:?}"
1284 )));
1285 };
1286
1287 let (mut result, dispatch_call) = match tx.to {
1289 Some(dest) => {
1291 if dest == RUNTIME_PALLETS_ADDR {
1292 let Ok(dispatch_call) = <T as Config>::RuntimeCall::decode(&mut &input[..])
1293 else {
1294 return Err(EthTransactError::Message(format!(
1295 "Failed to decode pallet-call {input:?}"
1296 )));
1297 };
1298
1299 if let Err(err) =
1300 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1301 {
1302 return Err(EthTransactError::Message(format!(
1303 "Failed to dispatch call: {err:?}"
1304 )));
1305 };
1306
1307 let result = EthTransactInfo {
1308 gas_required: dispatch_call.get_dispatch_info().total_weight(),
1309 ..Default::default()
1310 };
1311
1312 (result, dispatch_call)
1313 } else {
1314 let result = crate::Pallet::<T>::bare_call(
1316 T::RuntimeOrigin::signed(origin),
1317 dest,
1318 value,
1319 gas_limit,
1320 storage_deposit_limit,
1321 input.clone(),
1322 );
1323
1324 let data = match result.result {
1325 Ok(return_value) => {
1326 if return_value.did_revert() {
1327 return Err(EthTransactError::Data(return_value.data));
1328 }
1329 return_value.data
1330 },
1331 Err(err) => {
1332 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1333 return extract_error(err);
1334 },
1335 };
1336
1337 let result = EthTransactInfo {
1338 gas_required: result.gas_required,
1339 storage_deposit: result.storage_deposit.charge_or_zero(),
1340 data,
1341 eth_gas: Default::default(),
1342 };
1343
1344 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1345 result.gas_required,
1346 result.storage_deposit,
1347 );
1348 let dispatch_call: <T as Config>::RuntimeCall = crate::Call::<T>::eth_call {
1349 dest,
1350 value,
1351 gas_limit,
1352 storage_deposit_limit,
1353 data: input.clone(),
1354 }
1355 .into();
1356 (result, dispatch_call)
1357 }
1358 },
1359 None => {
1361 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1363 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1364 } else {
1365 (input, vec![])
1366 };
1367
1368 let result = crate::Pallet::<T>::bare_instantiate(
1370 T::RuntimeOrigin::signed(origin),
1371 value,
1372 gas_limit,
1373 storage_deposit_limit,
1374 Code::Upload(code.clone()),
1375 data.clone(),
1376 None,
1377 BumpNonce::No,
1378 );
1379
1380 let returned_data = match result.result {
1381 Ok(return_value) => {
1382 if return_value.result.did_revert() {
1383 return Err(EthTransactError::Data(return_value.result.data));
1384 }
1385 return_value.result.data
1386 },
1387 Err(err) => {
1388 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1389 return extract_error(err);
1390 },
1391 };
1392
1393 let result = EthTransactInfo {
1394 gas_required: result.gas_required,
1395 storage_deposit: result.storage_deposit.charge_or_zero(),
1396 data: returned_data,
1397 eth_gas: Default::default(),
1398 };
1399
1400 let (gas_limit, storage_deposit_limit) = T::EthGasEncoder::as_encoded_values(
1402 result.gas_required,
1403 result.storage_deposit,
1404 );
1405 let dispatch_call: <T as Config>::RuntimeCall =
1406 crate::Call::<T>::eth_instantiate_with_code {
1407 value,
1408 gas_limit,
1409 storage_deposit_limit,
1410 code,
1411 data,
1412 }
1413 .into();
1414 (result, dispatch_call)
1415 },
1416 };
1417
1418 let Ok(unsigned_tx) = tx.clone().try_into_unsigned() else {
1419 return Err(EthTransactError::Message("Invalid transaction".into()));
1420 };
1421
1422 let eth_transact_call =
1423 crate::Call::<T>::eth_transact { payload: unsigned_tx.dummy_signed_payload() };
1424 let fee = tx_fee(eth_transact_call.into(), dispatch_call);
1425 let raw_gas = Self::evm_fee_to_gas(fee);
1426 let eth_gas =
1427 T::EthGasEncoder::encode(raw_gas, result.gas_required, result.storage_deposit);
1428
1429 log::trace!(target: LOG_TARGET, "bare_eth_call: raw_gas: {raw_gas:?} eth_gas: {eth_gas:?}");
1430 result.eth_gas = eth_gas;
1431 Ok(result)
1432 }
1433
1434 pub fn evm_balance(address: &H160) -> U256 {
1436 let balance = AccountInfo::<T>::balance((*address).into());
1437 Self::convert_native_to_evm(balance)
1438 }
1439
1440 pub fn evm_nonce(address: &H160) -> u32
1442 where
1443 T::Nonce: Into<u32>,
1444 {
1445 let account = T::AddressMapper::to_account_id(&address);
1446 System::<T>::account_nonce(account).into()
1447 }
1448
1449 pub fn evm_fee_to_gas(fee: BalanceOf<T>) -> U256 {
1452 let fee = Self::convert_native_to_evm(fee);
1453 let gas_price = GAS_PRICE.into();
1454 let (quotient, remainder) = fee.div_mod(gas_price);
1455 if remainder.is_zero() {
1456 quotient
1457 } else {
1458 quotient + U256::one()
1459 }
1460 }
1461
1462 fn evm_gas_to_fee(gas: U256, gas_price: U256) -> Result<BalanceOf<T>, Error<T>> {
1464 let fee = gas.saturating_mul(gas_price);
1465 let value = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(fee)?;
1466 Ok(value.into_rounded_balance())
1467 }
1468
1469 pub fn evm_gas_from_weight(weight: Weight) -> U256 {
1471 let fee = T::WeightPrice::convert(weight);
1472 Self::evm_fee_to_gas(fee)
1473 }
1474
1475 pub fn evm_block_gas_limit() -> U256
1477 where
1478 <T as frame_system::Config>::RuntimeCall:
1479 Dispatchable<Info = frame_support::dispatch::DispatchInfo>,
1480 T: pallet_transaction_payment::Config,
1481 OnChargeTransactionBalanceOf<T>: Into<BalanceOf<T>>,
1482 {
1483 let max_block_weight = T::BlockWeights::get()
1484 .get(DispatchClass::Normal)
1485 .max_total
1486 .unwrap_or_else(|| T::BlockWeights::get().max_block);
1487
1488 let length_fee = pallet_transaction_payment::Pallet::<T>::length_to_fee(
1489 5 * 1024 * 1024, );
1491
1492 Self::evm_gas_from_weight(max_block_weight)
1493 .saturating_add(Self::evm_fee_to_gas(length_fee.into()))
1494 }
1495
1496 pub fn evm_gas_price() -> U256 {
1498 GAS_PRICE.into()
1499 }
1500
1501 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
1503 where
1504 T::Nonce: Into<u32>,
1505 {
1506 match tracer_type {
1507 TracerType::CallTracer(config) => CallTracer::new(
1508 config.unwrap_or_default(),
1509 Self::evm_gas_from_weight as fn(Weight) -> U256,
1510 )
1511 .into(),
1512 TracerType::PrestateTracer(config) =>
1513 PrestateTracer::new(config.unwrap_or_default()).into(),
1514 }
1515 }
1516
1517 pub fn bare_upload_code(
1521 origin: OriginFor<T>,
1522 code: Vec<u8>,
1523 storage_deposit_limit: BalanceOf<T>,
1524 ) -> CodeUploadResult<BalanceOf<T>> {
1525 let origin = T::UploadOrigin::ensure_origin(origin)?;
1526 let (module, deposit) =
1527 Self::try_upload_pvm_code(origin, code, storage_deposit_limit, false)?;
1528 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
1529 }
1530
1531 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
1533 let contract_info =
1534 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1535
1536 let maybe_value = contract_info.read(&Key::from_fixed(key));
1537 Ok(maybe_value)
1538 }
1539
1540 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
1542 let contract_info =
1543 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
1544
1545 let maybe_value = contract_info.read(
1546 &Key::try_from_var(key)
1547 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
1548 .into(),
1549 );
1550 Ok(maybe_value)
1551 }
1552
1553 fn try_upload_pvm_code(
1555 origin: T::AccountId,
1556 code: Vec<u8>,
1557 storage_deposit_limit: BalanceOf<T>,
1558 skip_transfer: bool,
1559 ) -> Result<(ContractBlob<T>, BalanceOf<T>), DispatchError> {
1560 let mut module = ContractBlob::from_pvm_code(code, origin)?;
1561 let deposit = module.store_code(skip_transfer)?;
1562 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
1563 Ok((module, deposit))
1564 }
1565
1566 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
1568 executing_contract::using_once(&mut false, || {
1569 executing_contract::with(|f| {
1570 if *f {
1572 return Err(())
1573 }
1574 *f = true;
1576 Ok(())
1577 })
1578 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
1579 .map_err(|_| <Error<T>>::ReenteredPallet.into())
1580 .map(|_| f())
1581 .and_then(|r| r)
1582 })
1583 }
1584
1585 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
1587 let (value, dust) = value.into().deconstruct();
1588 value
1589 .into()
1590 .saturating_mul(T::NativeToEthRatio::get().into())
1591 .saturating_add(dust.into())
1592 }
1593}
1594
1595impl<T: Config> Pallet<T> {
1596 pub fn account_id() -> T::AccountId {
1598 use frame_support::PalletId;
1599 use sp_runtime::traits::AccountIdConversion;
1600 PalletId(*b"py/reviv").into_account_truncating()
1601 }
1602
1603 fn has_dust(value: U256) -> bool {
1605 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
1606 }
1607
1608 fn has_balance(value: U256) -> bool {
1610 value >= U256::from(<T>::NativeToEthRatio::get())
1611 }
1612
1613 fn min_balance() -> BalanceOf<T> {
1615 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
1616 }
1617
1618 fn deposit_event(event: Event<T>) {
1620 <frame_system::Pallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
1621 }
1622
1623 pub fn block_author() -> Option<H160> {
1625 use frame_support::traits::FindAuthor;
1626
1627 let digest = <frame_system::Pallet<T>>::digest();
1628 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
1629
1630 let account_id = T::FindAuthor::find_author(pre_runtime_digests)?;
1631 Some(T::AddressMapper::to_address(&account_id))
1632 }
1633
1634 pub fn code(address: &H160) -> Vec<u8> {
1638 use precompiles::{All, Precompiles};
1639 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
1640 return code.into()
1641 }
1642 AccountInfo::<T>::load_contract(&address)
1643 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
1644 .map(|code| code.into())
1645 .unwrap_or_default()
1646 }
1647}
1648
1649pub const RUNTIME_PALLETS_ADDR: H160 =
1654 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
1655
1656environmental!(executing_contract: bool);
1658
1659sp_api::decl_runtime_apis! {
1660 #[api_version(1)]
1662 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber> where
1663 AccountId: Codec,
1664 Balance: Codec,
1665 Nonce: Codec,
1666 BlockNumber: Codec,
1667 {
1668 fn block_gas_limit() -> U256;
1670
1671 fn balance(address: H160) -> U256;
1673
1674 fn gas_price() -> U256;
1676
1677 fn nonce(address: H160) -> Nonce;
1679
1680 fn call(
1684 origin: AccountId,
1685 dest: H160,
1686 value: Balance,
1687 gas_limit: Option<Weight>,
1688 storage_deposit_limit: Option<Balance>,
1689 input_data: Vec<u8>,
1690 ) -> ContractResult<ExecReturnValue, Balance>;
1691
1692 fn instantiate(
1696 origin: AccountId,
1697 value: Balance,
1698 gas_limit: Option<Weight>,
1699 storage_deposit_limit: Option<Balance>,
1700 code: Code,
1701 data: Vec<u8>,
1702 salt: Option<[u8; 32]>,
1703 ) -> ContractResult<InstantiateReturnValue, Balance>;
1704
1705
1706 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
1710
1711 fn upload_code(
1715 origin: AccountId,
1716 code: Vec<u8>,
1717 storage_deposit_limit: Option<Balance>,
1718 ) -> CodeUploadResult<Balance>;
1719
1720 fn get_storage(
1726 address: H160,
1727 key: [u8; 32],
1728 ) -> GetStorageResult;
1729
1730 fn get_storage_var_key(
1736 address: H160,
1737 key: Vec<u8>,
1738 ) -> GetStorageResult;
1739
1740 fn trace_block(
1747 block: Block,
1748 config: TracerType
1749 ) -> Vec<(u32, Trace)>;
1750
1751 fn trace_tx(
1758 block: Block,
1759 tx_index: u32,
1760 config: TracerType
1761 ) -> Option<Trace>;
1762
1763 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
1767
1768 fn block_author() -> Option<H160>;
1770
1771 fn address(account_id: AccountId) -> H160;
1773
1774 fn runtime_pallets_address() -> H160;
1776
1777 fn code(address: H160) -> Vec<u8>;
1779 }
1780}
1781
1782#[macro_export]
1790macro_rules! impl_runtime_apis_plus_revive {
1791 ($Runtime: ty, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
1792
1793 impl_runtime_apis! {
1794 $($rest)*
1795
1796 impl pallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber> for $Runtime {
1797 fn balance(address: $crate::H160) -> $crate::U256 {
1798 $crate::Pallet::<Self>::evm_balance(&address)
1799 }
1800
1801 fn block_author() -> Option<$crate::H160> {
1802 $crate::Pallet::<Self>::block_author()
1803 }
1804
1805 fn block_gas_limit() -> $crate::U256 {
1806 $crate::Pallet::<Self>::evm_block_gas_limit()
1807 }
1808
1809 fn gas_price() -> $crate::U256 {
1810 $crate::Pallet::<Self>::evm_gas_price()
1811 }
1812
1813 fn nonce(address: $crate::H160) -> Nonce {
1814 use $crate::AddressMapper;
1815 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
1816 $crate::frame_system::Pallet::<Self>::account_nonce(account)
1817 }
1818
1819 fn address(account_id: AccountId) -> $crate::H160 {
1820 use $crate::AddressMapper;
1821 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
1822 }
1823
1824 fn eth_transact(
1825 tx: $crate::evm::GenericTransaction,
1826 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
1827 use $crate::{
1828 codec::Encode, evm::runtime::EthExtra, frame_support::traits::Get,
1829 sp_runtime::traits::TransactionExtension,
1830 sp_runtime::traits::Block as BlockT
1831 };
1832
1833 let tx_fee = |call: <Self as $crate::frame_system::Config>::RuntimeCall, dispatch_call: <Self as $crate::frame_system::Config>::RuntimeCall| {
1834 use $crate::frame_support::dispatch::GetDispatchInfo;
1835
1836 let mut dispatch_info = dispatch_call.get_dispatch_info();
1838 dispatch_info.extension_weight =
1839 <$EthExtra>::get_eth_extension(0, 0u32.into()).weight(&dispatch_call);
1840
1841 let uxt: <Block as BlockT>::Extrinsic =
1843 $crate::sp_runtime::generic::UncheckedExtrinsic::new_bare(call).into();
1844
1845 $crate::pallet_transaction_payment::Pallet::<Self>::compute_fee(
1847 uxt.encoded_size() as u32,
1848 &dispatch_info,
1849 0u32.into(),
1850 )
1851 };
1852
1853 let blockweights: $crate::BlockWeights =
1854 <Self as $crate::frame_system::Config>::BlockWeights::get();
1855 $crate::Pallet::<Self>::dry_run_eth_transact(tx, blockweights.max_block, tx_fee)
1856 }
1857
1858 fn call(
1859 origin: AccountId,
1860 dest: $crate::H160,
1861 value: Balance,
1862 gas_limit: Option<$crate::Weight>,
1863 storage_deposit_limit: Option<Balance>,
1864 input_data: Vec<u8>,
1865 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
1866 use $crate::frame_support::traits::Get;
1867 let blockweights: $crate::BlockWeights =
1868 <Self as $crate::frame_system::Config>::BlockWeights::get();
1869
1870 $crate::Pallet::<Self>::prepare_dry_run(&origin);
1871 $crate::Pallet::<Self>::bare_call(
1872 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
1873 dest,
1874 $crate::Pallet::<Self>::convert_native_to_evm(value),
1875 gas_limit.unwrap_or(blockweights.max_block),
1876 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
1877 input_data,
1878 )
1879 }
1880
1881 fn instantiate(
1882 origin: AccountId,
1883 value: Balance,
1884 gas_limit: Option<$crate::Weight>,
1885 storage_deposit_limit: Option<Balance>,
1886 code: $crate::Code,
1887 data: Vec<u8>,
1888 salt: Option<[u8; 32]>,
1889 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
1890 use $crate::frame_support::traits::Get;
1891 let blockweights: $crate::BlockWeights =
1892 <Self as $crate::frame_system::Config>::BlockWeights::get();
1893
1894 $crate::Pallet::<Self>::prepare_dry_run(&origin);
1895 $crate::Pallet::<Self>::bare_instantiate(
1896 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin),
1897 $crate::Pallet::<Self>::convert_native_to_evm(value),
1898 gas_limit.unwrap_or(blockweights.max_block),
1899 $crate::DepositLimit::Balance(storage_deposit_limit.unwrap_or(u128::MAX)),
1900 code,
1901 data,
1902 salt,
1903 $crate::BumpNonce::Yes,
1904 )
1905 }
1906
1907 fn upload_code(
1908 origin: AccountId,
1909 code: Vec<u8>,
1910 storage_deposit_limit: Option<Balance>,
1911 ) -> $crate::CodeUploadResult<Balance> {
1912 let origin =
1913 <Self as $crate::frame_system::Config>::RuntimeOrigin::signed(origin);
1914 $crate::Pallet::<Self>::bare_upload_code(
1915 origin,
1916 code,
1917 storage_deposit_limit.unwrap_or(u128::MAX),
1918 )
1919 }
1920
1921 fn get_storage_var_key(
1922 address: $crate::H160,
1923 key: Vec<u8>,
1924 ) -> $crate::GetStorageResult {
1925 $crate::Pallet::<Self>::get_storage_var_key(address, key)
1926 }
1927
1928 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
1929 $crate::Pallet::<Self>::get_storage(address, key)
1930 }
1931
1932 fn trace_block(
1933 block: Block,
1934 tracer_type: $crate::evm::TracerType,
1935 ) -> Vec<(u32, $crate::evm::Trace)> {
1936 use $crate::{sp_runtime::traits::Block, tracing::trace};
1937 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
1938 let mut traces = vec![];
1939 let (header, extrinsics) = block.deconstruct();
1940 <$Executive>::initialize_block(&header);
1941 for (index, ext) in extrinsics.into_iter().enumerate() {
1942 let t = tracer.as_tracing();
1943 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
1944
1945 if let Some(tx_trace) = tracer.collect_trace() {
1946 traces.push((index as u32, tx_trace));
1947 }
1948 }
1949
1950 traces
1951 }
1952
1953 fn trace_tx(
1954 block: Block,
1955 tx_index: u32,
1956 tracer_type: $crate::evm::TracerType,
1957 ) -> Option<$crate::evm::Trace> {
1958 use $crate::{sp_runtime::traits::Block, tracing::trace};
1959
1960 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
1961 let (header, extrinsics) = block.deconstruct();
1962
1963 <$Executive>::initialize_block(&header);
1964 for (index, ext) in extrinsics.into_iter().enumerate() {
1965 if index as u32 == tx_index {
1966 let t = tracer.as_tracing();
1967 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
1968 break;
1969 } else {
1970 let _ = <$Executive>::apply_extrinsic(ext);
1971 }
1972 }
1973
1974 tracer.collect_trace()
1975 }
1976
1977 fn trace_call(
1978 tx: $crate::evm::GenericTransaction,
1979 tracer_type: $crate::evm::TracerType,
1980 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
1981 use $crate::tracing::trace;
1982 let mut tracer = $crate::Pallet::<Self>::evm_tracer(tracer_type);
1983 let t = tracer.as_tracing();
1984
1985 t.watch_address(&tx.from.unwrap_or_default());
1986 t.watch_address(&$crate::Pallet::<Self>::block_author().unwrap_or_default());
1987 let result = trace(t, || Self::eth_transact(tx));
1988
1989 if let Some(trace) = tracer.collect_trace() {
1990 Ok(trace)
1991 } else if let Err(err) = result {
1992 Err(err)
1993 } else {
1994 Ok(tracer.empty_trace())
1995 }
1996 }
1997
1998 fn runtime_pallets_address() -> $crate::H160 {
1999 $crate::RUNTIME_PALLETS_ADDR
2000 }
2001
2002 fn code(address: $crate::H160) -> Vec<u8> {
2003 $crate::Pallet::<Self>::code(&address)
2004 }
2005 }
2006 }
2007 };
2008}