1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
34// Polkadot 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.
89// Polkadot 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.
1314// You should have received a copy of the GNU General Public License
15// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
1617use crate::xcm_config;
18use alloc::{boxed::Box, vec};
19use codec::{Decode, Encode};
20use core::marker::PhantomData;
21use frame_support::pallet_prelude::DispatchResult;
22use frame_system::RawOrigin;
23use polkadot_primitives::Balance;
24use polkadot_runtime_common::identity_migrator::{OnReapIdentity, WeightInfo};
25use rococo_runtime_constants::currency::*;
26use xcm::{latest::prelude::*, VersionedLocation, VersionedXcm};
27use xcm_executor::traits::TransactAsset;
2829/// A type containing the encoding of the People Chain pallets in its runtime. Used to construct any
30/// remote calls. The codec index must correspond to the index of `IdentityMigrator` in the
31/// `construct_runtime` of the remote chain.
32#[derive(Encode, Decode)]
33enum PeopleRuntimePallets<AccountId: Encode> {
34#[codec(index = 248)]
35IdentityMigrator(IdentityMigratorCalls<AccountId>),
36}
3738/// Call encoding for the calls needed from the Identity Migrator pallet.
39#[derive(Encode, Decode)]
40enum IdentityMigratorCalls<AccountId: Encode> {
41#[codec(index = 1)]
42PokeDeposit(AccountId),
43}
4445/// Type that implements `OnReapIdentity` that will send the deposit needed to store the same
46/// information on a parachain, sends the deposit there, and then updates it.
47pub struct ToParachainIdentityReaper<Runtime, AccountId>(PhantomData<(Runtime, AccountId)>);
48impl<Runtime, AccountId> ToParachainIdentityReaper<Runtime, AccountId> {
49/// Calculate the balance needed on the remote chain based on the `IdentityInfo` and `Subs` on
50 /// this chain. The total includes:
51 ///
52 /// - Identity basic deposit
53 /// - `IdentityInfo` byte deposit
54 /// - Sub accounts deposit
55 /// - 2x existential deposit (1 for account existence, 1 such that the user can transact)
56fn calculate_remote_deposit(bytes: u32, subs: u32) -> Balance {
57// Remote deposit constants. Parachain uses `deposit / 100`
58 // Source:
59 // https://github.com/paritytech/polkadot-sdk/blob/a146918/cumulus/parachains/common/src/rococo.rs#L29
60 //
61 // Parachain Deposit Configuration:
62 //
63 // pub const BasicDeposit: Balance = deposit(1, 17);
64 // pub const ByteDeposit: Balance = deposit(0, 1);
65 // pub const SubAccountDeposit: Balance = deposit(1, 53);
66 // pub const EXISTENTIAL_DEPOSIT: Balance = constants::currency::EXISTENTIAL_DEPOSIT / 10;
67let para_basic_deposit = deposit(1, 17) / 100;
68let para_byte_deposit = deposit(0, 1) / 100;
69let para_sub_account_deposit = deposit(1, 53) / 100;
70let para_existential_deposit = EXISTENTIAL_DEPOSIT / 10;
7172// pallet deposits
73let id_deposit =
74 para_basic_deposit.saturating_add(para_byte_deposit.saturating_mul(bytes as Balance));
75let subs_deposit = para_sub_account_deposit.saturating_mul(subs as Balance);
7677 id_deposit
78 .saturating_add(subs_deposit)
79 .saturating_add(para_existential_deposit.saturating_mul(2))
80 }
81}
8283// Note / Warning: This implementation should only be used in a transactional context. If not, then
84// an error could result in assets being burned.
85impl<Runtime, AccountId> OnReapIdentity<AccountId> for ToParachainIdentityReaper<Runtime, AccountId>
86where
87Runtime: frame_system::Config + pallet_xcm::Config,
88 AccountId: Into<[u8; 32]> + Clone + Encode,
89{
90fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult {
91use crate::{
92 impls::IdentityMigratorCalls::PokeDeposit,
93 weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights,
94 };
9596let total_to_send = Self::calculate_remote_deposit(fields, subs);
9798// define asset / destination from relay perspective
99let roc = Asset { id: AssetId(Here.into_location()), fun: Fungible(total_to_send) };
100// People Chain: ParaId 1004
101let destination: Location = Location::new(0, Parachain(1004));
102103// Do `check_out` accounting since the XCM Executor's `InitiateTeleport` doesn't support
104 // unpaid teleports.
105106 // withdraw the asset from `who`
107let who_origin =
108 Junction::AccountId32 { network: None, id: who.clone().into() }.into_location();
109let _withdrawn = xcm_config::LocalAssetTransactor::withdraw_asset(&roc, &who_origin, None)
110 .map_err(|err| {
111log::error!(
112 target: "runtime::on_reap_identity",
113"withdraw_asset(what: {:?}, who_origin: {:?}) error: {:?}",
114 roc, who_origin, err
115 );
116 pallet_xcm::Error::<Runtime>::LowBalance
117 })?;
118119// check out
120xcm_config::LocalAssetTransactor::can_check_out(
121&destination,
122&roc,
123// not used in AssetTransactor
124&XcmContext { origin: None, message_id: [0; 32], topic: None },
125 )
126 .map_err(|err| {
127log::error!(
128 target: "runtime::on_reap_identity",
129"can_check_out(destination: {:?}, asset: {:?}, _) error: {:?}",
130 destination, roc, err
131 );
132 pallet_xcm::Error::<Runtime>::CannotCheckOutTeleport
133 })?;
134 xcm_config::LocalAssetTransactor::check_out(
135&destination,
136&roc,
137// not used in AssetTransactor
138&XcmContext { origin: None, message_id: [0; 32], topic: None },
139 );
140141// reanchor
142let roc_reanchored: Assets =
143vec![Asset { id: AssetId(Location::new(1, Here)), fun: Fungible(total_to_send) }]
144 .into();
145146let poke = PeopleRuntimePallets::<AccountId>::IdentityMigrator(PokeDeposit(who.clone()));
147let remote_weight_limit = MigratorWeights::<Runtime>::poke_deposit().saturating_mul(2);
148149// Actual program to execute on People Chain.
150let program: Xcm<()> = Xcm(vec![
151// Unpaid as this is constructed by the system, once per user. The user shouldn't have
152 // their balance reduced by teleport fees for the favor of migrating.
153UnpaidExecution { weight_limit: Unlimited, check_origin: None },
154// Receive the asset into holding.
155ReceiveTeleportedAsset(roc_reanchored),
156// Deposit into the user's account.
157DepositAsset {
158 assets: Wild(AllCounted(1)),
159 beneficiary: Junction::AccountId32 { network: None, id: who.clone().into() }
160 .into_location()
161 .into(),
162 },
163// Poke the deposit to reserve the appropriate amount on the parachain.
164Transact {
165 origin_kind: OriginKind::Superuser,
166 fallback_max_weight: Some(remote_weight_limit),
167 call: poke.encode().into(),
168 },
169 ]);
170171// send
172<pallet_xcm::Pallet<Runtime>>::send(
173 RawOrigin::Root.into(),
174 Box::new(VersionedLocation::from(destination)),
175 Box::new(VersionedXcm::from(program)),
176 )?;
177Ok(())
178 }
179180#[cfg(feature = "runtime-benchmarks")]
181fn ensure_successful_identity_reaping(_: &AccountId, _: u32, _: u32) {
182crate::Dmp::make_parachain_reachable(1004);
183 }
184}