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 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::{fungible::Inspect, 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 Inspect<
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
116
#[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: From<pallet_parachain_staking::Call<Runtime>>,
56
	BalanceOf<Runtime>: TryFrom<U256> + Into<U256> + solidity::Codec,
57
	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
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
		// 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

            
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
		// AccountsPayable: Twox64Concat(8) + RoundIndex(4) + Twox64Concat(8) + AccountId(20)
94
		// + RewardPoint(4)
95
2
		handle.record_db_read::<Runtime>(44)?;
96

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

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

            
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
		// CandidatePool: UnBoundedVec(AccountId(20) + Balance(16))
109
		// 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

            
116
		// 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
		// 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

            
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
		// 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

            
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
		// AutoCompoundingDelegations:
170
		// Blake2128(16) + AccountId(20)
171
		// + BoundedVec(
172
		// 	AutoCompoundConfig * (MaxTopDelegationsPerCandidate + MaxBottomDelegationsPerCandidate)
173
		// )
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
		)?;
180

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

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

            
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
		// CandidateInfo:
199
		// 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
		)?;
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

            
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
		// TotalSelected
233
3
		handle.record_db_read::<Runtime>(4)?;
234
3
		let total_selected = pallet_parachain_staking::Pallet::<Runtime>::total_selected();
235
		// 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

            
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
		// DelegatorState:
254
		// 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
		)?;
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
1
			})
271
3
			.map_or(
272
3
				U256::zero(),
273
1
				|pallet_parachain_staking::Bond { amount, .. }| amount.into(),
274
			);
275

            
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
		// TopDelegations:
292
		// Twox64Concat(8) + AccountId(20) + Balance(16)
293
		// + (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
		)?;
299
2
		let is_in_top_delegations = pallet_parachain_staking::Pallet::<Runtime>::top_delegations(
300
2
			&candidate,
301
		)
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

            
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
		// DelegatorState:
318
		// 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
		)?;
323
2
		let is_delegator = pallet_parachain_staking::Pallet::<Runtime>::is_delegator(&delegator);
324

            
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

            
334
		// 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

            
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

            
350
		// TotalSelected
351
2
		handle.record_db_read::<Runtime>(4)?;
352
2
		let total_selected = pallet_parachain_staking::Pallet::<Runtime>::total_selected();
353
		// 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

            
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

            
372
		// DelegationScheduledRequests:
373
		// Blake2128(16) + AccountId(20)
374
		// + Vec(
375
		// 	ScheduledRequest(20 + 4 + DelegationAction(18))
376
		//	* (MaxTopDelegationsPerCandidate + MaxBottomDelegationsPerCandidate)
377
		// )
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
		)?;
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
		);
390

            
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

            
403
		// 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

            
433
		// 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

            
464
		// AutoCompoundingDelegations:
465
		// Blake2128(16) + AccountId(20)
466
		// + BoundedVec(
467
		// 	AutoCompoundConfig * (MaxTopDelegationsPerCandidate + MaxBottomDelegationsPerCandidate)
468
		// )
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
		)?;
475

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

            
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

            
495
		// 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

            
502
		// Dispatch call (if enough gas).
503
1
		RuntimeHelper::<Runtime>::try_dispatch(
504
1
			handle,
505
1
			frame_system::RawOrigin::Signed(origin).into(),
506
1
			call,
507
			0,
508
		)?;
509

            
510
1
		Ok(())
511
1
	}
512

            
513
	#[precompile::public("scheduleLeaveCandidates(uint256)")]
514
	#[precompile::public("schedule_leave_candidates(uint256)")]
515
2
	fn schedule_leave_candidates(
516
2
		handle: &mut impl PrecompileHandle,
517
2
		candidate_count: Convert<U256, u32>,
518
2
	) -> EvmResult {
519
2
		let candidate_count = candidate_count.converted();
520

            
521
		// Build call with origin.
522
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
523
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_leave_candidates {
524
2
			candidate_count,
525
2
		};
526

            
527
		// Dispatch call (if enough gas).
528
2
		RuntimeHelper::<Runtime>::try_dispatch(
529
2
			handle,
530
2
			frame_system::RawOrigin::Signed(origin).into(),
531
2
			call,
532
			0,
533
		)?;
534

            
535
2
		Ok(())
536
2
	}
