1#![doc = include_str!("../README.md")]
19#![allow(rustdoc::private_intra_doc_links)]
20#![cfg_attr(not(feature = "std"), no_std)]
21#![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")]
22
23extern crate alloc;
24
25mod address;
26mod benchmarking;
27mod call_builder;
28mod debug;
29mod exec;
30mod gas;
31mod impl_fungibles;
32mod limits;
33mod primitives;
34mod storage;
35#[cfg(test)]
36mod tests;
37mod transient_storage;
38mod vm;
39mod weightinfo_extension;
40
41pub mod evm;
42pub mod migrations;
43pub mod mock;
44pub mod precompiles;
45pub mod test_utils;
46pub mod tracing;
47pub mod weights;
48
49use crate::{
50 evm::{
51 block_hash::EthereumBlockBuilderIR,
52 block_storage, create_call,
53 fees::{Combinator, InfoT as FeeInfo},
54 runtime::SetWeightLimit,
55 CallTracer, GenericTransaction, PrestateTracer, Trace, Tracer, TracerType, TYPE_EIP1559,
56 },
57 exec::{AccountIdOf, ExecError, Stack as ExecStack},
58 gas::GasMeter,
59 storage::{meter::Meter as StorageMeter, AccountType, DeletionQueueManager},
60 tracing::if_tracing,
61 vm::{pvm::extract_code_and_data, CodeInfo, RuntimeCosts},
62 weightinfo_extension::OnFinalizeBlockParts,
63};
64use alloc::{boxed::Box, format, vec};
65use codec::{Codec, Decode, Encode};
66use environmental::*;
67use pezframe_support::{
68 dispatch::{
69 DispatchErrorWithPostInfo, DispatchResult, DispatchResultWithPostInfo, GetDispatchInfo,
70 Pays, PostDispatchInfo, RawOrigin,
71 },
72 ensure,
73 pezpallet_prelude::DispatchClass,
74 traits::{
75 fungible::{Balanced, Inspect, Mutate, MutateHold},
76 tokens::Balance,
77 ConstU32, ConstU64, EnsureOrigin, Get, IsSubType, IsType, OriginTrait,
78 },
79 weights::WeightMeter,
80 BoundedVec, RuntimeDebugNoBound,
81};
82use pezframe_system::{
83 ensure_signed,
84 pezpallet_prelude::{BlockNumberFor, OriginFor},
85 Pezpallet as System,
86};
87use pezsp_runtime::{
88 traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating, UniqueSaturatedInto, Zero},
89 AccountId32, DispatchError, FixedPointNumber, FixedU128,
90};
91use scale_info::TypeInfo;
92
93pub use crate::{
94 address::{
95 create1, create2, is_eth_derived, AccountId32Mapper, AddressMapper, TestAccountMapper,
96 },
97 debug::DebugSettings,
98 evm::{
99 block_hash::ReceiptGasInfo, Address as EthAddress, Block as EthBlock, DryRunConfig,
100 ReceiptInfo,
101 },
102 exec::{DelegateInfo, Executable, Key, MomentOf, Origin as ExecOrigin},
103 pezpallet::{genesis, *},
104 storage::{AccountInfo, ContractInfo},
105 vm::{BytecodeType, ContractBlob},
106};
107pub use codec;
108pub use pezframe_support::{self, dispatch::DispatchInfo, traits::Time, weights::Weight};
109pub use pezframe_system::{self, limits::BlockWeights};
110pub use pezsp_core::{keccak_256, H160, H256, U256};
111pub use pezsp_runtime;
112pub use primitives::*;
113pub use weights::WeightInfo;
114
115#[cfg(doc)]
116pub use crate::vm::pvm::SyscallDoc;
117
118pub type BalanceOf<T> = <T as Config>::Balance;
119type TrieId = BoundedVec<u8, ConstU32<128>>;
120type ImmutableData = BoundedVec<u8, ConstU32<{ limits::IMMUTABLE_BYTES }>>;
121type CallOf<T> = <T as Config>::RuntimeCall;
122
123const SENTINEL: u32 = u32::MAX;
130
131const LOG_TARGET: &str = "runtime::revive";
137
138#[pezframe_support::pezpallet]
139pub mod pezpallet {
140 use super::*;
141 use pezframe_support::{pezpallet_prelude::*, traits::FindAuthor};
142 use pezframe_system::pezpallet_prelude::*;
143 use pezsp_core::U256;
144 use pezsp_runtime::Perbill;
145
146 pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
148
149 #[pezpallet::pezpallet]
150 #[pezpallet::storage_version(STORAGE_VERSION)]
151 pub struct Pezpallet<T>(_);
152
153 #[pezpallet::config(with_default)]
154 pub trait Config: pezframe_system::Config {
155 type Time: Time<Moment: Into<U256>>;
157
158 #[pezpallet::no_default]
162 type Balance: Balance + TryFrom<U256> + Into<U256> + Bounded + UniqueSaturatedInto<u64>;
163
164 #[pezpallet::no_default]
166 type Currency: Inspect<Self::AccountId, Balance = Self::Balance>
167 + Mutate<Self::AccountId>
168 + MutateHold<Self::AccountId, Reason = Self::RuntimeHoldReason>
169 + Balanced<Self::AccountId>;
170
171 #[pezpallet::no_default_bounds]
173 #[allow(deprecated)]
174 type RuntimeEvent: From<Event<Self>>
175 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
176
177 #[pezpallet::no_default_bounds]
179 type RuntimeCall: Parameter
180 + Dispatchable<
181 RuntimeOrigin = OriginFor<Self>,
182 Info = DispatchInfo,
183 PostInfo = PostDispatchInfo,
184 > + IsType<<Self as pezframe_system::Config>::RuntimeCall>
185 + From<Call<Self>>
186 + IsSubType<Call<Self>>
187 + GetDispatchInfo;
188
189 #[pezpallet::no_default_bounds]
191 type RuntimeOrigin: IsType<OriginFor<Self>>
192 + From<Origin<Self>>
193 + Into<Result<Origin<Self>, OriginFor<Self>>>;
194
195 #[pezpallet::no_default_bounds]
197 type RuntimeHoldReason: From<HoldReason>;
198
199 type WeightInfo: WeightInfo;
202
203 #[pezpallet::no_default_bounds]
207 #[allow(private_bounds)]
208 type Precompiles: precompiles::Precompiles<Self>;
209
210 type FindAuthor: FindAuthor<Self::AccountId>;
212
213 #[pezpallet::constant]
219 #[pezpallet::no_default_bounds]
220 type DepositPerByte: Get<BalanceOf<Self>>;
221
222 #[pezpallet::constant]
228 #[pezpallet::no_default_bounds]
229 type DepositPerItem: Get<BalanceOf<Self>>;
230
231 #[pezpallet::constant]
241 #[pezpallet::no_default_bounds]
242 type DepositPerChildTrieItem: Get<BalanceOf<Self>>;
243
244 #[pezpallet::constant]
248 type CodeHashLockupDepositPercent: Get<Perbill>;
249
250 #[pezpallet::no_default]
252 type AddressMapper: AddressMapper<Self>;
253
254 #[pezpallet::constant]
264 type UnsafeUnstableInterface: Get<bool>;
265
266 #[pezpallet::constant]
268 type AllowEVMBytecode: Get<bool>;
269
270 #[pezpallet::no_default_bounds]
275 type UploadOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
276
277 #[pezpallet::no_default_bounds]
288 type InstantiateOrigin: EnsureOrigin<OriginFor<Self>, Success = Self::AccountId>;
289
290 type RuntimeMemory: Get<u32>;
295
296 type PVFMemory: Get<u32>;
304
305 #[pezpallet::constant]
310 type ChainId: Get<u64>;
311
312 #[pezpallet::constant]
314 type NativeToEthRatio: Get<u32>;
315
316 #[pezpallet::no_default_bounds]
321 type FeeInfo: FeeInfo<Self>;
322
323 #[pezpallet::constant]
336 type MaxEthExtrinsicWeight: Get<FixedU128>;
337
338 #[pezpallet::constant]
340 type DebugEnabled: Get<bool>;
341 }
342
343 pub mod config_preludes {
345 use super::*;
346 use pezframe_support::{
347 derive_impl,
348 traits::{ConstBool, ConstU32},
349 };
350 use pezframe_system::EnsureSigned;
351 use pezsp_core::parameter_types;
352
353 type Balance = u64;
354
355 pub const DOLLARS: Balance = 1_000_000_000_000;
356 pub const CENTS: Balance = DOLLARS / 100;
357 pub const MILLICENTS: Balance = CENTS / 1_000;
358
359 pub const fn deposit(items: u32, bytes: u32) -> Balance {
360 items as Balance * 20 * CENTS + (bytes as Balance) * MILLICENTS
361 }
362
363 parameter_types! {
364 pub const DepositPerItem: Balance = deposit(1, 0);
365 pub const DepositPerChildTrieItem: Balance = deposit(1, 0) / 100;
366 pub const DepositPerByte: Balance = deposit(0, 1);
367 pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
368 pub const MaxEthExtrinsicWeight: FixedU128 = FixedU128::from_rational(9, 10);
369 }
370
371 pub struct TestDefaultConfig;
373
374 impl Time for TestDefaultConfig {
375 type Moment = u64;
376 fn now() -> Self::Moment {
377 0u64
378 }
379 }
380
381 impl<T: From<u64>> Convert<Weight, T> for TestDefaultConfig {
382 fn convert(w: Weight) -> T {
383 w.ref_time().into()
384 }
385 }
386
387 #[derive_impl(pezframe_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
388 impl pezframe_system::DefaultConfig for TestDefaultConfig {}
389
390 #[pezframe_support::register_default_impl(TestDefaultConfig)]
391 impl DefaultConfig for TestDefaultConfig {
392 #[inject_runtime_type]
393 type RuntimeEvent = ();
394
395 #[inject_runtime_type]
396 type RuntimeHoldReason = ();
397
398 #[inject_runtime_type]
399 type RuntimeCall = ();
400
401 #[inject_runtime_type]
402 type RuntimeOrigin = ();
403
404 type Precompiles = ();
405 type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
406 type DepositPerByte = DepositPerByte;
407 type DepositPerItem = DepositPerItem;
408 type DepositPerChildTrieItem = DepositPerChildTrieItem;
409 type Time = Self;
410 type UnsafeUnstableInterface = ConstBool<true>;
411 type AllowEVMBytecode = ConstBool<true>;
412 type UploadOrigin = EnsureSigned<Self::AccountId>;
413 type InstantiateOrigin = EnsureSigned<Self::AccountId>;
414 type WeightInfo = ();
415 type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>;
416 type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>;
417 type ChainId = ConstU64<42>;
418 type NativeToEthRatio = ConstU32<1_000_000>;
419 type FindAuthor = ();
420 type FeeInfo = ();
421 type MaxEthExtrinsicWeight = MaxEthExtrinsicWeight;
422 type DebugEnabled = ConstBool<false>;
423 }
424 }
425
426 #[pezpallet::event]
427 pub enum Event<T: Config> {
428 ContractEmitted {
430 contract: H160,
432 data: Vec<u8>,
435 topics: Vec<H256>,
438 },
439
440 Instantiated { deployer: H160, contract: H160 },
442
443 EthExtrinsicRevert { dispatch_error: DispatchError },
450 }
451
452 #[pezpallet::error]
453 #[repr(u8)]
454 pub enum Error<T> {
455 InvalidSchedule = 0x01,
457 InvalidCallFlags = 0x02,
459 OutOfGas = 0x03,
461 TransferFailed = 0x04,
464 MaxCallDepthReached = 0x05,
467 ContractNotFound = 0x06,
469 CodeNotFound = 0x07,
471 CodeInfoNotFound = 0x08,
473 OutOfBounds = 0x09,
475 DecodingFailed = 0x0A,
477 ContractTrapped = 0x0B,
479 ValueTooLarge = 0x0C,
481 TerminatedWhileReentrant = 0x0D,
484 InputForwarded = 0x0E,
486 TooManyTopics = 0x0F,
488 DuplicateContract = 0x12,
490 TerminatedInConstructor = 0x13,
494 ReentranceDenied = 0x14,
496 ReenteredPallet = 0x15,
498 StateChangeDenied = 0x16,
500 StorageDepositNotEnoughFunds = 0x17,
502 StorageDepositLimitExhausted = 0x18,
504 CodeInUse = 0x19,
506 ContractReverted = 0x1A,
511 CodeRejected = 0x1B,
516 BlobTooLarge = 0x1C,
518 StaticMemoryTooLarge = 0x1D,
520 BasicBlockTooLarge = 0x1E,
522 InvalidInstruction = 0x1F,
524 MaxDelegateDependenciesReached = 0x20,
526 DelegateDependencyNotFound = 0x21,
528 DelegateDependencyAlreadyExists = 0x22,
530 CannotAddSelfAsDelegateDependency = 0x23,
532 OutOfTransientStorage = 0x24,
534 InvalidSyscall = 0x25,
536 InvalidStorageFlags = 0x26,
538 ExecutionFailed = 0x27,
540 BalanceConversionFailed = 0x28,
542 InvalidImmutableAccess = 0x2A,
545 AccountUnmapped = 0x2B,
549 AccountAlreadyMapped = 0x2C,
551 InvalidGenericTransaction = 0x2D,
553 RefcountOverOrUnderflow = 0x2E,
555 UnsupportedPrecompileAddress = 0x2F,
557 CallDataTooLarge = 0x30,
559 ReturnDataTooLarge = 0x31,
561 InvalidJump = 0x32,
563 StackUnderflow = 0x33,
565 StackOverflow = 0x34,
567 TxFeeOverdraw = 0x35,
571 EvmConstructorNonEmptyData = 0x36,
575 EvmConstructedFromHash = 0x37,
580 StorageRefundNotEnoughFunds = 0x38,
584 StorageRefundLocked = 0x39,
589 PrecompileDelegateDenied = 0x40,
594 #[cfg(feature = "runtime-benchmarks")]
596 BenchmarkingError = 0xFF,
597 }
598
599 #[pezpallet::composite_enum]
601 pub enum HoldReason {
602 CodeUploadDepositReserve,
604 StorageDepositReserve,
606 AddressMapping,
608 }
609
610 #[derive(
611 PartialEq,
612 Eq,
613 Clone,
614 MaxEncodedLen,
615 Encode,
616 Decode,
617 DecodeWithMemTracking,
618 TypeInfo,
619 RuntimeDebug,
620 )]
621 #[pezpallet::origin]
622 pub enum Origin<T: Config> {
623 EthTransaction(T::AccountId),
624 }
625
626 #[pezpallet::storage]
630 #[pezpallet::unbounded]
631 pub(crate) type PristineCode<T: Config> = StorageMap<_, Identity, H256, Vec<u8>>;
632
633 #[pezpallet::storage]
635 pub(crate) type CodeInfoOf<T: Config> = StorageMap<_, Identity, H256, CodeInfo<T>>;
636
637 #[pezpallet::storage]
639 pub(crate) type AccountInfoOf<T: Config> = StorageMap<_, Identity, H160, AccountInfo<T>>;
640
641 #[pezpallet::storage]
643 pub(crate) type ImmutableDataOf<T: Config> = StorageMap<_, Identity, H160, ImmutableData>;
644
645 #[pezpallet::storage]
650 pub(crate) type DeletionQueue<T: Config> = StorageMap<_, Twox64Concat, u32, TrieId>;
651
652 #[pezpallet::storage]
655 pub(crate) type DeletionQueueCounter<T: Config> =
656 StorageValue<_, DeletionQueueManager<T>, ValueQuery>;
657
658 #[pezpallet::storage]
665 pub(crate) type OriginalAccount<T: Config> = StorageMap<_, Identity, H160, AccountId32>;
666
667 #[pezpallet::storage]
677 #[pezpallet::unbounded]
678 pub(crate) type EthereumBlock<T> = StorageValue<_, EthBlock, ValueQuery>;
679
680 #[pezpallet::storage]
684 pub(crate) type BlockHash<T: Config> =
685 StorageMap<_, Identity, BlockNumberFor<T>, H256, ValueQuery>;
686
687 #[pezpallet::storage]
694 #[pezpallet::unbounded]
695 pub(crate) type ReceiptInfoData<T: Config> = StorageValue<_, Vec<ReceiptGasInfo>, ValueQuery>;
696
697 #[pezpallet::storage]
699 #[pezpallet::unbounded]
700 pub(crate) type EthBlockBuilderIR<T: Config> =
701 StorageValue<_, EthereumBlockBuilderIR<T>, ValueQuery>;
702
703 #[pezpallet::storage]
708 #[pezpallet::unbounded]
709 pub(crate) type EthBlockBuilderFirstValues<T: Config> =
710 StorageValue<_, Option<(Vec<u8>, Vec<u8>)>, ValueQuery>;
711
712 #[pezpallet::storage]
714 pub(crate) type DebugSettingsOf<T: Config> = StorageValue<_, DebugSettings, ValueQuery>;
715
716 pub mod genesis {
717 use super::*;
718 use crate::evm::Bytes32;
719
720 #[derive(Clone, PartialEq, Debug, Default, serde::Serialize, serde::Deserialize)]
722 pub struct ContractData {
723 pub code: Vec<u8>,
725 pub storage: alloc::collections::BTreeMap<Bytes32, Bytes32>,
727 }
728
729 #[derive(PartialEq, Default, Debug, Clone, serde::Serialize, serde::Deserialize)]
731 pub struct Account<T: Config> {
732 pub address: H160,
734 #[serde(default)]
736 pub balance: U256,
737 #[serde(default)]
739 pub nonce: T::Nonce,
740 #[serde(flatten, skip_serializing_if = "Option::is_none")]
742 pub contract_data: Option<ContractData>,
743 }
744 }
745
746 #[pezpallet::genesis_config]
747 #[derive(Debug, PartialEq, pezframe_support::DefaultNoBound)]
748 pub struct GenesisConfig<T: Config> {
749 #[serde(default, skip_serializing_if = "Vec::is_empty")]
752 pub mapped_accounts: Vec<T::AccountId>,
753
754 #[serde(default, skip_serializing_if = "Vec::is_empty")]
756 pub accounts: Vec<genesis::Account<T>>,
757
758 #[serde(default, skip_serializing_if = "Option::is_none")]
760 pub debug_settings: Option<DebugSettings>,
761 }
762
763 #[pezpallet::genesis_build]
764 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
765 fn build(&self) {
766 use crate::{exec::Key, vm::ContractBlob};
767 use pezframe_support::traits::fungible::Mutate;
768
769 if !System::<T>::account_exists(&Pezpallet::<T>::account_id()) {
770 let _ = T::Currency::mint_into(
771 &Pezpallet::<T>::account_id(),
772 T::Currency::minimum_balance(),
773 );
774 }
775
776 for id in &self.mapped_accounts {
777 if let Err(err) = T::AddressMapper::map_no_deposit(id) {
778 log::error!(target: LOG_TARGET, "Failed to map account {id:?}: {err:?}");
779 }
780 }
781
782 let owner = Pezpallet::<T>::account_id();
783
784 for genesis::Account { address, balance, nonce, contract_data } in &self.accounts {
785 let account_id = T::AddressMapper::to_account_id(address);
786
787 if !System::<T>::account_exists(&account_id) {
788 let _ = T::Currency::mint_into(&account_id, T::Currency::minimum_balance());
789 }
790
791 pezframe_system::Account::<T>::mutate(&account_id, |info| {
792 info.nonce = (*nonce).into();
793 });
794
795 match contract_data {
796 None => {
797 AccountInfoOf::<T>::insert(
798 address,
799 AccountInfo { account_type: AccountType::EOA, dust: 0 },
800 );
801 },
802 Some(genesis::ContractData { code, storage }) => {
803 let blob = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
804 ContractBlob::<T>::from_pvm_code( code.clone(), owner.clone()).inspect_err(|err| {
805 log::error!(target: LOG_TARGET, "Failed to create PVM ContractBlob for {address:?}: {err:?}");
806 })
807 } else {
808 ContractBlob::<T>::from_evm_runtime_code(code.clone(), account_id).inspect_err(|err| {
809 log::error!(target: LOG_TARGET, "Failed to create EVM ContractBlob for {address:?}: {err:?}");
810 })
811 };
812
813 let Ok(blob) = blob else {
814 continue;
815 };
816
817 let code_hash = *blob.code_hash();
818 let Ok(info) = <ContractInfo<T>>::new(&address, 0u32.into(), code_hash)
819 .inspect_err(|err| {
820 log::error!(target: LOG_TARGET, "Failed to create ContractInfo for {address:?}: {err:?}");
821 })
822 else {
823 continue;
824 };
825
826 AccountInfoOf::<T>::insert(
827 address,
828 AccountInfo { account_type: info.clone().into(), dust: 0 },
829 );
830
831 <PristineCode<T>>::insert(blob.code_hash(), code);
832 <CodeInfoOf<T>>::insert(blob.code_hash(), blob.code_info().clone());
833 for (k, v) in storage {
834 let _ = info.write(&Key::from_fixed(k.0), Some(v.0.to_vec()), None, false).inspect_err(|err| {
835 log::error!(target: LOG_TARGET, "Failed to write genesis storage for {address:?} at key {k:?}: {err:?}");
836 });
837 }
838 },
839 }
840
841 let _ = Pezpallet::<T>::set_evm_balance(address, *balance).inspect_err(|err| {
842 log::error!(target: LOG_TARGET, "Failed to set EVM balance for {address:?}: {err:?}");
843 });
844 }
845
846 block_storage::on_finalize_build_eth_block::<T>(
848 pezframe_system::Pezpallet::<T>::block_number(),
851 );
852
853 if let Some(settings) = self.debug_settings.as_ref() {
855 settings.write_to_storage::<T>()
856 }
857 }
858 }
859
860 #[pezpallet::hooks]
861 impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
862 fn on_idle(_block: BlockNumberFor<T>, limit: Weight) -> Weight {
863 let mut meter = WeightMeter::with_limit(limit);
864 ContractInfo::<T>::process_deletion_queue_batch(&mut meter);
865 meter.consumed()
866 }
867
868 fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
869 block_storage::on_initialize::<T>();
871
872 System::<T>::account_exists(&Pezpallet::<T>::account_id());
874 <T as Config>::WeightInfo::on_finalize_block_fixed()
876 }
877
878 fn on_finalize(block_number: BlockNumberFor<T>) {
879 block_storage::on_finalize_build_eth_block::<T>(block_number);
881 }
882
883 fn integrity_test() {
884 assert!(T::ChainId::get() > 0, "ChainId must be greater than 0");
885
886 T::FeeInfo::integrity_test();
887
888 let max_runtime_mem: u64 = T::RuntimeMemory::get().into();
890
891 const TOTAL_MEMORY_DEVIDER: u64 = 2;
894
895 let max_block_weight = T::BlockWeights::get()
901 .get(DispatchClass::Normal)
902 .max_total
903 .unwrap_or_else(|| T::BlockWeights::get().max_block);
904 let max_key_size: u64 =
905 Key::try_from_var(alloc::vec![0u8; limits::STORAGE_KEY_BYTES as usize])
906 .expect("Key of maximal size shall be created")
907 .hash()
908 .len()
909 .try_into()
910 .unwrap();
911
912 let max_immutable_key_size: u64 = T::AccountId::max_encoded_len().try_into().unwrap();
913 let max_immutable_size: u64 = max_block_weight
914 .checked_div_per_component(&<RuntimeCosts as gas::Token<T>>::weight(
915 &RuntimeCosts::SetImmutableData(limits::IMMUTABLE_BYTES),
916 ))
917 .unwrap()
918 .saturating_mul(
919 u64::from(limits::IMMUTABLE_BYTES)
920 .saturating_add(max_immutable_key_size)
921 .into(),
922 );
923
924 let max_pvf_mem: u64 = T::PVFMemory::get().into();
925 let storage_size_limit = max_pvf_mem.saturating_sub(max_runtime_mem) / 2;
926
927 let max_events_size = max_block_weight
931 .checked_div_per_component(
932 &(<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::DepositEvent {
933 num_topic: 0,
934 len: limits::EVENT_BYTES,
935 })
936 .saturating_add(<RuntimeCosts as gas::Token<T>>::weight(
937 &RuntimeCosts::HostFn,
938 ))),
939 )
940 .unwrap()
941 .saturating_mul(limits::EVENT_BYTES.into());
942
943 assert!(
944 max_events_size < storage_size_limit,
945 "Maximal events size {} exceeds the events limit {}",
946 max_events_size,
947 storage_size_limit
948 );
949
950 let max_eth_block_builder_bytes =
985 block_storage::block_builder_bytes_usage(max_events_size.try_into().unwrap());
986
987 log::debug!(
988 target: LOG_TARGET,
989 "Integrity check: max_eth_block_builder_bytes={} KB using max_events_size={} KB",
990 max_eth_block_builder_bytes / 1024,
991 max_events_size / 1024,
992 );
993
994 let memory_left = i128::from(max_runtime_mem)
999 .saturating_div(TOTAL_MEMORY_DEVIDER.into())
1000 .saturating_sub(limits::MEMORY_REQUIRED.into())
1001 .saturating_sub(max_eth_block_builder_bytes.into());
1002
1003 log::debug!(target: LOG_TARGET, "Integrity check: memory_left={} KB", memory_left / 1024);
1004
1005 assert!(
1006 memory_left >= 0,
1007 "Runtime does not have enough memory for current limits. Additional runtime memory required: {} KB",
1008 memory_left.saturating_mul(TOTAL_MEMORY_DEVIDER.into()).abs() / 1024
1009 );
1010
1011 let max_storage_size = max_block_weight
1014 .checked_div_per_component(
1015 &<RuntimeCosts as gas::Token<T>>::weight(&RuntimeCosts::SetStorage {
1016 new_bytes: limits::STORAGE_BYTES,
1017 old_bytes: 0,
1018 })
1019 .saturating_mul(u64::from(limits::STORAGE_BYTES).saturating_add(max_key_size)),
1020 )
1021 .unwrap()
1022 .saturating_add(max_immutable_size.into())
1023 .saturating_add(max_eth_block_builder_bytes.into());
1024
1025 assert!(
1026 max_storage_size < storage_size_limit,
1027 "Maximal storage size {} exceeds the storage limit {}",
1028 max_storage_size,
1029 storage_size_limit
1030 );
1031 }
1032 }
1033
1034 #[pezpallet::call]
1035 impl<T: Config> Pezpallet<T> {
1036 #[allow(unused_variables)]
1049 #[pezpallet::call_index(0)]
1050 #[pezpallet::weight(Weight::MAX)]
1051 pub fn eth_transact(origin: OriginFor<T>, payload: Vec<u8>) -> DispatchResultWithPostInfo {
1052 Err(pezframe_system::Error::CallFiltered::<T>.into())
1053 }
1054
1055 #[pezpallet::call_index(1)]
1072 #[pezpallet::weight(<T as Config>::WeightInfo::call().saturating_add(*gas_limit))]
1073 pub fn call(
1074 origin: OriginFor<T>,
1075 dest: H160,
1076 #[pezpallet::compact] value: BalanceOf<T>,
1077 gas_limit: Weight,
1078 #[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1079 data: Vec<u8>,
1080 ) -> DispatchResultWithPostInfo {
1081 Self::ensure_non_contract_if_signed(&origin)?;
1082 let mut output = Self::bare_call(
1083 origin,
1084 dest,
1085 Pezpallet::<T>::convert_native_to_evm(value),
1086 gas_limit,
1087 storage_deposit_limit,
1088 data,
1089 ExecConfig::new_bizinikiwi_tx(),
1090 );
1091
1092 if let Ok(return_value) = &output.result {
1093 if return_value.did_revert() {
1094 output.result = Err(<Error<T>>::ContractReverted.into());
1095 }
1096 }
1097 dispatch_result(output.result, output.gas_consumed, <T as Config>::WeightInfo::call())
1098 }
1099
1100 #[pezpallet::call_index(2)]
1106 #[pezpallet::weight(
1107 <T as Config>::WeightInfo::instantiate(data.len() as u32).saturating_add(*gas_limit)
1108 )]
1109 pub fn instantiate(
1110 origin: OriginFor<T>,
1111 #[pezpallet::compact] value: BalanceOf<T>,
1112 gas_limit: Weight,
1113 #[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1114 code_hash: pezsp_core::H256,
1115 data: Vec<u8>,
1116 salt: Option<[u8; 32]>,
1117 ) -> DispatchResultWithPostInfo {
1118 Self::ensure_non_contract_if_signed(&origin)?;
1119 let data_len = data.len() as u32;
1120 let mut output = Self::bare_instantiate(
1121 origin,
1122 Pezpallet::<T>::convert_native_to_evm(value),
1123 gas_limit,
1124 storage_deposit_limit,
1125 Code::Existing(code_hash),
1126 data,
1127 salt,
1128 ExecConfig::new_bizinikiwi_tx(),
1129 );
1130 if let Ok(retval) = &output.result {
1131 if retval.result.did_revert() {
1132 output.result = Err(<Error<T>>::ContractReverted.into());
1133 }
1134 }
1135 dispatch_result(
1136 output.result.map(|result| result.result),
1137 output.gas_consumed,
1138 <T as Config>::WeightInfo::instantiate(data_len),
1139 )
1140 }
1141
1142 #[pezpallet::call_index(3)]
1170 #[pezpallet::weight(
1171 <T as Config>::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32)
1172 .saturating_add(*gas_limit)
1173 )]
1174 pub fn instantiate_with_code(
1175 origin: OriginFor<T>,
1176 #[pezpallet::compact] value: BalanceOf<T>,
1177 gas_limit: Weight,
1178 #[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1179 code: Vec<u8>,
1180 data: Vec<u8>,
1181 salt: Option<[u8; 32]>,
1182 ) -> DispatchResultWithPostInfo {
1183 Self::ensure_non_contract_if_signed(&origin)?;
1184 let code_len = code.len() as u32;
1185 let data_len = data.len() as u32;
1186 let mut output = Self::bare_instantiate(
1187 origin,
1188 Pezpallet::<T>::convert_native_to_evm(value),
1189 gas_limit,
1190 storage_deposit_limit,
1191 Code::Upload(code),
1192 data,
1193 salt,
1194 ExecConfig::new_bizinikiwi_tx(),
1195 );
1196 if let Ok(retval) = &output.result {
1197 if retval.result.did_revert() {
1198 output.result = Err(<Error<T>>::ContractReverted.into());
1199 }
1200 }
1201 dispatch_result(
1202 output.result.map(|result| result.result),
1203 output.gas_consumed,
1204 <T as Config>::WeightInfo::instantiate_with_code(code_len, data_len),
1205 )
1206 }
1207
1208 #[pezpallet::call_index(10)]
1230 #[pezpallet::weight(
1231 <T as Config>::WeightInfo::eth_instantiate_with_code(code.len() as u32, data.len() as u32, Pezpallet::<T>::has_dust(*value).into())
1232 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1233 .saturating_add(*gas_limit)
1234 )]
1235 pub fn eth_instantiate_with_code(
1236 origin: OriginFor<T>,
1237 value: U256,
1238 gas_limit: Weight,
1239 code: Vec<u8>,
1240 data: Vec<u8>,
1241 transaction_encoded: Vec<u8>,
1242 effective_gas_price: U256,
1243 encoded_len: u32,
1244 ) -> DispatchResultWithPostInfo {
1245 let signer = Self::ensure_eth_signed(origin)?;
1246 let origin = OriginFor::<T>::signed(signer.clone());
1247 Self::ensure_non_contract_if_signed(&origin)?;
1248 let mut call = Call::<T>::eth_instantiate_with_code {
1249 value,
1250 gas_limit,
1251 code: code.clone(),
1252 data: data.clone(),
1253 transaction_encoded: transaction_encoded.clone(),
1254 effective_gas_price,
1255 encoded_len,
1256 }
1257 .into();
1258 let info = T::FeeInfo::dispatch_info(&call);
1259 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1260 drop(call);
1261
1262 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1263 let output = Self::bare_instantiate(
1264 origin,
1265 value,
1266 gas_limit,
1267 BalanceOf::<T>::max_value(),
1268 Code::Upload(code),
1269 data,
1270 None,
1271 ExecConfig::new_eth_tx(
1272 effective_gas_price,
1273 encoded_len,
1274 base_info.total_weight(),
1275 ),
1276 );
1277
1278 block_storage::EthereumCallResult::new::<T>(
1279 signer,
1280 output.map_result(|r| r.result),
1281 base_info.call_weight,
1282 encoded_len,
1283 &info,
1284 effective_gas_price,
1285 )
1286 })
1287 }
1288
1289 #[pezpallet::call_index(11)]
1292 #[pezpallet::weight(
1293 T::WeightInfo::eth_call(Pezpallet::<T>::has_dust(*value).into())
1294 .saturating_add(*gas_limit)
1295 .saturating_add(T::WeightInfo::on_finalize_block_per_tx(transaction_encoded.len() as u32))
1296 )]
1297 pub fn eth_call(
1298 origin: OriginFor<T>,
1299 dest: H160,
1300 value: U256,
1301 gas_limit: Weight,
1302 data: Vec<u8>,
1303 transaction_encoded: Vec<u8>,
1304 effective_gas_price: U256,
1305 encoded_len: u32,
1306 ) -> DispatchResultWithPostInfo {
1307 let signer = Self::ensure_eth_signed(origin)?;
1308 let origin = OriginFor::<T>::signed(signer.clone());
1309
1310 Self::ensure_non_contract_if_signed(&origin)?;
1311 let mut call = Call::<T>::eth_call {
1312 dest,
1313 value,
1314 gas_limit,
1315 data: data.clone(),
1316 transaction_encoded: transaction_encoded.clone(),
1317 effective_gas_price,
1318 encoded_len,
1319 }
1320 .into();
1321 let info = T::FeeInfo::dispatch_info(&call);
1322 let base_info = T::FeeInfo::base_dispatch_info(&mut call);
1323 drop(call);
1324
1325 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1326 let output = Self::bare_call(
1327 origin,
1328 dest,
1329 value,
1330 gas_limit,
1331 BalanceOf::<T>::max_value(),
1332 data,
1333 ExecConfig::new_eth_tx(
1334 effective_gas_price,
1335 encoded_len,
1336 base_info.total_weight(),
1337 ),
1338 );
1339
1340 block_storage::EthereumCallResult::new::<T>(
1341 signer,
1342 output,
1343 base_info.call_weight,
1344 encoded_len,
1345 &info,
1346 effective_gas_price,
1347 )
1348 })
1349 }
1350
1351 #[pezpallet::call_index(12)]
1362 #[pezpallet::weight(T::WeightInfo::eth_bizinikiwi_call(transaction_encoded.len() as u32).saturating_add(call.get_dispatch_info().call_weight))]
1363 pub fn eth_bizinikiwi_call(
1364 origin: OriginFor<T>,
1365 call: Box<<T as Config>::RuntimeCall>,
1366 transaction_encoded: Vec<u8>,
1367 ) -> DispatchResultWithPostInfo {
1368 let signer = Self::ensure_eth_signed(origin)?;
1371 let weight_overhead =
1372 T::WeightInfo::eth_bizinikiwi_call(transaction_encoded.len() as u32);
1373
1374 block_storage::with_ethereum_context::<T>(transaction_encoded, || {
1375 let call_weight = call.get_dispatch_info().call_weight;
1376 let mut call_result = call.dispatch(RawOrigin::Signed(signer).into());
1377
1378 match &mut call_result {
1380 Ok(post_info) | Err(DispatchErrorWithPostInfo { post_info, .. }) => {
1381 post_info.actual_weight = Some(
1382 post_info
1383 .actual_weight
1384 .unwrap_or_else(|| call_weight)
1385 .saturating_add(weight_overhead),
1386 );
1387 },
1388 }
1389
1390 block_storage::EthereumCallResult {
1393 receipt_gas_info: ReceiptGasInfo::default(),
1394 result: call_result,
1395 }
1396 })
1397 }
1398
1399 #[pezpallet::call_index(4)]
1414 #[pezpallet::weight(<T as Config>::WeightInfo::upload_code(code.len() as u32))]
1415 pub fn upload_code(
1416 origin: OriginFor<T>,
1417 code: Vec<u8>,
1418 #[pezpallet::compact] storage_deposit_limit: BalanceOf<T>,
1419 ) -> DispatchResult {
1420 Self::ensure_non_contract_if_signed(&origin)?;
1421 Self::bare_upload_code(origin, code, storage_deposit_limit).map(|_| ())
1422 }
1423
1424 #[pezpallet::call_index(5)]
1429 #[pezpallet::weight(<T as Config>::WeightInfo::remove_code())]
1430 pub fn remove_code(
1431 origin: OriginFor<T>,
1432 code_hash: pezsp_core::H256,
1433 ) -> DispatchResultWithPostInfo {
1434 let origin = ensure_signed(origin)?;
1435 <ContractBlob<T>>::remove(&origin, code_hash)?;
1436 Ok(Pays::No.into())
1438 }
1439
1440 #[pezpallet::call_index(6)]
1451 #[pezpallet::weight(<T as Config>::WeightInfo::set_code())]
1452 pub fn set_code(
1453 origin: OriginFor<T>,
1454 dest: H160,
1455 code_hash: pezsp_core::H256,
1456 ) -> DispatchResult {
1457 ensure_root(origin)?;
1458 <AccountInfoOf<T>>::try_mutate(&dest, |account| {
1459 let Some(account) = account else {
1460 return Err(<Error<T>>::ContractNotFound.into());
1461 };
1462
1463 let AccountType::Contract(ref mut contract) = account.account_type else {
1464 return Err(<Error<T>>::ContractNotFound.into());
1465 };
1466
1467 <CodeInfo<T>>::increment_refcount(code_hash)?;
1468 let _ = <CodeInfo<T>>::decrement_refcount(contract.code_hash)?;
1469 contract.code_hash = code_hash;
1470
1471 Ok(())
1472 })
1473 }
1474
1475 #[pezpallet::call_index(7)]
1480 #[pezpallet::weight(<T as Config>::WeightInfo::map_account())]
1481 pub fn map_account(origin: OriginFor<T>) -> DispatchResult {
1482 Self::ensure_non_contract_if_signed(&origin)?;
1483 let origin = ensure_signed(origin)?;
1484 T::AddressMapper::map(&origin)
1485 }
1486
1487 #[pezpallet::call_index(8)]
1492 #[pezpallet::weight(<T as Config>::WeightInfo::unmap_account())]
1493 pub fn unmap_account(origin: OriginFor<T>) -> DispatchResult {
1494 let origin = ensure_signed(origin)?;
1495 T::AddressMapper::unmap(&origin)
1496 }
1497
1498 #[pezpallet::call_index(9)]
1504 #[pezpallet::weight({
1505 let dispatch_info = call.get_dispatch_info();
1506 (
1507 <T as Config>::WeightInfo::dispatch_as_fallback_account().saturating_add(dispatch_info.call_weight),
1508 dispatch_info.class
1509 )
1510 })]
1511 pub fn dispatch_as_fallback_account(
1512 origin: OriginFor<T>,
1513 call: Box<<T as Config>::RuntimeCall>,
1514 ) -> DispatchResultWithPostInfo {
1515 Self::ensure_non_contract_if_signed(&origin)?;
1516 let origin = ensure_signed(origin)?;
1517 let unmapped_account =
1518 T::AddressMapper::to_fallback_account_id(&T::AddressMapper::to_address(&origin));
1519 call.dispatch(RawOrigin::Signed(unmapped_account).into())
1520 }
1521 }
1522}
1523
1524fn dispatch_result<R>(
1526 result: Result<R, DispatchError>,
1527 gas_consumed: Weight,
1528 base_weight: Weight,
1529) -> DispatchResultWithPostInfo {
1530 let post_info = PostDispatchInfo {
1531 actual_weight: Some(gas_consumed.saturating_add(base_weight)),
1532 pays_fee: Default::default(),
1533 };
1534
1535 result
1536 .map(|_| post_info)
1537 .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e })
1538}
1539
1540impl<T: Config> Pezpallet<T> {
1541 pub fn bare_call(
1548 origin: OriginFor<T>,
1549 dest: H160,
1550 evm_value: U256,
1551 gas_limit: Weight,
1552 storage_deposit_limit: BalanceOf<T>,
1553 data: Vec<u8>,
1554 exec_config: ExecConfig<T>,
1555 ) -> ContractResult<ExecReturnValue, BalanceOf<T>> {
1556 let mut gas_meter = GasMeter::new(gas_limit);
1557 let mut storage_deposit = Default::default();
1558
1559 let try_call = || {
1560 let origin = ExecOrigin::from_runtime_origin(origin)?;
1561 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1562 let result = ExecStack::<T, ContractBlob<T>>::run_call(
1563 origin.clone(),
1564 dest,
1565 &mut gas_meter,
1566 &mut storage_meter,
1567 evm_value,
1568 data,
1569 &exec_config,
1570 )?;
1571 storage_deposit =
1572 storage_meter.try_into_deposit(&origin, &exec_config).inspect_err(|err| {
1573 log::debug!(target: LOG_TARGET, "Failed to transfer deposit: {err:?}");
1574 })?;
1575 Ok(result)
1576 };
1577 let result = Self::run_guarded(try_call);
1578 ContractResult {
1579 result: result.map_err(|r| r.error),
1580 gas_consumed: gas_meter.gas_consumed(),
1581 gas_required: gas_meter.gas_required(),
1582 storage_deposit,
1583 }
1584 }
1585
1586 pub fn prepare_dry_run(account: &T::AccountId) {
1592 pezframe_system::Pezpallet::<T>::inc_account_nonce(account);
1595 }
1596
1597 pub fn bare_instantiate(
1603 origin: OriginFor<T>,
1604 evm_value: U256,
1605 gas_limit: Weight,
1606 mut storage_deposit_limit: BalanceOf<T>,
1607 code: Code,
1608 data: Vec<u8>,
1609 salt: Option<[u8; 32]>,
1610 exec_config: ExecConfig<T>,
1611 ) -> ContractResult<InstantiateReturnValue, BalanceOf<T>> {
1612 let mut gas_meter = GasMeter::new(gas_limit);
1613 let mut storage_deposit = Default::default();
1614 let try_instantiate = || {
1615 let instantiate_account = T::InstantiateOrigin::ensure_origin(origin.clone())?;
1616
1617 if_tracing(|t| t.instantiate_code(&code, salt.as_ref()));
1618 let (executable, upload_deposit) = match code {
1619 Code::Upload(code) if code.starts_with(&polkavm_common::program::BLOB_MAGIC) => {
1620 let upload_account = T::UploadOrigin::ensure_origin(origin)?;
1621 let (executable, upload_deposit) = Self::try_upload_code(
1622 upload_account,
1623 code,
1624 BytecodeType::Pvm,
1625 storage_deposit_limit,
1626 &exec_config,
1627 )?;
1628 storage_deposit_limit.saturating_reduce(upload_deposit);
1629 (executable, upload_deposit)
1630 },
1631 Code::Upload(code) => {
1632 if T::AllowEVMBytecode::get() {
1633 ensure!(data.is_empty(), <Error<T>>::EvmConstructorNonEmptyData);
1634 let origin = T::UploadOrigin::ensure_origin(origin)?;
1635 let executable = ContractBlob::from_evm_init_code(code, origin)?;
1636 (executable, Default::default())
1637 } else {
1638 return Err(<Error<T>>::CodeRejected.into());
1639 }
1640 },
1641 Code::Existing(code_hash) => {
1642 let executable = ContractBlob::from_storage(code_hash, &mut gas_meter)?;
1643 ensure!(executable.code_info().is_pvm(), <Error<T>>::EvmConstructedFromHash);
1644 (executable, Default::default())
1645 },
1646 };
1647 let instantiate_origin = ExecOrigin::from_account_id(instantiate_account.clone());
1648 let mut storage_meter = StorageMeter::new(storage_deposit_limit);
1649 let result = ExecStack::<T, ContractBlob<T>>::run_instantiate(
1650 instantiate_account,
1651 executable,
1652 &mut gas_meter,
1653 &mut storage_meter,
1654 evm_value,
1655 data,
1656 salt.as_ref(),
1657 &exec_config,
1658 );
1659 storage_deposit = storage_meter
1660 .try_into_deposit(&instantiate_origin, &exec_config)?
1661 .saturating_add(&StorageDeposit::Charge(upload_deposit));
1662 result
1663 };
1664 let output = Self::run_guarded(try_instantiate);
1665 ContractResult {
1666 result: output
1667 .map(|(addr, result)| InstantiateReturnValue { result, addr })
1668 .map_err(|e| e.error),
1669 gas_consumed: gas_meter.gas_consumed(),
1670 gas_required: gas_meter.gas_required(),
1671 storage_deposit,
1672 }
1673 }
1674
1675 pub fn dry_run_eth_transact(
1681 mut tx: GenericTransaction,
1682 dry_run_config: DryRunConfig<<<T as Config>::Time as Time>::Moment>,
1683 ) -> Result<EthTransactInfo<BalanceOf<T>>, EthTransactError>
1684 where
1685 T::Nonce: Into<U256>,
1686 CallOf<T>: SetWeightLimit,
1687 {
1688 log::debug!(target: LOG_TARGET, "dry_run_eth_transact: {tx:?}");
1689
1690 let origin = T::AddressMapper::to_account_id(&tx.from.unwrap_or_default());
1691 Self::prepare_dry_run(&origin);
1692
1693 let base_fee = Self::evm_base_fee();
1694 let effective_gas_price = tx.effective_gas_price(base_fee).unwrap_or(base_fee);
1695
1696 if effective_gas_price < base_fee {
1697 Err(EthTransactError::Message(format!(
1698 "Effective gas price {effective_gas_price:?} lower than base fee {base_fee:?}"
1699 )))?;
1700 }
1701
1702 if tx.nonce.is_none() {
1703 tx.nonce = Some(<System<T>>::account_nonce(&origin).into());
1704 }
1705 if tx.chain_id.is_none() {
1706 tx.chain_id = Some(T::ChainId::get().into());
1707 }
1708 if tx.gas_price.is_none() {
1709 tx.gas_price = Some(effective_gas_price);
1710 }
1711 if tx.max_priority_fee_per_gas.is_none() {
1712 tx.max_priority_fee_per_gas = Some(effective_gas_price);
1713 }
1714 if tx.max_fee_per_gas.is_none() {
1715 tx.max_fee_per_gas = Some(effective_gas_price);
1716 }
1717
1718 let gas = tx.gas;
1719 if tx.gas.is_none() {
1720 tx.gas = Some(Self::evm_block_gas_limit());
1721 }
1722 if tx.r#type.is_none() {
1723 tx.r#type = Some(TYPE_EIP1559.into());
1724 }
1725
1726 let value = tx.value.unwrap_or_default();
1728 let input = tx.input.clone().to_vec();
1729 let from = tx.from;
1730 let to = tx.to;
1731
1732 let mut call_info = create_call::<T>(tx, None, false)
1735 .map_err(|err| EthTransactError::Message(format!("Invalid call: {err:?}")))?;
1736
1737 let exec_config = {
1741 let base_info = T::FeeInfo::base_dispatch_info(&mut call_info.call);
1742 ExecConfig::new_eth_tx(
1743 effective_gas_price,
1744 call_info.encoded_len,
1745 base_info.total_weight(),
1746 )
1747 .with_dry_run(dry_run_config)
1748 };
1749
1750 let fees = call_info.tx_fee.saturating_add(call_info.storage_deposit);
1752 if let Some(from) = &from {
1753 let fees = if gas.is_some() { fees } else { Zero::zero() };
1754 let balance = Self::evm_balance(from);
1755 if balance < Pezpallet::<T>::convert_native_to_evm(fees).saturating_add(value) {
1756 return Err(EthTransactError::Message(format!(
1757 "insufficient funds for gas * price + value ({fees:?}): address {from:?} have {balance:?} (supplied gas {gas:?})",
1758 )));
1759 }
1760 }
1761
1762 T::FeeInfo::deposit_txfee(T::Currency::issue(fees));
1765
1766 let extract_error = |err| {
1767 if err == Error::<T>::StorageDepositNotEnoughFunds.into() {
1768 Err(EthTransactError::Message(format!("Not enough gas supplied: {err:?}")))
1769 } else {
1770 Err(EthTransactError::Message(format!("failed to run contract: {err:?}")))
1771 }
1772 };
1773
1774 let mut dry_run = match to {
1776 Some(dest) => {
1778 if dest == RUNTIME_PALLETS_ADDR {
1779 let Ok(dispatch_call) = <CallOf<T>>::decode(&mut &input[..]) else {
1780 return Err(EthTransactError::Message(format!(
1781 "Failed to decode pezpallet-call {input:?}"
1782 )));
1783 };
1784
1785 if let Err(result) =
1786 dispatch_call.clone().dispatch(RawOrigin::Signed(origin).into())
1787 {
1788 return Err(EthTransactError::Message(format!(
1789 "Failed to dispatch call: {:?}",
1790 result.error,
1791 )));
1792 };
1793
1794 Default::default()
1795 } else {
1796 let result = crate::Pezpallet::<T>::bare_call(
1798 OriginFor::<T>::signed(origin),
1799 dest,
1800 value,
1801 call_info.weight_limit,
1802 BalanceOf::<T>::max_value(),
1803 input.clone(),
1804 exec_config,
1805 );
1806
1807 let data = match result.result {
1808 Ok(return_value) => {
1809 if return_value.did_revert() {
1810 return Err(EthTransactError::Data(return_value.data));
1811 }
1812 return_value.data
1813 },
1814 Err(err) => {
1815 log::debug!(target: LOG_TARGET, "Failed to execute call: {err:?}");
1816 return extract_error(err);
1817 },
1818 };
1819
1820 EthTransactInfo {
1821 gas_required: result.gas_required,
1822 storage_deposit: result.storage_deposit.charge_or_zero(),
1823 data,
1824 eth_gas: Default::default(),
1825 }
1826 }
1827 },
1828 None => {
1830 let (code, data) = if input.starts_with(&polkavm_common::program::BLOB_MAGIC) {
1832 extract_code_and_data(&input).unwrap_or_else(|| (input, Default::default()))
1833 } else {
1834 (input, vec![])
1835 };
1836
1837 let result = crate::Pezpallet::<T>::bare_instantiate(
1839 OriginFor::<T>::signed(origin),
1840 value,
1841 call_info.weight_limit,
1842 BalanceOf::<T>::max_value(),
1843 Code::Upload(code.clone()),
1844 data.clone(),
1845 None,
1846 exec_config,
1847 );
1848
1849 let returned_data = match result.result {
1850 Ok(return_value) => {
1851 if return_value.result.did_revert() {
1852 return Err(EthTransactError::Data(return_value.result.data));
1853 }
1854 return_value.result.data
1855 },
1856 Err(err) => {
1857 log::debug!(target: LOG_TARGET, "Failed to instantiate: {err:?}");
1858 return extract_error(err);
1859 },
1860 };
1861
1862 EthTransactInfo {
1863 gas_required: result.gas_required,
1864 storage_deposit: result.storage_deposit.charge_or_zero(),
1865 data: returned_data,
1866 eth_gas: Default::default(),
1867 }
1868 },
1869 };
1870
1871 call_info.call.set_weight_limit(dry_run.gas_required);
1873
1874 let total_weight = T::FeeInfo::dispatch_info(&call_info.call).total_weight();
1876 let max_weight = Self::evm_max_extrinsic_weight();
1877 if total_weight.any_gt(max_weight) {
1878 Err(EthTransactError::Message(format!(
1879 "\
1880 The transaction consumes more than the allowed weight. \
1881 needed={total_weight} \
1882 allowed={max_weight} \
1883 overweight_by={}\
1884 ",
1885 total_weight.saturating_sub(max_weight),
1886 )))?;
1887 }
1888
1889 let transaction_fee = T::FeeInfo::tx_fee(call_info.encoded_len, &call_info.call);
1891 let available_fee = T::FeeInfo::remaining_txfee();
1892 if transaction_fee > available_fee {
1893 Err(EthTransactError::Message(format!(
1894 "Not enough gas supplied: Off by: {:?}",
1895 call_info.tx_fee.saturating_sub(available_fee),
1896 )))?;
1897 }
1898
1899 let eth_gas: U256 = T::FeeInfo::next_fee_multiplier_reciprocal()
1902 .saturating_mul_int(transaction_fee.saturating_add(dry_run.storage_deposit))
1903 .saturating_add(1_u32.into())
1904 .into();
1905
1906 log::debug!(target: LOG_TARGET, "\
1907 dry_run_eth_transact: \
1908 weight_limit={} \
1909 total_weight={total_weight} \
1910 max_weight={max_weight} \
1911 weight_left={} \
1912 eth_gas={eth_gas}) \
1913 encoded_len={} \
1914 tx_fee={transaction_fee:?} \
1915 storage_deposit={:?}\
1916 ",
1917 dry_run.gas_required,
1918 max_weight.saturating_sub(total_weight),
1919 call_info.encoded_len,
1920 dry_run.storage_deposit,
1921
1922 );
1923 dry_run.eth_gas = eth_gas;
1924 Ok(dry_run)
1925 }
1926
1927 pub fn evm_balance(address: &H160) -> U256 {
1931 let balance = AccountInfo::<T>::balance_of((*address).into());
1932 Self::convert_native_to_evm(balance)
1933 }
1934
1935 pub fn eth_block() -> EthBlock {
1937 EthereumBlock::<T>::get()
1938 }
1939
1940 pub fn eth_block_hash_from_number(number: U256) -> Option<H256> {
1947 let number = BlockNumberFor::<T>::try_from(number).ok()?;
1948 let hash = <BlockHash<T>>::get(number);
1949 if hash == H256::zero() {
1950 None
1951 } else {
1952 Some(hash)
1953 }
1954 }
1955
1956 pub fn eth_receipt_data() -> Vec<ReceiptGasInfo> {
1958 ReceiptInfoData::<T>::get()
1959 }
1960
1961 pub fn set_evm_balance(address: &H160, evm_value: U256) -> Result<(), Error<T>> {
1967 let (balance, dust) = Self::new_balance_with_dust(evm_value)
1968 .map_err(|_| <Error<T>>::BalanceConversionFailed)?;
1969 let account_id = T::AddressMapper::to_account_id(&address);
1970 T::Currency::set_balance(&account_id, balance);
1971 AccountInfoOf::<T>::mutate(&address, |account| {
1972 if let Some(account) = account {
1973 account.dust = dust;
1974 } else {
1975 *account = Some(AccountInfo { dust, ..Default::default() });
1976 }
1977 });
1978
1979 Ok(())
1980 }
1981
1982 pub fn new_balance_with_dust(
1986 evm_value: U256,
1987 ) -> Result<(BalanceOf<T>, u32), BalanceConversionError> {
1988 let ed = T::Currency::minimum_balance();
1989 let balance_with_dust = BalanceWithDust::<BalanceOf<T>>::from_value::<T>(evm_value)?;
1990 let (value, dust) = balance_with_dust.deconstruct();
1991
1992 Ok((ed.saturating_add(value), dust))
1993 }
1994
1995 pub fn evm_nonce(address: &H160) -> u32
1997 where
1998 T::Nonce: Into<u32>,
1999 {
2000 let account = T::AddressMapper::to_account_id(&address);
2001 System::<T>::account_nonce(account).into()
2002 }
2003
2004 pub fn evm_block_gas_limit() -> U256 {
2006 let max_block_weight = T::BlockWeights::get()
2007 .get(DispatchClass::Normal)
2008 .max_total
2009 .unwrap_or_else(|| T::BlockWeights::get().max_block);
2010
2011 let length_fee = T::FeeInfo::next_fee_multiplier_reciprocal().saturating_mul_int(
2012 T::FeeInfo::length_to_fee(*T::BlockLength::get().max.get(DispatchClass::Normal)),
2013 );
2014
2015 Self::evm_gas_from_weight(max_block_weight).saturating_add(length_fee.into())
2016 }
2017
2018 pub fn evm_max_extrinsic_weight() -> Weight {
2020 let factor = <T as Config>::MaxEthExtrinsicWeight::get();
2021 let max_weight = <T as pezframe_system::Config>::BlockWeights::get()
2022 .get(DispatchClass::Normal)
2023 .max_extrinsic
2024 .unwrap_or_else(|| <T as pezframe_system::Config>::BlockWeights::get().max_block);
2025 Weight::from_parts(
2026 factor.saturating_mul_int(max_weight.ref_time()),
2027 factor.saturating_mul_int(max_weight.proof_size()),
2028 )
2029 }
2030
2031 pub fn evm_base_fee() -> U256 {
2033 let multiplier = T::FeeInfo::next_fee_multiplier();
2034 multiplier.saturating_mul_int::<u128>(T::NativeToEthRatio::get().into()).into()
2035 }
2036
2037 pub fn evm_tracer(tracer_type: TracerType) -> Tracer<T>
2039 where
2040 T::Nonce: Into<u32>,
2041 {
2042 match tracer_type {
2043 TracerType::CallTracer(config) => CallTracer::new(
2044 config.unwrap_or_default(),
2045 Self::evm_gas_from_weight as fn(Weight) -> U256,
2046 )
2047 .into(),
2048 TracerType::PrestateTracer(config) => {
2049 PrestateTracer::new(config.unwrap_or_default()).into()
2050 },
2051 }
2052 }
2053
2054 pub fn bare_upload_code(
2058 origin: OriginFor<T>,
2059 code: Vec<u8>,
2060 storage_deposit_limit: BalanceOf<T>,
2061 ) -> CodeUploadResult<BalanceOf<T>> {
2062 let origin = T::UploadOrigin::ensure_origin(origin)?;
2063
2064 let bytecode_type = if code.starts_with(&polkavm_common::program::BLOB_MAGIC) {
2065 BytecodeType::Pvm
2066 } else {
2067 if !T::AllowEVMBytecode::get() {
2068 return Err(<Error<T>>::CodeRejected.into());
2069 }
2070 BytecodeType::Evm
2071 };
2072
2073 let (module, deposit) = Self::try_upload_code(
2074 origin,
2075 code,
2076 bytecode_type,
2077 storage_deposit_limit,
2078 &ExecConfig::new_bizinikiwi_tx(),
2079 )?;
2080 Ok(CodeUploadReturnValue { code_hash: *module.code_hash(), deposit })
2081 }
2082
2083 pub fn get_storage(address: H160, key: [u8; 32]) -> GetStorageResult {
2085 let contract_info =
2086 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2087
2088 let maybe_value = contract_info.read(&Key::from_fixed(key));
2089 Ok(maybe_value)
2090 }
2091
2092 pub fn get_immutables(address: H160) -> Option<ImmutableData> {
2096 let immutable_data = <ImmutableDataOf<T>>::get(address);
2097 immutable_data
2098 }
2099
2100 pub fn set_immutables(address: H160, data: ImmutableData) -> Result<(), ContractAccessError> {
2108 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2109 <ImmutableDataOf<T>>::insert(address, data);
2110 Ok(())
2111 }
2112
2113 pub fn get_storage_var_key(address: H160, key: Vec<u8>) -> GetStorageResult {
2115 let contract_info =
2116 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2117
2118 let maybe_value = contract_info.read(
2119 &Key::try_from_var(key)
2120 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2121 .into(),
2122 );
2123 Ok(maybe_value)
2124 }
2125
2126 pub fn convert_native_to_evm(value: impl Into<BalanceWithDust<BalanceOf<T>>>) -> U256 {
2128 let (value, dust) = value.into().deconstruct();
2129 value
2130 .into()
2131 .saturating_mul(T::NativeToEthRatio::get().into())
2132 .saturating_add(dust.into())
2133 }
2134
2135 pub fn set_storage(address: H160, key: [u8; 32], value: Option<Vec<u8>>) -> SetStorageResult {
2145 let contract_info =
2146 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2147
2148 contract_info
2149 .write(&Key::from_fixed(key), value, None, false)
2150 .map_err(ContractAccessError::StorageWriteFailed)
2151 }
2152
2153 pub fn set_storage_var_key(
2164 address: H160,
2165 key: Vec<u8>,
2166 value: Option<Vec<u8>>,
2167 ) -> SetStorageResult {
2168 let contract_info =
2169 AccountInfo::<T>::load_contract(&address).ok_or(ContractAccessError::DoesntExist)?;
2170
2171 contract_info
2172 .write(
2173 &Key::try_from_var(key)
2174 .map_err(|_| ContractAccessError::KeyDecodingFailed)?
2175 .into(),
2176 value,
2177 None,
2178 false,
2179 )
2180 .map_err(ContractAccessError::StorageWriteFailed)
2181 }
2182
2183 pub fn account_id() -> T::AccountId {
2185 use pezframe_support::PalletId;
2186 use pezsp_runtime::traits::AccountIdConversion;
2187 PalletId(*b"py/reviv").into_account_truncating()
2188 }
2189
2190 pub fn block_author() -> H160 {
2192 use pezframe_support::traits::FindAuthor;
2193
2194 let digest = <pezframe_system::Pezpallet<T>>::digest();
2195 let pre_runtime_digests = digest.logs.iter().filter_map(|d| d.as_pre_runtime());
2196
2197 T::FindAuthor::find_author(pre_runtime_digests)
2198 .map(|account_id| T::AddressMapper::to_address(&account_id))
2199 .unwrap_or_default()
2200 }
2201
2202 pub fn code(address: &H160) -> Vec<u8> {
2206 use precompiles::{All, Precompiles};
2207 if let Some(code) = <All<T>>::code(address.as_fixed_bytes()) {
2208 return code.into();
2209 }
2210 AccountInfo::<T>::load_contract(&address)
2211 .and_then(|contract| <PristineCode<T>>::get(contract.code_hash))
2212 .map(|code| code.into())
2213 .unwrap_or_default()
2214 }
2215
2216 pub fn try_upload_code(
2218 origin: T::AccountId,
2219 code: Vec<u8>,
2220 code_type: BytecodeType,
2221 storage_deposit_limit: BalanceOf<T>,
2222 exec_config: &ExecConfig<T>,
2223 ) -> Result<(ContractBlob<T>, BalanceOf<T>), DispatchError> {
2224 let mut module = match code_type {
2225 BytecodeType::Pvm => ContractBlob::from_pvm_code(code, origin)?,
2226 BytecodeType::Evm => ContractBlob::from_evm_runtime_code(code, origin)?,
2227 };
2228 let deposit = module.store_code(exec_config, None)?;
2229 ensure!(storage_deposit_limit >= deposit, <Error<T>>::StorageDepositLimitExhausted);
2230 Ok((module, deposit))
2231 }
2232
2233 fn run_guarded<R, F: FnOnce() -> Result<R, ExecError>>(f: F) -> Result<R, ExecError> {
2235 executing_contract::using_once(&mut false, || {
2236 executing_contract::with(|f| {
2237 if *f {
2239 return Err(())
2240 }
2241 *f = true;
2243 Ok(())
2244 })
2245 .expect("Returns `Ok` if called within `using_once`. It is syntactically obvious that this is the case; qed")
2246 .map_err(|_| <Error<T>>::ReenteredPallet.into())
2247 .map(|_| f())
2248 .and_then(|r| r)
2249 })
2250 }
2251
2252 pub fn evm_gas_from_weight(weight: Weight) -> U256 {
2254 T::FeeInfo::weight_to_fee(&weight, Combinator::Max).into()
2255 }
2256
2257 fn charge_deposit(
2262 hold_reason: Option<HoldReason>,
2263 from: &T::AccountId,
2264 to: &T::AccountId,
2265 amount: BalanceOf<T>,
2266 exec_config: &ExecConfig<T>,
2267 ) -> DispatchResult {
2268 use pezframe_support::traits::tokens::{Fortitude, Precision, Preservation};
2269
2270 if amount.is_zero() {
2271 return Ok(());
2272 }
2273
2274 match (exec_config.collect_deposit_from_hold.is_some(), hold_reason) {
2275 (true, hold_reason) => {
2276 T::FeeInfo::withdraw_txfee(amount)
2277 .ok_or(())
2278 .and_then(|credit| T::Currency::resolve(to, credit).map_err(|_| ()))
2279 .and_then(|_| {
2280 if let Some(hold_reason) = hold_reason {
2281 T::Currency::hold(&hold_reason.into(), to, amount).map_err(|_| ())?;
2282 }
2283 Ok(())
2284 })
2285 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2286 },
2287 (false, Some(hold_reason)) => {
2288 T::Currency::transfer_and_hold(
2289 &hold_reason.into(),
2290 from,
2291 to,
2292 amount,
2293 Precision::Exact,
2294 Preservation::Preserve,
2295 Fortitude::Polite,
2296 )
2297 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2298 },
2299 (false, None) => {
2300 T::Currency::transfer(from, to, amount, Preservation::Preserve)
2301 .map_err(|_| Error::<T>::StorageDepositNotEnoughFunds)?;
2302 },
2303 }
2304 Ok(())
2305 }
2306
2307 fn refund_deposit(
2312 hold_reason: HoldReason,
2313 from: &T::AccountId,
2314 to: &T::AccountId,
2315 amount: BalanceOf<T>,
2316 exec_config: Option<&ExecConfig<T>>,
2317 ) -> Result<(), DispatchError> {
2318 use pezframe_support::traits::{
2319 fungible::InspectHold,
2320 tokens::{Fortitude, Precision, Preservation, Restriction},
2321 };
2322
2323 if amount.is_zero() {
2324 return Ok(());
2325 }
2326
2327 let hold_reason = hold_reason.into();
2328 let result = if exec_config.map(|c| c.collect_deposit_from_hold.is_some()).unwrap_or(false)
2329 {
2330 T::Currency::release(&hold_reason, from, amount, Precision::Exact)
2331 .and_then(|amount| {
2332 T::Currency::withdraw(
2333 from,
2334 amount,
2335 Precision::Exact,
2336 Preservation::Preserve,
2337 Fortitude::Polite,
2338 )
2339 })
2340 .map(T::FeeInfo::deposit_txfee)
2341 } else {
2342 T::Currency::transfer_on_hold(
2343 &hold_reason,
2344 from,
2345 to,
2346 amount,
2347 Precision::Exact,
2348 Restriction::Free,
2349 Fortitude::Polite,
2350 )
2351 .map(|_| ())
2352 };
2353
2354 result.map_err(|_| {
2355 let available = T::Currency::balance_on_hold(&hold_reason, from);
2356 if available < amount {
2357 log::error!(
2360 target: LOG_TARGET,
2361 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. Not enough deposit: {:?}. This is a bug.",
2362 amount, from, to, available,
2363 );
2364 Error::<T>::StorageRefundNotEnoughFunds.into()
2365 } else {
2366 log::warn!(
2371 target: LOG_TARGET,
2372 "Failed to refund storage deposit {:?} from contract {:?} to origin {:?}. First remove locks (staking, governance) from the contracts account.",
2373 amount, from, to,
2374 );
2375 Error::<T>::StorageRefundLocked.into()
2376 }
2377 })
2378 }
2379
2380 fn has_dust(value: U256) -> bool {
2382 value % U256::from(<T>::NativeToEthRatio::get()) != U256::zero()
2383 }
2384
2385 fn has_balance(value: U256) -> bool {
2387 value >= U256::from(<T>::NativeToEthRatio::get())
2388 }
2389
2390 fn min_balance() -> BalanceOf<T> {
2392 <T::Currency as Inspect<AccountIdOf<T>>>::minimum_balance()
2393 }
2394
2395 fn deposit_event(event: Event<T>) {
2400 <pezframe_system::Pezpallet<T>>::deposit_event(<T as Config>::RuntimeEvent::from(event))
2401 }
2402
2403 fn ensure_eth_signed(origin: OriginFor<T>) -> Result<AccountIdOf<T>, DispatchError> {
2405 match <T as Config>::RuntimeOrigin::from(origin).into() {
2406 Ok(Origin::EthTransaction(signer)) => Ok(signer),
2407 _ => Err(BadOrigin.into()),
2408 }
2409 }
2410
2411 fn ensure_non_contract_if_signed(origin: &OriginFor<T>) -> DispatchResult {
2415 if DebugSettings::bypass_eip_3607::<T>() {
2416 return Ok(());
2417 }
2418 let Some(address) = origin
2419 .as_system_ref()
2420 .and_then(|o| o.as_signed())
2421 .map(<T::AddressMapper as AddressMapper<T>>::to_address)
2422 else {
2423 return Ok(());
2424 };
2425 if exec::is_precompile::<T, ContractBlob<T>>(&address)
2426 || <AccountInfo<T>>::is_contract(&address)
2427 {
2428 log::debug!(
2429 target: crate::LOG_TARGET,
2430 "EIP-3607: reject tx as pre-compile or account exist at {address:?}",
2431 );
2432 Err(DispatchError::BadOrigin)
2433 } else {
2434 Ok(())
2435 }
2436 }
2437}
2438
2439pub const RUNTIME_PALLETS_ADDR: H160 =
2444 H160(hex_literal::hex!("6d6f646c70792f70616464720000000000000000"));
2445
2446environmental!(executing_contract: bool);
2448
2449pezsp_api::decl_runtime_apis! {
2450 #[api_version(1)]
2452 pub trait ReviveApi<AccountId, Balance, Nonce, BlockNumber, Moment> where
2453 AccountId: Codec,
2454 Balance: Codec,
2455 Nonce: Codec,
2456 BlockNumber: Codec,
2457 Moment: Codec,
2458 {
2459 fn eth_block() -> EthBlock;
2463
2464 fn eth_block_hash(number: U256) -> Option<H256>;
2466
2467 fn eth_receipt_data() -> Vec<ReceiptGasInfo>;
2473
2474 fn block_gas_limit() -> U256;
2476
2477 fn balance(address: H160) -> U256;
2479
2480 fn gas_price() -> U256;
2482
2483 fn nonce(address: H160) -> Nonce;
2485
2486 fn call(
2490 origin: AccountId,
2491 dest: H160,
2492 value: Balance,
2493 gas_limit: Option<Weight>,
2494 storage_deposit_limit: Option<Balance>,
2495 input_data: Vec<u8>,
2496 ) -> ContractResult<ExecReturnValue, Balance>;
2497
2498 fn instantiate(
2502 origin: AccountId,
2503 value: Balance,
2504 gas_limit: Option<Weight>,
2505 storage_deposit_limit: Option<Balance>,
2506 code: Code,
2507 data: Vec<u8>,
2508 salt: Option<[u8; 32]>,
2509 ) -> ContractResult<InstantiateReturnValue, Balance>;
2510
2511
2512 fn eth_transact(tx: GenericTransaction) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2517
2518 fn eth_transact_with_config(
2522 tx: GenericTransaction,
2523 config: DryRunConfig<Moment>,
2524 ) -> Result<EthTransactInfo<Balance>, EthTransactError>;
2525
2526 fn upload_code(
2530 origin: AccountId,
2531 code: Vec<u8>,
2532 storage_deposit_limit: Option<Balance>,
2533 ) -> CodeUploadResult<Balance>;
2534
2535 fn get_storage(
2541 address: H160,
2542 key: [u8; 32],
2543 ) -> GetStorageResult;
2544
2545 fn get_storage_var_key(
2551 address: H160,
2552 key: Vec<u8>,
2553 ) -> GetStorageResult;
2554
2555 fn trace_block(
2562 block: Block,
2563 config: TracerType
2564 ) -> Vec<(u32, Trace)>;
2565
2566 fn trace_tx(
2573 block: Block,
2574 tx_index: u32,
2575 config: TracerType
2576 ) -> Option<Trace>;
2577
2578 fn trace_call(tx: GenericTransaction, config: TracerType) -> Result<Trace, EthTransactError>;
2582
2583 fn block_author() -> H160;
2585
2586 fn address(account_id: AccountId) -> H160;
2588
2589 fn account_id(address: H160) -> AccountId;
2591
2592 fn runtime_pallets_address() -> H160;
2594
2595 fn code(address: H160) -> Vec<u8>;
2597
2598 fn new_balance_with_dust(balance: U256) -> Result<(Balance, u32), BalanceConversionError>;
2600 }
2601}
2602
2603#[macro_export]
2617macro_rules! impl_runtime_apis_plus_revive_traits {
2618 ($Runtime: ty, $Revive: ident, $Executive: ty, $EthExtra: ty, $($rest:tt)*) => {
2619
2620 type __ReviveMacroMoment = <<$Runtime as $crate::Config>::Time as $crate::Time>::Moment;
2621
2622 impl $crate::evm::runtime::SetWeightLimit for RuntimeCall {
2623 fn set_weight_limit(&mut self, weight_limit: Weight) -> Weight {
2624 use $crate::pezpallet::Call as ReviveCall;
2625 match self {
2626 Self::$Revive(
2627 ReviveCall::eth_call{ gas_limit, .. } |
2628 ReviveCall::eth_instantiate_with_code{ gas_limit, .. }
2629 ) => {
2630 let old = *gas_limit;
2631 *gas_limit = weight_limit;
2632 old
2633 },
2634 _ => Weight::default(),
2635 }
2636 }
2637 }
2638
2639 impl_runtime_apis! {
2640 $($rest)*
2641
2642
2643 impl pezpallet_revive::ReviveApi<Block, AccountId, Balance, Nonce, BlockNumber, __ReviveMacroMoment> for $Runtime
2644 {
2645 fn eth_block() -> $crate::EthBlock {
2646 $crate::Pezpallet::<Self>::eth_block()
2647 }
2648
2649 fn eth_block_hash(number: $crate::U256) -> Option<$crate::H256> {
2650 $crate::Pezpallet::<Self>::eth_block_hash_from_number(number)
2651 }
2652
2653 fn eth_receipt_data() -> Vec<$crate::ReceiptGasInfo> {
2654 $crate::Pezpallet::<Self>::eth_receipt_data()
2655 }
2656
2657 fn balance(address: $crate::H160) -> $crate::U256 {
2658 $crate::Pezpallet::<Self>::evm_balance(&address)
2659 }
2660
2661 fn block_author() -> $crate::H160 {
2662 $crate::Pezpallet::<Self>::block_author()
2663 }
2664
2665 fn block_gas_limit() -> $crate::U256 {
2666 $crate::Pezpallet::<Self>::evm_block_gas_limit()
2667 }
2668
2669 fn gas_price() -> $crate::U256 {
2670 $crate::Pezpallet::<Self>::evm_base_fee()
2671 }
2672
2673 fn nonce(address: $crate::H160) -> Nonce {
2674 use $crate::AddressMapper;
2675 let account = <Self as $crate::Config>::AddressMapper::to_account_id(&address);
2676 $crate::pezframe_system::Pezpallet::<Self>::account_nonce(account)
2677 }
2678
2679 fn address(account_id: AccountId) -> $crate::H160 {
2680 use $crate::AddressMapper;
2681 <Self as $crate::Config>::AddressMapper::to_address(&account_id)
2682 }
2683
2684 fn eth_transact(
2685 tx: $crate::evm::GenericTransaction,
2686 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2687 use $crate::{
2688 codec::Encode, evm::runtime::EthExtra, pezframe_support::traits::Get,
2689 pezsp_runtime::traits::TransactionExtension,
2690 pezsp_runtime::traits::Block as BlockT
2691 };
2692 $crate::Pezpallet::<Self>::dry_run_eth_transact(tx, Default::default())
2693 }
2694
2695 fn eth_transact_with_config(
2696 tx: $crate::evm::GenericTransaction,
2697 config: $crate::DryRunConfig<__ReviveMacroMoment>,
2698 ) -> Result<$crate::EthTransactInfo<Balance>, $crate::EthTransactError> {
2699 use $crate::{
2700 codec::Encode, evm::runtime::EthExtra, pezframe_support::traits::Get,
2701 pezsp_runtime::traits::TransactionExtension,
2702 pezsp_runtime::traits::Block as BlockT
2703 };
2704 $crate::Pezpallet::<Self>::dry_run_eth_transact(tx, config)
2705 }
2706
2707 fn call(
2708 origin: AccountId,
2709 dest: $crate::H160,
2710 value: Balance,
2711 gas_limit: Option<$crate::Weight>,
2712 storage_deposit_limit: Option<Balance>,
2713 input_data: Vec<u8>,
2714 ) -> $crate::ContractResult<$crate::ExecReturnValue, Balance> {
2715 use $crate::pezframe_support::traits::Get;
2716 let blockweights: $crate::BlockWeights =
2717 <Self as $crate::pezframe_system::Config>::BlockWeights::get();
2718
2719 $crate::Pezpallet::<Self>::prepare_dry_run(&origin);
2720 $crate::Pezpallet::<Self>::bare_call(
2721 <Self as $crate::pezframe_system::Config>::RuntimeOrigin::signed(origin),
2722 dest,
2723 $crate::Pezpallet::<Self>::convert_native_to_evm(value),
2724 gas_limit.unwrap_or(blockweights.max_block),
2725 storage_deposit_limit.unwrap_or(u128::MAX),
2726 input_data,
2727 $crate::ExecConfig::new_bizinikiwi_tx().with_dry_run(Default::default()),
2728 )
2729 }
2730
2731 fn instantiate(
2732 origin: AccountId,
2733 value: Balance,
2734 gas_limit: Option<$crate::Weight>,
2735 storage_deposit_limit: Option<Balance>,
2736 code: $crate::Code,
2737 data: Vec<u8>,
2738 salt: Option<[u8; 32]>,
2739 ) -> $crate::ContractResult<$crate::InstantiateReturnValue, Balance> {
2740 use $crate::pezframe_support::traits::Get;
2741 let blockweights: $crate::BlockWeights =
2742 <Self as $crate::pezframe_system::Config>::BlockWeights::get();
2743
2744 $crate::Pezpallet::<Self>::prepare_dry_run(&origin);
2745 $crate::Pezpallet::<Self>::bare_instantiate(
2746 <Self as $crate::pezframe_system::Config>::RuntimeOrigin::signed(origin),
2747 $crate::Pezpallet::<Self>::convert_native_to_evm(value),
2748 gas_limit.unwrap_or(blockweights.max_block),
2749 storage_deposit_limit.unwrap_or(u128::MAX),
2750 code,
2751 data,
2752 salt,
2753 $crate::ExecConfig::new_bizinikiwi_tx().with_dry_run(Default::default()),
2754 )
2755 }
2756
2757 fn upload_code(
2758 origin: AccountId,
2759 code: Vec<u8>,
2760 storage_deposit_limit: Option<Balance>,
2761 ) -> $crate::CodeUploadResult<Balance> {
2762 let origin =
2763 <Self as $crate::pezframe_system::Config>::RuntimeOrigin::signed(origin);
2764 $crate::Pezpallet::<Self>::bare_upload_code(
2765 origin,
2766 code,
2767 storage_deposit_limit.unwrap_or(u128::MAX),
2768 )
2769 }
2770
2771 fn get_storage_var_key(
2772 address: $crate::H160,
2773 key: Vec<u8>,
2774 ) -> $crate::GetStorageResult {
2775 $crate::Pezpallet::<Self>::get_storage_var_key(address, key)
2776 }
2777
2778 fn get_storage(address: $crate::H160, key: [u8; 32]) -> $crate::GetStorageResult {
2779 $crate::Pezpallet::<Self>::get_storage(address, key)
2780 }
2781
2782 fn trace_block(
2783 block: Block,
2784 tracer_type: $crate::evm::TracerType,
2785 ) -> Vec<(u32, $crate::evm::Trace)> {
2786 use $crate::{pezsp_runtime::traits::Block, tracing::trace};
2787 let mut traces = vec![];
2788 let (header, extrinsics) = block.deconstruct();
2789 <$Executive>::initialize_block(&header);
2790 for (index, ext) in extrinsics.into_iter().enumerate() {
2791 let mut tracer = $crate::Pezpallet::<Self>::evm_tracer(tracer_type.clone());
2792 let t = tracer.as_tracing();
2793 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2794
2795 if let Some(tx_trace) = tracer.collect_trace() {
2796 traces.push((index as u32, tx_trace));
2797 }
2798 }
2799
2800 traces
2801 }
2802
2803 fn trace_tx(
2804 block: Block,
2805 tx_index: u32,
2806 tracer_type: $crate::evm::TracerType,
2807 ) -> Option<$crate::evm::Trace> {
2808 use $crate::{pezsp_runtime::traits::Block, tracing::trace};
2809
2810 let mut tracer = $crate::Pezpallet::<Self>::evm_tracer(tracer_type);
2811 let (header, extrinsics) = block.deconstruct();
2812
2813 <$Executive>::initialize_block(&header);
2814 for (index, ext) in extrinsics.into_iter().enumerate() {
2815 if index as u32 == tx_index {
2816 let t = tracer.as_tracing();
2817 let _ = trace(t, || <$Executive>::apply_extrinsic(ext));
2818 break;
2819 } else {
2820 let _ = <$Executive>::apply_extrinsic(ext);
2821 }
2822 }
2823
2824 tracer.collect_trace()
2825 }
2826
2827 fn trace_call(
2828 tx: $crate::evm::GenericTransaction,
2829 tracer_type: $crate::evm::TracerType,
2830 ) -> Result<$crate::evm::Trace, $crate::EthTransactError> {
2831 use $crate::tracing::trace;
2832 let mut tracer = $crate::Pezpallet::<Self>::evm_tracer(tracer_type.clone());
2833 let t = tracer.as_tracing();
2834
2835 t.watch_address(&tx.from.unwrap_or_default());
2836 t.watch_address(&$crate::Pezpallet::<Self>::block_author());
2837 let result = trace(t, || Self::eth_transact(tx));
2838
2839 if let Some(trace) = tracer.collect_trace() {
2840 Ok(trace)
2841 } else if let Err(err) = result {
2842 Err(err)
2843 } else {
2844 Ok($crate::Pezpallet::<Self>::evm_tracer(tracer_type).empty_trace())
2845 }
2846 }
2847
2848 fn runtime_pallets_address() -> $crate::H160 {
2849 $crate::RUNTIME_PALLETS_ADDR
2850 }
2851
2852 fn code(address: $crate::H160) -> Vec<u8> {
2853 $crate::Pezpallet::<Self>::code(&address)
2854 }
2855
2856 fn account_id(address: $crate::H160) -> AccountId {
2857 use $crate::AddressMapper;
2858 <Self as $crate::Config>::AddressMapper::to_account_id(&address)
2859 }
2860
2861 fn new_balance_with_dust(balance: $crate::U256) -> Result<(Balance, u32), $crate::BalanceConversionError> {
2862 $crate::Pezpallet::<Self>::new_balance_with_dust(balance)
2863 }
2864 }
2865 }
2866 };
2867}