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 parachain-staking runtime methods via the EVM
18

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

            
21
#[cfg(test)]
22
mod mock;
23
#[cfg(test)]
24
mod tests;
25

            
26
use fp_evm::PrecompileHandle;
27
use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo};
28
use frame_support::sp_runtime::Percent;
29
use frame_support::traits::{Currency, Get};
30
use pallet_evm::AddressMapping;
31
use precompile_utils::prelude::*;
32
use sp_core::{H160, U256};
33
use sp_runtime::traits::Dispatchable;
34
use sp_std::{convert::TryInto, marker::PhantomData, vec::Vec};
35

            
36
type BalanceOf<Runtime> = <<Runtime as pallet_parachain_staking::Config>::Currency as Currency<
37
	<Runtime as frame_system::Config>::AccountId,
38
>>::Balance;
39

            
40
/// A precompile to wrap the functionality from parachain_staking.
41
///
42
/// EXAMPLE USECASE:
43
/// A simple example usecase is a contract that allows donors to donate, and stakes all the funds
44
/// toward one fixed address chosen by the deployer.
45
/// Such a contract could be deployed by a collator candidate, and the deploy address distributed to
46
/// supporters who want to donate toward a perpetual nomination fund.
47
pub struct ParachainStakingPrecompile<Runtime>(PhantomData<Runtime>);
48

            
49
1048
#[precompile_utils::precompile]
50
impl<Runtime> ParachainStakingPrecompile<Runtime>
51
where
52
	Runtime: pallet_parachain_staking::Config + pallet_evm::Config,
53
	Runtime::AccountId: Into<H160>,
54
	Runtime::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
55
	<Runtime::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<Runtime::AccountId>>,
56
	Runtime::RuntimeCall: From<pallet_parachain_staking::Call<Runtime>>,
57
	BalanceOf<Runtime>: TryFrom<U256> + Into<U256> + solidity::Codec,
