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)]
79#[repr(u8)]
80pub(crate) enum SmartContractTypes {
81 Evm,
82 Wasm,
83}
84
85impl Codec for SmartContractTypes {
86 fn read(reader: &mut Reader) -> MayRevert<SmartContractTypes> {
87 let value256: U256 = reader
88 .read()
89 .map_err(|_| RevertReason::read_out_of_bounds(Self::signature()))?;
90
91 let value_as_u8: u8 = value256
92 .try_into()
93 .map_err(|_| RevertReason::value_is_too_large(Self::signature()))?;
94
95 value_as_u8
96 .try_into()
97 .map_err(|_| RevertReason::custom("Unknown smart contract type").into())
98 }
99
100 fn write(writer: &mut Writer, value: Self) {
101 let value_as_u8: u8 = value.into();
102 U256::write(writer, value_as_u8.into());
103 }
104
105 fn has_static_size() -> bool {
106 true
107 }
108
109 fn signature() -> String {
110 "uint8".into()
111 }
112}
113
114pub struct DappStakingV3Precompile<R>(PhantomData<R>);
115#[precompile_utils::precompile]
116impl<R> DappStakingV3Precompile<R>
117where
118 R: pallet_evm::Config
119 + pallet_dapp_staking::Config
120 + frame_system::Config<AccountId = AccountId>,
121 BlockNumberFor<R>: IsType<BlockNumber>,
122 <R::RuntimeCall as Dispatchable>::RuntimeOrigin: From<Option<R::AccountId>>,
123 <R as pallet_evm::Config>::AddressMapping: AddressMapping<R::AccountId>,
124 R::RuntimeCall: Dispatchable<PostInfo = PostDispatchInfo> + GetDispatchInfo,
125 R::RuntimeCall: From<pallet_dapp_staking::Call<R>>,
126{
127 #[precompile::public("read_current_era()")]
131 #[precompile::view]
132 fn read_current_era(handle: &mut impl PrecompileHandle) -> EvmResult<U256> {
133 handle.record_db_read::<R>(8 + ProtocolState::max_encoded_len())?;
137
138 let current_era = ActiveProtocolState::<R>::get().era();
139
140 Ok(current_era.into())
141 }
142
143 #[precompile::public("read_unbonding_period()")]
145 #[precompile::view]
146 fn read_unbonding_period(_: &mut impl PrecompileHandle) -> EvmResult<U256> {
147 Ok(<R as pallet_dapp_staking::Config>::UnlockingPeriod::get().into())
149 }
150
151 #[precompile::public("read_era_reward(uint32)")]
155 #[precompile::view]
156 fn read_era_reward(handle: &mut impl PrecompileHandle, era: u32) -> EvmResult<u128> {
157 handle.record_db_read::<R>(12 + EraRewardSpanFor::<R>::max_encoded_len())?;
161
162 let era_span_index = DAppStaking::<R>::era_reward_span_index(era);
164 let reward_span = EraRewards::<R>::get(&era_span_index).unwrap_or_default();
165
166 let reward = reward_span.get(era).map_or(Zero::zero(), |r| {
168 r.staker_reward_pool().saturating_add(r.dapp_reward_pool())
169 });
170
171 Ok(reward)
172 }
173
174 #[precompile::public("read_era_staked(uint32)")]
181 #[precompile::view]
182 fn read_era_staked(handle: &mut impl PrecompileHandle, era: u32) -> EvmResult<u128> {
183 handle.record_db_read::<R>(8 + ProtocolState::max_encoded_len())?;
187
188 let current_era = ActiveProtocolState::<R>::get().era();
189
190 if era < current_era {
195 handle.record_db_read::<R>(20 + EraRewardSpanFor::<R>::max_encoded_len())?;
199
200 let era_span_index = DAppStaking::<R>::era_reward_span_index(era);
201 let reward_span = EraRewards::<R>::get(&era_span_index).unwrap_or_default();
202
203 let staked = reward_span.get(era).map_or(Zero::zero(), |r| r.staked());
204
205 Ok(staked.into())
206 } else if era == current_era || era == current_era.saturating_add(1) {
207 handle.record_db_read::<R>(8 + EraInfo::max_encoded_len())?;
211
212 let current_era_info = CurrentEraInfo::<R>::get();
213
214 if era == current_era {
215 Ok(current_era_info.current_stake_amount().total())
216 } else {
217 Ok(current_era_info.next_stake_amount().total())
218 }
219 } else {
220 Err(RevertReason::custom("Era is in the future").into())
221 }
222 }
223
224 #[precompile::public("read_staked_amount(bytes)")]
226 #[precompile::view]
227 fn read_staked_amount(
228 handle: &mut impl PrecompileHandle,
229 staker: DynamicAddress,
230 ) -> EvmResult<u128> {
231 handle.record_db_read::<R>(
237 24 + AccountLedgerFor::<R>::max_encoded_len()
238 + ProtocolState::max_encoded_len()
239 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len(),
240 )?;
241
242 let staker = Self::parse_input_address(staker.into())?;
243
244 let ledger = Ledger::<R>::get(&staker);
246 log::trace!(target: "ds-precompile", "read_staked_amount for account: {:?}, ledger: {:?}", staker, ledger);
247
248 let current_period_number = ActiveProtocolState::<R>::get().period_number();
250
251 Ok(ledger.staked_amount(current_period_number))
252 }
253
254 #[precompile::public("read_staked_amount_on_contract(address,bytes)")]
256 #[precompile::view]
257 fn read_staked_amount_on_contract(
258 handle: &mut impl PrecompileHandle,
259 contract_h160: Address,
260 staker: DynamicAddress,
261 ) -> EvmResult<u128> {
262 handle.record_db_read::<R>(
268 24 + ProtocolState::max_encoded_len()
269 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
270 + SingularStakingInfo::max_encoded_len(),
271 )?;
272
273 let smart_contract =
274 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
275
276 let staker = Self::parse_input_address(staker.into())?;
278
279 let staking_info = StakerInfo::<R>::get(&staker, &smart_contract).unwrap_or_default();
281 log::trace!(target: "ds-precompile", "read_staked_amount_on_contract for account:{:?}, staking_info: {:?}", staker, staking_info);
282
283 let current_period_number = ActiveProtocolState::<R>::get().period_number();
285
286 if staking_info.period_number() == current_period_number {
287 Ok(staking_info.total_staked_amount())
288 } else {
289 Ok(0_u128)
290 }
291 }
292
293 #[precompile::public("read_contract_stake(address)")]
295 #[precompile::view]
296 fn read_contract_stake(
297 handle: &mut impl PrecompileHandle,
298 contract_h160: Address,
299 ) -> EvmResult<u128> {
300 handle.record_db_read::<R>(
308 36 + ProtocolState::max_encoded_len()
309 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
310 + DAppInfoFor::<R>::max_encoded_len()
311 + ContractStakeAmount::max_encoded_len(),
312 )?;
313
314 let smart_contract =
315 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
316
317 let current_period_number = ActiveProtocolState::<R>::get().period_number();
318 let dapp_info = match IntegratedDApps::<R>::get(&smart_contract) {
319 Some(dapp_info) => dapp_info,
320 None => {
321 return Ok(0_u128);
323 }
324 };
325
326 let contract_stake = ContractStake::<R>::get(&dapp_info.id());
328
329 Ok(contract_stake.total_staked_amount(current_period_number))
330 }
331
332 #[precompile::public("register(address)")]
335 fn register(_: &mut impl PrecompileHandle, _address: Address) -> EvmResult<bool> {
336 Err(RevertReason::custom("register via evm precompile is not allowed").into())
338 }
339
340 #[precompile::public("bond_and_stake(address,uint128)")]
345 fn bond_and_stake(
346 handle: &mut impl PrecompileHandle,
347 contract_h160: Address,
348 amount: u128,
349 ) -> EvmResult<bool> {
350 handle.record_db_read::<R>(
356 24 + AccountLedgerFor::<R>::max_encoded_len()
357 + ProtocolState::max_encoded_len()
358 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len(),
359 )?;
360
361 let smart_contract =
362 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
363 log::trace!(target: "ds-precompile", "bond_and_stake {:?}, {:?}", smart_contract, amount);
364
365 let origin = R::AddressMapping::into_account_id(handle.context().caller);
367 let protocol_state = ActiveProtocolState::<R>::get();
368 let ledger = Ledger::<R>::get(&origin);
369
370 let stakeable_amount = ledger.stakeable_amount(protocol_state.period_number());
372
373 if stakeable_amount < amount {
375 let delta = amount.saturating_sub(stakeable_amount);
376
377 let lock_call = pallet_dapp_staking::Call::<R>::lock { amount: delta };
378 RuntimeHelper::<R>::try_dispatch(handle, Some(origin.clone()).into(), lock_call, 0)?;
379 }
380
381 let stake_call = pallet_dapp_staking::Call::<R>::stake {
383 smart_contract,
384 amount,
385 };
386 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), stake_call, 0)?;
387
388 Ok(true)
389 }
390
391 #[precompile::public("unbond_and_unstake(address,uint128)")]
393 fn unbond_and_unstake(
394 handle: &mut impl PrecompileHandle,
395 contract_h160: Address,
396 amount: u128,
397 ) -> EvmResult<bool> {
398 handle.record_db_read::<R>(
404 24 + ProtocolState::max_encoded_len()
405 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
406 + SingularStakingInfo::max_encoded_len(),
407 )?;
408
409 let smart_contract =
410 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
411 let origin = R::AddressMapping::into_account_id(handle.context().caller);
412 log::trace!(target: "ds-precompile", "unbond_and_unstake {:?}, {:?}", smart_contract, amount);
413
414 let protocol_state = ActiveProtocolState::<R>::get();
416 let staker_info = StakerInfo::<R>::get(&origin, &smart_contract).unwrap_or_default();
417
418 if staker_info.period_number() == protocol_state.period_number() {
420 let unstake_call = pallet_dapp_staking::Call::<R>::unstake {
421 smart_contract,
422 amount,
423 };
424 RuntimeHelper::<R>::try_dispatch(handle, Some(origin.clone()).into(), unstake_call, 0)?;
425 }
426
427 let unlock_call = pallet_dapp_staking::Call::<R>::unlock { amount };
429 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), unlock_call, 0)?;
430
431 Ok(true)
432 }
433
434 #[precompile::public("withdraw_unbonded()")]
436 fn withdraw_unbonded(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
437 let origin = R::AddressMapping::into_account_id(handle.context().caller);
438 let call = pallet_dapp_staking::Call::<R>::claim_unlocked {};
439
440 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
441
442 Ok(true)
443 }
444
445 #[precompile::public("claim_dapp(address,uint128)")]
447 fn claim_dapp(
448 handle: &mut impl PrecompileHandle,
449 contract_h160: Address,
450 era: u128,
451 ) -> EvmResult<bool> {
452 let smart_contract =
453 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
454
455 let era = era
457 .try_into()
458 .map_err::<Revert, _>(|_| RevertReason::value_is_too_large("era type").into())
459 .in_field("era")?;
460
461 log::trace!(target: "ds-precompile", "claim_dapp {:?}, era {:?}", smart_contract, era);
462
463 let origin = R::AddressMapping::into_account_id(handle.context().caller);
464 let call = pallet_dapp_staking::Call::<R>::claim_dapp_reward {
465 smart_contract,
466 era,
467 };
468
469 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
470
471 Ok(true)
472 }
473
474 #[precompile::public("claim_staker(address)")]
478 fn claim_staker(
479 handle: &mut impl PrecompileHandle,
480 _contract_h160: Address,
481 ) -> EvmResult<bool> {
482 let origin = R::AddressMapping::into_account_id(handle.context().caller);
483 let call = pallet_dapp_staking::Call::<R>::claim_staker_rewards {};
484
485 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
486
487 Ok(true)
488 }
489
490 #[precompile::public("set_reward_destination(uint8)")]
494 fn set_reward_destination(_: &mut impl PrecompileHandle, _destination: u8) -> EvmResult<bool> {
495 Err(RevertReason::custom("Setting reward destination is no longer supported.").into())
496 }
497
498 #[precompile::public("withdraw_from_unregistered(address)")]
500 fn withdraw_from_unregistered(
501 handle: &mut impl PrecompileHandle,
502 contract_h160: Address,
503 ) -> EvmResult<bool> {
504 let smart_contract =
505 <R as pallet_dapp_staking::Config>::SmartContract::evm(contract_h160.into());
506 log::trace!(target: "ds-precompile", "withdraw_from_unregistered {:?}", smart_contract);
507
508 let origin = R::AddressMapping::into_account_id(handle.context().caller);
509 let call = pallet_dapp_staking::Call::<R>::unstake_from_unregistered { smart_contract };
510
511 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), call, 0)?;
512
513 Ok(true)
514 }
515
516 #[precompile::public("nomination_transfer(address,uint128,address)")]
520 fn nomination_transfer(
521 handle: &mut impl PrecompileHandle,
522 origin_contract_h160: Address,
523 amount: u128,
524 target_contract_h160: Address,
525 ) -> EvmResult<bool> {
526 handle.record_db_read::<R>(
530 16 + <R as pallet_dapp_staking::Config>::SmartContract::max_encoded_len()
531 + SingularStakingInfo::max_encoded_len(),
532 )?;
533
534 let origin_smart_contract =
535 <R as pallet_dapp_staking::Config>::SmartContract::evm(origin_contract_h160.into());
536 let target_smart_contract =
537 <R as pallet_dapp_staking::Config>::SmartContract::evm(target_contract_h160.into());
538 log::trace!(target: "ds-precompile", "nomination_transfer {:?} {:?} {:?}", origin_smart_contract, amount, target_smart_contract);
539
540 let origin = R::AddressMapping::into_account_id(handle.context().caller);
542 let staker_info = StakerInfo::<R>::get(&origin, &origin_smart_contract).unwrap_or_default();
543
544 let staked_amount = staker_info.total_staked_amount();
547 let minimum_allowed_stake_amount =
548 <R as pallet_dapp_staking::Config>::MinimumStakeAmount::get();
549
550 let stake_amount = if staked_amount > 0
556 && staked_amount.saturating_sub(amount) < minimum_allowed_stake_amount
557 {
558 staked_amount
559 } else {
560 amount
561 };
562
563 let unstake_call = pallet_dapp_staking::Call::<R>::unstake {
565 smart_contract: origin_smart_contract,
566 amount,
567 };
568 RuntimeHelper::<R>::try_dispatch(handle, Some(origin.clone()).into(), unstake_call, 0)?;
569
570 let stake_call = pallet_dapp_staking::Call::<R>::stake {
572 smart_contract: target_smart_contract,
573 amount: stake_amount,
574 };
575 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), stake_call, 0)?;
576
577 Ok(true)
578 }
579
580 #[precompile::public("protocol_state()")]
584 #[precompile::view]
585 fn protocol_state(handle: &mut impl PrecompileHandle) -> EvmResult<PrecompileProtocolState> {
586 handle.record_db_read::<R>(8 + ProtocolState::max_encoded_len())?;
590
591 let protocol_state = ActiveProtocolState::<R>::get();
592
593 Ok(PrecompileProtocolState {
594 era: protocol_state.era().into(),
595 period: protocol_state.period_number().into(),
596 subperiod: subperiod_id(&protocol_state.subperiod()),
597 })
598 }
599
600 #[precompile::public("unlocking_period()")]
602 #[precompile::view]
603 fn unlocking_period(_: &mut impl PrecompileHandle) -> EvmResult<U256> {
604 Ok(DAppStaking::<R>::unlocking_period().into())
606 }
607
608 #[precompile::public("lock(uint128)")]
610 fn lock(handle: &mut impl PrecompileHandle, amount: u128) -> EvmResult<bool> {
611 let origin = R::AddressMapping::into_account_id(handle.context().caller);
613 let lock_call = pallet_dapp_staking::Call::<R>::lock { amount };
614 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), lock_call, 0)?;
615
616 Ok(true)
617 }
618
619 #[precompile::public("unlock(uint128)")]
621 fn unlock(handle: &mut impl PrecompileHandle, amount: u128) -> EvmResult<bool> {
622 let origin = R::AddressMapping::into_account_id(handle.context().caller);
624 let unlock_call = pallet_dapp_staking::Call::<R>::unlock { amount };
625 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), unlock_call, 0)?;
626
627 Ok(true)
628 }
629
630 #[precompile::public("claim_unlocked()")]
632 fn claim_unlocked(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
633 let origin = R::AddressMapping::into_account_id(handle.context().caller);
635 let claim_unlocked_call = pallet_dapp_staking::Call::<R>::claim_unlocked {};
636 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), claim_unlocked_call, 0)?;
637
638 Ok(true)
639 }
640
641 #[precompile::public("stake((uint8,bytes),uint128)")]
643 fn stake(
644 handle: &mut impl PrecompileHandle,
645 smart_contract: SmartContractV2,
646 amount: Balance,
647 ) -> EvmResult<bool> {
648 let smart_contract = Self::decode_smart_contract(smart_contract)?;
649
650 let origin = R::AddressMapping::into_account_id(handle.context().caller);
652 let stake_call = pallet_dapp_staking::Call::<R>::stake {
653 smart_contract,
654 amount,
655 };
656 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), stake_call, 0)?;
657
658 Ok(true)
659 }
660
661 #[precompile::public("unstake((uint8,bytes),uint128)")]
663 fn unstake(
664 handle: &mut impl PrecompileHandle,
665 smart_contract: SmartContractV2,
666 amount: Balance,
667 ) -> EvmResult<bool> {
668 let smart_contract = Self::decode_smart_contract(smart_contract)?;
669
670 let origin = R::AddressMapping::into_account_id(handle.context().caller);
672 let unstake_call = pallet_dapp_staking::Call::<R>::unstake {
673 smart_contract,
674 amount,
675 };
676 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), unstake_call, 0)?;
677
678 Ok(true)
679 }
680
681 #[precompile::public("claim_staker_rewards()")]
683 fn claim_staker_rewards(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
684 let origin = R::AddressMapping::into_account_id(handle.context().caller);
686 let claim_staker_rewards_call = pallet_dapp_staking::Call::<R>::claim_staker_rewards {};
687 RuntimeHelper::<R>::try_dispatch(
688 handle,
689 Some(origin).into(),
690 claim_staker_rewards_call,
691 0,
692 )?;
693
694 Ok(true)
695 }
696
697 #[precompile::public("claim_bonus_reward((uint8,bytes))")]
699 fn claim_bonus_reward(
700 handle: &mut impl PrecompileHandle,
701 smart_contract: SmartContractV2,
702 ) -> EvmResult<bool> {
703 let smart_contract = Self::decode_smart_contract(smart_contract)?;
704
705 let origin = R::AddressMapping::into_account_id(handle.context().caller);
707 let claim_bonus_reward_call =
708 pallet_dapp_staking::Call::<R>::claim_bonus_reward { smart_contract };
709 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), claim_bonus_reward_call, 0)?;
710
711 Ok(true)
712 }
713
714 #[precompile::public("claim_bonus_reward((uint8,bytes),uint256)")]
716 fn claim_dapp_reward(
717 handle: &mut impl PrecompileHandle,
718 smart_contract: SmartContractV2,
719 era: U256,
720 ) -> EvmResult<bool> {
721 let smart_contract = Self::decode_smart_contract(smart_contract)?;
722 let era = era
723 .try_into()
724 .map_err::<Revert, _>(|_| RevertReason::value_is_too_large("Era number.").into())
725 .in_field("era")?;
726
727 let origin = R::AddressMapping::into_account_id(handle.context().caller);
729 let claim_dapp_reward_call = pallet_dapp_staking::Call::<R>::claim_dapp_reward {
730 smart_contract,
731 era,
732 };
733 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), claim_dapp_reward_call, 0)?;
734
735 Ok(true)
736 }
737
738 #[precompile::public("unstake_from_unregistered((uint8,bytes))")]
740 fn unstake_from_unregistered(
741 handle: &mut impl PrecompileHandle,
742 smart_contract: SmartContractV2,
743 ) -> EvmResult<bool> {
744 let smart_contract = Self::decode_smart_contract(smart_contract)?;
745
746 let origin = R::AddressMapping::into_account_id(handle.context().caller);
748 let unstake_from_unregistered_call =
749 pallet_dapp_staking::Call::<R>::unstake_from_unregistered { smart_contract };
750 RuntimeHelper::<R>::try_dispatch(
751 handle,
752 Some(origin).into(),
753 unstake_from_unregistered_call,
754 0,
755 )?;
756
757 Ok(true)
758 }
759
760 #[precompile::public("cleanup_expired_entries()")]
762 fn cleanup_expired_entries(handle: &mut impl PrecompileHandle) -> EvmResult<bool> {
763 let origin = R::AddressMapping::into_account_id(handle.context().caller);
765 let cleanup_expired_entries_call =
766 pallet_dapp_staking::Call::<R>::cleanup_expired_entries {};
767 RuntimeHelper::<R>::try_dispatch(
768 handle,
769 Some(origin).into(),
770 cleanup_expired_entries_call,
771 0,
772 )?;
773
774 Ok(true)
775 }
776
777 #[precompile::public("move_stake((uint8,bytes),(uint8,bytes),uint128)")]
778 fn move_stake(
779 handle: &mut impl PrecompileHandle,
780 source_contract: SmartContractV2,
781 destination_contract: SmartContractV2,
782 amount: Balance,
783 ) -> EvmResult<bool> {
784 let source_contract = Self::decode_smart_contract(source_contract)?;
785 let destination_contract = Self::decode_smart_contract(destination_contract)?;
786
787 let origin = R::AddressMapping::into_account_id(handle.context().caller);
789 let move_call = pallet_dapp_staking::Call::<R>::move_stake {
790 source_contract,
791 destination_contract,
792 amount,
793 };
794 RuntimeHelper::<R>::try_dispatch(handle, Some(origin).into(), move_call, 0)?;
795
796 Ok(true)
797 }
798
799 pub(crate) fn decode_smart_contract(
803 smart_contract: SmartContractV2,
804 ) -> EvmResult<<R as pallet_dapp_staking::Config>::SmartContract> {
805 let smart_contract = match smart_contract.contract_type {
806 SmartContractTypes::Evm => {
807 ensure!(
808 smart_contract.address.as_bytes().len() == 20,
809 revert("Invalid address length for Astar EVM smart contract.")
810 );
811 let h160_address = H160::from_slice(smart_contract.address.as_bytes());
812 <R as pallet_dapp_staking::Config>::SmartContract::evm(h160_address)
813 }
814 SmartContractTypes::Wasm => {
815 ensure!(
816 smart_contract.address.as_bytes().len() == 32,
817 revert("Invalid address length for Astar WASM smart contract.")
818 );
819 let mut staker_bytes = [0_u8; 32];
820 staker_bytes[..].clone_from_slice(&smart_contract.address.as_bytes());
821
822 <R as pallet_dapp_staking::Config>::SmartContract::wasm(staker_bytes.into())
823 }
824 };
825
826 Ok(smart_contract)
827 }
828
829 pub(crate) fn parse_input_address(staker_vec: Vec<u8>) -> EvmResult<R::AccountId> {
831 let staker: R::AccountId = match staker_vec.len() {
832 32 => {
834 let mut staker_bytes = [0_u8; 32];
835 staker_bytes[..].clone_from_slice(&staker_vec[0..32]);
836
837 staker_bytes.into()
838 }
839 20 => {
841 let mut staker_bytes = [0_u8; 20];
842 staker_bytes[..].clone_from_slice(&staker_vec[0..20]);
843
844 R::AddressMapping::into_account_id(staker_bytes.into())
845 }
846 _ => {
847 return Err(revert("Error while parsing staker's address"));
849 }
850 };
851
852 Ok(staker)
853 }
854}
855
856pub(crate) fn subperiod_id(subperiod: &Subperiod) -> u8 {
858 match subperiod {
859 Subperiod::Voting => 0,
860 Subperiod::BuildAndEarn => 1,
861 }
862}