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
//! Precompile to call pallet-crowdloan-rewards runtime methods via the EVM
18

            
19
#![cfg_attr(not(feature = "std"), no_std)]
20

            
21
use fp_evm::PrecompileHandle;
22
use frame_support::{
23
	dispatch::{GetDispatchInfo, PostDispatchInfo},
24
	traits::Currency,
25
};
26
use pallet_evm::AddressMapping;
27
use precompile_utils::prelude::*;
28

            
29
use sp_core::{H160, U256};
30
use sp_runtime::traits::Dispatchable;
31
use sp_std::{
32
	convert::{TryFrom, TryInto},
33
	fmt::Debug,
34
	marker::PhantomData,
35
};
36

            
37
#[cfg(test)]
38
mod mock;
39
#[cfg(test)]
40
mod tests;
41

            
42
pub type BalanceOf<Runtime> =
43
	<<Runtime as pallet_crowdloan_rewards::Config>::RewardCurrency as Currency<
44
		<Runtime as frame_system::Config>::AccountId,
45
	>>::Balance;
46

            
47
/// A precompile to wrap the functionality from pallet_crowdloan_rewards.
48
pub struct CrowdloanRewardsPrecompile<Runtime>(PhantomData<Runtime>);
49

            
50
313
#[precompile_utils::precompile]
51
impl<Runtime> CrowdloanRewardsPrecompile<Runtime>
52
where
53
	Runtime: pallet_crowdloan_rewards::Config + pallet_evm::Config + frame_system::Config,
54
	BalanceOf<Runtime>: TryFrom<U256> + TryInto<u128> + Debug,
55
	Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
56
	<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
57
	Runtime::RuntimeCall: From<pallet_crowdloan_rewards::Call<Runtime>>,
58
	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
59
{
60
	// The accessors are first.
61
	#[precompile::public("isContributor(address)")]
62
	#[precompile::public("is_contributor(address)")]
63
	#[precompile::view]
64
8
	fn is_contributor(handle: &mut impl PrecompileHandle, contributor: Address) -> EvmResult<bool> {
65
8
		// AccountsPayable: Blake2128(16) + 20 + RewardInfo(16 + 16 + UnBoundedVec<AccountId32(32)>)
66
8
		// TODO RewardInfo.contributed_relay_addresses is unbounded, we set a safe length of 100.
67
8
		handle.record_db_read::<Runtime>(3268)?;
68

            
69
8
		let contributor: H160 = contributor.into();
70
8

            
71
8
		let account = Runtime::AddressMapping::into_account_id(contributor);
72
8

            
73
8
		log::trace!(
74
			target: "crowdloan-rewards-precompile",
75
			"Checking whether {:?} is a contributor",
76
			contributor
77
		);
78

            
79
		// fetch data from pallet
80
8
		let is_contributor: bool =
81
8
			pallet_crowdloan_rewards::Pallet::<Runtime>::accounts_payable(account).is_some();
82
8

            
83
8
		log::trace!(target: "crowldoan-rewards-precompile", "Result from pallet is {:?}", is_contributor);
84

            
85
8
		Ok(is_contributor)
86
8
	}
87

            
88
	#[precompile::public("rewardInfo(address)")]
89
	#[precompile::public("reward_info(address)")]
90
	#[precompile::view]
91
4
	fn reward_info(
92
4
		handle: &mut impl PrecompileHandle,
93
4
		contributor: Address,
94
4
	) -> EvmResult<(U256, U256)> {
95
4
		// AccountsPayable: Blake2128(16) + 20 + RewardInfo(16 + 16 + UnBoundedVec<AccountId32(32)>)
96
4
		// TODO RewardInfo.contributed_relay_addresses is unbounded, we set a safe length of 100.
97
4
		handle.record_db_read::<Runtime>(3268)?;
98

            
99
4
		let contributor: H160 = contributor.into();
100
4

            
101
4
		let account = Runtime::AddressMapping::into_account_id(contributor);
102
4

            
103
4
		log::trace!(
104
			target: "crowdloan-rewards-precompile",
105
			"Checking reward info for {:?}",
106
			contributor
107
		);
108

            
109
		// fetch data from pallet
110
4
		let reward_info = pallet_crowdloan_rewards::Pallet::<Runtime>::accounts_payable(account);
111

            
112
4
		let (total, claimed): (U256, U256) = if let Some(reward_info) = reward_info {
113
4
			let total_reward: u128 = reward_info
114
4
				.total_reward
115
4
				.try_into()
116
4
				.map_err(|_| RevertReason::value_is_too_large("balance type"))?;
117
4
			let claimed_reward: u128 = reward_info
118
4
				.claimed_reward
119
4
				.try_into()
120
4
				.map_err(|_| RevertReason::value_is_too_large("balance type"))?;
121

            
122
4
			(total_reward.into(), claimed_reward.into())
123
		} else {
124
			(0u128.into(), 0u128.into())
125
		};
126

            
127
4
		log::trace!(
128
			target: "crowldoan-rewards-precompile", "Result from pallet is {:?}  {:?}",
129
			total, claimed
130
		);
131

            
132
4
		Ok((total, claimed))
133
4
	}
134

            
135
	#[precompile::public("claim()")]
136
21
	fn claim(handle: &mut impl PrecompileHandle) -> EvmResult {
137
21
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
138
21
		let call = pallet_crowdloan_rewards::Call::<Runtime>::claim {};
139
21

            
140
21
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
141

            
142
21
		Ok(())
143
21
	}
144

            
145
	#[precompile::public("updateRewardAddress(address)")]
146
	#[precompile::public("update_reward_address(address)")]
147
21
	fn update_reward_address(
148
21
		handle: &mut impl PrecompileHandle,
149
21
		new_address: Address,
150
21
	) -> EvmResult {
151
21
		log::trace!(
152
			target: "crowdloan-rewards-precompile",
153
			"In update_reward_address dispatchable wrapper"
154
		);
155

            
156
21
		let new_address: H160 = new_address.into();
157
21

            
158
21
		let new_reward_account = Runtime::AddressMapping::into_account_id(new_address);
159
21

            
160
21
		log::trace!(target: "crowdloan-rewards-precompile", "New account is {:?}", new_address);
161

            
162
21
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
163
21
		let call =
164
21
			pallet_crowdloan_rewards::Call::<Runtime>::update_reward_address { new_reward_account };
165
21

            
166
21
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
167

            
168
21
		Ok(())
169
21
	}
170
}