Lines
100 %
Functions
Branches
// Copyright 2025 Moonbeam Foundation.
// This file is part of Moonbeam.
// Moonbeam is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Moonbeam is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Moonbeam. If not, see <http://www.gnu.org/licenses/>.
use crate::*;
use mock::*;
use frame_support::traits::Currency;
use frame_support::{assert_noop, assert_ok};
use precompile_utils::testing::Bob;
use xcm::latest::prelude::*;
fn encode_ticker(str_: &str) -> BoundedVec<u8, ConstU32<256>> {
BoundedVec::try_from(str_.as_bytes().to_vec()).expect("too long")
}
fn encode_token_name(str_: &str) -> BoundedVec<u8, ConstU32<256>> {
#[test]
fn create_foreign_and_freeze_unfreeze_using_xcm() {
ExtBuilder::default().build().execute_with(|| {
let deposit = ForeignAssetCreationDeposit::get();
Balances::make_free_balance_be(&PARA_A, deposit);
let asset_location: Location = (Parent, Parachain(1), PalletInstance(13)).into();
// create foreign asset
assert_ok!(EvmForeignAssets::create_foreign_asset(
RuntimeOrigin::signed(PARA_A),
1,
asset_location.clone(),
18,
encode_ticker("MTT"),
encode_token_name("Mytoken"),
));
assert_eq!(
EvmForeignAssets::assets_by_id(1),
Some(asset_location.clone())
);
EvmForeignAssets::assets_by_location(asset_location.clone()),
Some((1, AssetStatus::Active)),
expect_events(vec![Event::ForeignAssetCreated {
contract_address: H160([
255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
]),
asset_id: 1,
xcm_location: asset_location.clone(),
deposit: Some(deposit),
}]);
let (xcm_location, asset_id): (Location, u128) = get_asset_created_hook_invocation()
.expect("Decoding of invocation data should not fail");
assert_eq!(xcm_location, asset_location.clone());
assert_eq!(asset_id, 1u128);
// Check storage
EvmForeignAssets::assets_by_id(&1),
EvmForeignAssets::assets_by_location(&asset_location),
Some((1, AssetStatus::Active))
// Unfreeze should return AssetNotFrozen error
assert_noop!(
EvmForeignAssets::unfreeze_foreign_asset(RuntimeOrigin::signed(PARA_A), 1),
Error::<Test>::AssetNotFrozen
// Freeze should work
assert_ok!(EvmForeignAssets::freeze_foreign_asset(
true
),);
Some((1, AssetStatus::FrozenXcmDepositAllowed))
// Should not be able to freeze an asset already frozen
EvmForeignAssets::freeze_foreign_asset(RuntimeOrigin::signed(PARA_A), 1, true),
Error::<Test>::AssetAlreadyFrozen
// Unfreeze should work
assert_ok!(EvmForeignAssets::unfreeze_foreign_asset(
1
});
fn create_foreign_and_freeze_unfreeze_using_root() {
RuntimeOrigin::root(),
Location::parent(),
assert_eq!(EvmForeignAssets::assets_by_id(1), Some(Location::parent()));
EvmForeignAssets::assets_by_location(Location::parent()),
expect_events(vec![crate::Event::ForeignAssetCreated {
xcm_location: Location::parent(),
deposit: None,
assert_eq!(xcm_location, Location::parent());
assert_eq!(EvmForeignAssets::assets_by_id(&1), Some(Location::parent()));
EvmForeignAssets::assets_by_location(&Location::parent()),
EvmForeignAssets::unfreeze_foreign_asset(RuntimeOrigin::root(), 1),
EvmForeignAssets::freeze_foreign_asset(RuntimeOrigin::root(), 1, true),
fn test_asset_exists_error() {
EvmForeignAssets::assets_by_id(1).unwrap(),
Location::parent()
EvmForeignAssets::create_foreign_asset(
),
Error::<Test>::AssetAlreadyExists
fn test_regular_user_cannot_call_extrinsics() {
RuntimeOrigin::signed(Bob.into()),
sp_runtime::DispatchError::BadOrigin
EvmForeignAssets::change_xcm_location(
fn test_root_can_change_foreign_asset_for_asset_id() {
assert_ok!(EvmForeignAssets::change_xcm_location(
Location::here()
// New associations are stablished
assert_eq!(EvmForeignAssets::assets_by_id(1).unwrap(), Location::here());
EvmForeignAssets::assets_by_location(Location::here()).unwrap(),
(1, AssetStatus::Active),
// Old ones are deleted
assert!(EvmForeignAssets::assets_by_location(Location::parent()).is_none());
expect_events(vec![
crate::Event::ForeignAssetCreated {
},
crate::Event::ForeignAssetXcmLocationChanged {
previous_xcm_location: Location::parent(),
new_xcm_location: Location::here(),
])
fn test_asset_id_non_existent_error() {
EvmForeignAssets::change_xcm_location(RuntimeOrigin::root(), 1, Location::parent()),
Error::<Test>::AssetDoesNotExist
fn test_location_already_exist_error() {
// Setup: create a first foreign asset taht we will try to override
2,
Error::<Test>::LocationAlreadyExists
// Setup: create a second foreign asset that will try to override the first one
Location::new(2, *&[]),
EvmForeignAssets::change_xcm_location(RuntimeOrigin::root(), 2, Location::parent()),
fn test_governance_can_change_any_asset_location() {
Balances::make_free_balance_be(&PARA_C, deposit + 10);
let asset_location: Location = (Parent, Parachain(3), PalletInstance(22)).into();
let asset_id = 5;
// create foreign asset using para c
RuntimeOrigin::signed(PARA_C),
asset_id,
10,
encode_ticker("PARC"),
encode_token_name("Parachain C Token"),
assert_eq!(Balances::free_balance(&PARA_C), 10);
EvmForeignAssets::assets_by_id(asset_id),
EvmForeignAssets::assets_by_location(asset_location),
Some((asset_id, AssetStatus::Active)),
// This asset doesn't belong to PARA A, so it should not be able to change the location
EvmForeignAssets::freeze_foreign_asset(RuntimeOrigin::signed(PARA_A), asset_id, true),
Error::<Test>::LocationOutsideOfOrigin,
let new_asset_location: Location = (Parent, Parachain(1), PalletInstance(1)).into();
// Also PARA A cannot change the location
new_asset_location.clone(),
// Change location using root, now PARA A can control this asset
Some(new_asset_location.clone())
EvmForeignAssets::assets_by_location(new_asset_location),
// Freeze will not work since this asset has been moved from PARA C to PARA A
EvmForeignAssets::freeze_foreign_asset(RuntimeOrigin::signed(PARA_C), asset_id, true),
// But if we try using PARA A, it should work