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::pallet_prelude::MaxEncodedLen;
29
use frame_support::sp_runtime::Percent;
30
use frame_support::traits::{fungible::Inspect, Get};
31
use pallet_evm::AddressMapping;
32
use pallet_parachain_staking::ScheduledRequest;
33
use precompile_utils::prelude::*;
34
use sp_core::{H160, U256};
35
use sp_runtime::traits::Dispatchable;
36
use sp_std::{convert::TryInto, marker::PhantomData, vec::Vec};
37

            
38
type BalanceOf<Runtime> = <<Runtime as pallet_parachain_staking::Config>::Currency as Inspect<
39
	<Runtime as frame_system::Config>::AccountId,
40
>>::Balance;
41

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

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

            
73
3
		Ok(min_nomination)
74
3
	}
75

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

            
85
2
		Ok(points)
86
2
	}
87

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

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

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

            
103
2
		Ok(points)
104
2
	}
105

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

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

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

            
129
11
		Ok(round)
130
11
	}
131

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

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

            
162
1
		Ok(result)
163
1
	}
164

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

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

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

            
189
2
		Ok(count)
190
2
	}
191

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

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

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

            
227
1
		Ok(result)
228
1
	}
229

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

            
245
3
		Ok(selected_candidates)
246
3
	}
247

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

            
278
3
		Ok(amount)
279
3
	}
280

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

            
311
2
		Ok(is_in_top_delegations)
312
2
	}
313

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

            
327
2
		Ok(is_delegator)
328
2
	}
329

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

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

            
340
2
		Ok(is_candidate)
341
2
	}
342

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

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

            
360
2
		Ok(is_selected)
361
2
	}
362

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

            
374
		// DelegationScheduledRequests:
375
		// Blake2_128Concat(16) + AccountId(20)         <-- collator key
376
		// + Blake2_128Concat(16) + AccountId(20)       <-- delegator key
377
		// + BoundedVec(
378
		//     ScheduledRequest(when_executable, action(Balance))
379
		//     * MaxScheduledRequestsPerDelegator
380
		//   )
381
		//
382
		// We keep a conservative upper bound for the encoded size of each
383
		// `ScheduledRequest` (42 bytes), and multiply it by the maximum
384
		// number of scheduled requests per (collator, delegator) queue.
385
3
		handle.record_db_read::<Runtime>(
386
3
			72 + ScheduledRequest::<BalanceOf<Runtime>>::max_encoded_len()
387
3
				* (<Runtime as pallet_parachain_staking::Config>::MaxScheduledRequestsPerDelegator::get()
388
3
					as usize),
389
		)?;
390

            
391
		// If we are not able to get delegator state, we return false
392
		// Users can call `is_delegator` to determine when this happens
393
3
		let pending = <pallet_parachain_staking::Pallet<Runtime>>::delegation_request_exists(
394
3
			&candidate, &delegator,
395
		);
396

            
397
3
		Ok(pending)
398
3
	}
399

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

            
409
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
410
3
		handle.record_db_read::<Runtime>(133)?;
411

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

            
427
3
		Ok(pending)
428
3
	}
429

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

            
439
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
440
3
		handle.record_db_read::<Runtime>(133)?;
441

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

            
457
3
		Ok(pending)
458
3
	}
459

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

            
470
		// AutoCompoundingDelegations:
471
		// Blake2128(16) + AccountId(20)
472
		// + BoundedVec(
473
		// 	AutoCompoundConfig * (MaxTopDelegationsPerCandidate + MaxBottomDelegationsPerCandidate)
474
		// )
475
2
		handle.record_db_read::<Runtime>(
476
2
			36 + (
477
2
				22 * (<Runtime as pallet_parachain_staking::Config>::MaxTopDelegationsPerCandidate::get()
478
2
				+ <Runtime as pallet_parachain_staking::Config>::MaxBottomDelegationsPerCandidate::get())
479
2
				as usize),
480
		)?;
