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::{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
936
#[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
	<Runtime as pallet_evm::Config>::AddressMapping: AddressMapping<Runtime::AccountId>,
59
{
60
	// Constants
61
	#[precompile::public("minDelegation()")]
62
	#[precompile::public("min_delegation()")]
63
	#[precompile::view]
64
3
	fn min_delegation(_handle: &mut impl PrecompileHandle) -> EvmResult<u128> {
65
3
		let min_nomination: u128 =
66
3
			<<Runtime as pallet_parachain_staking::Config>::MinDelegation as Get<
67
3
				BalanceOf<Runtime>,
68
3
			>>::get()
69
3
			.try_into()
70
3
			.map_err(|_| revert("Amount is too large for provided balance type"))?;
71

            
72
3
		Ok(min_nomination)
73
3
	}
74

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

            
84
2
		Ok(points)
85
2
	}
86

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

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

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

            
102
2
		Ok(points)
103
2
	}
104

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

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

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

            
128
11
		Ok(round)
129
11
	}
130

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

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

            
161
1
		Ok(result)
162
1
	}
163

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

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

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

            
188
2
		Ok(count)
189
2
	}
190

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

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

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

            
226
1
		Ok(result)
227
1
	}
228

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

            
244
3
		Ok(selected_candidates)
245
3
	}
246

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

            
277
3
		Ok(amount)
278
3
	}
279

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

            
310
2
		Ok(is_in_top_delegations)
311
2
	}
312

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

            
326
2
		Ok(is_delegator)
327
2
	}
328

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

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

            
339
2
		Ok(is_candidate)
340
2
	}
341

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

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

            
359
2
		Ok(is_selected)
360
2
	}
361

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

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

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

            
392
3
		Ok(pending)
393
3
	}
394

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

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

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

            
422
3
		Ok(pending)
423
3
	}
424

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

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

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

            
452
3
		Ok(pending)
453
3
	}
454

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

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

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

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

            
484
	// Runtime Methods (dispatchables)
485

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

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

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

            
506
1
		Ok(())
507
1
	}
508

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

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

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

            
526
2
		Ok(())
527
2
	}
528

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

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

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

            
549
1
		Ok(())
550
1
	}
551

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

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

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

            
568
1
		Ok(())
569
1
	}
570

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

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

            
581
1
		Ok(())
582
1
	}
583

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

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

            
594
1
		Ok(())
595
1
	}
596

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

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

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

            
609
1
		Ok(())
610
1
	}
611

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

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

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

            
624
2
		Ok(())
625
2
	}
626

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

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

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

            
643
1
		Ok(())
644
1
	}
645

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

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

            
656
1
		Ok(())
657
1
	}
658

            
659
	#[precompile::public("delegateWithAutoCompound(address,uint256,uint8,uint256,uint256,uint256)")]
660
6
	fn delegate_with_auto_compound(
661
6
		handle: &mut impl PrecompileHandle,
662
6
		candidate: Address,
663
6
		amount: U256,
664
6
		auto_compound: u8,
665
6
		candidate_delegation_count: Convert<U256, u32>,
666
6
		candidate_auto_compounding_delegation_count: Convert<U256, u32>,
667
6
		delegator_delegation_count: Convert<U256, u32>,
668
6
	) -> EvmResult {
669
6
		if auto_compound > 100 {
670
2
			return Err(
671
2
				RevertReason::custom("Must be an integer between 0 and 100 included")
672
2
					.in_field("auto_compound")
673
2
					.into(),
674
2
			);
675
4
		}
676

            
677
4
		let amount = Self::u256_to_amount(amount).in_field("amount")?;
678
4
		let auto_compound = Percent::from_percent(auto_compound);
679
4
		let candidate_delegation_count = candidate_delegation_count.converted();
680
4
		let candidate_auto_compounding_delegation_count =
681
4
			candidate_auto_compounding_delegation_count.converted();
682
4
		let delegator_delegation_count = delegator_delegation_count.converted();
683
4

            
684
4
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
685
4

            
686
4
		// Build call with origin.
687
4
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
688
4
		let call = pallet_parachain_staking::Call::<Runtime>::delegate_with_auto_compound {
689
4
			candidate,
690
4
			amount,
691
4
			auto_compound,
692
4
			candidate_delegation_count,
693
4
			candidate_auto_compounding_delegation_count,
694
4
			delegation_count: delegator_delegation_count,
695
4
		};
696
4

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

            
700
4
		Ok(())
701
6
	}
702

            
703
	#[precompile::public("scheduleRevokeDelegation(address)")]
704
	#[precompile::public("schedule_revoke_delegation(address)")]
705
2
	fn schedule_revoke_delegation(
706
2
		handle: &mut impl PrecompileHandle,
707
2
		candidate: Address,
708
2
	) -> EvmResult {
709
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
710
2

            
711
2
		// Build call with origin.
712
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
713
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_revoke_delegation {
714
2
			collator: candidate,
715
2
		};
716
2

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

            
720
2
		Ok(())
721
2
	}
722

            
723
	#[precompile::public("delegatorBondMore(address,uint256)")]
724
	#[precompile::public("delegator_bond_more(address,uint256)")]
