1#![cfg_attr(not(feature = "std"), no_std)]
22
23use fp_evm::PrecompileHandle;
24use frame_system::pallet_prelude::BlockNumberFor;
25use num_enum::{IntoPrimitive, TryFromPrimitive};
26use parity_scale_codec::MaxEncodedLen;
27
28use frame_support::{
29 dispatch::{GetDispatchInfo, PostDispatchInfo},
30 ensure,
31 traits::{ConstU32, IsType},
32};
33
34use pallet_evm::AddressMapping;
35use precompile_utils::{
36 prelude::*,
37 solidity::{
38 codec::{Reader, Writer},
39 Codec,
40 },
41};
42use sp_core::{Get, H160, U256};
43use sp_runtime::traits::{Dispatchable, Zero};
44use sp_std::{marker::PhantomData, prelude::*};
45extern crate alloc;
46
47use astar_primitives::{dapp_staking::SmartContractHandle, AccountId, Balance, BlockNumber};
48use pallet_dapp_staking::{
49 AccountLedgerFor, ActiveProtocolState, ContractStake, ContractStakeAmount, CurrentEraInfo,
50 DAppInfoFor, EraInfo, EraRewardSpanFor, EraRewards, IntegratedDApps, Ledger,
51 Pallet as DAppStaking, ProtocolState, SingularStakingInfo, StakerInfo, Subperiod,
52};
53
54pub const STAKER_BYTES_LIMIT: u32 = 32;
55type GetStakerBytesLimit = ConstU32<STAKER_BYTES_LIMIT>;
56
57pub type DynamicAddress = BoundedBytes<GetStakerBytesLimit>;
58
59#[cfg(test)]
60mod test;
61
62#[derive(Debug, Clone, solidity::Codec)]
64pub(crate) struct PrecompileProtocolState {
65 era: U256,
66 period: U256,
67 subperiod: u8,
68}
69
70#[derive(Debug, Clone, solidity::Codec)]
72pub struct SmartContractV2 {
73 contract_type: SmartContractTypes,
74 address: DynamicAddress,
75}
76
77#[derive(Clone, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
83#[repr(u8)]
84pub(crate) enum SmartContractTypes {
85 Evm,
86 Wasm,
87}
88
89impl Codec for SmartContractTypes {
90 fn read(reader: &mut Reader) -> MayRevert<SmartContractTypes> {
91 let value256: U256 = reader
92 .read()
93 .map_err(|_| RevertReason::read_out_of_bounds(Self::signature()))?;
94
95 let value_as_u8: u8 = value256
96 .try_into()
97 .map_err(|_| RevertReason::value_is_too_large(Self::signature()))?;
98
99 value_as_u8
100 .try_into()
101 .map_err(|_| RevertReason::custom("Unknown smart contract type").into())
102 }
103
104 fn write(writer: &mut Writer, value: Self) {
105 let value_as_u8: u8 = value.into();
106 U256::write(writer, value_as_u8.into());
107 }
108
109 fn has_static_size() -> bool {
110 true
111 }
112
113 fn signature() -> String {
114 "uint8".into()
115 }
116}
117
118pub struct DappStakingV3Precompile<R>(PhantomData<R>);
119#[precompile_utils::precompile]
120impl<R> DappStakingV3Precompile<R>
121where
122 R: pallet_evm::Config
123 + pallet_dapp_staking::Config
124 + frame_system::Config<AccountId = AccountId>,
125 BlockNumberFor<R>: IsType<BlockNumber>,
126 <R::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<R::AccountId>>,
127 <R as pallet_evm::Config>::AddressMapping: AddressMapping<R::AccountId>,
128 R::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
129 R::RuntimeCall: From<pallet_dapp_staking::Call<R>>,
130{
131 #[precompile::public("read_current_era()")]
135 #[precompile::view]
136 fn read_current_era(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
137 handle.record_db_read::<R>(8 + ProtocolState::max_encoded_len())?;
141
142 let current_era = ActiveProtocolState::<R>::get().era();
143
144 Ok(current_era.into())
145 }
146
147 #[precompile::public("read_unbonding_period()")]
149 #[precompile::view]
150 fn read_unbonding_period(_: &mut impl PrecompileHandle) -> EvmResult<U256> {
151 Ok(<R as pallet_dapp_staking::Config>::UnlockingPeriod::get().into())
153 }
154
155 #[precompile::public("read_era_reward(uint32)")]
159 #[precompile::view]
160 fn read_era_reward(handle: &mut impl PrecompileHandle, era: u32) -> EvmResult<u128> {
161 handle.record_db_read::<R>(12 + EraRewardSpanFor::<R>::max_encoded_len())?;
165
166 let era_span_index = DAppStaking::<R>::era_reward_span_index(era);
168 let reward_span = EraRewards::<R>::get(&era_span_index).unwrap_or_default();
169
170 let reward = reward_span.get(era).map_or(Zero::zero(), |r| {
172 r.staker_reward_pool().saturating_add(r.dapp_reward_pool())
173 });
174
175 Ok(reward)
176 }
177
178 #[precompile::public("read_era_staked(uint32)")]
185 #[precompile::view]
186 fn read_era_staked(handle: &mut impl PrecompileHandle, era: u32) -> EvmResult<u128> {
187 handle.record_db_read::<R>(8 + ProtocolState::max_encoded_len())?;
191
192 let current_era = ActiveProtocolState::<R>::get().era();
193
194 if era < current_era {
199 handle.record_db_read::<R>(20 + EraRewardSpanFor::<R>::max_encoded_len())?;
203
204 let era_span_index = DAppStaking::<R>::era_reward_span_index(era);
205 let reward_span = EraRewards::<R>::get(&era_span_index).unwrap_or_default();
206
207 let staked = reward_span.get(era).map_or(Zero::zero(), |r| r.staked());
208
209 Ok(staked.into())
210 } else if era == current_era || era == current_era.saturating_add(1) {
211 handle.record_db_read::<R>(8 + EraInfo::max_encoded_len())?;
215
216 let current_era_info = CurrentEraInfo::<R>::get();
217
218 if era == current_era {
219 Ok(current_era_info.current_stake_amount().total())
220 } else {
221 Ok(current_era_info.next_stake_amount().total())
222 }
223 } else {
224 Err(RevertReason::custom("Era is in the future").into())
225 }
226 }
227
228 #[precompile::public("read_staked_amount(bytes)")]
230 #[precompile::view]
231 fn read_staked_amount(
232 handle: &mut impl PrecompileHandle,
233 staker: DynamicAddress,
234 ) -> EvmResult<u128> {
235 handle.record_db_read::<R>(
241 24 + AccountLedgerFor::<R>::max_encoded_len()
242 + ProtocolState::max_encoded_len()
243 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len(),
244 )?;
245
246 let staker = Self::parse_input_address(staker.into())?;
247
248 let ledger = Ledger::<R>::get(&staker);
250 log::trace!(target: "ds-precompile", "read_staked_amount for account: {:?}, ledger: {:?}", staker, ledger);
251
252 let current_period_number = ActiveProtocolState::<R>::get().period_number();
254
255 Ok(ledger.staked_amount(current_period_number))
256 }
257
258 #[precompile::public("read_staked_amount_on_contract(address,bytes)")]
260 #[precompile::view]
261 fn read_staked_amount_on_contract(
262 handle: &mut impl PrecompileHandle,
263 contract_h160: Address,
264 staker: DynamicAddress,
265 ) -> EvmResult<u128> {
266 handle.record_db_read::<R>(
272 24 + ProtocolState::max_encoded_len()
273 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
274 + SingularStakingInfo::max_encoded_len(),
275 )?;
276
277 let smart_contract =
278 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
279
280 let staker = Self::parse_input_address(staker.into())?;
282
283 let staking_info = StakerInfo::<R>::get(&staker, &smart_contract).unwrap_or_default();
285 log::trace!(target: "ds-precompile", "read_staked_amount_on_contract for account:{:?}, staking_info: {:?}", staker, staking_info);
286
287 let current_period_number = ActiveProtocolState::<R>::get().period_number();
289
290 if staking_info.period_number() == current_period_number {
291 Ok(staking_info.total_staked_amount())
292 } else {
293 Ok(0_u128)
294 }
295 }
296
297 #[precompile::public("read_contract_stake(address)")]
299 #[precompile::view]
300 fn read_contract_stake(
301 handle: &mut impl PrecompileHandle,
302 contract_h160: Address,
303 ) -> EvmResult<u128> {
304 handle.record_db_read::<R>(
312 36 + ProtocolState::max_encoded_len()
313 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
314 + DAppInfoFor::<R>::max_encoded_len()
315 + ContractStakeAmount::max_encoded_len(),
316 )?;
317
318 let smart_contract =
319 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
320
321 let current_period_number = ActiveProtocolState::<R>::get().period_number();
322 let dapp_info = match IntegratedDApps::<R>::get(&smart_contract) {
323 Some(dapp_info) => dapp_info,
324 None => {
325 return Ok(0_u128);
327 }
328 };
329
330 let contract_stake = ContractStake::<R>::get(&dapp_info.id());
332
333 Ok(contract_stake.total_staked_amount(current_period_number))
334 }
335
336 #[precompile::public("register(address)")]
339 fn register(_: &mut impl PrecompileHandle, _address: Address) -> EvmResult<bool> {
340 Err(RevertReason::custom("register via evm precompile is not allowed").into())
342 }
343
344 #[precompile::public("bond_and_stake(address,uint128)")]
349 fn bond_and_stake(
350 handle: &mut impl PrecompileHandle,
351 contract_h160: Address,
352 amount: u128,
353 ) -> EvmResult<bool> {
354 handle.record_db_read::<R>(
360 24 + AccountLedgerFor::<R>::max_encoded_len()
361 + ProtocolState::max_encoded_len()
362 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len(),
363 )?;
364
365 let smart_contract =
366 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
367 log::trace!(target: "ds-precompile", "bond_and_stake {:?}, {:?}", smart_contract, amount);
368
369 let origin = R::AddressMapping::into_account_id(handle.context().caller);
371 let protocol_state = ActiveProtocolState::<R>::get();
372 let ledger = Ledger::<R>::get(&origin);
373
374 let stakeable_amount = ledger.stakeable_amount(protocol_state.period_number());
376
377 if stakeable_amount < amount {
379 let delta = amount.saturating_sub(stakeable_amount);
380
381 let lock_call = pallet_dapp_staking::Call::<R>::lock { amount: delta };
382 RuntimeHelper::<R>::try_dispatch(handle, Some(origin.clone()).into(), lock_call, 0)?;
383 }
384
385 let stake_call = pallet_dapp_staking::Call::<R>::stake {
387 smart_contract,
388 amount,
389 };
390 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), stake_call, 0)?;
391
392 Ok(true)
393 }
394
395 #[precompile::public("unbond_and_unstake(address,uint128)")]
397 fn unbond_and_unstake(
398 handle: &mut impl PrecompileHandle,
399 contract_h160: Address,
400 amount: u128,
401 ) -> EvmResult<bool> {
402 handle.record_db_read::<R>(
408 24 + ProtocolState::max_encoded_len()
409 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
410 + SingularStakingInfo::max_encoded_len(),
411 )?;
412
413 let smart_contract =
414 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
415 let origin = R::AddressMapping::into_account_id(handle.context().caller);
416 log::trace!(target: "ds-precompile", "unbond_and_unstake {:?}, {:?}", smart_contract, amount);
417
418 let protocol_state = ActiveProtocolState::<R>::get();
420 let staker_info = StakerInfo::<R>::get(&origin, &smart_contract).unwrap_or_default();
421
422 if staker_info.period_number() == protocol_state.period_number() {
424 let unstake_call = pallet_dapp_staking::Call::<R>::unstake {
425 smart_contract,
426 amount,
427 };
428 RuntimeHelper::<R>::try_dispatch(handle, Some(origin.clone()).into(), unstake_call, 0)?;
429 }
430
431 let unlock_call = pallet_dapp_staking::Call::<R>::unlock { amount };
433 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), unlock_call, 0)?;
434
435 Ok(true)
436 }
437
438 #[precompile::public("withdraw_unbonded()")]
440 fn withdraw_unbonded(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
441 let origin = R::AddressMapping::into_account_id(handle.context().caller);
442 let call = pallet_dapp_staking::Call::<R>::claim_unlocked {};
443
444 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
445
446 Ok(true)
447 }
448
449 #[precompile::public("claim_dapp(address,uint128)")]
451 fn claim_dapp(
452 handle: &mut impl PrecompileHandle,
453 contract_h160: Address,
454 era: u128,
455 ) -> EvmResult<bool> {
456 let smart_contract =
457 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
458
459 let era = era
461 .try_into()
462 .map_err::<Revert, _>(|_| RevertReason::value_is_too_large("era type").into())
463 .in_field("era")?;
464
465 log::trace!(target: "ds-precompile", "claim_dapp {:?}, era {:?}", smart_contract, era);
466
467 let origin = R::AddressMapping::into_account_id(handle.context().caller);
468 let call = pallet_dapp_staking::Call::<R>::claim_dapp_reward {
469 smart_contract,
470 era,
471 };
472
473 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
474
475 Ok(true)
476 }
477
478 #[precompile::public("claim_staker(address)")]
482 fn claim_staker(
483 handle: &mut impl PrecompileHandle,
484 _contract_h160: Address,
485 ) -> EvmResult<bool> {
486 let origin = R::AddressMapping::into_account_id(handle.context().caller);
487 let call = pallet_dapp_staking::Call::<R>::claim_staker_rewards {};
488
489 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
490
491 Ok(true)
492 }
493
494 #[precompile::public("set_reward_destination(uint8)")]
498 fn set_reward_destination(_: &mut impl PrecompileHandle, _destination: u8) -> EvmResult<bool> {
499 Err(RevertReason::custom("Setting reward destination is no longer supported.").into())
500 }
501
502 #[precompile::public("withdraw_from_unregistered(address)")]
504 fn withdraw_from_unregistered(
505 handle: &mut impl PrecompileHandle,
506 contract_h160: Address,
507 ) -> EvmResult<bool> {
508 let smart_contract =
509 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
510 log::trace!(target: "ds-precompile", "withdraw_from_unregistered {:?}", smart_contract);
511
512 let origin = R::AddressMapping::into_account_id(handle.context().caller);
513 let call = pallet_dapp_staking::Call::<R>::unstake_from_unregistered { smart_contract };
514
515 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
516
517 Ok(true)
518 }
519
520 #[precompile::public("nomination_transfer(address,uint128,address)")]
524 fn nomination_transfer(
525 handle: &mut impl PrecompileHandle,
526 origin_contract_h160: Address,
527 amount: u128,
528 target_contract_h160: Address,
529 ) -> EvmResult<bool> {
530 handle.record_db_read::<R>(
534 16 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
535 + SingularStakingInfo::max_encoded_len(),
536 )?;
537
538 let origin_smart_contract =
539 <R as pallet_dapp_staking::Config>::SmartContract::evm(origin_contract_h160.into());
540 let target_smart_contract =
541 <R as pallet_dapp_staking::Config>::SmartContract::evm(target_contract_h160.into());
542 log::trace!(target: "ds-precompile", "nomination_transfer {:?} {:?} {:?}", origin_smart_contract, amount, target_smart_contract);
543
544 let origin = R::AddressMapping::into_account_id(handle.context().caller);
546 let staker_info = StakerInfo::<R>::get(&origin, &origin_smart_contract).unwrap_or_default();
547
548 let staked_amount = staker_info.total_staked_amount();
551 let minimum_allowed_stake_amount =
552 <R as pallet_dapp_staking::Config>::MinimumStakeAmount::get();
553
554 let stake_amount = if staked_amount > 0
560 && staked_amount.saturating_sub(amount) < minimum_allowed_stake_amount
561 {
562 staked_amount
563 } else {
564 amount
565 };
566
567 let unstake_call = pallet_dapp_staking::Call::<R>::unstake {
569 smart_contract: origin_smart_contract,
570 amount,
571 };
572 RuntimeHelper::<R>::try_dispatch(handle, Some(origin.clone()).into(), unstake_call, 0)?;
573
574 let stake_call = pallet_dapp_staking::Call::<R>::stake {
576 smart_contract: target_smart_contract,
577 amount: stake_amount,
578 };
579 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), stake_call, 0)?;
580
581 Ok(true)
582 }
583
584 #[precompile::public("protocol_state()")]
588 #[precompile::view]
589 fn protocol_state(handle: &mut impl PrecompileHandle) -> EvmResult<PrecompileProtocolState> {
590 handle.record_db_read::<R>(8 + ProtocolState::max_encoded_len())?;
594
595 let protocol_state = ActiveProtocolState::<R>::get();
596
597 Ok(PrecompileProtocolState {
598 era: protocol_state.era().into(),
599 period: protocol_state.period_number().into(),
600 subperiod: subperiod_id(&protocol_state.subperiod()),
601 })
602 }
603
604 #[precompile::public("unlocking_period()")]
606 #[precompile::view]
607 fn unlocking_period(_: &mut impl PrecompileHandle) -> EvmResult<U256> {
608 Ok(DAppStaking::<R>::unlocking_period().into())
610 }
611
612 #[precompile::public("lock(uint128)")]
614 fn lock(handle: &mut impl PrecompileHandle, amount: u128) -> EvmResult<bool> {
615 let origin = R::AddressMapping::into_account_id(handle.context().caller);
617 let lock_call = pallet_dapp_staking::Call::<R>::lock { amount };
618 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), lock_call, 0)?;
619
620 Ok(true)
621 }
622
623 #[precompile::public("unlock(uint128)")]
625 fn unlock(handle: &mut impl PrecompileHandle, amount: u128) -> EvmResult<bool> {
626 let origin = R::AddressMapping::into_account_id(handle.context().caller);
628 let unlock_call = pallet_dapp_staking::Call::<R>::unlock { amount };
629 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), unlock_call, 0)?;
630
631 Ok(true)
632 }
633
634 #[precompile::public("claim_unlocked()")]
636 fn claim_unlocked(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
637 let origin = R::AddressMapping::into_account_id(handle.context().caller);
639 let claim_unlocked_call = pallet_dapp_staking::Call::<R>::claim_unlocked {};
640 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), claim_unlocked_call, 0)?;
641
642 Ok(true)
643 }
644
645 #[precompile::public("stake((uint8,bytes),uint128)")]
647 fn stake(
648 handle: &mut impl PrecompileHandle,
649 smart_contract: SmartContractV2,
650 amount: Balance,
651 ) -> EvmResult<bool> {
652 let smart_contract = Self::decode_smart_contract(smart_contract)?;
653
654 let origin = R::AddressMapping::into_account_id(handle.context().caller);
656 let stake_call = pallet_dapp_staking::Call::<R>::stake {
657 smart_contract,
658 amount,
659 };
660 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), stake_call, 0)?;
661
662 Ok(true)
663 }
664
665 #[precompile::public("unstake((uint8,bytes),uint128)")]
667 fn unstake(
668 handle: &mut impl PrecompileHandle,
669 smart_contract: SmartContractV2,
670 amount: Balance,
671 ) -> EvmResult<bool> {
672 let smart_contract = Self::decode_smart_contract(smart_contract)?;
673
674 let origin = R::AddressMapping::into_account_id(handle.context().caller);
676 let unstake_call = pallet_dapp_staking::Call::<R>::unstake {
677 smart_contract,
678 amount,
679 };
680 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), unstake_call, 0)?;
681
682 Ok(true)
683 }
684
685 #[precompile::public("claim_staker_rewards()")]
687 fn claim_staker_rewards(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
688 let origin = R::AddressMapping::into_account_id(handle.context().caller);
690 let claim_staker_rewards_call = pallet_dapp_staking::Call::<R>::claim_staker_rewards {};
691 RuntimeHelper::<R>::try_dispatch(
692 handle,
693 Some(origin).into(),
694 claim_staker_rewards_call,
695 0,
696 )?;
697
698 Ok(true)
699 }
700
701 #[precompile::public("claim_bonus_reward((uint8,bytes))")]
703 fn claim_bonus_reward(
704 handle: &mut impl PrecompileHandle,
705 smart_contract: SmartContractV2,
706 ) -> EvmResult<bool> {
707 let smart_contract = Self::decode_smart_contract(smart_contract)?;
708
709 let origin = R::AddressMapping::into_account_id(handle.context().caller);
711 let claim_bonus_reward_call =
712 pallet_dapp_staking::Call::<R>::claim_bonus_reward { smart_contract };
713 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), claim_bonus_reward_call, 0)?;
714
715 Ok(true)
716 }
717
718 #[precompile::public("claim_bonus_reward((uint8,bytes),uint256)")]
720 fn claim_dapp_reward(
721 handle: &mut impl PrecompileHandle,
722 smart_contract: SmartContractV2,
723 era: U256,
724 ) -> EvmResult<bool> {
725 let smart_contract = Self::decode_smart_contract(smart_contract)?;
726 let era = era
727 .try_into()
728 .map_err::<Revert, _>(|_| RevertReason::value_is_too_large("Era number.").into())
729 .in_field("era")?;
730
731 let origin = R::AddressMapping::into_account_id(handle.context().caller);
733 let claim_dapp_reward_call = pallet_dapp_staking::Call::<R>::claim_dapp_reward {
734 smart_contract,
735 era,
736 };
737 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), claim_dapp_reward_call, 0)?;
738
739 Ok(true)
740 }
741
742 #[precompile::public("unstake_from_unregistered((uint8,bytes))")]
744 fn unstake_from_unregistered(
745 handle: &mut impl PrecompileHandle,
746 smart_contract: SmartContractV2,
747 ) -> EvmResult<bool> {
748 let smart_contract = Self::decode_smart_contract(smart_contract)?;
749
750 let origin = R::AddressMapping::into_account_id(handle.context().caller);
752 let unstake_from_unregistered_call =
753 pallet_dapp_staking::Call::<R>::unstake_from_unregistered { smart_contract };
754 RuntimeHelper::<R>::try_dispatch(
755 handle,
756 Some(origin).into(),
757 unstake_from_unregistered_call,
758 0,
759 )?;
760
761 Ok(true)
762 }
763
764 #[precompile::public("cleanup_expired_entries()")]
766 fn cleanup_expired_entries(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
767 let origin = R::AddressMapping::into_account_id(handle.context().caller);
769 let cleanup_expired_entries_call =
770 pallet_dapp_staking::Call::<R>::cleanup_expired_entries {};
771 RuntimeHelper::<R>::try_dispatch(
772 handle,
773 Some(origin).into(),
774 cleanup_expired_entries_call,
775 0,
776 )?;
777
778 Ok(true)
779 }
780
781 #[precompile::public("move_stake((uint8,bytes),(uint8,bytes),uint128)")]
782 fn move_stake(
783 handle: &mut impl PrecompileHandle,
784 source_contract: SmartContractV2,
785 destination_contract: SmartContractV2,
786 amount: Balance,
787 ) -> EvmResult<bool> {
788 let source_contract = Self::decode_smart_contract(source_contract)?;
789 let destination_contract = Self::decode_smart_contract(destination_contract)?;
790
791 let origin = R::AddressMapping::into_account_id(handle.context().caller);
793 let move_call = pallet_dapp_staking::Call::<R>::move_stake {
794 source_contract,
795 destination_contract,
796 amount,
797 };
798 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), move_call, 0)?;
799
800 Ok(true)
801 }
802
803 pub(crate) fn decode_smart_contract(
807 smart_contract: SmartContractV2,
808 ) -> EvmResult<<R as pallet_dapp_staking::Config>::SmartContract> {
809 let smart_contract = match smart_contract.contract_type {
810 SmartContractTypes::Evm => {
811 ensure!(
812 smart_contract.address.as_bytes().len() == 20,
813 revert("Invalid address length for Astar EVM smart contract.")
814 );
815 let h160_address = H160::from_slice(smart_contract.address.as_bytes());
816 <R as pallet_dapp_staking::Config>::SmartContract::evm(h160_address)
817 }
818 SmartContractTypes::Wasm => {
821 return Err(revert(
822 "Wasm smart contracts are no longer supported by dApp staking.",
823 ))
824 }
825 };
826
827 Ok(smart_contract)
828 }
829
830 pub(crate) fn parse_input_address(staker_vec: Vec<u8>) -> EvmResult<R::AccountId> {
832 let staker: R::AccountId = match staker_vec.len() {
833 32 => {
835 let mut staker_bytes = [0_u8; 32];
836 staker_bytes[..].clone_from_slice(&staker_vec[0..32]);
837
838 staker_bytes.into()
839 }
840 20 => {
842 let mut staker_bytes = [0_u8; 20];
843 staker_bytes[..].clone_from_slice(&staker_vec[0..20]);
844
845 R::AddressMapping::into_account_id(staker_bytes.into())
846 }
847 _ => {
848 return Err(revert("Error while parsing staker's address"));
850 }
851 };
852
853 Ok(staker)
854 }
855}
856
857pub(crate) fn subperiod_id(subperiod: &Subperiod) -> u8 {
859 match subperiod {
860 Subperiod::Voting => 0,
861 Subperiod::BuildAndEarn => 1,
862 }
863}