481

            
482
2
		let value = <pallet_parachain_staking::Pallet<Runtime>>::delegation_auto_compound(
483
2
			&candidate, &delegator,
484
		);
485

            
486
2
		Ok(value.deconstruct())
487
2
	}
488

            
489
	// Runtime Methods (dispatchables)
490

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

            
501
		// Build call with origin.
502
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
503
1
		let call = pallet_parachain_staking::Call::<Runtime>::join_candidates {
504
1
			bond: amount,
505
1
			candidate_count,
506
1
		};
507

            
508
		// Dispatch call (if enough gas).
509
1
		RuntimeHelper::<Runtime>::try_dispatch(
510
1
			handle,
511
1
			frame_system::RawOrigin::Signed(origin).into(),
512
1
			call,
513
			0,
514
		)?;
515

            
516
1
		Ok(())
517
1
	}
518

            
519
	#[precompile::public("scheduleLeaveCandidates(uint256)")]
520
	#[precompile::public("schedule_leave_candidates(uint256)")]
521
2
	fn schedule_leave_candidates(
522
2
		handle: &mut impl PrecompileHandle,
523
2
		candidate_count: Convert<U256, u32>,
524
2
	) -> EvmResult {
525
2
		let candidate_count = candidate_count.converted();
526

            
527
		// Build call with origin.
528
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
529
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_leave_candidates {
530
2
			candidate_count,
531
2
		};
532

            
533
		// Dispatch call (if enough gas).
534
2
		RuntimeHelper::<Runtime>::try_dispatch(
535
2
			handle,
536
2
			frame_system::RawOrigin::Signed(origin).into(),
537
2
			call,
538
			0,
539
		)?;
540

            
541
2
		Ok(())
542
2
	}
543

            
544
	#[precompile::public("executeLeaveCandidates(address,uint256)")]
545
	#[precompile::public("execute_leave_candidates(address,uint256)")]
546
1
	fn execute_leave_candidates(
547
1
		handle: &mut impl PrecompileHandle,
548
1
		candidate: Address,
549
1
		candidate_count: Convert<U256, u32>,
550
1
	) -> EvmResult {
551
1
		let candidate_count = candidate_count.converted();
552
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
553

            
554
		// Build call with origin.
555
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
556
1
		let call = pallet_parachain_staking::Call::<Runtime>::execute_leave_candidates {
557
1
			candidate,
558
1
			candidate_delegation_count: candidate_count,
559
1
		};
560

            
561
		// Dispatch call (if enough gas).
562
1
		RuntimeHelper::<Runtime>::try_dispatch(
563
1
			handle,
564
1
			frame_system::RawOrigin::Signed(origin).into(),
565
1
			call,
566
			0,
567
		)?;
568

            
569
1
		Ok(())
570
1
	}
571

            
572
	#[precompile::public("cancelLeaveCandidates(uint256)")]
573
	#[precompile::public("cancel_leave_candidates(uint256)")]
574
1
	fn cancel_leave_candidates(
575
1
		handle: &mut impl PrecompileHandle,
576
1
		candidate_count: Convert<U256, u32>,
577
1
	) -> EvmResult {
578
1
		let candidate_count = candidate_count.converted();
579

            
580
		// Build call with origin.
581
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
582
1
		let call =
583
1
			pallet_parachain_staking::Call::<Runtime>::cancel_leave_candidates { candidate_count };
584

            
585
		// Dispatch call (if enough gas).
586
1
		RuntimeHelper::<Runtime>::try_dispatch(
587
1
			handle,
588
1
			frame_system::RawOrigin::Signed(origin).into(),
589
1
			call,
590
			0,
591
		)?;
592

            
593
1
		Ok(())
594
1
	}
595

            
596
	#[precompile::public("goOffline()")]
597
	#[precompile::public("go_offline()")]
