Lines
97.81 %
Functions
100 %
Branches
// Copyright 2024 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/>.
//! Unit testing
use crate::{
foreign_asset::ForeignAssetMigrationStatus,
mock::{AssetId, Balances},
};
use frame_support::traits::fungibles::approvals::Inspect;
use sp_runtime::{BoundedVec, DispatchError};
use xcm::latest::Location;
use {
crate::{
mock::{
AccountId, AssetManager, Assets, ExtBuilder, LazyMigrations, MockAssetType,
RuntimeOrigin, Test, ALITH, BOB,
},
Error,
frame_support::{assert_noop, assert_ok},
sp_core::{H160, H256},
sp_io::hashing::keccak_256,
fn address_build(seed: u8) -> H160 {
let address = H160::from(H256::from(keccak_256(&[seed; 32])));
address
}
fn create_dummy_contract_without_metadata(seed: u8) -> H160 {
let address = address_build(seed);
let dummy_code = vec![1, 2, 3];
pallet_evm::AccountCodes::<Test>::insert(address, dummy_code);
#[test]
fn test_create_contract_metadata_contract_not_exist() {
ExtBuilder::default().build().execute_with(|| {
assert_noop!(
LazyMigrations::create_contract_metadata(
RuntimeOrigin::signed(AccountId::from([45; 20])),
address_build(1),
),
Error::<Test>::ContractNotExist
);
});
fn test_create_contract_metadata_success_path() {
// Setup: create a dummy contract
let address = create_dummy_contract_without_metadata(1);
assert_ok!(LazyMigrations::create_contract_metadata(
address,
));
assert!(pallet_evm::AccountCodesMetadata::<Test>::get(address).is_some());
// Should not be able to set metadata again
Error::<Test>::ContractMetadataAlreadySet
// Helper function to create a foreign asset with basic metadata
fn create_old_foreign_asset(location: Location) -> AssetId {
let asset = MockAssetType::Xcm(location.clone().into());
let asset_id: AssetId = asset.clone().into();
// First register asset in asset manager with a Location
assert_ok!(AssetManager::register_foreign_asset(
RuntimeOrigin::root(),
asset,
1,
true,
// Set metadata for the asset
assert_ok!(Assets::set_metadata(
RuntimeOrigin::signed(AssetManager::account_id()),
asset_id,
b"Test".to_vec(),
b"TEST".to_vec(),
12,
asset_id
fn test_approve_foreign_asset_migration_unauthorized() {
let location = xcm::latest::Location::new(1, [xcm::latest::Junction::Parachain(1000)]);
let asset_id = create_old_foreign_asset(location);
// Try to approve migration with non-root origin
LazyMigrations::approve_assets_to_migrate(
RuntimeOrigin::signed(BOB),
BoundedVec::try_from(vec![asset_id]).unwrap()
DispatchError::BadOrigin
fn test_approve_foreign_asset_migration_success() {
// Try to approve migration with root origin
assert_ok!(LazyMigrations::approve_assets_to_migrate(
// Verify the asset is approved for migration
assert!(crate::pallet::ApprovedForeignAssets::<Test>::contains_key(
fn test_start_foreign_asset_migration_success() {
assert_ok!(Assets::mint(
asset_id.into(),
ALITH.into(),
100,
// Verify asset is live by calling transfer in pallet assets
assert_ok!(Assets::transfer(
RuntimeOrigin::signed(ALITH),
BOB.into(),
// Approve the migration of the asset
BoundedVec::try_from(vec![asset_id]).unwrap(),
// Try to migrate the asset
assert_ok!(LazyMigrations::start_foreign_assets_migration(
// Verify asset is frozen by calling transfer in pallet assets
Assets::transfer(
pallet_assets::Error::<Test>::AssetNotLive
// Verify migration status
match crate::pallet::ForeignAssetMigrationStatusValue::<Test>::get() {
ForeignAssetMigrationStatus::Migrating(info) => {
assert_eq!(info.asset_id, asset_id);
assert_eq!(info.remaining_balances, 1);
assert_eq!(info.remaining_approvals, 0);
_ => panic!("Expected migration status to be Migrating"),
fn test_start_foreign_asset_migration_already_migrating() {
// Start first migrationJunction::Parachain(1000)
// Try to start another migration while one is in progress
LazyMigrations::start_foreign_assets_migration(RuntimeOrigin::signed(ALITH), 2u128),
Error::<Test>::MigrationNotFinished
fn test_start_foreign_asset_migration_asset_not_found() {
// Try to migrate non-existent asset
LazyMigrations::start_foreign_assets_migration(RuntimeOrigin::signed(ALITH), 1u128),
Error::<Test>::AssetNotFound
fn test_start_foreign_asset_migration_asset_type_not_found() {
let asset_id = 1u128;
// Create asset without registering in asset manager
assert_ok!(Assets::create(
LazyMigrations::start_foreign_assets_migration(RuntimeOrigin::signed(ALITH), asset_id),
Error::<Test>::AssetTypeNotFound
fn test_start_foreign_asset_migration_with_balances_and_approvals() {
// Add some balances
// Add some approvals
assert_ok!(Assets::approve_transfer(
AccountId::from([3; 20]).into(),
50,
// Start migration
// Verify migration status includes the balances and approvals
assert_eq!(info.remaining_approvals, 1);
fn test_migrate_foreign_asset_balances_without_migration_started() {
LazyMigrations::migrate_foreign_asset_balances(RuntimeOrigin::signed(ALITH), 100),
Error::<Test>::NoMigrationInProgress
fn test_migrate_foreign_asset_balances_zero_limit() {
LazyMigrations::migrate_foreign_asset_balances(RuntimeOrigin::signed(ALITH), 0),
Error::<Test>::LimitCannotBeZero
fn test_migrate_foreign_asset_balances_single_account() {
// Mint some tokens to an account
// Migrate balances
assert_ok!(LazyMigrations::migrate_foreign_asset_balances(
10
assert_eq!(info.remaining_balances, 0);
// Verify the balance was migrated by
assert_eq!(Assets::balance(asset_id, BOB), 0);
fn test_migrate_foreign_asset_balances_multiple_accounts() {
// Mint tokens to multiple accounts
for i in 1..=5 {
AccountId::from([i as u8; 20]).into(),
100 * i as u128,
// Migrate balances in batches
3
// Check intermediate state
assert_eq!(info.remaining_balances, 2);
// Migrate remaining balances
2
// Verify final state
// Verify all balances were migrated correctly
assert_eq!(Assets::balance(asset_id, AccountId::from([i as u8; 20])), 0);
fn test_migrate_foreign_asset_balances_with_reserved_deposits() {
// Create account with reserved deposit
let account = BOB;
assert_ok!(Assets::touch(
RuntimeOrigin::signed(account.clone()),
// Mint some tokens
account.clone().into(),
// Check initial reserved balance
let initial_reserved = Balances::reserved_balance(&account);
assert!(initial_reserved > 0);
// Verify the balance was migrated
assert_eq!(Assets::balance(asset_id, account.clone()), 0);
// Verify the deposit was unreserved
assert_eq!(Balances::reserved_balance(&account), 0);
fn test_migrate_foreign_asset_approvals_without_migration_started() {
LazyMigrations::migrate_foreign_asset_approvals(
100
fn test_migrate_foreign_asset_approvals_zero_limit() {
// Create and start migration for an asset
0
fn test_migrate_foreign_asset_approvals_single_approval() {
// Create an approval
// Check initial migration status
// Initial reserved balance for approval
let initial_reserved = Balances::reserved_balance(&BOB);
// Migrate approvals
assert_ok!(LazyMigrations::migrate_foreign_asset_approvals(
// Check final migration status
assert_eq!(Balances::reserved_balance(&BOB), 0);
fn test_migrate_foreign_asset_approvals_multiple_approvals() {
// Create multiple approvals from different accounts
let owner = AccountId::from([i as u8; 20]);
// Add balance for each account
assert_ok!(Balances::force_set_balance(
owner.clone(),
let spender = AccountId::from([i as u8 + 1; 20]);
RuntimeOrigin::signed(owner.clone()),
spender.into(),
50 * i as u128,
assert_eq!(info.remaining_approvals, 5);
// Migrate approvals in batches
assert_eq!(info.remaining_approvals, 2);
// Migrate remaining approvals
// Verify all deposits were unreserved
assert_eq!(Balances::reserved_balance(&owner), 0);
fn test_migrate_foreign_asset_approvals_with_balances() {
// Create balance and approval for account
// Migrate approvals first
// Check that approvals were migrated but balances remain
fn test_migrate_foreign_asset_approvals_exceed_limit() {
for i in 1..=10 {
// Add balance and approval for each account
// Migrate with limit less than total approvals
5
// Verify partial migration
fn test_finish_foreign_assets_migration_success() {
// Setup: Create and start migration for an asset
// Initial balances
assert_eq!(Assets::balance(asset_id, BOB), 100);
assert!(Balances::reserved_balance(&BOB) > 0);
assert_eq!(
Assets::allowance(asset_id, &BOB, &AccountId::from([3; 20])),
50
assert!(Balances::reserved_balance(&AssetManager::account_id()) > 0);
// Start and complete migration
assert_ok!(LazyMigrations::finish_foreign_assets_migration(
RuntimeOrigin::signed(ALITH)
// Verify status is reset to Idle
crate::pallet::ForeignAssetMigrationStatusValue::<Test>::get(),
ForeignAssetMigrationStatus::Idle
// Verify all deposits are unreserved
assert_eq!(Balances::reserved_balance(&AssetManager::account_id()), 0);
// Verify the old asset is completely removed
assert!(pallet_assets::Asset::<Test>::get(asset_id).is_none());
assert_eq!(pallet_assets::Metadata::<Test>::get(asset_id).deposit, 0);
fn test_finish_foreign_assets_migration_no_migration_in_progress() {
LazyMigrations::finish_foreign_assets_migration(RuntimeOrigin::signed(ALITH)),
fn test_finish_foreign_assets_migration_with_remaining_balances() {
// Create balance but no approvals
// Start migration but don't migrate balances
// Verify we're still in migrating state
fn test_finish_foreign_assets_migration_with_remaining_approvals() {
// Create approval but no balances
// Start migration but don't migrate approvals