Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions prdoc/pr_11807.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
title: 'PSM init: skip assets with mismatched decimals instead of panicking'
doc:
- audience: Runtime Dev
description: |-
Replaces the `assert!` in the PSM `InitializePsm` migration with a log and skip
when an asset's decimals don't match the stable asset. Panicking in a runtime
upgrade bricks the chain. Migrations must be infallible.

crates:
- name: pallet-psm
bump: patch
73 changes: 67 additions & 6 deletions substrate/frame/psm/src/migrations/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,14 @@ impl<T: Config, I: InitialPsmConfig<T>> frame_support::traits::OnRuntimeUpgrade
continue;
}

assert!(
T::Fungibles::decimals(*asset_id) == stable_decimals,
"PSM migration: asset {:?} decimals do not match stable asset decimals",
asset_id,
);
if T::Fungibles::decimals(*asset_id) != stable_decimals {
log::error!(
target: LOG_TARGET,
"Asset {:?} decimals do not match stable asset decimals, skipping",
asset_id,
);
continue;
}
ExternalAssets::<T>::insert(asset_id, CircuitBreakerLevel::AllEnabled);
MintingFee::<T>::insert(asset_id, minting_fee);
RedemptionFee::<T>::insert(asset_id, redemption_fee);
Expand Down Expand Up @@ -195,7 +198,8 @@ impl<T: Config, I: InitialPsmConfig<T>> frame_support::traits::OnRuntimeUpgrade
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{new_test_ext, Test, USDC_ASSET_ID, USDT_ASSET_ID};
use crate::mock::{new_test_ext, Assets, Test, ALICE, USDC_ASSET_ID, USDT_ASSET_ID};
use frame_support::assert_ok;

struct TestPsmConfig;

Expand Down Expand Up @@ -301,6 +305,63 @@ mod tests {
});
}

#[test]
fn initialize_psm_skips_assets_with_wrong_decimals() {
use frame_support::traits::{
fungibles::{metadata::Mutate as MetadataMutate, Create as FungiblesCreate},
OnRuntimeUpgrade,
};

const WRONG_DECIMALS_ID: u32 = 99;

new_test_ext().execute_with(|| {
// Create an asset with 8 decimals (stable asset has 6).
assert_ok!(<Assets as FungiblesCreate<u64>>::create(WRONG_DECIMALS_ID, ALICE, true, 1));
assert_ok!(<Assets as MetadataMutate<u64>>::set(
WRONG_DECIMALS_ID,
&ALICE,
b"Wrong".to_vec(),
b"WRG".to_vec(),
8, // mismatched decimals
));

struct MixedDecimalsConfig;
impl InitialPsmConfig<Test> for MixedDecimalsConfig {
fn max_psm_debt_of_total() -> Permill {
Permill::from_percent(50)
}
fn asset_configs() -> BTreeMap<u32, (Permill, Permill, Permill)> {
[
(
WRONG_DECIMALS_ID,
(Permill::zero(), Permill::zero(), Permill::from_percent(50)),
),
(
USDC_ASSET_ID, // 6 decimals — matches stable asset
(Permill::zero(), Permill::zero(), Permill::from_percent(50)),
),
]
.into_iter()
.collect()
}
}

ExternalAssets::<Test>::remove(WRONG_DECIMALS_ID);
ExternalAssets::<Test>::remove(USDC_ASSET_ID);

InitializePsm::<Test, MixedDecimalsConfig>::on_runtime_upgrade();

// Wrong decimals asset was skipped.
assert_eq!(ExternalAssets::<Test>::get(WRONG_DECIMALS_ID), None);

// Matching decimals asset was configured.
assert_eq!(
ExternalAssets::<Test>::get(USDC_ASSET_ID),
Some(CircuitBreakerLevel::AllEnabled)
);
});
}

#[test]
fn initialize_psm_is_idempotent() {
use frame_support::traits::OnRuntimeUpgrade;
Expand Down
Loading