598
1
	fn go_offline(handle: &mut impl PrecompileHandle) -> EvmResult {
599
		// Build call with origin.
600
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
601
1
		let call = pallet_parachain_staking::Call::<Runtime>::go_offline {};
602

            
603
		// Dispatch call (if enough gas).
604
1
		RuntimeHelper::<Runtime>::try_dispatch(
605
1
			handle,
606
1
			frame_system::RawOrigin::Signed(origin).into(),
607
1
			call,
608
			0,
609
		)?;
610

            
611
1
		Ok(())
612
1
	}
613

            
614
	#[precompile::public("goOnline()")]
615
	#[precompile::public("go_online()")]
616
1
	fn go_online(handle: &mut impl PrecompileHandle) -> EvmResult {
617
		// Build call with origin.
618
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
619
1
		let call = pallet_parachain_staking::Call::<Runtime>::go_online {};
620

            
621
		// Dispatch call (if enough gas).
622
1
		RuntimeHelper::<Runtime>::try_dispatch(
623
1
			handle,
624
1
			frame_system::RawOrigin::Signed(origin).into(),
625
1
			call,
626
			0,
627
		)?;
628

            
629
1
		Ok(())
630
1
	}
631

            
632
	#[precompile::public("candidateBondMore(uint256)")]
633
	#[precompile::public("candidate_bond_more(uint256)")]
634
1
	fn candidate_bond_more(handle: &mut impl PrecompileHandle, more: U256) -> EvmResult {
635
1
		let more = Self::u256_to_amount(more).in_field("more")?;
636

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

            
641
		// Dispatch call (if enough gas).
642
1
		RuntimeHelper::<Runtime>::try_dispatch(
643
1
			handle,
644
1
			frame_system::RawOrigin::Signed(origin).into(),
645
1
			call,
646
			0,
647
		)?;
648

            
649
1
		Ok(())
650
1
	}
651

            
652
	#[precompile::public("scheduleCandidateBondLess(uint256)")]
653
	#[precompile::public("schedule_candidate_bond_less(uint256)")]
654
2
	fn schedule_candidate_bond_less(handle: &mut impl PrecompileHandle, less: U256) -> EvmResult {
655
2
		let less = Self::u256_to_amount(less).in_field("less")?;
656

            
657
		// Build call with origin.
658
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
659
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_candidate_bond_less { less };
660

            
661
		// Dispatch call (if enough gas).
662
2
		RuntimeHelper::<Runtime>::try_dispatch(
663
2
			handle,
664
2
			frame_system::RawOrigin::Signed(origin).into(),
665
2
			call,
666
			0,
667
		)?;
668

            
669
2
		Ok(())
670
2
	}
671

            
672
	#[precompile::public("executeCandidateBondLess(address)")]
673
	#[precompile::public("execute_candidate_bond_less(address)")]
674
1
	fn execute_candidate_bond_less(
675
1
		handle: &mut impl PrecompileHandle,
676
1
		candidate: Address,
677
1
	) -> EvmResult {
678
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
679

            
680
		// Build call with origin.
681
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
682
1
		let call =
683
1
			pallet_parachain_staking::Call::<Runtime>::execute_candidate_bond_less { candidate };
684

            
685
		// Dispatch call (if enough gas).
686
1
		RuntimeHelper::<Runtime>::try_dispatch(
687
1
			handle,
688
1
			frame_system::RawOrigin::Signed(origin).into(),
689
1
			call,
690
			0,
691
		)?;
692

            
693
1
		Ok(())
694
1
	}
695

            
696
	#[precompile::public("cancelCandidateBondLess()")]
697
	#[precompile::public("cancel_candidate_bond_less()")]
698
1
	fn cancel_candidate_bond_less(handle: &mut impl PrecompileHandle) -> EvmResult {
699
		// Build call with origin.
700
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
701
1
		let call = pallet_parachain_staking::Call::<Runtime>::cancel_candidate_bond_less {};
702

            
703
		// Dispatch call (if enough gas).
704
1
		RuntimeHelper::<Runtime>::try_dispatch(
705
1
			handle,
706
1
			frame_system::RawOrigin::Signed(origin).into(),
707
1
			call,
708
			0,
709
		)?;
710

            
711
1
		Ok(())
712
1
	}
