Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Asset wrapper and fee updates #65

Merged
merged 13 commits into from Feb 24, 2022
60 changes: 58 additions & 2 deletions pallets/xtransfer/src/assets_wrapper.rs
Expand Up @@ -4,7 +4,9 @@ pub use self::pallet::*;
#[frame_support::pallet]
pub mod pallet {
use codec::{Decode, Encode};
use frame_support::{dispatch::DispatchResult, pallet_prelude::*, traits::StorageVersion};
use frame_support::{
dispatch::DispatchResult, pallet_prelude::*, traits::StorageVersion, transactional,
};
use frame_system::pallet_prelude::*;
use scale_info::TypeInfo;
use sp_runtime::traits::StaticLookup;
Expand Down Expand Up @@ -35,11 +37,19 @@ pub mod pallet {
// Potential other bridge solutions
}

#[derive(Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo)]
pub struct AssetProperties {
pub name: Vec<u8>,
pub symbol: Vec<u8>,
pub decimals: u8,
}

#[derive(Clone, Decode, Encode, Eq, PartialEq, Ord, PartialOrd, Debug, TypeInfo)]
pub struct AssetRegistryInfo {
location: MultiLocation,
reserve_location: Option<MultiLocation>,
enabled_bridges: Vec<XBridge>,
properties: AssetProperties,
}

impl From<MultiLocation> for XTransferAsset {
Expand Down Expand Up @@ -215,10 +225,12 @@ pub mod pallet {
T: pallet_assets::Config,
{
#[pallet::weight(195_000_000)]
#[transactional]
pub fn force_register_asset(
origin: OriginFor<T>,
asset: XTransferAsset,
asset_id: T::AssetId,
properties: AssetProperties,
owner: <T::Lookup as StaticLookup>::Source,
) -> DispatchResult {
T::AssetsCommitteeOrigin::ensure_origin(origin.clone())?;
Expand All @@ -233,14 +245,15 @@ pub mod pallet {
Error::<T>::AssetAlreadyExist
);
pallet_assets::pallet::Pallet::<T>::force_create(
origin,
origin.clone(),
asset_id,
owner,
true,
T::MinBalance::get(),
)?;
IdByAssets::<T>::insert(&asset, asset_id);
AssetByIds::<T>::insert(asset_id, &asset);

RegistryInfoByIds::<T>::insert(
asset_id,
AssetRegistryInfo {
Expand All @@ -251,8 +264,18 @@ pub mod pallet {
config: XBridgeConfig::Xcmp,
metadata: Box::new(Vec::new()),
}],
properties: properties.clone(),
},
);
pallet_assets::pallet::Pallet::<T>::force_set_metadata(
origin,
asset_id,
properties.name,
properties.symbol,
properties.decimals,
false,
)?;

Self::deposit_event(Event::AssetRegistered { asset_id, asset });
Ok(())
}
Expand All @@ -262,6 +285,7 @@ pub mod pallet {
/// will not success anymore, we should call pallet_assets::destory() manually
/// if we want to delete this asset from our chain
#[pallet::weight(195_000_000)]
#[transactional]
pub fn force_unregister_asset(
origin: OriginFor<T>,
asset_id: T::AssetId,
Expand Down Expand Up @@ -293,6 +317,32 @@ pub mod pallet {
}

#[pallet::weight(195_000_000)]
#[transactional]
pub fn force_set_metadata(
origin: OriginFor<T>,
asset_id: T::AssetId,
properties: AssetProperties,
) -> DispatchResult {
T::AssetsCommitteeOrigin::ensure_origin(origin.clone())?;

let mut info =
RegistryInfoByIds::<T>::get(&asset_id).ok_or(Error::<T>::AssetNotRegistered)?;
info.properties = properties.clone();
RegistryInfoByIds::<T>::insert(&asset_id, &info);
pallet_assets::pallet::Pallet::<T>::force_set_metadata(
origin,
asset_id,
properties.name,
properties.symbol,
properties.decimals,
false,
)?;

Ok(())
}

#[pallet::weight(195_000_000)]
#[transactional]
pub fn force_enable_chainbridge(
origin: OriginFor<T>,
asset_id: T::AssetId,
Expand Down Expand Up @@ -340,6 +390,7 @@ pub mod pallet {
}

#[pallet::weight(195_000_000)]
#[transactional]
pub fn force_disable_chainbridge(
origin: OriginFor<T>,
asset_id: T::AssetId,
Expand Down Expand Up @@ -385,6 +436,7 @@ pub mod pallet {
fn id(asset: &XTransferAsset) -> Option<AssetId>;
fn asset(id: &AssetId) -> Option<XTransferAsset>;
fn lookup_by_resource_id(resource_id: &[u8; 32]) -> Option<XTransferAsset>;
fn decimals(id: &AssetId) -> Option<u8>;
}

impl<T: Config> GetAssetRegistryInfo<<T as pallet_assets::Config>::AssetId> for Pallet<T> {
Expand All @@ -399,5 +451,9 @@ pub mod pallet {
fn lookup_by_resource_id(resource_id: &[u8; 32]) -> Option<XTransferAsset> {
AssetByResourceIds::<T>::get(resource_id)
}

fn decimals(id: &<T as pallet_assets::Config>::AssetId) -> Option<u8> {
RegistryInfoByIds::<T>::get(&id).map(|m| m.properties.decimals)
}
}
}
2 changes: 1 addition & 1 deletion pallets/xtransfer/src/bridge_transfer/mock.rs
Expand Up @@ -131,7 +131,7 @@ parameter_types! {
);
pub ExecutionPriceInAsset2: (AssetId, u128) = (
AssetId2::get(),
1
2
);
pub NativeExecutionPrice: u128 = 1;
pub ExecutionPrices: Vec<(AssetId, u128)> = [
Expand Down
118 changes: 67 additions & 51 deletions pallets/xtransfer/src/bridge_transfer/mod.rs
Expand Up @@ -322,9 +322,6 @@ pub mod pallet {

let dest_location: MultiLocation =
Decode::decode(&mut dest.as_slice()).map_err(|_| Error::<T>::DestUnrecognized)?;
let dest_reserve_location = dest_location
.reserve_location()
.ok_or(Error::<T>::DestUnrecognized)?;

let asset_location = Self::rid_to_location(&rid)?;
let asset_reserve_location = asset_location
Expand Down Expand Up @@ -397,44 +394,38 @@ pub mod pallet {
}
// To relaychain or other parachain, forward it by xcm
(1, X1(AccountId32 { .. })) | (1, X2(Parachain(_), AccountId32 { .. })) => {
let dest_reserve_account = dest_reserve_location.clone().into_account();
if asset_reserve_location != dest_reserve_location {
log::trace!(
target: LOG_TARGET,
"Reserve of asset and dest dismatch, deposit asset to dest reserve location.",
let temporary_account =
MultiLocation::new(0, X1(GeneralKey(b"bridge_transfer".to_vec())))
.into_account();
log::trace!(
target: LOG_TARGET,
"Deposit withdrawn asset to a temporary account: {:?}",
&temporary_account,
);
if rid == Self::gen_pha_rid(src_chainid) {
<T as Config>::Currency::deposit_creating(
&temporary_account.clone().into(),
amount,
);
if rid == Self::gen_pha_rid(src_chainid) {
<T as Config>::Currency::deposit_creating(
&dest_reserve_account.clone().into(),
amount,
);
} else {
let asset_id = Self::rid_to_assetid(&rid)?;
let asset_amount =
T::BalanceConverter::to_asset_balance(amount, asset_id)
.map_err(|_| Error::<T>::BalanceConversionFailed)?;
// Mint asset into dest reserve account
pallet_assets::pallet::Pallet::<T>::mint_into(
asset_id,
&dest_reserve_account.clone().into(),
asset_amount,
)
.map_err(|_| Error::<T>::FailedToTransactAsset)?;
}
} else {
let asset_id = Self::rid_to_assetid(&rid)?;
let asset_amount = T::BalanceConverter::to_asset_balance(amount, asset_id)
.map_err(|_| Error::<T>::BalanceConversionFailed)?;
// Mint asset into dest temporary account
pallet_assets::pallet::Pallet::<T>::mint_into(
asset_id,
&temporary_account.clone().into(),
asset_amount,
)
.map_err(|_| Error::<T>::FailedToTransactAsset)?;
}

// Two main tasks of transfer_fungible is:
// first) withdraw asset from reserve_id
// second) deposit asset into sovereign account of dest chain.(MINT OP)
//
// So if the reserve account does not have enough asset, transaction would fail.
// When someone transfer assets to EVM account from local chain our other parachains,
// assets would be deposited into reserve account, in other words, bridge transfer
// always based on reserve mode.
// After deposited asset into the temporary account, let xcm executor determine how to
// handle the asset.
T::XcmTransactor::transfer_fungible(
Junction::AccountId32 {
network: NetworkId::Any,
id: dest_reserve_account,
id: temporary_account,
}
.into(),
(asset_location, amount.into()).into(),
Expand All @@ -456,7 +447,10 @@ pub mod pallet {
}
}

impl<T: Config> Pallet<T> {
impl<T: Config> Pallet<T>
where
BalanceOf<T>: From<u128> + Into<u128>,
{
// TODO.wf: A more proper way to estimate fee
pub fn estimate_fee_in_pha(
dest_id: bridge::BridgeChainId,
Expand All @@ -471,6 +465,27 @@ pub mod pallet {
}
}

pub fn to_e12(amount: u128, decimals: u8) -> u128 {
if decimals > 12 {
amount.saturating_div(10u128.saturating_pow(decimals as u32 - 12))
} else {
amount.saturating_mul(10u128.saturating_pow(12 - decimals as u32))
}
}

pub fn from_e12(amount: u128, decimals: u8) -> u128 {
if decimals > 12 {
amount.saturating_mul(10u128.saturating_pow(decimals as u32 - 12))
} else {
amount.saturating_div(10u128.saturating_pow(12 - decimals as u32))
}
}

pub fn convert_fee_from_pha(fee_in_pha: BalanceOf<T>, price: u128, decimals: u8) -> u128 {
let fee_e12: u128 = fee_in_pha.into() * price / T::NativeExecutionPrice::get();
Self::from_e12(fee_e12.into(), decimals)
}

pub fn rid_to_location(rid: &[u8; 32]) -> Result<MultiLocation, DispatchError> {
let src_chainid: bridge::BridgeChainId = Self::get_chainid(rid);
let asset_location: MultiLocation = if *rid == Self::gen_pha_rid(src_chainid) {
Expand Down Expand Up @@ -516,24 +531,25 @@ pub mod pallet {
{
fn get_fee(chain_id: bridge::BridgeChainId, asset: &MultiAsset) -> Option<u128> {
match (&asset.id, &asset.fun) {
(Concrete(asset_id), Fungible(amount)) => {
let fee_estimated_in_pha =
Self::estimate_fee_in_pha(chain_id, (*amount).into());
(Concrete(location), Fungible(amount)) => {
let id = T::AssetsWrapper::id(&XTransferAsset(location.clone()))?;
let decimals = T::AssetsWrapper::decimals(&id).unwrap_or(12);
let fee_in_pha = Self::estimate_fee_in_pha(
chain_id,
(Self::to_e12(*amount, decimals)).into(),
);
if T::NativeChecker::is_native_asset(asset) {
Some(fee_estimated_in_pha.into())
Some(fee_in_pha.into())
} else {
let fee_in_asset;
let fee_prices = T::ExecutionPriceInfo::get();
if let Some(idx) = fee_prices.iter().position(|(fee_asset_id, _)| {
fee_asset_id == &Concrete(asset_id.clone())
}) {
fee_in_asset = Some(
fee_estimated_in_pha.into() * fee_prices[idx].1
/ T::NativeExecutionPrice::get(),
)
} else {
fee_in_asset = None
}
let fee_in_asset = fee_prices
.iter()
.position(|(fee_asset_id, _)| {
fee_asset_id == &Concrete(location.clone())
})
.map(|idx| {
Self::convert_fee_from_pha(fee_in_pha, fee_prices[idx].1, decimals)
});
fee_in_asset
}
}
Expand Down