537

            
538
	#[precompile::public("executeLeaveCandidates(address,uint256)")]
539
	#[precompile::public("execute_leave_candidates(address,uint256)")]
540
1
	fn execute_leave_candidates(
541
1
		handle: &mut impl PrecompileHandle,
542
1
		candidate: Address,
543
1
		candidate_count: Convert<U256, u32>,
544
1
	) -> EvmResult {
545
1
		let candidate_count = candidate_count.converted();
546
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
547

            
548
		// Build call with origin.
549
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
550
1
		let call = pallet_parachain_staking::Call::<Runtime>::execute_leave_candidates {
551
1
			candidate,
552
1
			candidate_delegation_count: candidate_count,
553
1
		};
554

            
555
		// Dispatch call (if enough gas).
556
1
		RuntimeHelper::<Runtime>::try_dispatch(
557
1
			handle,
558
1
			frame_system::RawOrigin::Signed(origin).into(),
559
1
			call,
560
			0,
561
		)?;
562

            
563
1
		Ok(())
564
1
	}
565

            
566
	#[precompile::public("cancelLeaveCandidates(uint256)")]
567
	#[precompile::public("cancel_leave_candidates(uint256)")]
568
1
	fn cancel_leave_candidates(
569
1
		handle: &mut impl PrecompileHandle,
570
1
		candidate_count: Convert<U256, u32>,
571
1
	) -> EvmResult {
572
1
		let candidate_count = candidate_count.converted();
573

            
574
		// Build call with origin.
575
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
576
1
		let call =
577
1
			pallet_parachain_staking::Call::<Runtime>::cancel_leave_candidates { candidate_count };
578

            
579
		// Dispatch call (if enough gas).
580
1
		RuntimeHelper::<Runtime>::try_dispatch(
581
1
			handle,
582
1
			frame_system::RawOrigin::Signed(origin).into(),
583
1
			call,
584
			0,
585
		)?;
586

            
587
1
		Ok(())
588
1
	}
589

            
590
	#[precompile::public("goOffline()")]
591
	#[precompile::public("go_offline()")]
592
1
	fn go_offline(handle: &mut impl PrecompileHandle) -> EvmResult {
593
		// Build call with origin.
594
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
595
1
		let call = pallet_parachain_staking::Call::<Runtime>::go_offline {};
596

            
597
		// Dispatch call (if enough gas).
598
1
		RuntimeHelper::<Runtime>::try_dispatch(
599
1
			handle,
600
1
			frame_system::RawOrigin::Signed(origin).into(),
601
1
			call,
602
			0,
603
		)?;
604

            
605
1
		Ok(())
606
1
	}
607

            
608
	#[precompile::public("goOnline()")]
609
	#[precompile::public("go_online()")]
610
1
	fn go_online(handle: &mut impl PrecompileHandle) -> EvmResult {
611
		// Build call with origin.
612
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
613
1
		let call = pallet_parachain_staking::Call::<Runtime>::go_online {};
614

            
615
		// Dispatch call (if enough gas).
616
1
		RuntimeHelper::<Runtime>::try_dispatch(
617
1
			handle,
618
1
			frame_system::RawOrigin::Signed(origin).into(),
619
1
			call,
620
			0,
621
		)?;
622

            
623
1
		Ok(())
624
1
	}
625

            
626
	#[precompile::public("candidateBondMore(uint256)")]
627
	#[precompile::public("candidate_bond_more(uint256)")]
628
1
	fn candidate_bond_more(handle: &mut impl PrecompileHandle, more: U256) -> EvmResult {
629
1
		let more = Self::u256_to_amount(more).in_field("more")?;
630

            
631
		// Build call with origin.
632
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
633
1
		let call = pallet_parachain_staking::Call::<Runtime>::candidate_bond_more { more };
634

            
635
		// Dispatch call (if enough gas).
636
1
		RuntimeHelper::<Runtime>::try_dispatch(
637
1
			handle,
638
1
			frame_system::RawOrigin::Signed(origin).into(),
639
1
			call,
640
			0,
641
		)?;
642

            
643
1
		Ok(())
644
1
	}