58
{
59
	// Constants
60
	#[precompile::public("minDelegation()")]
61
	#[precompile::public("min_delegation()")]
62
	#[precompile::view]
63
3
	fn min_delegation(_handle: &mut impl PrecompileHandle) -> EvmResult<u128> {
64
3
		let min_nomination: u128 =
65
3
			<<Runtime as pallet_parachain_staking::Config>::MinDelegation as Get<
66
3
				BalanceOf<Runtime>,
67
3
			>>::get()
68
3
			.try_into()
69
3
			.map_err(|_| revert("Amount is too large for provided balance type"))?;
70

            
71
3
		Ok(min_nomination)
72
3
	}
73

            
74
	// Storage Getters
75
	#[precompile::public("points(uint256)")]
76
	#[precompile::view]
77
2
	fn points(handle: &mut impl PrecompileHandle, round: Convert<U256, u32>) -> EvmResult<u32> {
78
2
		let round = round.converted();
79
2
		// AccountsPayable: Twox64Concat(8) + RoundIndex(4) + RewardPoint(4)
80
2
		handle.record_db_read::<Runtime>(16)?;
81
2
		let points: u32 = pallet_parachain_staking::Pallet::<Runtime>::points(round);
82
2

            
83
2
		Ok(points)
84
2
	}
85

            
86
	#[precompile::public("awardedPoints(uint32,address)")]
87
	#[precompile::view]
88
2
	fn awarded_points(
89
2
		handle: &mut impl PrecompileHandle,
90
2
		round: u32,
91
2
		candidate: Address,
92
2
	) -> EvmResult<u32> {
93
2
		// AccountsPayable: Twox64Concat(8) + RoundIndex(4) + Twox64Concat(8) + AccountId(20)
94
2
		// + RewardPoint(4)
95
2
		handle.record_db_read::<Runtime>(44)?;
96

            
97
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
98
2

            
99
2
		let points = <pallet_parachain_staking::Pallet<Runtime>>::awarded_pts(&round, &candidate);
100
2

            
101
2
		Ok(points)
102
2
	}
103

            
104
	#[precompile::public("candidateCount()")]
105
	#[precompile::public("candidate_count()")]
106
	#[precompile::view]
107
2
	fn candidate_count(handle: &mut impl PrecompileHandle) -> EvmResult<u32> {
108
2
		// CandidatePool: UnBoundedVec(AccountId(20) + Balance(16))
109
2
		// TODO CandidatePool is unbounded, we account for a theoretical 200 pool.
110
2
		handle.record_db_read::<Runtime>(7200)?;
111
		// Fetch info.
112
2
		let candidate_count: u32 = <pallet_parachain_staking::Pallet<Runtime>>::candidate_pool()
113
2
			.0
114
2
			.len() as u32;
115
2

            
116
2
		// Build output.
117
2
		Ok(candidate_count)
118
2
	}
119

            
120
	#[precompile::public("round()")]
121
	#[precompile::view]
122
11
	fn round(handle: &mut impl PrecompileHandle) -> EvmResult<u32> {
123
11
		// Round: RoundInfo(RoundIndex(4) + BlockNumber(4) + 4)
124
11
		handle.record_db_read::<Runtime>(12)?;
125
11
		let round: u32 = <pallet_parachain_staking::Pallet<Runtime>>::round().current;
126
11

            
127
11
		Ok(round)
128
11
	}
129

            
130
	#[precompile::public("candidateDelegationCount(address)")]
131
	#[precompile::public("candidate_delegation_count(address)")]
132
	#[precompile::view]
133
1
	fn candidate_delegation_count(
134
1
		handle: &mut impl PrecompileHandle,
135
1
		candidate: Address,
136
1
	) -> EvmResult<u32> {
137
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
138
1
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
139
1
		handle.record_db_read::<Runtime>(133)?;
140
1
		let result = if let Some(state) =
141
1
			<pallet_parachain_staking::Pallet<Runtime>>::candidate_info(&candidate)
142
		{
143
1
			let candidate_delegation_count: u32 = state.delegation_count;
144
1

            
145
1
			log::trace!(
146
				target: "staking-precompile",
147
				"Result from pallet is {:?}",
148
				candidate_delegation_count
149
			);
150
1
			candidate_delegation_count
151
		} else {
152
			log::trace!(
153
				target: "staking-precompile",
154
				"Candidate {:?} not found, so delegation count is 0",
155
				candidate
156
			);
157
			0u32
158
		};
159

            
160
1
		Ok(result)
161
1
	}
162

            
163
	#[precompile::public("candidateAutoCompoundingDelegationCount(address)")]
164
	#[precompile::view]
165
2
	fn candidate_auto_compounding_delegation_count(
166
2
		handle: &mut impl PrecompileHandle,
167
2
		candidate: Address,
168
2
	) -> EvmResult<u32> {
169
2
		// AutoCompoundingDelegations:
170
2
		// Blake2128(16) + AccountId(20)
171
2
		// + BoundedVec(
172
2
		// 	AutoCompoundConfig * (MaxTopDelegationsPerCandidate + MaxBottomDelegationsPerCandidate)
173
2
		// )
174
2
		handle.record_db_read::<Runtime>(
175
2
			36 + (
176
2
				22 * (<Runtime as pallet_parachain_staking::Config>::MaxTopDelegationsPerCandidate::get()
177
2
				+ <Runtime as pallet_parachain_staking::Config>::MaxBottomDelegationsPerCandidate::get())
178
2
				as usize),
179
2
		)?;
180

            
181
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
182
2

            
183
2
		let count =
184
2
			<pallet_parachain_staking::Pallet<Runtime>>::auto_compounding_delegations(&candidate)
185
2
				.len() as u32;
186
2

            
187
2
		Ok(count)
188
2
	}
189

            
190
	#[precompile::public("delegatorDelegationCount(address)")]
191
	#[precompile::public("delegator_delegation_count(address)")]
192
	#[precompile::view]
193
1
	fn delegator_delegation_count(
194
1
		handle: &mut impl PrecompileHandle,
195
1
		delegator: Address,
196
1
	) -> EvmResult<u32> {
197
1
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
198
1
		// CandidateInfo:
199
1
		// Twox64Concat(8) + AccountId(20) + Delegator(56 + MaxDelegationsPerDelegator)
200
1
		handle.record_db_read::<Runtime>(
201
1
			84 + (<Runtime as pallet_parachain_staking::Config>::MaxDelegationsPerDelegator::get()
202
1
				as usize),
203
1
		)?;
204
1
		let result = if let Some(state) =
205
1
			<pallet_parachain_staking::Pallet<Runtime>>::delegator_state(&delegator)
206
		{
207
1
			let delegator_delegation_count: u32 = state.delegations.0.len() as u32;
208
1

            
209
1
			log::trace!(
210
				target: "staking-precompile",
211
				"Result from pallet is {:?}",
212
				delegator_delegation_count
213
			);
214

            
215
1
			delegator_delegation_count
216
		} else {
217
			log::trace!(
218
				target: "staking-precompile",
219
				"Delegator {:?} not found, so delegation count is 0",
220
				delegator
221
			);
222
			0u32
223
		};
224

            
225
1
		Ok(result)
226
1
	}
227

            
228
	#[precompile::public("selectedCandidates()")]
229
	#[precompile::public("selected_candidates()")]
230
	#[precompile::view]
231
3
	fn selected_candidates(handle: &mut impl PrecompileHandle) -> EvmResult<Vec<Address>> {
232
3
		// TotalSelected
233
3
		handle.record_db_read::<Runtime>(4)?;
234
3
		let total_selected = pallet_parachain_staking::Pallet::<Runtime>::total_selected();
235
3
		// SelectedCandidates: total_selected * AccountId(20)
236
3
		handle.record_db_read::<Runtime>(20 * (total_selected as usize))?;
237
3
		let selected_candidates: Vec<Address> =
238
3
			pallet_parachain_staking::Pallet::<Runtime>::selected_candidates()
239
3
				.into_iter()
240
3
				.map(|address| Address(address.into()))
241
3
				.collect();
242
3

            
243
3
		Ok(selected_candidates)
244
3
	}
245

            
246
	#[precompile::public("delegationAmount(address,address)")]
247
	#[precompile::view]
248
3
	fn delegation_amount(
249
3
		handle: &mut impl PrecompileHandle,
250
3
		delegator: Address,
251
3
		candidate: Address,
252
3
	) -> EvmResult<U256> {
253
3
		// DelegatorState:
254
3
		// Twox64Concat(8) + AccountId(20) + Delegator(56 + MaxDelegationsPerDelegator)
255
3
		handle.record_db_read::<Runtime>(
256
3
			84 + (<Runtime as pallet_parachain_staking::Config>::MaxDelegationsPerDelegator::get()
257
3
				as usize),
258
3
		)?;
259
3
		let (candidate, delegator) = (
260
3
			Runtime::AddressMapping::into_account_id(candidate.0),
261
3
			Runtime::AddressMapping::into_account_id(delegator.0),
262
3
		);
263
3
		let amount = pallet_parachain_staking::Pallet::<Runtime>::delegator_state(&delegator)
264
3
			.and_then(|state| {
265
1
				state
266
1
					.delegations
267
1
					.0
268
1
					.into_iter()
269
1
					.find(|b| b.owner == candidate)
270
3
			})
271
3
			.map_or(
272
3
				U256::zero(),
273
3
				|pallet_parachain_staking::Bond { amount, .. }| amount.into(),
274
3
			);
275
3

            
276
3
		Ok(amount)
277
3
	}
278

            
279
	// Role Verifiers
280
	#[precompile::public("isInTopDelegations(address,address)")]
281
	#[precompile::view]
282
2
	fn is_in_top_delegations(
283
2
		handle: &mut impl PrecompileHandle,
284
2
		delegator: Address,
285
2
		candidate: Address,
286
2
	) -> EvmResult<bool> {
287
2
		let (candidate, delegator) = (
288
2
			Runtime::AddressMapping::into_account_id(candidate.0),
289
2
			Runtime::AddressMapping::into_account_id(delegator.0),
290
2
		);
291
2
		// TopDelegations:
292
2
		// Twox64Concat(8) + AccountId(20) + Balance(16)
293
2
		// + (AccountId(20) + Balance(16) * MaxTopDelegationsPerCandidate)
294
2
		handle.record_db_read::<Runtime>(
295
2
			44 + ((36
296
2
				* <Runtime as pallet_parachain_staking::Config>::MaxTopDelegationsPerCandidate::get(
297
2
				)) as usize),
298
2
		)?;
299
2
		let is_in_top_delegations = pallet_parachain_staking::Pallet::<Runtime>::top_delegations(
300
2
			&candidate,
301
2
		)
302
2
		.map_or(false, |delegations| {
303
2
			delegations
304
2
				.delegations
305
2
				.into_iter()
306
3
				.any(|b| b.owner == delegator)
307
2
		});
308
2

            
309
2
		Ok(is_in_top_delegations)
310
2
	}
311

            
312
	#[precompile::public("isDelegator(address)")]
313
	#[precompile::public("is_delegator(address)")]
314
	#[precompile::view]
315
2
	fn is_delegator(handle: &mut impl PrecompileHandle, delegator: Address) -> EvmResult<bool> {
316
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
317
2
		// DelegatorState:
318
2
		// Twox64Concat(8) + AccountId(20) + Delegator(56 + MaxDelegationsPerDelegator)
319
2
		handle.record_db_read::<Runtime>(
320
2
			84 + (<Runtime as pallet_parachain_staking::Config>::MaxDelegationsPerDelegator::get()
321
2
				as usize),
322
2
		)?;
323
2
		let is_delegator = pallet_parachain_staking::Pallet::<Runtime>::is_delegator(&delegator);
324
2

            
325
2
		Ok(is_delegator)
326
2
	}
327

            
328
	#[precompile::public("isCandidate(address)")]
329
	#[precompile::public("is_candidate(address)")]
330
	#[precompile::view]
331
2
	fn is_candidate(handle: &mut impl PrecompileHandle, candidate: Address) -> EvmResult<bool> {
332
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
333
2

            
334
2
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
335
2
		handle.record_db_read::<Runtime>(133)?;
336
2
		let is_candidate = pallet_parachain_staking::Pallet::<Runtime>::is_candidate(&candidate);
337
2

            
338
2
		Ok(is_candidate)
339
2
	}
340

            
341
	#[precompile::public("isSelectedCandidate(address)")]
342
	#[precompile::public("is_selected_candidate(address)")]
343
	#[precompile::view]
344
2
	fn is_selected_candidate(
345
2
		handle: &mut impl PrecompileHandle,
346
2
		candidate: Address,
347
2
	) -> EvmResult<bool> {
348
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
349
2

            
350
2
		// TotalSelected
351
2
		handle.record_db_read::<Runtime>(4)?;
352
2
		let total_selected = pallet_parachain_staking::Pallet::<Runtime>::total_selected();
353
2
		// SelectedCandidates: total_selected * AccountId(20)
354
2
		handle.record_db_read::<Runtime>(20 * (total_selected as usize))?;
355
2
		let is_selected =
356
2
			pallet_parachain_staking::Pallet::<Runtime>::is_selected_candidate(&candidate);
357
2

            
358
2
		Ok(is_selected)
359
2
	}
360

            
361
	#[precompile::public("delegationRequestIsPending(address,address)")]
362
	#[precompile::public("delegation_request_is_pending(address,address)")]
363
	#[precompile::view]
364
3
	fn delegation_request_is_pending(
365
3
		handle: &mut impl PrecompileHandle,
366
3
		delegator: Address,
367
3
		candidate: Address,
368
3
	) -> EvmResult<bool> {
369
3
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
370
3
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
371
3

            
372
3
		// DelegationScheduledRequests:
373
3
		// Blake2128(16) + AccountId(20)
374
3
		// + Vec(
375
3
		// 	ScheduledRequest(20 + 4 + DelegationAction(18))
376
3
		//	* (MaxTopDelegationsPerCandidate + MaxBottomDelegationsPerCandidate)
377
3
		// )
378
3
		handle.record_db_read::<Runtime>(
379
3
			36 + (
380
3
				42 * (<Runtime as pallet_parachain_staking::Config>::MaxTopDelegationsPerCandidate::get()
381
3
				+ <Runtime as pallet_parachain_staking::Config>::MaxBottomDelegationsPerCandidate::get())
382
3
				as usize),
383
3
		)?;
384

            
385
		// If we are not able to get delegator state, we return false
386
		// Users can call `is_delegator` to determine when this happens
387
3
		let pending = <pallet_parachain_staking::Pallet<Runtime>>::delegation_request_exists(
388
3
			&candidate, &delegator,
389
3
		);
390
3

            
391
3
		Ok(pending)
392
3
	}
393

            
394
	#[precompile::public("candidateExitIsPending(address)")]
395
	#[precompile::public("candidate_exit_is_pending(address)")]
396
	#[precompile::view]
397
3
	fn candidate_exit_is_pending(
398
3
		handle: &mut impl PrecompileHandle,
399
3
		candidate: Address,
400
3
	) -> EvmResult<bool> {
401
3
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
402
3

            
403
3
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
404
3
		handle.record_db_read::<Runtime>(133)?;
405

            
406
		// If we are not able to get delegator state, we return false
407
		// Users can call `is_candidate` to determine when this happens
408
3
		let pending = if let Some(state) =
409
3
			<pallet_parachain_staking::Pallet<Runtime>>::candidate_info(&candidate)
410
		{
411
2
			state.is_leaving()
412
		} else {
413
1
			log::trace!(
414
				target: "staking-precompile",
415
				"Candidate state for {:?} not found, so pending exit is false",
416
				candidate
417
			);
418
1
			false
419
		};
420

            
421
3
		Ok(pending)
422
3
	}
423

            
424
	#[precompile::public("candidateRequestIsPending(address)")]
425
	#[precompile::public("candidate_request_is_pending(address)")]
426
	#[precompile::view]
427
3
	fn candidate_request_is_pending(
428
3
		handle: &mut impl PrecompileHandle,
429
3
		candidate: Address,
430
3
	) -> EvmResult<bool> {
431
3
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
432
3

            
433
3
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
434
3
		handle.record_db_read::<Runtime>(133)?;
435

            
436
		// If we are not able to get candidate metadata, we return false
437
		// Users can call `is_candidate` to determine when this happens
438
3
		let pending = if let Some(state) =
439
3
			<pallet_parachain_staking::Pallet<Runtime>>::candidate_info(&candidate)
440
		{
441
2
			state.request.is_some()
442
		} else {
443
1
			log::trace!(
444
				target: "staking-precompile",
445
				"Candidate metadata for {:?} not found, so pending request is false",
446
				candidate
447
			);
448
1
			false
449
		};
450

            
451
3
		Ok(pending)
452
3
	}
453

            
454
	#[precompile::public("delegationAutoCompound(address,address)")]
455
	#[precompile::view]
456
2
	fn delegation_auto_compound(
457
2
		handle: &mut impl PrecompileHandle,
458
2
		delegator: Address,
459
2
		candidate: Address,
460
2
	) -> EvmResult<u8> {
461
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
462
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
463
2

            
464
2
		// AutoCompoundingDelegations:
465
2
		// Blake2128(16) + AccountId(20)
466
2
		// + BoundedVec(
467
2
		// 	AutoCompoundConfig * (MaxTopDelegationsPerCandidate + MaxBottomDelegationsPerCandidate)
468
2
		// )
469
2
		handle.record_db_read::<Runtime>(
470
2
			36 + (
471
2
				22 * (<Runtime as pallet_parachain_staking::Config>::MaxTopDelegationsPerCandidate::get()
472
2
				+ <Runtime as pallet_parachain_staking::Config>::MaxBottomDelegationsPerCandidate::get())
473
2
				as usize),
474
2
		)?;
475

            
476
2
		let value = <pallet_parachain_staking::Pallet<Runtime>>::delegation_auto_compound(
477
2
			&candidate, &delegator,
478
2
		);
479
2

            
480
2
		Ok(value.deconstruct())
481
2
	}
482

            
483
	// Runtime Methods (dispatchables)
484

            
485
	#[precompile::public("joinCandidates(uint256,uint256)")]
486
	#[precompile::public("join_candidates(uint256,uint256)")]
487
1
	fn join_candidates(
488
1
		handle: &mut impl PrecompileHandle,
489
1
		amount: U256,
490
1
		candidate_count: Convert<U256, u32>,
491
1
	) -> EvmResult {
492
1
		let amount = Self::u256_to_amount(amount).in_field("amount")?;
493
1
		let candidate_count = candidate_count.converted();
494
1

            
495
1
		// Build call with origin.
496
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
497
1
		let call = pallet_parachain_staking::Call::<Runtime>::join_candidates {
498
1
			bond: amount,
499
1
			candidate_count,
500
1
		};
501
1

            
502
1
		// Dispatch call (if enough gas).
503
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
504

            
505
1
		Ok(())
506
1
	}
507

            
508
	#[precompile::public("scheduleLeaveCandidates(uint256)")]
509
	#[precompile::public("schedule_leave_candidates(uint256)")]
510
2
	fn schedule_leave_candidates(
511
2
		handle: &mut impl PrecompileHandle,
512
2
		candidate_count: Convert<U256, u32>,
513
2
	) -> EvmResult {
514
2
		let candidate_count = candidate_count.converted();
515
2

            
516
2
		// Build call with origin.
517
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
518
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_leave_candidates {
519
2
			candidate_count,
520
2
		};
521
2

            
522
2
		// Dispatch call (if enough gas).
523
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
524

            
525
2
		Ok(())
526
2
	}
527

            
528
	#[precompile::public("executeLeaveCandidates(address,uint256)")]
529
	#[precompile::public("execute_leave_candidates(address,uint256)")]
530
1
	fn execute_leave_candidates(
531
1
		handle: &mut impl PrecompileHandle,
532
1
		candidate: Address,
533
1
		candidate_count: Convert<U256, u32>,
534
1
	) -> EvmResult {
535
1
		let candidate_count = candidate_count.converted();
536
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
537
1

            
538
1
		// Build call with origin.
539
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
540
1
		let call = pallet_parachain_staking::Call::<Runtime>::execute_leave_candidates {
541
1
			candidate,
542
1
			candidate_delegation_count: candidate_count,
543
1
		};
544
1

            
545
1
		// Dispatch call (if enough gas).
546
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
547

            
548
1
		Ok(())
549
1
	}
550

            
551
	#[precompile::public("cancelLeaveCandidates(uint256)")]
552
	#[precompile::public("cancel_leave_candidates(uint256)")]
553
1
	fn cancel_leave_candidates(
554
1
		handle: &mut impl PrecompileHandle,
555
1
		candidate_count: Convert<U256, u32>,
556
1
	) -> EvmResult {
557
1
		let candidate_count = candidate_count.converted();
558
1

            
559
1
		// Build call with origin.
560
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
561
1
		let call =
562
1
			pallet_parachain_staking::Call::<Runtime>::cancel_leave_candidates { candidate_count };
563
1

            
564
1
		// Dispatch call (if enough gas).
565
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
566

            
567
1
		Ok(())
568
1
	}
569

            
570
	#[precompile::public("goOffline()")]
571
	#[precompile::public("go_offline()")]
572
1
	fn go_offline(handle: &mut impl PrecompileHandle) -> EvmResult {
573
1
		// Build call with origin.
574
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
575
1
		let call = pallet_parachain_staking::Call::<Runtime>::go_offline {};
576
1

            
577
1
		// Dispatch call (if enough gas).
578
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
579

            
580
1
		Ok(())
581
1
	}
582

            
583
	#[precompile::public("goOnline()")]
584
	#[precompile::public("go_online()")]
585
1
	fn go_online(handle: &mut impl PrecompileHandle) -> EvmResult {
586
1
		// Build call with origin.
587
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
588
1
		let call = pallet_parachain_staking::Call::<Runtime>::go_online {};
589
1

            
590
1
		// Dispatch call (if enough gas).
591
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
592

            
593
1
		Ok(())
594
1
	}
595

            
596
	#[precompile::public("candidateBondMore(uint256)")]
597
	#[precompile::public("candidate_bond_more(uint256)")]
598
1
	fn candidate_bond_more(handle: &mut impl PrecompileHandle, more: U256) -> EvmResult {
599
1
		let more = Self::u256_to_amount(more).in_field("more")?;
600

            
601
		// Build call with origin.
602
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
603
1
		let call = pallet_parachain_staking::Call::<Runtime>::candidate_bond_more { more };
604
1

            
605
1
		// Dispatch call (if enough gas).
606
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
607

            
608
1
		Ok(())
609
1
	}
610

            
611
	#[precompile::public("scheduleCandidateBondLess(uint256)")]
612
	#[precompile::public("schedule_candidate_bond_less(uint256)")]
613
2
	fn schedule_candidate_bond_less(handle: &mut impl PrecompileHandle, less: U256) -> EvmResult {
614
2
		let less = Self::u256_to_amount(less).in_field("less")?;
615

            
616
		// Build call with origin.
617
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
618
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_candidate_bond_less { less };
619
2

            
620
2
		// Dispatch call (if enough gas).
621
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
622

            
623
2
		Ok(())
624
2
	}
625

            
626
	#[precompile::public("executeCandidateBondLess(address)")]
627
	#[precompile::public("execute_candidate_bond_less(address)")]
628
1
	fn execute_candidate_bond_less(
629
1
		handle: &mut impl PrecompileHandle,
630
1
		candidate: Address,
631
1
	) -> EvmResult {
632
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
633
1

            
634
1
		// Build call with origin.
635
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
636
1
		let call =
637
1
			pallet_parachain_staking::Call::<Runtime>::execute_candidate_bond_less { candidate };
638
1

            
639
1
		// Dispatch call (if enough gas).
640
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
641

            
642
1
		Ok(())
643
1
	}
644

            
645
	#[precompile::public("cancelCandidateBondLess()")]
646
	#[precompile::public("cancel_candidate_bond_less()")]
647
1
	fn cancel_candidate_bond_less(handle: &mut impl PrecompileHandle) -> EvmResult {
648
1
		// Build call with origin.
649
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
650
1
		let call = pallet_parachain_staking::Call::<Runtime>::cancel_candidate_bond_less {};
651
1

            
652
1
		// Dispatch call (if enough gas).
653
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
654

            
655
1
		Ok(())
656
1
	}
657

            
658
	#[precompile::public("delegate(address,uint256,uint256,uint256)")]
659
1
	fn delegate(
660
1
		handle: &mut impl PrecompileHandle,
661
1
		candidate: Address,
662
1
		amount: U256,
663
1
		candidate_delegation_count: Convert<U256, u32>,
664
1
		delegator_delegation_count: Convert<U256, u32>,
665
1
	) -> EvmResult {
666
1
		let amount = Self::u256_to_amount(amount).in_field("amount")?;
667
1
		let candidate_delegation_count = candidate_delegation_count.converted();
668
1
		let delegator_delegation_count = delegator_delegation_count.converted();
669
1

            
670
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
671
1

            
672
1
		// Build call with origin.
673
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
674
1
		let call = pallet_parachain_staking::Call::<Runtime>::delegate {
675
1
			candidate,
676
1
			amount,
677
1
			candidate_delegation_count,
678
1
			delegation_count: delegator_delegation_count,
679
1
		};
680
1

            
681
1
		// Dispatch call (if enough gas).
682
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
683

            
684
1
		Ok(())
685
1
	}
686

            
687
	#[precompile::public("delegateWithAutoCompound(address,uint256,uint8,uint256,uint256,uint256)")]
688
5
	fn delegate_with_auto_compound(
689
5
		handle: &mut impl PrecompileHandle,
690
5
		candidate: Address,
691
5
		amount: U256,
692
5
		auto_compound: u8,
693
5
		candidate_delegation_count: Convert<U256, u32>,
694
5
		candidate_auto_compounding_delegation_count: Convert<U256, u32>,
695
5
		delegator_delegation_count: Convert<U256, u32>,
696
5
	) -> EvmResult {
697
5
		if auto_compound > 100 {
698
2
			return Err(
699
2
				RevertReason::custom("Must be an integer between 0 and 100 included")
700
2
					.in_field("auto_compound")
701
2
					.into(),
702
2
			);
703
3
		}
704

            
705
3
		let amount = Self::u256_to_amount(amount).in_field("amount")?;
706
3
		let auto_compound = Percent::from_percent(auto_compound);
707
3
		let candidate_delegation_count = candidate_delegation_count.converted();
708
3
		let candidate_auto_compounding_delegation_count =
709
3
			candidate_auto_compounding_delegation_count.converted();
710
3
		let delegator_delegation_count = delegator_delegation_count.converted();
711
3

            
712
3
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
713
3

            
714
3
		// Build call with origin.
715
3
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
716
3
		let call = pallet_parachain_staking::Call::<Runtime>::delegate_with_auto_compound {
717
3
			candidate,
718
3
			amount,
719
3
			auto_compound,
720
3
			candidate_delegation_count,
721
3
			candidate_auto_compounding_delegation_count,
722
3
			delegation_count: delegator_delegation_count,
723
3
		};
724
3

            
725
3
		// Dispatch call (if enough gas).
726
3
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
727

            
728
3
		Ok(())
729
5
	}
730

            
731
	#[precompile::public("scheduleRevokeDelegation(address)")]
732
	#[precompile::public("schedule_revoke_delegation(address)")]
733
2
	fn schedule_revoke_delegation(
734
2
		handle: &mut impl PrecompileHandle,
735
2
		candidate: Address,
736
2
	) -> EvmResult {
737
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
738
2

            
739
2
		// Build call with origin.
740
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
741
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_revoke_delegation {
742
2
			collator: candidate,
743
2
		};
744
2

            
745
2
		// Dispatch call (if enough gas).
746
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
747

            
748
2
		Ok(())
749
2
	}
750

            
751
	#[precompile::public("delegatorBondMore(address,uint256)")]
752
	#[precompile::public("delegator_bond_more(address,uint256)")]
753
1
	fn delegator_bond_more(
754
1
		handle: &mut impl PrecompileHandle,
755
1
		candidate: Address,
756
1
		more: U256,
757
1
	) -> EvmResult {
758
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
759
1
		let more = Self::u256_to_amount(more).in_field("more")?;
760

            
761
		// Build call with origin.
762
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
763
1
		let call =
764
1
			pallet_parachain_staking::Call::<Runtime>::delegator_bond_more { candidate, more };
765
1

            
766
1
		// Dispatch call (if enough gas).
767
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
768

            
769
1
		Ok(())
770
1
	}
771

            
772
	#[precompile::public("scheduleDelegatorBondLess(address,uint256)")]
773
	#[precompile::public("schedule_delegator_bond_less(address,uint256)")]
774
1
	fn schedule_delegator_bond_less(
775
1
		handle: &mut impl PrecompileHandle,
776
1
		candidate: Address,
777
1
		less: U256,
778
1
	) -> EvmResult {
779
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
780
1
		let less = Self::u256_to_amount(less).in_field("less")?;
781

            
782
		// Build call with origin.
783
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
784
1
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_delegator_bond_less {
785
1
			candidate,
786
1
			less,
787
1
		};
788
1

            
789
1
		// Dispatch call (if enough gas).
790
1
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
791

            
792
1
		Ok(())
793
1
	}
794

            
795
	#[precompile::public("executeDelegationRequest(address,address)")]
796
	#[precompile::public("execute_delegation_request(address,address)")]
797
2
	fn execute_delegation_request(
798
2
		handle: &mut impl PrecompileHandle,
799
2
		delegator: Address,
800
2
		candidate: Address,
801
2
	) -> EvmResult {
802
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
803
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
804
2

            
805
2
		// Build call with origin.
806
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
807
2
		let call = pallet_parachain_staking::Call::<Runtime>::execute_delegation_request {
808
2
			delegator,
809
2
			candidate,
810
2
		};
811
2

            
812
2
		// Dispatch call (if enough gas).
813
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
814

            
815
2
		Ok(())
816
2
	}
817

            
818
	#[precompile::public("cancelDelegationRequest(address)")]
819
	#[precompile::public("cancel_delegation_request(address)")]
820
2
	fn cancel_delegation_request(
821
2
		handle: &mut impl PrecompileHandle,
822
2
		candidate: Address,
823
2
	) -> EvmResult {
824
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
825
2

            
826
2
		// Build call with origin.
827
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
828
2
		let call =
829
2
			pallet_parachain_staking::Call::<Runtime>::cancel_delegation_request { candidate };
830
2

            
831
2
		// Dispatch call (if enough gas).
832
2
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
833

            
834
2
		Ok(())
835
2
	}
836

            
837
	#[precompile::public("setAutoCompound(address,uint8,uint256,uint256)")]
838
6
	fn set_auto_compound(
839
6
		handle: &mut impl PrecompileHandle,
840
6
		candidate: Address,
841
6
		value: u8,
842
6
		candidate_auto_compounding_delegation_count: Convert<U256, u32>,
843
6
		delegator_delegation_count: Convert<U256, u32>,
844
6
	) -> EvmResult {
845
6
		if value > 100 {
846
2
			return Err(
847
2
				RevertReason::custom("Must be an integer between 0 and 100 included")
848
2
					.in_field("value")
849
2
					.into(),
850
2
			);
851
4
		}
852
4

            
853
4
		let value = Percent::from_percent(value);
854
4
		let candidate_auto_compounding_delegation_count_hint =
855
4
			candidate_auto_compounding_delegation_count.converted();
856
4
		let delegation_count_hint = delegator_delegation_count.converted();
857
4

            
858
4
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
859
4

            
860
4
		// Build call with origin.
861
4
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
862
4
		let call = pallet_parachain_staking::Call::<Runtime>::set_auto_compound {
863
4
			candidate,
864
4
			value,
865
4
			candidate_auto_compounding_delegation_count_hint,
866
4
			delegation_count_hint,
867
4
		};
868
4

            
869
4
		// Dispatch call (if enough gas).
870
4
		RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call, 0)?;
871

            
872
3
		Ok(())
873
6
	}
