referrerpolicy=no-referrer-when-downgrade

rococo_runtime/
impls.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// 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.
8
9// 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.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17use 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;
28
29/// 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)]
35	IdentityMigrator(IdentityMigratorCalls<AccountId>),
36}
37
38/// Call encoding for the calls needed from the Identity Migrator pallet.
39#[derive(Encode, Decode)]
40enum IdentityMigratorCalls<AccountId: Encode> {
41	#[codec(index = 1)]
42	PokeDeposit(AccountId),
43}
44
45/// 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)
56	fn 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;
67		let para_basic_deposit = deposit(1, 17) / 100;
68		let para_byte_deposit = deposit(0, 1) / 100;
69		let para_sub_account_deposit = deposit(1, 53) / 100;
70		let para_existential_deposit = EXISTENTIAL_DEPOSIT / 10;
71
72		// pallet deposits
73		let id_deposit =
74			para_basic_deposit.saturating_add(para_byte_deposit.saturating_mul(bytes as Balance));
75		let subs_deposit = para_sub_account_deposit.saturating_mul(subs as Balance);
76
77		id_deposit
78			.saturating_add(subs_deposit)
79			.saturating_add(para_existential_deposit.saturating_mul(2))
80	}
81}
82
83// 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
87	Runtime: frame_system::Config + pallet_xcm::Config,
88	AccountId: Into<[u8; 32]> + Clone + Encode,
89{
90	fn on_reap_identity(who: &AccountId, fields: u32, subs: u32) -> DispatchResult {
91		use crate::{
92			impls::IdentityMigratorCalls::PokeDeposit,
93			weights::polkadot_runtime_common_identity_migrator::WeightInfo as MigratorWeights,
94		};
95
96		let total_to_send = Self::calculate_remote_deposit(fields, subs);
97
98		// define asset / destination from relay perspective
99		let roc = Asset { id: AssetId(Here.into_location()), fun: Fungible(total_to_send) };
100		// People Chain: ParaId 1004
101		let destination: Location = Location::new(0, Parachain(1004));
102
103		// Do `check_out` accounting since the XCM Executor's `InitiateTeleport` doesn't support
104		// unpaid teleports.
105
106		// withdraw the asset from `who`
107		let who_origin =
108			Junction::AccountId32 { network: None, id: who.clone().into() }.into_location();
109		let _withdrawn = xcm_config::LocalAssetTransactor::withdraw_asset(&roc, &who_origin, None)
110			.map_err(|err| {
111				log::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			})?;
118
119		// check out
120		xcm_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| {
127			log::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		);
140
141		// reanchor
142		let roc_reanchored: Assets =
143			vec![Asset { id: AssetId(Location::new(1, Here)), fun: Fungible(total_to_send) }]
144				.into();
145
146		let poke = PeopleRuntimePallets::<AccountId>::IdentityMigrator(PokeDeposit(who.clone()));
147		let remote_weight_limit = MigratorWeights::<Runtime>::poke_deposit().saturating_mul(2);
148
149		// Actual program to execute on People Chain.
150		let 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.
153			UnpaidExecution { weight_limit: Unlimited, check_origin: None },
154			// Receive the asset into holding.
155			ReceiveTeleportedAsset(roc_reanchored),
156			// Deposit into the user's account.
157			DepositAsset {
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.
164			Transact {
165				origin_kind: OriginKind::Superuser,
166				fallback_max_weight: Some(remote_weight_limit),
167				call: poke.encode().into(),
168			},
169		]);
170
171		// send
172		<pallet_xcm::Pallet<Runtime>>::send(
173			RawOrigin::Root.into(),
174			Box::new(VersionedLocation::from(destination)),
175			Box::new(VersionedXcm::from(program)),
176		)?;
177		Ok(())
178	}
179
180	#[cfg(feature = "runtime-benchmarks")]
181	fn ensure_successful_identity_reaping(_: &AccountId, _: u32, _: u32) {
182		crate::Dmp::make_parachain_reachable(1004);
183	}
184}