713

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

            
732
4
		let amount = Self::u256_to_amount(amount).in_field("amount")?;
733
4
		let auto_compound = Percent::from_percent(auto_compound);
734
4
		let candidate_delegation_count = candidate_delegation_count.converted();
735
4
		let candidate_auto_compounding_delegation_count =
736
4
			candidate_auto_compounding_delegation_count.converted();
737
4
		let delegator_delegation_count = delegator_delegation_count.converted();
738

            
739
4
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
740

            
741
		// Build call with origin.
742
4
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
743
4
		let call = pallet_parachain_staking::Call::<Runtime>::delegate_with_auto_compound {
744
4
			candidate,
745
4
			amount,
746
4
			auto_compound,
747
4
			candidate_delegation_count,
748
4
			candidate_auto_compounding_delegation_count,
749
4
			delegation_count: delegator_delegation_count,
750
4
		};
751

            
752
		// Dispatch call (if enough gas).
753
4
		RuntimeHelper::<Runtime>::try_dispatch(
754
4
			handle,
755
4
			frame_system::RawOrigin::Signed(origin).into(),
756
4
			call,
757
			0,
758
		)?;
759

            
760
4
		Ok(())
761
6
	}
762

            
763
	#[precompile::public("scheduleRevokeDelegation(address)")]
764
	#[precompile::public("schedule_revoke_delegation(address)")]
765
2
	fn schedule_revoke_delegation(
766
2
		handle: &mut impl PrecompileHandle,
767
2
		candidate: Address,
768
2
	) -> EvmResult {
769
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
770

            
771
		// Build call with origin.
772
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
773
2
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_revoke_delegation {
774
2
			collator: candidate,
775
2
		};
776

            
777
		// Dispatch call (if enough gas).
778
2
		RuntimeHelper::<Runtime>::try_dispatch(
779
2
			handle,
780
2
			frame_system::RawOrigin::Signed(origin).into(),
781
2
			call,
782
			0,
783
		)?;
784

            
785
2
		Ok(())
786
2
	}
787

            
788
	#[precompile::public("delegatorBondMore(address,uint256)")]
789
	#[precompile::public("delegator_bond_more(address,uint256)")]
790
1
	fn delegator_bond_more(
791
1
		handle: &mut impl PrecompileHandle,
792
1
		candidate: Address,
793
1
		more: U256,
794
1
	) -> EvmResult {
795
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
796
1
		let more = Self::u256_to_amount(more).in_field("more")?;
797

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

            
803
		// Dispatch call (if enough gas).
804
1
		RuntimeHelper::<Runtime>::try_dispatch(
805
1
			handle,
806
1
			frame_system::RawOrigin::Signed(origin).into(),
807
1
			call,
808
			0,
809
		)?;
810

            
811
1
		Ok(())
812
1
	}
813

            
814
	#[precompile::public("scheduleDelegatorBondLess(address,uint256)")]
815
	#[precompile::public("schedule_delegator_bond_less(address,uint256)")]
816
1
	fn schedule_delegator_bond_less(
817
1
		handle: &mut impl PrecompileHandle,
818
1
		candidate: Address,
819
1
		less: U256,
820
1
	) -> EvmResult {
821
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
822
1
		let less = Self::u256_to_amount(less).in_field("less")?;
823

            
824
		// Build call with origin.
825
1
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
826
1
		let call = pallet_parachain_staking::Call::<Runtime>::schedule_delegator_bond_less {
827
1
			candidate,
828
1
			less,
829
1
		};
830

            
831
		// Dispatch call (if enough gas).
832
1
		RuntimeHelper::<Runtime>::try_dispatch(
833
1
			handle,
834
1
			frame_system::RawOrigin::Signed(origin).into(),
835
1
			call,
836
			0,
837
		)?;
838

            
839
1
		Ok(())
840
1
	}
841

            
842
	#[precompile::public("executeDelegationRequest(address,address)")]
843
	#[precompile::public("execute_delegation_request(address,address)")]