645

            
646
	#[precompile::public("scheduleCandidateBondLess(uint256)")]
647
	#[precompile::public("schedule_candidate_bond_less(uint256)")]
648
2
	fn schedule_candidate_bond_less(handle: &mut impl PrecompileHandle, less: U256) -> EvmResult {
649
2
		let less = Self::u256_to_amount(less).in_field("less")?;
650

            
651
		// Build call with origin.
652
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
653
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_candidate_bond_less { less };
654

            
655
		// Dispatch call (if enough gas).
656
2
		RuntimeHelper::<Runtime>::try_dispatch(
657
2
			handle,
658
2
			frame_system::RawOrigin::Signed(origin).into(),
659
2
			call,
660
			0,
661
		)?;
662

            
663
2
		Ok(())
664
2
	}
665

            
666
	#[precompile::public("executeCandidateBondLess(address)")]
667
	#[precompile::public("execute_candidate_bond_less(address)")]
668
1
	fn execute_candidate_bond_less(
669
1
		handle: &mut impl PrecompileHandle,
670
1
		candidate: Address,
671
1
	) -> EvmResult {
672
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
673

            
674
		// Build call with origin.
675
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
676
1
		let call =
677
1
			pallet_parachain_staking::Call::<Runtime>::execute_candidate_bond_less { candidate };
678

            
679
		// Dispatch call (if enough gas).
680
1
		RuntimeHelper::<Runtime>::try_dispatch(
681
1
			handle,
682
1
			frame_system::RawOrigin::Signed(origin).into(),
683
1
			call,
684
			0,
685
		)?;
686

            
687
1
		Ok(())
688
1
	}
689

            
690
	#[precompile::public("cancelCandidateBondLess()")]
691
	#[precompile::public("cancel_candidate_bond_less()")]
692
1
	fn cancel_candidate_bond_less(handle: &mut impl PrecompileHandle) -> EvmResult {
693
		// Build call with origin.
694
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
695
1
		let call = pallet_parachain_staking::Call::<Runtime>::cancel_candidate_bond_less {};
696

            
697
		// Dispatch call (if enough gas).
698
1
		RuntimeHelper::<Runtime>::try_dispatch(
699
1
			handle,
700
1
			frame_system::RawOrigin::Signed(origin).into(),
701
1
			call,
702
			0,
703
		)?;
704

            
705
1
		Ok(())
706
1
	}
707

            
708
	#[precompile::public("delegateWithAutoCompound(address,uint256,uint8,uint256,uint256,uint256)")]
709
6
	fn delegate_with_auto_compound(
710
6
		handle: &mut impl PrecompileHandle,
711
6
		candidate: Address,
712
6
		amount: U256,
713
6
		auto_compound: u8,
714
6
		candidate_delegation_count: Convert<U256, u32>,
715
6
		candidate_auto_compounding_delegation_count: Convert<U256, u32>,
716
6
		delegator_delegation_count: Convert<U256, u32>,
717
6
	) -> EvmResult {
718
6
		if auto_compound > 100 {
719
2
			return Err(
720
2
				RevertReason::custom("Must be an integer between 0 and 100 included")
721
2
					.in_field("auto_compound")
722
2
					.into(),
723
2
			);
724
4
		}
725

            
726
4
		let amount = Self::u256_to_amount(amount).in_field("amount")?;
727
4
		let auto_compound = Percent::from_percent(auto_compound);
728
4
		let candidate_delegation_count = candidate_delegation_count.converted();
729
4
		let candidate_auto_compounding_delegation_count =
730
4
			candidate_auto_compounding_delegation_count.converted();
731
4
		let delegator_delegation_count = delegator_delegation_count.converted();
732

            
733
4
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
734

            
735
		// Build call with origin.
736
4
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
737
4
		let call = pallet_parachain_staking::Call::<Runtime>::delegate_with_auto_compound {
738
4
			candidate,
739
4
			amount,
740
4
			auto_compound,
741
4
			candidate_delegation_count,
742
4
			candidate_auto_compounding_delegation_count,
743
4
			delegation_count: delegator_delegation_count,
744
4
		};
745

            
746
		// Dispatch call (if enough gas).
747
4
		RuntimeHelper::<Runtime>::try_dispatch(
748
4
			handle,
749
4
			frame_system::RawOrigin::Signed(origin).into(),
750
4
			call,
751
			0,
752
		)?;
753

            
754
4
		Ok(())
755
6
	}
