1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
#![cfg_attr(not(feature = "std"), no_std)]
18

            
19
#[cfg(test)]
20
mod mock;
21
#[cfg(test)]
22
mod tests;
23

            
24
use core::marker::PhantomData;
25
use fp_evm::{ExitError, IsPrecompileResult, PrecompileFailure};
26
use precompile_utils::{
27
	precompile_set::{is_precompile_or_fail, IsActivePrecompile},
28
	prelude::*,
29
};
30
use sp_core::Get;
31

            
32
const DUMMY_CODE: [u8; 5] = [0x60, 0x00, 0x60, 0x00, 0xfd];
33

            
34
pub struct PrecompileRegistry<Runtime>(PhantomData<Runtime>);
35

            
36
97
#[precompile_utils::precompile]
37
impl<Runtime> PrecompileRegistry<Runtime>
38
where
39
	Runtime: pallet_evm::Config,
40
	Runtime::PrecompilesType: IsActivePrecompile,
41
{
42
	#[precompile::public("isPrecompile(address)")]
43
	#[precompile::view]
44
4
	fn is_precompile(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<bool> {
45
4
		// We consider the precompile set is optimized to do at most one storage read.
46
4
		// In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
47
4
		// (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
48
4
		// Storage item: Asset:
49
4
		// Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
50
4
		handle.record_db_read::<Runtime>(175)?;
51
4
		is_precompile_or_fail::<Runtime>(address.0, handle.remaining_gas())
52
4
	}
53

            
54
	#[precompile::public("isActivePrecompile(address)")]
55
	#[precompile::view]
56
4
	fn is_active_precompile(
57
4
		handle: &mut impl PrecompileHandle,
58
4
		address: Address,
59
4
	) -> EvmResult<bool> {
60
4
		// We consider the precompile set is optimized to do at most one storage read.
61
4
		// In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
62
4
		// (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
63
4
		// Storage item: Asset:
64
4
		// Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
65
4
		handle.record_db_read::<Runtime>(175)?;
66
4
		match <Runtime::PrecompilesValue>::get()
67
4
			.is_active_precompile(address.0, handle.remaining_gas())
68
		{
69
4
			IsPrecompileResult::Answer { is_precompile, .. } => Ok(is_precompile),
70
			IsPrecompileResult::OutOfGas => Err(PrecompileFailure::Error {
71
				exit_status: ExitError::OutOfGas,
72
			}),
73
		}
74
4
	}
75

            
76
	#[precompile::public("updateAccountCode(address)")]
77
4
	fn update_account_code(handle: &mut impl PrecompileHandle, address: Address) -> EvmResult<()> {
78
4
		// Prevent touching addresses that are not precompiles.
79
4
		//
80
4
		// We consider the precompile set is optimized to do at most one storage read.
81
4
		// In the case of moonbeam, the storage item that can be read is pallet_asset::Asset
82
4
		// (TODO make it more generic, maybe add a const generic on PrecompileRegistry type)
83
4
		// Storage item: Asset:
84
4
		// Blake2_128(16) + AssetId(16) + AssetDetails((4 * AccountId(20)) + (3 * Balance(16)) + 15)
85
4
		handle.record_db_read::<Runtime>(175)?;
86
4
		if !is_precompile_or_fail::<Runtime>(address.0, handle.remaining_gas())? {
87
2
			return Err(revert("provided address is not a precompile"));
88
2
		}
89
2

            
90
2
		// pallet_evm::create_account read storage item pallet_evm::AccountCodes
91
2
		//
92
2
		// AccountCodes: Blake2128(16) + H160(20) + Vec(5)
93
2
		// We asume an existing precompile can hold at most 5 bytes worth of dummy code.
94
2
		handle.record_db_read::<Runtime>(41)?;
95
2
		pallet_evm::Pallet::<Runtime>::create_account(address.0, DUMMY_CODE.to_vec());
96
2

            
97
2
		Ok(())
98
4
	}
99
}