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 parity_scale_codec::EncodeLike;
28
use precompile_utils::prelude::*;
29

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

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

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

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

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

            
77
2
		let contributor: H160 = contributor.into();
78

            
79
2
		let account = Runtime::AddressMapping::into_account_id(contributor);
80

            
81
2
		log::trace!(
82
			target: "crowdloan-rewards-precompile",
83
			"Checking whether {:?} is a contributor",
84
			contributor
85
		);
86

            
87
		// fetch data from pallet
88
2
		let is_contributor: bool =
89
2
			pallet_crowdloan_rewards::Pallet::<Runtime>::accounts_payable(account.into()).is_some();
90

            
91
2
		log::trace!(target: "crowldoan-rewards-precompile", "Result from pallet is {:?}", is_contributor);
92

            
93
2
		Ok(is_contributor)
94
2
	}
95

            
96
	#[precompile::public("rewardInfo(address)")]
97
	#[precompile::public("reward_info(address)")]
98
	#[precompile::view]
99
1
	fn reward_info(
100
1
		handle: &mut impl PrecompileHandle,
101
1
		contributor: Address,
102
1
	) -> EvmResult<(U256, U256)> {
103
		// AccountsPayable: Blake2128(16) + 20 + RewardInfo(16 + 16 + UnBoundedVec<AccountId32(32)>)
104
		// TODO RewardInfo.contributed_relay_addresses is unbounded, we set a safe length of 100.
105
1
		handle.record_db_read::<Runtime>(3268)?;
106

            
107
1
		let contributor: H160 = contributor.into();
108

            
109
1
		let account = Runtime::AddressMapping::into_account_id(contributor);
110

            
111
1
		log::trace!(
112
			target: "crowdloan-rewards-precompile",
113
			"Checking reward info for {:?}",
114
			contributor
115
		);
116

            
117
		// fetch data from pallet
118
1
		let reward_info =
119
1
			pallet_crowdloan_rewards::Pallet::<Runtime>::accounts_payable(account.into());
120

            
121
1
		let (total, claimed): (U256, U256) = if let Some(reward_info) = reward_info {
122
1
			let total_reward: u128 = reward_info
123
1
				.total_reward
124
1
				.try_into()
125
1
				.map_err(|_| RevertReason::value_is_too_large("balance type"))?;
126
1
			let claimed_reward: u128 = reward_info
127
1
				.claimed_reward
128
1
				.try_into()
129
1
				.map_err(|_| RevertReason::value_is_too_large("balance type"))?;
130

            
131
1
			(total_reward.into(), claimed_reward.into())
132
		} else {
133
			(0u128.into(), 0u128.into())
134
		};
135

            
136
1
		log::trace!(
137
			target: "crowldoan-rewards-precompile", "Result from pallet is {:?}  {:?}",
138
			total, claimed
139
		);
140

            
141
1
		Ok((total, claimed))
142
1
	}
143

            
144
	#[precompile::public("claim()")]
145
1
	fn claim(handle: &mut impl PrecompileHandle) -> EvmResult {
146
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
147
1
		let call = pallet_crowdloan_rewards::Call::<Runtime>::claim {};
148

            
149
1
		RuntimeHelper::<Runtime>::try_dispatch(
150
1
			handle,
151
1
			frame_system::RawOrigin::Signed(origin.into()).into(),
152
1
			call,
153
			0,
154
		)?;
155

            
156
1
		Ok(())
157
1
	}
158

            
159
	#[precompile::public("updateRewardAddress(address)")]
160
	#[precompile::public("update_reward_address(address)")]
161
1
	fn update_reward_address(
162
1
		handle: &mut impl PrecompileHandle,
163
1
		new_address: Address,
164
1
	) -> EvmResult {
165
1
		log::trace!(
166
			target: "crowdloan-rewards-precompile",
167
			"In update_reward_address dispatchable wrapper"
168
		);
169

            
170
1
		let new_address: H160 = new_address.into();
171

            
172
1
		let new_reward_account = Runtime::AddressMapping::into_account_id(new_address);
173

            
174
1
		log::trace!(target: "crowdloan-rewards-precompile", "New account is {:?}", new_address);
175

            
176
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
177
1
		let call = pallet_crowdloan_rewards::Call::<Runtime>::update_reward_address {
178
1
			new_reward_account: new_reward_account.into(),
179
1
		};
180

            
181
1
		RuntimeHelper::<Runtime>::try_dispatch(
182
1
			handle,
183
1
			frame_system::RawOrigin::Signed(origin.into()).into(),
184
1
			call,
185
			0,
186
		)?;
187

            
188
1
		Ok(())
189
1
	}
190
}