874

            
875
	#[precompile::public("getDelegatorTotalStaked(address)")]
876
	#[precompile::view]
877
2
	fn get_delegator_total_staked(
878
2
		handle: &mut impl PrecompileHandle,
879
2
		delegator: Address,
880
2
	) -> EvmResult<U256> {
881
2
		// DelegatorState:
882
2
		// Twox64Concat(8) + AccountId(20) + Delegator(56 + MaxDelegationsPerDelegator)
883
2
		handle.record_db_read::<Runtime>(
884
2
			84 + (<Runtime as pallet_parachain_staking::Config>::MaxDelegationsPerDelegator::get()
885
2
				as usize),
886
2
		)?;
887

            
888
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
889
2

            
890
2
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::delegator_state(&delegator)
891
2
			.map(|state| state.total)
892
2
			.unwrap_or_default();
893
2

            
894
2
		Ok(amount.into())
895
2
	}
896

            
897
	#[precompile::public("getCandidateTotalCounted(address)")]
898
	#[precompile::view]
899
1
	fn get_candidate_total_counted(
900
1
		handle: &mut impl PrecompileHandle,
901
1
		candidate: Address,
902
1
	) -> EvmResult<U256> {
903
1
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
904
1
		handle.record_db_read::<Runtime>(133)?;
905

            
906
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
907
1

            
908
1
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::candidate_info(&candidate)
909
1
			.map(|state| state.total_counted)
910
1
			.unwrap_or_default();
911
1

            
912
1
		Ok(amount.into())
913
1
	}
914

            
915
10
	fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime>> {
916
10
		value
917
10
			.try_into()
918
10
			.map_err(|_| RevertReason::value_is_too_large("balance type").into())
919
10
	}
920
}