756

            
757
	#[precompile::public("scheduleRevokeDelegation(address)")]
758
	#[precompile::public("schedule_revoke_delegation(address)")]
759
2
	fn schedule_revoke_delegation(
760
2
		handle: &mut impl PrecompileHandle,
761
2
		candidate: Address,
762
2
	) -> EvmResult {
763
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
764

            
765
		// Build call with origin.
766
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
767
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_revoke_delegation {
768
2
			collator: candidate,
769
2
		};
770

            
771
		// Dispatch call (if enough gas).
772
2
		RuntimeHelper::<Runtime>::try_dispatch(
773
2
			handle,
774
2
			frame_system::RawOrigin::Signed(origin).into(),
775
2
			call,
776
			0,
777
		)?;
778

            
779
2
		Ok(())
780
2
	}
781

            
782
	#[precompile::public("delegatorBondMore(address,uint256)")]
783
	#[precompile::public("delegator_bond_more(address,uint256)")]
784
1
	fn delegator_bond_more(
785
1
		handle: &mut impl PrecompileHandle,
786
1
		candidate: Address,
787
1
		more: U256,
788
1
	) -> EvmResult {
789
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
790
1
		let more = Self::u256_to_amount(more).in_field("more")?;
791

            
792
		// Build call with origin.
793
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
794
1
		let call =
795
1
			pallet_parachain_staking::Call::<Runtime>::delegator_bond_more { candidate, more };
796

            
797
		// Dispatch call (if enough gas).
798
1
		RuntimeHelper::<Runtime>::try_dispatch(
799
1
			handle,
800
1
			frame_system::RawOrigin::Signed(origin).into(),
801
1
			call,
802
			0,
803
		)?;
804

            
805
1
		Ok(())
806
1
	}
807

            
808
	#[precompile::public("scheduleDelegatorBondLess(address,uint256)")]
809
	#[precompile::public("schedule_delegator_bond_less(address,uint256)")]
810
1
	fn schedule_delegator_bond_less(
811
1
		handle: &mut impl PrecompileHandle,
812
1
		candidate: Address,
813
1
		less: U256,
814
1
	) -> EvmResult {
815
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
816
1
		let less = Self::u256_to_amount(less).in_field("less")?;
817

            
818
		// Build call with origin.
819
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
820
1
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_delegator_bond_less {
821
1
			candidate,
822
1
			less,
823
1
		};
824

            
825
		// Dispatch call (if enough gas).
826
1
		RuntimeHelper::<Runtime>::try_dispatch(
827
1
			handle,
828
1
			frame_system::RawOrigin::Signed(origin).into(),
829
1
			call,
830
			0,
831
		)?;
832

            
833
1
		Ok(())
834
1
	}
835

            
836
	#[precompile::public("executeDelegationRequest(address,address)")]
837
	#[precompile::public("execute_delegation_request(address,address)")]
838
2
	fn execute_delegation_request(
839
2
		handle: &mut impl PrecompileHandle,
840
2
		delegator: Address,
841
2
		candidate: Address,
842
2
	) -> EvmResult {
843
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
844
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
845

            
846
		// Build call with origin.
847
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
848
2
		let call = pallet_parachain_staking::Call::<Runtime>::execute_delegation_request {
849
2
			delegator,
850
2
			candidate,
851
2
		};
852

            
853
		// Dispatch call (if enough gas).
854
2
		RuntimeHelper::<Runtime>::try_dispatch(
855
2
			handle,
856
2
			frame_system::RawOrigin::Signed(origin).into(),
857
2
			call,
858
			0,
859
		)?;
860

            
861
2
		Ok(())
862
2
	}
