pallet_asset_conversion_ops/
lib.rs1#![deny(missing_docs)]
33#![cfg_attr(not(feature = "std"), no_std)]
34
35#[cfg(feature = "runtime-benchmarks")]
36mod benchmarking;
37#[cfg(test)]
38mod mock;
39#[cfg(test)]
40mod tests;
41pub mod weights;
42pub use pallet::*;
43pub use weights::WeightInfo;
44
45extern crate alloc;
46
47use alloc::boxed::Box;
48use frame_support::traits::{
49 fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate},
50 fungibles::{roles::ResetTeam, Inspect, Mutate, Refund},
51 tokens::{Fortitude, Precision, Preservation},
52 AccountTouch,
53};
54use pallet_asset_conversion::{PoolLocator, Pools};
55use sp_runtime::traits::{TryConvert, Zero};
56
57#[frame_support::pallet]
58pub mod pallet {
59 use super::*;
60 use frame_support::pallet_prelude::*;
61 use frame_system::pallet_prelude::*;
62
63 #[pallet::pallet]
64 pub struct Pallet<T>(_);
65
66 #[pallet::config]
67 pub trait Config:
68 pallet_asset_conversion::Config<
69 PoolId = (
70 <Self as pallet_asset_conversion::Config>::AssetKind,
71 <Self as pallet_asset_conversion::Config>::AssetKind,
72 ),
73 > + frame_system::Config
74 {
75 #[allow(deprecated)]
77 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
78
79 type PriorAccountIdConverter: for<'a> TryConvert<
82 &'a (Self::AssetKind, Self::AssetKind),
83 Self::AccountId,
84 >;
85
86 type AssetsRefund: Refund<
89 Self::AccountId,
90 AssetId = Self::AssetKind,
91 Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
92 >;
93
94 type PoolAssetsRefund: Refund<
98 Self::AccountId,
99 AssetId = Self::PoolAssetId,
100 Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
101 >;
102
103 type PoolAssetsTeam: ResetTeam<Self::AccountId, AssetId = Self::PoolAssetId>;
106
107 type DepositAsset: FungibleMutate<Self::AccountId>;
111
112 type WeightInfo: WeightInfo;
114 }
115
116 #[pallet::event]
118 #[pallet::generate_deposit(pub(super) fn deposit_event)]
119 pub enum Event<T: Config> {
120 MigratedToNewAccount {
122 pool_id: T::PoolId,
124 prior_account: T::AccountId,
126 new_account: T::AccountId,
128 },
129 }
130
131 #[pallet::error]
132 pub enum Error<T> {
133 InvalidAssetPair,
135 PoolNotFound,
137 ZeroBalance,
139 PartialTransfer,
141 }
142
143 #[pallet::call]
145 impl<T: Config> Pallet<T> {
146 #[pallet::call_index(0)]
151 #[pallet::weight(<T as Config>::WeightInfo::migrate_to_new_account())]
152 pub fn migrate_to_new_account(
153 origin: OriginFor<T>,
154 asset1: Box<T::AssetKind>,
155 asset2: Box<T::AssetKind>,
156 ) -> DispatchResultWithPostInfo {
157 ensure_signed(origin)?;
158
159 let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
160 .map_err(|_| Error::<T>::InvalidAssetPair)?;
161 let info = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
162
163 let (prior_account, new_account) =
164 Self::addresses(&pool_id).ok_or(Error::<T>::InvalidAssetPair)?;
165
166 let (asset1, asset2) = pool_id.clone();
167
168 let balance1 = T::Assets::total_balance(asset1.clone(), &prior_account);
170 let balance2 = T::Assets::total_balance(asset2.clone(), &prior_account);
171 let lp_balance = T::PoolAssets::total_balance(info.lp_token.clone(), &prior_account);
172
173 ensure!(!balance1.is_zero(), Error::<T>::ZeroBalance);
174 ensure!(!balance2.is_zero(), Error::<T>::ZeroBalance);
175 ensure!(!lp_balance.is_zero(), Error::<T>::ZeroBalance);
176
177 let deposit_asset_ed = T::DepositAsset::minimum_balance();
185
186 if let Some((depositor, deposit)) =
187 T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
188 {
189 T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
190 T::Assets::touch(asset1.clone(), &new_account, &depositor)?;
191 }
192
193 if let Some((depositor, deposit)) =
194 T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
195 {
196 T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
197 T::Assets::touch(asset2.clone(), &new_account, &depositor)?;
198 }
199
200 if let Some((depositor, deposit)) =
201 T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
202 {
203 T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
204 T::PoolAssets::touch(info.lp_token.clone(), &new_account, &depositor)?;
205 }
206
207 ensure!(
210 balance1 ==
211 T::Assets::transfer(
212 asset1.clone(),
213 &prior_account,
214 &new_account,
215 balance1,
216 Preservation::Expendable,
217 )?,
218 Error::<T>::PartialTransfer
219 );
220
221 ensure!(
222 balance2 ==
223 T::Assets::transfer(
224 asset2.clone(),
225 &prior_account,
226 &new_account,
227 balance2,
228 Preservation::Expendable,
229 )?,
230 Error::<T>::PartialTransfer
231 );
232
233 ensure!(
234 lp_balance ==
235 T::PoolAssets::transfer(
236 info.lp_token.clone(),
237 &prior_account,
238 &new_account,
239 lp_balance,
240 Preservation::Expendable,
241 )?,
242 Error::<T>::PartialTransfer
243 );
244
245 if let Some((depositor, deposit)) =
248 T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
249 {
250 T::AssetsRefund::refund(asset1.clone(), prior_account.clone())?;
251 T::DepositAsset::burn_from(
252 &depositor,
253 deposit + deposit_asset_ed,
254 Preservation::Expendable,
255 Precision::Exact,
256 Fortitude::Force,
257 )?;
258 }
259
260 if let Some((depositor, deposit)) =
261 T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
262 {
263 T::AssetsRefund::refund(asset2.clone(), prior_account.clone())?;
264 T::DepositAsset::burn_from(
265 &depositor,
266 deposit + deposit_asset_ed,
267 Preservation::Expendable,
268 Precision::Exact,
269 Fortitude::Force,
270 )?;
271 }
272
273 if let Some((depositor, deposit)) =
274 T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
275 {
276 T::PoolAssetsRefund::refund(info.lp_token.clone(), prior_account.clone())?;
277 T::DepositAsset::burn_from(
278 &depositor,
279 deposit + deposit_asset_ed,
280 Preservation::Expendable,
281 Precision::Exact,
282 Fortitude::Force,
283 )?;
284 }
285
286 T::PoolAssetsTeam::reset_team(
287 info.lp_token,
288 new_account.clone(),
289 new_account.clone(),
290 new_account.clone(),
291 new_account.clone(),
292 )?;
293
294 Self::deposit_event(Event::MigratedToNewAccount {
295 pool_id,
296 prior_account,
297 new_account,
298 });
299
300 Ok(Pays::No.into())
301 }
302 }
303
304 impl<T: Config> Pallet<T> {
305 #[cfg(not(any(test, feature = "runtime-benchmarks")))]
308 fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
309 match (
310 T::PriorAccountIdConverter::try_convert(pool_id),
311 T::PoolLocator::address(pool_id),
312 ) {
313 (Ok(a), Ok(b)) if a != b => Some((a, b)),
314 _ => None,
315 }
316 }
317
318 #[cfg(any(test, feature = "runtime-benchmarks"))]
327 pub(crate) fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
328 match (
329 T::PoolLocator::address(pool_id),
330 T::PriorAccountIdConverter::try_convert(pool_id),
331 ) {
332 (Ok(a), Ok(b)) if a != b => Some((a, b)),
333 _ => None,
334 }
335 }
336 }
337}