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

Foreign assets creation via token reserve #3104

Draft
wants to merge 66 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
e12f440
add basics for foreign asset creation via token reserve WIP
gonzamontiel Dec 9, 2024
31616cd
create new extrinsic for the new way of creating foreign assets
gonzamontiel Dec 12, 2024
a50448e
ensure the modifier is the owner of the asset (migration needed)
gonzamontiel Dec 12, 2024
942bf9c
add default weights
gonzamontiel Dec 12, 2024
793236a
format
gonzamontiel Dec 12, 2024
0c891f5
Merge remote-tracking branch 'origin/master' into gonza/foreign-asset…
gonzamontiel Dec 27, 2024
d3cedc2
simplify logic, rollback to granular origins per operation
gonzamontiel Jan 2, 2025
abb9290
remove unused extrinsic
gonzamontiel Jan 3, 2025
f7b300e
improve readability
gonzamontiel Jan 3, 2025
4283fd4
make ForeignAssetCreationDeposit dynamic
gonzamontiel Jan 3, 2025
6938955
Merge branch 'master' into gonza/foreign-assets-via-token-lock
gonzamontiel Jan 6, 2025
a75ef40
remove unused block number
gonzamontiel Jan 7, 2025
c365093
fix rust tests
gonzamontiel Jan 7, 2025
2e396b4
fix moonbase xcm config
gonzamontiel Jan 7, 2025
0f5c5d1
fmt
gonzamontiel Jan 7, 2025
e02f62a
Merge branch 'master' into gonza/foreign-assets-via-token-lock
gonzamontiel Jan 7, 2025
a79f5d7
fix manager origin for moonriver
gonzamontiel Jan 7, 2025
dd6e3f6
tidy
gonzamontiel Jan 7, 2025
8cf4b8a
change back MaxFreezes to 0 as not needed anymore
gonzamontiel Jan 7, 2025
07c6cab
fix foreign assets rust tests
gonzamontiel Jan 7, 2025
ef4856c
make helper use correct call for creating foreign assets
gonzamontiel Jan 7, 2025
9bafa10
reformat test
gonzamontiel Jan 7, 2025
80d5ecb
fix benchmarks
gonzamontiel Jan 8, 2025
a9216fc
format
gonzamontiel Jan 8, 2025
2ae2dd4
Update test/helpers/assets.ts
gonzamontiel Jan 8, 2025
77ecd67
fix call
gonzamontiel Jan 8, 2025
aaab255
add UNITS multiplier
gonzamontiel Jan 8, 2025
a0b0e64
Merge branch 'master' into gonza/foreign-assets-via-token-lock
gonzamontiel Jan 8, 2025
1424ca3
rollback sudo
gonzamontiel Jan 8, 2025
2dfd940
expect balance diff
gonzamontiel Jan 8, 2025
c543215
remove unused weights
gonzamontiel Jan 8, 2025
f1dba45
lint
gonzamontiel Jan 8, 2025
a68bb12
remove typeinfo bound as already computed
gonzamontiel Jan 10, 2025
251497f
Add custom origin that ensures only XCM origins, which contain a cert…
gonzamontiel Jan 13, 2025
5e62ad1
modify foreign assets origin to use ForeignAssetOwnerOrigin
gonzamontiel Jan 13, 2025
8709f57
remove Root as possible origin
gonzamontiel Jan 14, 2025
75d0acb
get account from ensure_origin
gonzamontiel Jan 14, 2025
c24aa67
format
gonzamontiel Jan 14, 2025
9a07cf7
copy
gonzamontiel Jan 14, 2025
e749a81
Merge remote-tracking branch 'origin/master' into gonza/foreign-asset…
gonzamontiel Jan 14, 2025
4fd2e86
update mock
gonzamontiel Jan 14, 2025
cf918bf
remove governance asset owner
TarekkMA Jan 22, 2025
ebc7469
fix tests
TarekkMA Jan 22, 2025
1541ea4
fix tests
TarekkMA Jan 22, 2025
82a711e
fix formatting
TarekkMA Jan 23, 2025
3d6002d
Merge remote-tracking branch 'origin/master' into gonza/foreign-asset…
TarekkMA Jan 23, 2025
ac7ada3
fmt
TarekkMA Jan 23, 2025
a7a6df0
change how we ensure sibling
TarekkMA Jan 24, 2025
3294d8b
fmt: formatting
TarekkMA Jan 24, 2025
e9945c7
refactor: events
TarekkMA Jan 28, 2025
30051fe
fix: fix tests compile time issues
TarekkMA Jan 28, 2025
83e38c6
fix: fix test
TarekkMA Jan 28, 2025
58a823d
refactor: rename
TarekkMA Jan 28, 2025
3763644
Add governance + sibling parachain
TarekkMA Jan 29, 2025
79a9232
Add governance + sibling parachain
TarekkMA Jan 29, 2025
3a427a2
fix event
TarekkMA Jan 29, 2025
b2cd461
fix event
TarekkMA Jan 29, 2025
8c7464c
test: add tests
TarekkMA Jan 29, 2025
9ee915f
test: add tests
TarekkMA Jan 29, 2025
d74895b
test: add tests
TarekkMA Jan 30, 2025
2b2826d
test: add tests
TarekkMA Jan 30, 2025
458d92d
style: formatting
TarekkMA Jan 30, 2025
2a5085d
Merge remote-tracking branch 'origin/master' into gonza/foreign-asset…
TarekkMA Jan 30, 2025
be5e626
Merge remote-tracking branch 'origin/master' into gonza/foreign-asset…
TarekkMA Feb 6, 2025
1736256
Merge branch 'master' into gonza/foreign-assets-via-token-lock
RomarQ Feb 10, 2025
5f31a91
test: interact w/ assets via xcm
pLabarta Feb 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions pallets/moonbeam-foreign-assets/src/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,27 @@

