pezpallet_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 pezpallet::*;
43pub use weights::WeightInfo;
44
45extern crate alloc;
46
47use alloc::boxed::Box;
48use pezframe_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 pezpallet_asset_conversion::{PoolLocator, Pools};
55use pezsp_runtime::traits::{TryConvert, Zero};
56
57#[pezframe_support::pezpallet]
58pub mod pezpallet {
59 use super::*;
60 use pezframe_support::pezpallet_prelude::*;
61 use pezframe_system::pezpallet_prelude::*;
62
63 #[pezpallet::pezpallet]
64 pub struct Pezpallet<T>(_);
65
66 #[pezpallet::config]
67 pub trait Config:
68 pezpallet_asset_conversion::Config<
69 PoolId = (
70 <Self as pezpallet_asset_conversion::Config>::AssetKind,
71 <Self as pezpallet_asset_conversion::Config>::AssetKind,
72 ),
73 > + pezframe_system::Config
74 {
75 #[allow(deprecated)]
77 type RuntimeEvent: From<Event<Self>>
78 + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
79
80 type PriorAccountIdConverter: for<'a> TryConvert<
83 &'a (Self::AssetKind, Self::AssetKind),
84 Self::AccountId,
85 >;
86
87 type AssetsRefund: Refund<
90 Self::AccountId,
91 AssetId = Self::AssetKind,
92 Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
93 >;
94
95 type PoolAssetsRefund: Refund<
99 Self::AccountId,
100 AssetId = Self::PoolAssetId,
101 Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
102 >;
103
104 type PoolAssetsTeam: ResetTeam<Self::AccountId, AssetId = Self::PoolAssetId>;
107
108 type DepositAsset: FungibleMutate<Self::AccountId>;
112
113 type WeightInfo: WeightInfo;
115 }
116
117 #[pezpallet::event]
119 #[pezpallet::generate_deposit(pub(super) fn deposit_event)]
120 pub enum Event<T: Config> {
121 MigratedToNewAccount {
123 pool_id: T::PoolId,
125 prior_account: T::AccountId,
127 new_account: T::AccountId,
129 },
130 }
131
132 #[pezpallet::error]
133 pub enum Error<T> {
134 InvalidAssetPair,
136 PoolNotFound,
138 ZeroBalance,
140 PartialTransfer,
142 }
143
144 #[pezpallet::call]
146 impl<T: Config> Pezpallet<T> {
147 #[pezpallet::call_index(0)]
152 #[pezpallet::weight(<T as Config>::WeightInfo::migrate_to_new_account())]
153 pub fn migrate_to_new_account(
154 origin: OriginFor<T>,
155 asset1: Box<T::AssetKind>,
156 asset2: Box<T::AssetKind>,
157 ) -> DispatchResultWithPostInfo {
158 ensure_signed(origin)?;
159
160 let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
161 .map_err(|_| Error::<T>::InvalidAssetPair)?;
162 let info = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
163
164 let (prior_account, new_account) =
165 Self::addresses(&pool_id).ok_or(Error::<T>::InvalidAssetPair)?;
166
167 let (asset1, asset2) = pool_id.clone();
168
169 let balance1 = T::Assets::total_balance(asset1.clone(), &prior_account);
171 let balance2 = T::Assets::total_balance(asset2.clone(), &prior_account);
172 let lp_balance = T::PoolAssets::total_balance(info.lp_token.clone(), &prior_account);
173
174 ensure!(!balance1.is_zero(), Error::<T>::ZeroBalance);
175 ensure!(!balance2.is_zero(), Error::<T>::ZeroBalance);
176 ensure!(!lp_balance.is_zero(), Error::<T>::ZeroBalance);
177
178 let deposit_asset_ed = T::DepositAsset::minimum_balance();
186
187 if let Some((depositor, deposit)) =
188 T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
189 {
190 T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
191 T::Assets::touch(asset1.clone(), &new_account, &depositor)?;
192 }
193
194 if let Some((depositor, deposit)) =
195 T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
196 {
197 T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
198 T::Assets::touch(asset2.clone(), &new_account, &depositor)?;
199 }
200
201 if let Some((depositor, deposit)) =
202 T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
203 {
204 T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
205 T::PoolAssets::touch(info.lp_token.clone(), &new_account, &depositor)?;
206 }
207
208 ensure!(
211 balance1
212 == T::Assets::transfer(
213 asset1.clone(),
214 &prior_account,
215 &new_account,
216 balance1,
217 Preservation::Expendable,
218 )?,
219 Error::<T>::PartialTransfer
220 );
221
222 ensure!(
223 balance2
224 == T::Assets::transfer(
225 asset2.clone(),
226 &prior_account,
227 &new_account,
228 balance2,
229 Preservation::Expendable,
230 )?,
231 Error::<T>::PartialTransfer
232 );
233
234 ensure!(
235 lp_balance
236 == T::PoolAssets::transfer(
237 info.lp_token.clone(),
238 &prior_account,
239 &new_account,
240 lp_balance,
241 Preservation::Expendable,
242 )?,
243 Error::<T>::PartialTransfer
244 );
245
246 if let Some((depositor, deposit)) =
249 T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
250 {
251 T::AssetsRefund::refund(asset1.clone(), prior_account.clone())?;
252 T::DepositAsset::burn_from(
253 &depositor,
254 deposit + deposit_asset_ed,
255 Preservation::Expendable,
256 Precision::Exact,
257 Fortitude::Force,
258 )?;
259 }
260
261 if let Some((depositor, deposit)) =
262 T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
263 {
264 T::AssetsRefund::refund(asset2.clone(), prior_account.clone())?;
265 T::DepositAsset::burn_from(
266 &depositor,
267 deposit + deposit_asset_ed,
268 Preservation::Expendable,
269 Precision::Exact,
270 Fortitude::Force,
271 )?;
272 }
273
274 if let Some((depositor, deposit)) =
275 T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
276 {
277 T::PoolAssetsRefund::refund(info.lp_token.clone(), prior_account.clone())?;
278 T::DepositAsset::burn_from(
279 &depositor,
280 deposit + deposit_asset_ed,
281 Preservation::Expendable,
282 Precision::Exact,
283 Fortitude::Force,
284 )?;
285 }
286
287 T::PoolAssetsTeam::reset_team(
288 info.lp_token,
289 new_account.clone(),
290 new_account.clone(),
291 new_account.clone(),
292 new_account.clone(),
293 )?;
294
295 Self::deposit_event(Event::MigratedToNewAccount {
296 pool_id,
297 prior_account,
298 new_account,
299 });
300
301 Ok(Pays::No.into())
302 }
303 }
304
305 impl<T: Config> Pezpallet<T> {
306 #[cfg(not(any(test, feature = "runtime-benchmarks")))]
309 fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
310 match (
311 T::PriorAccountIdConverter::try_convert(pool_id),
312 T::PoolLocator::address(pool_id),
313 ) {
314 (Ok(a), Ok(b)) if a != b => Some((a, b)),
315 _ => None,
316 }
317 }
318
319 #[cfg(any(test, feature = "runtime-benchmarks"))]
328 pub(crate) fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
329 match (
330 T::PoolLocator::address(pool_id),
331 T::PriorAccountIdConverter::try_convert(pool_id),
332 ) {
333 (Ok(a), Ok(b)) if a != b => Some((a, b)),
334 _ => None,
335 }
336 }
337 }
338}