844
2
	fn execute_delegation_request(
845
2
		handle: &mut impl PrecompileHandle,
846
2
		delegator: Address,
847
2
		candidate: Address,
848
2
	) -> EvmResult {
849
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
850
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
851

            
852
		// Build call with origin.
853
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
854
2
		let call = pallet_parachain_staking::Call::<Runtime>::execute_delegation_request {
855
2
			delegator,
856
2
			candidate,
857
2
		};
858

            
859
		// Dispatch call (if enough gas).
860
2
		RuntimeHelper::<Runtime>::try_dispatch(
861
2
			handle,
862
2
			frame_system::RawOrigin::Signed(origin).into(),
863
2
			call,
864
			0,
865
		)?;
866

            
867
2
		Ok(())
868
2
	}
869

            
870
	#[precompile::public("cancelDelegationRequest(address)")]
871
	#[precompile::public("cancel_delegation_request(address)")]
872
2
	fn cancel_delegation_request(
873
2
		handle: &mut impl PrecompileHandle,
874
2
		candidate: Address,
875
2
	) -> EvmResult {
876
2
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
877

            
878
		// Build call with origin.
879
2
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
880
2
		let call =
881
2
			pallet_parachain_staking::Call::<Runtime>::cancel_delegation_request { candidate };
882

            
883
		// Dispatch call (if enough gas).
884
2
		RuntimeHelper::<Runtime>::try_dispatch(
885
2
			handle,
886
2
			frame_system::RawOrigin::Signed(origin).into(),
887
2
			call,
888
			0,
889
		)?;
890

            
891
2
		Ok(())
892
2
	}
893

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

            
910
4
		let value = Percent::from_percent(value);
911
4
		let candidate_auto_compounding_delegation_count_hint =
912
4
			candidate_auto_compounding_delegation_count.converted();
913
4
		let delegation_count_hint = delegator_delegation_count.converted();
914

            
915
4
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
916

            
917
		// Build call with origin.
918
4
		let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
919
4
		let call = pallet_parachain_staking::Call::<Runtime>::set_auto_compound {
920
4
			candidate,
921
4
			value,
922
4
			candidate_auto_compounding_delegation_count_hint,
923
4
			delegation_count_hint,
924
4
		};
925

            
926
		// Dispatch call (if enough gas).
927
4
		RuntimeHelper::<Runtime>::try_dispatch(
928
4
			handle,
929
4
			frame_system::RawOrigin::Signed(origin).into(),
930
4
			call,
931
			0,
932
1
		)?;
933

            
934
3
		Ok(())
935
6
	}
936

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

            
950
2
		let delegator = Runtime::AddressMapping::into_account_id(delegator.0);
951

            
952
2
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::delegator_state(&delegator)
953
2
			.map(|state| state.total)
954
2
			.unwrap_or_default();
955

            
956
2
		Ok(amount.into())
957
2
	}
958

            
959
	#[precompile::public("getCandidateTotalCounted(address)")]
960
	#[precompile::view]
961
1
	fn get_candidate_total_counted(
962
1
		handle: &mut impl PrecompileHandle,
963
1
		candidate: Address,
964
1
	) -> EvmResult<U256> {
965
		// CandidateInfo: Twox64Concat(8) + AccountId(20) + CandidateMetadata(105)
966
1
		handle.record_db_read::<Runtime>(133)?;
967

            
968
1
		let candidate = Runtime::AddressMapping::into_account_id(candidate.0);
969

            
970
1
		let amount = <pallet_parachain_staking::Pallet<Runtime>>::candidate_info(&candidate)
971
1
			.map(|state| state.total_counted)
972
1
			.unwrap_or_default();
973

            
974
1
		Ok(amount.into())
975
1
	}
976

            
977
10
	fn u256_to_amount(value: U256) -> MayRevert<BalanceOf<Runtime>> {
978
10
		value
979
10
			.try_into()
980
10
			.map_err(|_| RevertReason::value_is_too_large("balance type").into())
981
10
	}
982
}