|
1 | 1 | mod v1_to_v5; |
2 | 2 |
|
| 3 | +pub(crate) use share_price_v0::OperatorEpochSharePrice as OperatorEpochSharePriceV0; |
3 | 4 | pub use v1_to_v5::VersionCheckedMigrateDomainsV1ToV5; |
| 5 | + |
| 6 | +mod share_price_v0 { |
| 7 | + use crate::staking::{DomainEpoch, SharePrice as SharePriceV1}; |
| 8 | + use crate::{Config, Pallet}; |
| 9 | + use frame_support::pallet_prelude::OptionQuery; |
| 10 | + use frame_support::{Identity, storage_alias}; |
| 11 | + use parity_scale_codec::{Decode, Encode}; |
| 12 | + use scale_info::TypeInfo; |
| 13 | + use sp_domains::OperatorId; |
| 14 | + use sp_runtime::{Perbill, Perquintill}; |
| 15 | + |
| 16 | + #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Default)] |
| 17 | + pub struct SharePrice(Perbill); |
| 18 | + |
| 19 | + impl From<SharePrice> for SharePriceV1 { |
| 20 | + fn from(val: SharePrice) -> Self { |
| 21 | + SharePriceV1(Perquintill::from_parts( |
| 22 | + (val.0.deconstruct() as u64).saturating_mul(1_000_000_000u64), |
| 23 | + )) |
| 24 | + } |
| 25 | + } |
| 26 | + |
| 27 | + #[storage_alias] |
| 28 | + pub(crate) type OperatorEpochSharePrice<T: Config> = StorageDoubleMap< |
| 29 | + Pallet<T>, |
| 30 | + Identity, |
| 31 | + OperatorId, |
| 32 | + Identity, |
| 33 | + DomainEpoch, |
| 34 | + SharePrice, |
| 35 | + OptionQuery, |
| 36 | + >; |
| 37 | + |
| 38 | + #[cfg(test)] |
| 39 | + mod tests { |
| 40 | + use super::*; |
| 41 | + use crate::staking::{SharePrice as SharePriceV1, get_share_price}; |
| 42 | + use crate::tests::{Test, new_test_ext}; |
| 43 | + |
| 44 | + #[test] |
| 45 | + fn test_share_price_structure_migration() { |
| 46 | + for share_price_v0 in [ |
| 47 | + SharePrice(Perbill::from_parts(1)), |
| 48 | + SharePrice(Perbill::one()), |
| 49 | + SharePrice(Perbill::from_rational(123456789u32, 987654321u32)), |
| 50 | + SharePrice(Perbill::from_rational( |
| 51 | + 1_000_000_000u32, |
| 52 | + 1_000_000_000u32 + 123456u32, |
| 53 | + )), |
| 54 | + ] { |
| 55 | + let share_price_v1: SharePriceV1 = share_price_v0.clone().into(); |
| 56 | + |
| 57 | + for n in [ |
| 58 | + 1213u128, |
| 59 | + 940u128, |
| 60 | + 9678231u128, |
| 61 | + 2367u128, |
| 62 | + 834228u128, |
| 63 | + 298749827u128, |
| 64 | + 1234567890987654321u128, |
| 65 | + ] { |
| 66 | + assert_eq!( |
| 67 | + share_price_v0.0.mul_floor::<u128>(n), |
| 68 | + share_price_v1.stake_to_shares::<Test>(n) |
| 69 | + ); |
| 70 | + |
| 71 | + // The v1 share price `shares_to_stake` will return a strict rounding down result |
| 72 | + // while the v0 may not, thus v0 share price may return more stake. |
| 73 | + assert!( |
| 74 | + share_price_v0.0.saturating_reciprocal_mul_floor::<u128>(n) |
| 75 | + >= share_price_v1.shares_to_stake::<Test>(n) |
| 76 | + ); |
| 77 | + } |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | + #[test] |
| 82 | + fn test_share_price_getter_migration() { |
| 83 | + new_test_ext().execute_with(|| { |
| 84 | + let operator_id = 0; |
| 85 | + let domain_epoch = (0u32.into(), 0u32).into(); |
| 86 | + let share_price_v0 = SharePrice(Perbill::from_rational(123456789u32, 987654321u32)); |
| 87 | + let share_price_v1: SharePriceV1 = share_price_v0.clone().into(); |
| 88 | + |
| 89 | + // Decode a v0 share price to v1 should result in an error |
| 90 | + let decode_result = |
| 91 | + <SharePriceV1 as Decode>::decode(&mut share_price_v0.encode().as_slice()); |
| 92 | + assert!(decode_result.is_err()); |
| 93 | + |
| 94 | + // Insert an v0 share price |
| 95 | + OperatorEpochSharePrice::<Test>::insert(operator_id, domain_epoch, &share_price_v0); |
| 96 | + |
| 97 | + // `get_share_price` should internally convert the v0 share price to v1. |
| 98 | + let share_price = get_share_price::<Test>(operator_id, domain_epoch); |
| 99 | + |
| 100 | + assert_eq!(share_price, Some(share_price_v1)); |
| 101 | + }) |
| 102 | + } |
| 103 | + } |
| 104 | +} |
0 commit comments