#![cfg(feature = "runtime-benchmarks")]

use crate::{AssetStatus, Call, Config, Pallet};
use frame_benchmarking::{benchmarks, impl_benchmark_test_suite};
use crate::{pallet, AssetStatus, Call, Config, Pallet};
use frame_benchmarking::{account, benchmarks, impl_benchmark_test_suite};
use frame_support::pallet_prelude::*;
use frame_support::traits::Currency;
use frame_system::RawOrigin;
use sp_runtime::traits::ConstU32;
use sp_runtime::BoundedVec;
use xcm::latest::prelude::*;

fn create_funded_user<T: Config>(string: &'static str, n: u32, balance: u32) -> T::AccountId {
const SEED: u32 = 0;
let user = account(string, n, SEED);
let _ = <T as pallet::Config>::Currency::make_free_balance_be(&user, balance.into());
let _ = <T as pallet::Config>::Currency::issue(balance.into());
user
}
fn create_n_foreign_asset<T: Config>(n: u32) -> DispatchResult {
let user: T::AccountId = create_funded_user::<T>("user", n, 100);
for i in 1..=n {
Pallet::<T>::create_foreign_asset(
RawOrigin::Root.into(),
RawOrigin::Signed(user.clone()).into(),
i as u128,
location_of(i),
18,
Expand All @@ -53,7 +62,7 @@ benchmarks! {
create_foreign_asset {
create_n_foreign_asset::<T>(T::MaxForeignAssets::get().saturating_sub(1))?;
let asset_id = T::MaxForeignAssets::get() as u128;
}: _(RawOrigin::Root, asset_id, Location::parent(), 18, str_to_bv("MT"), str_to_bv("Mytoken"))
}: _(RawOrigin::Signed(create_funded_user::<T>("user", 1, 100)), asset_id, Location::parent(), 18, str_to_bv("MT"), str_to_bv("Mytoken"))
verify {
assert_eq!(
Pallet::<T>::assets_by_id(asset_id),
Expand Down
119 changes: 97 additions & 22 deletions pallets/moonbeam-foreign-assets/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ pub enum AssetStatus {
#[pallet]
pub mod pallet {
use super::*;
use frame_support::traits::{Currency, ReservableCurrency};
use pallet_evm::{GasWeightMapping, Runner};
use sp_runtime::traits::{AccountIdConversion, Convert};
use sp_runtime::traits::{AccountIdConversion, AtLeast32BitUnsigned, Convert};
use xcm_executor::traits::ConvertLocation;
use xcm_executor::traits::Error as MatchError;
use xcm_executor::AssetsInHolding;
Expand All @@ -121,7 +122,7 @@ pub mod pallet {
pub const PALLET_ID: frame_support::PalletId = frame_support::PalletId(*b"forgasst");

#[pallet::config]
pub trait Config: frame_system::Config + pallet_evm::Config {
pub trait Config: frame_system::Config + pallet_evm::Config + scale_info::TypeInfo {
gonzamontiel marked this conversation as resolved.
Show resolved Hide resolved
// Convert AccountId to H160
type AccountIdToH160: Convert<Self::AccountId, H160>;

Expand All @@ -131,23 +132,13 @@ pub mod pallet {
/// EVM runner
type EvmRunner: Runner<Self>;

/// Origin that is allowed to create a new foreign assets
type ForeignAssetCreatorOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// Origin that is allowed to freeze all tokens of a foreign asset
type ForeignAssetFreezerOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// Origin that is allowed to modify asset information for foreign assets
type ForeignAssetModifierOrigin: EnsureOrigin<Self::RuntimeOrigin>;

/// Origin that is allowed to unfreeze all tokens of a foreign asset that was previously
/// frozen
type ForeignAssetUnfreezerOrigin: EnsureOrigin<Self::RuntimeOrigin>;
/// Origin that is allowed to create new foreign assets
type EnsureXcmLocation: EnsureXcmLocation<Self>;

/// Hook to be called when new foreign asset is registered.
type OnForeignAssetCreated: ForeignAssetCreatedHook<Location>;

/// Maximum nulmbers of differnt foreign assets
/// Maximum numbers of differnt foreign assets
type MaxForeignAssets: Get<u32>;

/// The overarching event type.
Expand All @@ -158,8 +149,27 @@ pub mod pallet {

// Convert XCM Location to H160
type XcmLocationToH160: ConvertLocation<H160>;

/// Amount of tokens required to lock for creating a new foreign asset
type ForeignAssetCreationDeposit: Get<BalanceOf<Self>>;

/// The balance type for locking funds
type Balance: Member
+ Parameter
+ AtLeast32BitUnsigned
+ Default
+ Copy
+ MaybeSerializeDeserialize
+ MaxEncodedLen
+ TypeInfo;

/// The currency type for locking funds
type Currency: ReservableCurrency<Self::AccountId>;
}

type BalanceOf<T> =
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;

pub type AssetBalance = U256;
pub type AssetId = u128;

Expand All @@ -177,6 +187,10 @@ pub mod pallet {
EvmCallPauseFail,
EvmCallUnpauseFail,
EvmInternalError,
/// Account has insufficient balance for locking
InsufficientBalance,
OriginIsNotAssetCreator,
CannotConvertLocationToAccount,
InvalidSymbol,
InvalidTokenName,
LocationAlreadyExists,
Expand All @@ -191,6 +205,7 @@ pub mod pallet {
contract_address: H160,
asset_id: AssetId,
xcm_location: Location,
deposit: Option<BalanceOf<T>>,
},
/// Changed the xcm type mapping for a given asset id
ForeignAssetXcmLocationChanged {
TarekkMA marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -207,6 +222,10 @@ pub mod pallet {
asset_id: AssetId,
xcm_location: Location,
},
/// Tokens have been locked for asset creation
TokensLocked(T::AccountId, AssetId, AssetBalance),
/// Lock verification failed
LockVerificationFailed(T::AccountId, AssetId),
TarekkMA marked this conversation as resolved.
Show resolved Hide resolved
}

/// Mapping from an asset id to a Foreign asset type.
Expand All @@ -225,6 +244,18 @@ pub mod pallet {
pub type AssetsByLocation<T: Config> =
StorageMap<_, Blake2_128Concat, Location, (AssetId, AssetStatus)>;

/// Mapping from an asset id to its creation details
#[pallet::storage]
#[pallet::getter(fn assets_creation_details)]
pub type AssetsCreationDetails<T: Config> =
StorageMap<_, Blake2_128Concat, AssetId, AssetCreationDetails<T>>;

#[derive(Clone, Decode, Encode, Eq, PartialEq, Debug, TypeInfo, MaxEncodedLen)]
pub struct AssetCreationDetails<T: Config> {
pub owner: T::AccountId,
pub deposit: Option<BalanceOf<T>>,
}

impl<T: Config> Pallet<T> {
/// The account ID of this pallet
#[inline]
Expand All @@ -242,6 +273,8 @@ pub mod pallet {
H160(buffer)
}

/// This method only exists for migration purposes and will be deleted once the
/// foreign assets migration is finished.
pub fn register_foreign_asset(
asset_id: AssetId,
xcm_location: Location,
Expand Down Expand Up @@ -274,18 +307,26 @@ pub mod pallet {
let name = core::str::from_utf8(&name).map_err(|_| Error::<T>::InvalidTokenName)?;

let contract_address = EvmCaller::<T>::erc20_create(asset_id, decimals, symbol, name)?;
let owner = T::EnsureXcmLocation::account_for_location(&xcm_location)
.ok_or(Error::<T>::CannotConvertLocationToAccount)?;

// Insert the association assetId->foreigAsset
// Insert the association foreigAsset->assetId
AssetsById::<T>::insert(&asset_id, &xcm_location);
AssetsByLocation::<T>::insert(&xcm_location, (asset_id, AssetStatus::Active));

T::OnForeignAssetCreated::on_asset_created(&xcm_location, &asset_id);
AssetsCreationDetails::<T>::insert(
&asset_id,
AssetCreationDetails {
owner,
deposit: None,
},
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How important is this storage for assets that were created with governance? Asking because the assets were already migrated on stagenet, and I do not see it being breaking if we don't fix the storage there.

Seems only important for assets created by normal accounts.


Self::deposit_event(Event::ForeignAssetCreated {
contract_address,
asset_id,
xcm_location,
deposit: None,
});
Ok(())
}
Expand Down Expand Up @@ -355,7 +396,8 @@ pub mod pallet {
symbol: BoundedVec<u8, ConstU32<256>>,
name: BoundedVec<u8, ConstU32<256>>,
) -> DispatchResult {
T::ForeignAssetCreatorOrigin::ensure_origin(origin)?;
let owner_account =
T::EnsureXcmLocation::ensure_xcm_origin(origin.clone(), Some(&xcm_location))?;

// Ensure such an assetId does not exist
ensure!(
Expand All @@ -380,20 +422,34 @@ pub mod pallet {

let symbol = core::str::from_utf8(&symbol).map_err(|_| Error::<T>::InvalidSymbol)?;
let name = core::str::from_utf8(&name).map_err(|_| Error::<T>::InvalidTokenName)?;

let contract_address = EvmCaller::<T>::erc20_create(asset_id, decimals, symbol, name)?;
let deposit = T::ForeignAssetCreationDeposit::get();
let owner = owner_account.clone();

// Insert the association assetId->foreigAsset
// Insert the association foreigAsset->assetId
AssetsById::<T>::insert(&asset_id, &xcm_location);
AssetsByLocation::<T>::insert(&xcm_location, (asset_id, AssetStatus::Active));

// Reserve _deposit_ amount of funds from the caller
<T as Config>::Currency::reserve(&owner_account, deposit)?;

// Insert the amount that is reserved from the user
AssetsCreationDetails::<T>::insert(
&asset_id,
AssetCreationDetails {
owner,
deposit: Some(deposit),
},
);

T::OnForeignAssetCreated::on_asset_created(&xcm_location, &asset_id);

Self::deposit_event(Event::ForeignAssetCreated {
contract_address,
asset_id,
xcm_location,
deposit: Some(deposit),
});
Ok(())
}
Expand All @@ -408,11 +464,14 @@ pub mod pallet {
asset_id: AssetId,
new_xcm_location: Location,
) -> DispatchResult {
T::ForeignAssetModifierOrigin::ensure_origin(origin)?;
// Ensures that the origin is an XCM location that contains the asset
T::EnsureXcmLocation::ensure_xcm_origin(origin.clone(), Some(&new_xcm_location))?;

let previous_location =
AssetsById::<T>::get(&asset_id).ok_or(Error::<T>::AssetDoesNotExist)?;

T::EnsureXcmLocation::ensure_xcm_origin(origin, Some(&previous_location))?;

ensure!(
!AssetsByLocation::<T>::contains_key(&new_xcm_location),
Error::<T>::LocationAlreadyExists
Expand Down Expand Up @@ -441,11 +500,16 @@ pub mod pallet {
asset_id: AssetId,
allow_xcm_deposit: bool,
) -> DispatchResult {
T::ForeignAssetFreezerOrigin::ensure_origin(origin)?;
// Ensure that the origin is coming from an XCM.
T::EnsureXcmLocation::ensure_xcm_origin(origin.clone(), None)?;

let xcm_location =
AssetsById::<T>::get(&asset_id).ok_or(Error::<T>::AssetDoesNotExist)?;

// Ensures that the origin is an XCM location that owns the asset
// represented by the assets xcm location
T::EnsureXcmLocation::ensure_xcm_origin(origin, Some(&xcm_location))?;

let (_asset_id, asset_status) = AssetsByLocation::<T>::get(&xcm_location)
.ok_or(Error::<T>::CorruptedStorageOrphanLocation)?;

Expand Down Expand Up @@ -475,10 +539,12 @@ pub mod pallet {
#[pallet::call_index(3)]
#[pallet::weight(<T as Config>::WeightInfo::unfreeze_foreign_asset())]
pub fn unfreeze_foreign_asset(origin: OriginFor<T>, asset_id: AssetId) -> DispatchResult {
T::ForeignAssetUnfreezerOrigin::ensure_origin(origin)?;
T::EnsureXcmLocation::ensure_xcm_origin(origin.clone(), None)?;

let xcm_location =
AssetsById::<T>::get(&asset_id).ok_or(Error::<T>::AssetDoesNotExist)?;
// Ensures that the origin is an XCM location that contains the asset
T::EnsureXcmLocation::ensure_xcm_origin(origin, Some(&xcm_location))?;

let (_asset_id, asset_status) = AssetsByLocation::<T>::get(&xcm_location)
.ok_or(Error::<T>::CorruptedStorageOrphanLocation)?;
Expand Down Expand Up @@ -596,4 +662,13 @@ pub mod pallet {
AssetsById::<T>::get(asset_id)
}
}

/// A trait to ensure that the origin is an XCM location that contains the asset
pub trait EnsureXcmLocation<T: Config> {
fn ensure_xcm_origin(
origin: T::RuntimeOrigin,
location: Option<&Location>,
) -> Result<T::AccountId, DispatchError>;
fn account_for_location(location: &Location) -> Option<T::AccountId>;
}
}
Loading
Loading