863

            
864
	#[precompile::public("cancelDelegationRequest(address)")]
865
	#[precompile::public("cancel_delegation_request(address)")]
866
2
	fn cancel_delegation_request(
867
2
		handle: &mut impl PrecompileHandle,
868
2
		candidate: Address,
869
2
	) -> EvmResult {
870
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
871

            
872
		// Build call with origin.
873
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
874
2
		let call =
875
2
			pallet_parachain_staking::Call::<Runtime>::cancel_delegation_request { candidate };
876

            
877
		// Dispatch call (if enough gas).
878
2
		RuntimeHelper::<Runtime>::try_dispatch(
879
2
			handle,
880
2
			frame_system::RawOrigin::Signed(origin).into(),
881
2
			call,
882
			0,
883
		)?;
884

            
885
2
		Ok(())
886
2
	}
887

            
888
	#[precompile::public("setAutoCompound(address,uint8,uint256,uint256)")]
889
6
	fn set_auto_compound(
890
6
		handle: &mut impl PrecompileHandle,
891
6
		candidate: Address,
892
6
		value: u8,
893
6
		candidate_auto_compounding_delegation_count: Convert<U256, u32>,
894
6
		delegator_delegation_count: Convert<U256, u32>,
895
6
	) -> EvmResult {
896
6
		if value > 100 {
897
2
			return Err(
898
2
				RevertReason::custom("Must be an integer between 0 and 100 included")
899
2
					.in_field("value")
900
2
					.into(),
901
2
			);
902
4
		}
903

            
904
4
		let value = Percent::from_percent(value);
905
4
		let candidate_auto_compounding_delegation_count_hint =
906
4
			candidate_auto_compounding_delegation_count.converted();
907
4
		let delegation_count_hint = delegator_delegation_count.converted();
908

            
909
4
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
910

            
911
		// Build call with origin.
912
4
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
913
4
		let call = pallet_parachain_staking::Call::<Runtime>::set_auto_compound {
914
4
			candidate,
915
4
			value,
916
4
			candidate_auto_compounding_delegation_count_hint,
917
4
			delegation_count_hint,
918
4
		};
919

            
920
		// Dispatch call (if enough gas).
921
4
		RuntimeHelper::<Runtime>::try_dispatch(
922
4
			handle,
923
4
			frame_system::RawOrigin::Signed(origin).into(),
924
4
			call,
925
			0,
926
1
		)?;
927

            
928
3
		Ok(())
929
6
	}
930

            
931
	#[precompile::public("getDelegatorTotalStaked(address)")]
932
	#[precompile::view]
933
2
	fn get_delegator_total_staked(
934
2
		handle: &mut impl PrecompileHandle,
935
2
		delegator: Address,
936
2
	) -> EvmResult<U256> {
937
		// DelegatorState:
938
		// Twox64Concat(8) + AccountId(20) + Delegator(56 + MaxDelegationsPerDelegator)
939
2
		handle.record_db_read::<Runtime>(
940
2
			84 + (<Runtime as pallet_parachain_staking::Config>::MaxDelegationsPerDelegator::get()
941
2
				as usize),
942
		)?;
943

            
944
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
945

            
946
2
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::delegator_state(&delegator)
947
2
			.map(|state| state.total)
948
2
			.unwrap_or_default();
949

            
950
2
		Ok(amount.into())
951
2
	}
952

            
953
	#[precompile::public("getCandidateTotalCounted(address)")]
954
	#[precompile::view]
955
1
	fn get_candidate_total_counted(
956
1
		handle: &mut impl PrecompileHandle,
957
1
		candidate: Address,
958
1
	) -> EvmResult<U256> {
959
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
960
1
		handle.record_db_read::<Runtime>(133)?;
961

            
962
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
963

            
964
1
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::candidate_info(&candidate)
965
1
			.map(|state| state.total_counted)
966
1
			.unwrap_or_default();
967

            
968
1
		Ok(amount.into())
969
1
	}
970

            
971
10
	fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime>> {
972
10
		value
973
10
			.try_into()
974
10
			.map_err(|_| RevertReason::value_is_too_large("balance type").into())
975
10
	}
976
}