1
// Copyright 2019-2022 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
344
#[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
{
59
	// The accessors are first.
60
	#[precompile::public("isContributor(address)")]
61
	#[precompile::public("is_contributor(address)")]
62
	#[precompile::view]
63
8
	fn is_contributor(handle: &mut impl PrecompileHandle, contributor: Address) -> EvmResult<bool> {
64
8
		// AccountsPayable: Blake2128(16) + 20 + RewardInfo(16 + 16 + UnBoundedVec<AccountId32(32)>)
65
8
		// TODO RewardInfo.contributed_relay_addresses is unbounded, we set a safe length of 100.
66
8
		handle.record_db_read::<Runtime>(3268)?;
67

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

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

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

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

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

            
84
8
		Ok(is_contributor)
85
8
	}
86

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

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

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

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

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

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

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

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

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

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

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

            
141
23
		Ok(())
142
23
	}
143

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

            
155
23
		let new_address: H160 = new_address.into();
156
23

            
157
23
		let new_reward_account = Runtime::AddressMapping::into_account_id(new_address);
158
23

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

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

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

            
167
23
		Ok(())
168
23
	}
169
}