Lines
100 %
Functions
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::mock::*,
crate::{Error, Trader, XcmPaymentApiError},
frame_support::pallet_prelude::Weight,
frame_support::{assert_noop, assert_ok},
sp_runtime::DispatchError,
xcm::v5::{
Asset, AssetId as XcmAssetId, Error as XcmError, Fungibility, Location, XcmContext, XcmHash,
},
xcm::{IntoVersion, VersionedAssetId},
xcm_executor::traits::WeightTrader,
};
fn xcm_fees_account() -> <Test as frame_system::Config>::AccountId {
<Test as crate::Config>::XcmFeesAccount::get()
}
#[test]
fn test_add_supported_asset() {
new_test_ext().execute_with(|| {
// Call with bad origin
assert_noop!(
XcmWeightTrader::add_asset(
RuntimeOrigin::signed(EditAccount::get()),
Location::parent(),
1_000,
),
DispatchError::BadOrigin
);
// Call with invalid location
RuntimeOrigin::signed(AddAccount::get()),
Location::new(2, []),
Error::<Test>::XcmLocationFiltered
// Call with invalid price
0,
Error::<Test>::PriceCannotBeZero
// Call with the right origin
assert_ok!(XcmWeightTrader::add_asset(
));
// The account should be supported
assert_eq!(
XcmWeightTrader::get_asset_relative_price(&Location::parent()),
Some(1_000),
// Check storage
crate::pallet::SupportedAssets::<Test>::get(&Location::parent()),
Some((true, 1_000))
// Try to add the same asset twice (should fail)
Error::<Test>::AssetAlreadyAdded
})
fn test_edit_supported_asset() {
// Should not be able to edit an asset not added yet
XcmWeightTrader::edit_asset(
2_000,
Error::<Test>::AssetNotFound
// Setup (add a supported asset)
// Call with right origin and valid params
assert_ok!(XcmWeightTrader::edit_asset(
),);
Some(2_000),
Some((true, 2_000))
fn test_pause_asset_support() {
// Should not be able to pause an asset not added yet
XcmWeightTrader::pause_asset_support(
RuntimeOrigin::signed(PauseAccount::get()),
assert_ok!(XcmWeightTrader::pause_asset_support(
// The asset should be paused
None,
Some((false, 1_000))
// Should not be able to pause an asset already paused
Error::<Test>::AssetAlreadyPaused
// Should be able to udpate relative price of paused asset
500
// The asset should still be paused
Some((false, 500))
fn test_resume_asset_support() {
// Setup (add a supported asset and pause it)
XcmWeightTrader::resume_asset_support(
RuntimeOrigin::signed(ResumeAccount::get()),
assert_ok!(XcmWeightTrader::resume_asset_support(
// The asset should be supported again
// Should not be able to resume an asset already active
Error::<Test>::AssetNotPaused
fn test_remove_asset_support() {
// Should not be able to remove an asset not added yet
XcmWeightTrader::remove_asset(
RuntimeOrigin::signed(RemoveAccount::get()),
assert_ok!(XcmWeightTrader::remove_asset(
// The account should be removed
None
// Should not be able to pause an asset already removed
fn test_trader_native_asset() {
let weight_to_buy = Weight::from_parts(10_000, 0);
let dummy_xcm_context = XcmContext::with_message_id(XcmHash::default());
// Should not be able to buy weight with too low asset balance
Trader::<Test>::new().buy_weight(
weight_to_buy,
Asset {
fun: Fungibility::Fungible(9_999),
id: XcmAssetId(Location::here()),
.into(),
&dummy_xcm_context
Err(XcmError::TooExpensive)
// Should not be able to buy weight with unsupported asset
fun: Fungibility::Fungible(10_000),
id: XcmAssetId(Location::parent()),
Err(XcmError::AssetNotFound)
// Should not be able to buy weight without asset
Trader::<Test>::new().buy_weight(weight_to_buy, Default::default(), &dummy_xcm_context),
// Should be able to buy weight with just enough native asset
let mut trader = Trader::<Test>::new();
trader.buy_weight(
Ok(Default::default())
// Should not refund any funds
let actual_weight = weight_to_buy;
trader.refund_weight(actual_weight, &dummy_xcm_context),
// Should not be able to buy weight again with the same trader
Err(XcmError::NotWithdrawable)
// Fees asset should be deposited into XcmFeesAccount
drop(trader);
assert_eq!(Balances::free_balance(&xcm_fees_account()), 10_000);
// Should be able to buy weight with more native asset (and get back unused amount)
fun: Fungibility::Fungible(11_000),
Ok(Asset {
fun: Fungibility::Fungible(1_000),
.into())
// Should be able to refund unused weights
let actual_weight = weight_to_buy.saturating_sub(Weight::from_parts(2_000, 0));
Some(Asset {
fun: Fungibility::Fungible(2_000),
// Fees asset should be deposited again into XcmFeesAccount (2 times cost minus one refund)
Balances::free_balance(&xcm_fees_account()),
(2 * 10_000) - 2_000
fn test_trader_parent_asset() {
500_000_000,
Some(500_000_000),
// Should be able to pay fees with registered asset
fun: Fungibility::Fungible(22_000_000_000_000),
fun: Fungibility::Fungible(2_000_000_000_000),
fun: Fungibility::Fungible(4_000_000_000_000),
get_parent_asset_deposited(),
Some((xcm_fees_account(), 20_000_000_000_000 - 4_000_000_000_000))
// Should not be able to buy weight if the asset is not a first position
vec![
fun: Fungibility::Fungible(10),
fun: Fungibility::Fungible(30_000),
]
fn test_query_acceptable_payment_assets() {
// By default, only native asset should be supported
XcmWeightTrader::query_acceptable_payment_assets(5),
Ok(vec![VersionedAssetId::from(XcmAssetId(
<Test as crate::Config>::NativeLocation::get()
))])
// We should support XCMv3
XcmWeightTrader::query_acceptable_payment_assets(3),
))
.into_version(xcm::v3::VERSION)
.expect("native location should be convertible to v3")])
// We should not support XCMv2
XcmWeightTrader::query_acceptable_payment_assets(2),
Err(XcmPaymentApiError::UnhandledXcmVersion)
// We should support parent asset now
Ok(vec![
VersionedAssetId::from(XcmAssetId(<Test as crate::Config>::NativeLocation::get())),
VersionedAssetId::from(XcmAssetId(Location::parent()))
])
// Setup: pause parent asset
// We should not support paused assets
)),])
fn test_query_weight_to_asset_fee() {
let native_asset =
VersionedAssetId::from(XcmAssetId(<Test as crate::Config>::NativeLocation::get()));
let parent_asset = VersionedAssetId::from(XcmAssetId(Location::parent()));
// Native asset price should be 1:1
XcmWeightTrader::query_weight_to_asset_fee(weight_to_buy, native_asset.clone()),
Ok(10_000)
// Should not be able to query fees for an unsupported asset
XcmWeightTrader::query_weight_to_asset_fee(weight_to_buy, parent_asset.clone()),
Err(XcmPaymentApiError::AssetNotFound)
// Parent asset price should be 0.5
Ok(2 * 10_000_000_000_000)
// Setup: unpause parent asset and edit price
2_000_000_000,
Some(2_000_000_000),
// We should support unpaused asset with new price
XcmWeightTrader::query_weight_to_asset_fee(weight_to_buy, parent_asset),
Ok(10_000_000_000_000 / 2)