#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub mod weights;
pub use pezpallet::*;
pub use weights::WeightInfo;
extern crate alloc;
use alloc::boxed::Box;
use pezframe_support::traits::{
fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate},
fungibles::{roles::ResetTeam, Inspect, Mutate, Refund},
tokens::{Fortitude, Precision, Preservation},
AccountTouch,
};
use pezpallet_asset_conversion::{PoolLocator, Pools};
use pezsp_runtime::traits::{TryConvert, Zero};
#[pezframe_support::pezpallet]
pub mod pezpallet {
use super::*;
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
#[pezpallet::pezpallet]
pub struct Pezpallet<T>(_);
#[pezpallet::config]
pub trait Config:
pezpallet_asset_conversion::Config<
PoolId = (
<Self as pezpallet_asset_conversion::Config>::AssetKind,
<Self as pezpallet_asset_conversion::Config>::AssetKind,
),
> + pezframe_system::Config
{
#[allow(deprecated)]
type RuntimeEvent: From<Event<Self>>
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
type PriorAccountIdConverter: for<'a> TryConvert<
&'a (Self::AssetKind, Self::AssetKind),
Self::AccountId,
>;
type AssetsRefund: Refund<
Self::AccountId,
AssetId = Self::AssetKind,
Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
>;
type PoolAssetsRefund: Refund<
Self::AccountId,
AssetId = Self::PoolAssetId,
Balance = <Self::DepositAsset as FungibleInspect<Self::AccountId>>::Balance,
>;
type PoolAssetsTeam: ResetTeam<Self::AccountId, AssetId = Self::PoolAssetId>;
type DepositAsset: FungibleMutate<Self::AccountId>;
type WeightInfo: WeightInfo;
}
#[pezpallet::event]
#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
MigratedToNewAccount {
pool_id: T::PoolId,
prior_account: T::AccountId,
new_account: T::AccountId,
},
}
#[pezpallet::error]
pub enum Error<T> {
InvalidAssetPair,
PoolNotFound,
ZeroBalance,
PartialTransfer,
}
#[pezpallet::call]
impl<T: Config> Pezpallet<T> {
#[pezpallet::call_index(0)]
#[pezpallet::weight(<T as Config>::WeightInfo::migrate_to_new_account())]
pub fn migrate_to_new_account(
origin: OriginFor<T>,
asset1: Box<T::AssetKind>,
asset2: Box<T::AssetKind>,
) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
let pool_id = T::PoolLocator::pool_id(&asset1, &asset2)
.map_err(|_| Error::<T>::InvalidAssetPair)?;
let info = Pools::<T>::get(&pool_id).ok_or(Error::<T>::PoolNotFound)?;
let (prior_account, new_account) =
Self::addresses(&pool_id).ok_or(Error::<T>::InvalidAssetPair)?;
let (asset1, asset2) = pool_id.clone();
let balance1 = T::Assets::total_balance(asset1.clone(), &prior_account);
let balance2 = T::Assets::total_balance(asset2.clone(), &prior_account);
let lp_balance = T::PoolAssets::total_balance(info.lp_token.clone(), &prior_account);
ensure!(!balance1.is_zero(), Error::<T>::ZeroBalance);
ensure!(!balance2.is_zero(), Error::<T>::ZeroBalance);
ensure!(!lp_balance.is_zero(), Error::<T>::ZeroBalance);
let deposit_asset_ed = T::DepositAsset::minimum_balance();
if let Some((depositor, deposit)) =
T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
{
T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
T::Assets::touch(asset1.clone(), &new_account, &depositor)?;
}
if let Some((depositor, deposit)) =
T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
{
T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
T::Assets::touch(asset2.clone(), &new_account, &depositor)?;
}
if let Some((depositor, deposit)) =
T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
{
T::DepositAsset::mint_into(&depositor, deposit + deposit_asset_ed)?;
T::PoolAssets::touch(info.lp_token.clone(), &new_account, &depositor)?;
}
ensure!(
balance1
== T::Assets::transfer(
asset1.clone(),
&prior_account,
&new_account,
balance1,
Preservation::Expendable,
)?,
Error::<T>::PartialTransfer
);
ensure!(
balance2
== T::Assets::transfer(
asset2.clone(),
&prior_account,
&new_account,
balance2,
Preservation::Expendable,
)?,
Error::<T>::PartialTransfer
);
ensure!(
lp_balance
== T::PoolAssets::transfer(
info.lp_token.clone(),
&prior_account,
&new_account,
lp_balance,
Preservation::Expendable,
)?,
Error::<T>::PartialTransfer
);
if let Some((depositor, deposit)) =
T::AssetsRefund::deposit_held(asset1.clone(), prior_account.clone())
{
T::AssetsRefund::refund(asset1.clone(), prior_account.clone())?;
T::DepositAsset::burn_from(
&depositor,
deposit + deposit_asset_ed,
Preservation::Expendable,
Precision::Exact,
Fortitude::Force,
)?;
}
if let Some((depositor, deposit)) =
T::AssetsRefund::deposit_held(asset2.clone(), prior_account.clone())
{
T::AssetsRefund::refund(asset2.clone(), prior_account.clone())?;
T::DepositAsset::burn_from(
&depositor,
deposit + deposit_asset_ed,
Preservation::Expendable,
Precision::Exact,
Fortitude::Force,
)?;
}
if let Some((depositor, deposit)) =
T::PoolAssetsRefund::deposit_held(info.lp_token.clone(), prior_account.clone())
{
T::PoolAssetsRefund::refund(info.lp_token.clone(), prior_account.clone())?;
T::DepositAsset::burn_from(
&depositor,
deposit + deposit_asset_ed,
Preservation::Expendable,
Precision::Exact,
Fortitude::Force,
)?;
}
T::PoolAssetsTeam::reset_team(
info.lp_token,
new_account.clone(),
new_account.clone(),
new_account.clone(),
new_account.clone(),
)?;
Self::deposit_event(Event::MigratedToNewAccount {
pool_id,
prior_account,
new_account,
});
Ok(Pays::No.into())
}
}
impl<T: Config> Pezpallet<T> {
#[cfg(not(any(test, feature = "runtime-benchmarks")))]
fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
match (
T::PriorAccountIdConverter::try_convert(pool_id),
T::PoolLocator::address(pool_id),
) {
(Ok(a), Ok(b)) if a != b => Some((a, b)),
_ => None,
}
}
#[cfg(any(test, feature = "runtime-benchmarks"))]
pub(crate) fn addresses(pool_id: &T::PoolId) -> Option<(T::AccountId, T::AccountId)> {
match (
T::PoolLocator::address(pool_id),
T::PriorAccountIdConverter::try_convert(pool_id),
) {
(Ok(a), Ok(b)) if a != b => Some((a, b)),
_ => None,
}
}
}
}