725
1
	fn delegator_bond_more(
726
1
		handle: &mut impl PrecompileHandle,
727
1
		candidate: Address,
728
1
		more: U256,
729
1
	) -> EvmResult {
730
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
731
1
		let more = Self::u256_to_amount(more).in_field("more")?;
732

            
733
		// Build call with origin.
734
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
735
1
		let call =
736
1
			pallet_parachain_staking::Call::<Runtime>::delegator_bond_more { candidate, more };
737
1

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

            
741
1
		Ok(())
742
1
	}
743

            
744
	#[precompile::public("scheduleDelegatorBondLess(address,uint256)")]
745
	#[precompile::public("schedule_delegator_bond_less(address,uint256)")]
746
1
	fn schedule_delegator_bond_less(
747
1
		handle: &mut impl PrecompileHandle,
748
1
		candidate: Address,
749
1
		less: U256,
750
1
	) -> EvmResult {
751
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
752
1
		let less = Self::u256_to_amount(less).in_field("less")?;
753

            
754
		// Build call with origin.
755
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
756
1
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_delegator_bond_less {
757
1
			candidate,
758
1
			less,
759
1
		};
760
1

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

            
764
1
		Ok(())
765
1
	}
766

            
767
	#[precompile::public("executeDelegationRequest(address,address)")]
768
	#[precompile::public("execute_delegation_request(address,address)")]
769
2
	fn execute_delegation_request(
770
2
		handle: &mut impl PrecompileHandle,
771
2
		delegator: Address,
772
2
		candidate: Address,
773
2
	) -> EvmResult {
774
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
775
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
776
2

            
777
2
		// Build call with origin.
778
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
779
2
		let call = pallet_parachain_staking::Call::<Runtime>::execute_delegation_request {
780
2
			delegator,
781
2
			candidate,
782
2
		};
783
2

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

            
787
2
		Ok(())
788
2
	}
789

            
790
	#[precompile::public("cancelDelegationRequest(address)")]
791
	#[precompile::public("cancel_delegation_request(address)")]
792
2
	fn cancel_delegation_request(
793
2
		handle: &mut impl PrecompileHandle,
794
2
		candidate: Address,
795
2
	) -> EvmResult {
796
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
797
2

            
798
2
		// Build call with origin.
799
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
800
2
		let call =
801
2
			pallet_parachain_staking::Call::<Runtime>::cancel_delegation_request { candidate };
802
2

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

            
806
2
		Ok(())
807
2
	}
808

            
809
	#[precompile::public("setAutoCompound(address,uint8,uint256,uint256)")]
810
6
	fn set_auto_compound(
811
6
		handle: &mut impl PrecompileHandle,
812
6
		candidate: Address,
813
6
		value: u8,
814
6
		candidate_auto_compounding_delegation_count: Convert<U256, u32>,
815
6
		delegator_delegation_count: Convert<U256, u32>,
816
6
	) -> EvmResult {
817
6
		if value > 100 {
818
2
			return Err(
819
2
				RevertReason::custom("Must be an integer between 0 and 100 included")
820
2
					.in_field("value")
821
2
					.into(),
822
2
			);
823
4
		}
824
4

            
825
4
		let value = Percent::from_percent(value);
826
4
		let candidate_auto_compounding_delegation_count_hint =
827
4
			candidate_auto_compounding_delegation_count.converted();
828
4
		let delegation_count_hint = delegator_delegation_count.converted();
829
4

            
830
4
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
831
4

            
832
4
		// Build call with origin.
833
4
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
834
4
		let call = pallet_parachain_staking::Call::<Runtime>::set_auto_compound {
835
4
			candidate,
836
4
			value,
837
4
			candidate_auto_compounding_delegation_count_hint,
838
4
			delegation_count_hint,
839
4
		};
840
4

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

            
844
3
		Ok(())
845
6
	}
846

            
847
	#[precompile::public("getDelegatorTotalStaked(address)")]
848
	#[precompile::view]
849
2
	fn get_delegator_total_staked(
850
2
		handle: &mut impl PrecompileHandle,
851
2
		delegator: Address,
852
2
	) -> EvmResult<U256> {
853
2
		// DelegatorState:
854
2
		// Twox64Concat(8) + AccountId(20) + Delegator(56 + MaxDelegationsPerDelegator)
855
2
		handle.record_db_read::<Runtime>(
856
2
			84 + (<Runtime as pallet_parachain_staking::Config>::MaxDelegationsPerDelegator::get()
857
2
				as usize),
858
2
		)?;
859

            
860
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
861
2

            
862
2
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::delegator_state(&delegator)
863
2
			.map(|state| state.total)
864
2
			.unwrap_or_default();
865
2

            
866
2
		Ok(amount.into())
867
2
	}
868

            
869
	#[precompile::public("getCandidateTotalCounted(address)")]
870
	#[precompile::view]
871
1
	fn get_candidate_total_counted(
872
1
		handle: &mut impl PrecompileHandle,
873
1
		candidate: Address,
874
1
	) -> EvmResult<U256> {
875
1
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
876
1
		handle.record_db_read::<Runtime>(133)?;
877

            
878
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
879
1

            
880
1
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::candidate_info(&candidate)
881
1
			.map(|state| state.total_counted)
882
1
			.unwrap_or_default();
883
1

            
884
1
		Ok(amount.into())
885
1
	}
886

            
887
10
	fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime>> {
888
10
		value
889
10
			.try_into()
890
10
			.map_err(|_| RevertReason::value_is_too_large("balance type").into())
891
10
	}
892
}