1#![cfg_attr(not(feature = "std"), no_std)]
18
19use frame_support::{pallet_prelude::*, traits::tokens::fungible};
20use sp_runtime::{
21 traits::{CheckedDiv, CheckedMul},
22 FixedPointNumber, RuntimeDebug,
23};
24use sp_std::prelude::*;
25pub use xcm::v4::{opaque::Xcm, Assets, Location, QueryId, SendError, SendResult, SendXcm, XcmHash};
26
27pub mod credentials;
28
29pub trait ReleaseSchedule<AccountId, Reason> {
32 type Moment;
34
35 type Currency: fungible::InspectHold<AccountId>
37 + fungible::MutateHold<AccountId>
38 + fungible::BalancedHold<AccountId>;
39
40 fn vesting_balance(
42 who: &AccountId,
43 reason: Reason,
44 ) -> Option<<Self::Currency as fungible::Inspect<AccountId>>::Balance>;
45
46 fn total_scheduled_amount(
48 who: &AccountId,
49 reason: Reason,
50 ) -> Option<<Self::Currency as fungible::Inspect<AccountId>>::Balance>;
51
52 fn vest(
54 who: AccountId,
55 reason: Reason,
56 ) -> Result<<Self::Currency as fungible::Inspect<AccountId>>::Balance, DispatchError>;
57
58 fn add_release_schedule(
67 who: &AccountId,
68 locked: <Self::Currency as fungible::Inspect<AccountId>>::Balance,
69 per_block: <Self::Currency as fungible::Inspect<AccountId>>::Balance,
70 starting_block: Self::Moment,
71 reason: Reason,
72 ) -> DispatchResult;
73
74 fn set_release_schedule(
83 who: &AccountId,
84 locked: <Self::Currency as fungible::Inspect<AccountId>>::Balance,
85 per_block: <Self::Currency as fungible::Inspect<AccountId>>::Balance,
86 starting_block: Self::Moment,
87 reason: Reason,
88 ) -> DispatchResult;
89
90 fn can_add_release_schedule(
92 who: &AccountId,
93 locked: <Self::Currency as fungible::Inspect<AccountId>>::Balance,
94 per_block: <Self::Currency as fungible::Inspect<AccountId>>::Balance,
95 starting_block: Self::Moment,
96 reason: Reason,
97 ) -> DispatchResult;
98
99 fn remove_vesting_schedule(who: &AccountId, schedule_index: u32, reason: Reason) -> DispatchResult;
103
104 fn remove_all_vesting_schedules(who: &AccountId, reason: Reason) -> DispatchResult;
105}
106
107pub mod migration_types {
108 #[allow(clippy::wildcard_imports)]
109 use super::*;
110
111 #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
112 pub struct MigrationOrigin {
113 pub user: Location,
114 pub id: u32,
115 pub participation_type: ParticipationType,
116 }
117 impl PartialOrd for MigrationOrigin {
118 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
119 Some(self.cmp(other))
120 }
121 }
122 impl Ord for MigrationOrigin {
123 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
124 if self.participation_type == other.participation_type {
125 self.id.cmp(&other.id)
126 } else {
127 self.participation_type.cmp(&other.participation_type)
128 }
129 }
130 }
131
132 #[derive(Clone, Copy, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen)]
133 pub enum ParticipationType {
134 Evaluation,
135 Bid,
136 Contribution,
137 }
138
139 #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
140 pub struct MigrationInfo {
141 pub contribution_token_amount: u128,
142 pub vesting_time: u64,
143 }
144 impl From<(u128, u64)> for MigrationInfo {
145 fn from((contribution_token_amount, vesting_time): (u128, u64)) -> Self {
146 Self { contribution_token_amount, vesting_time }
147 }
148 }
149
150 #[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, MaxEncodedLen)]
151 pub enum MigrationStatus {
152 NotStarted,
153 Sent(QueryId),
154 Confirmed,
155 Failed,
156 }
157
158 #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
159 pub struct Migration {
160 pub origin: MigrationOrigin,
161 pub info: MigrationInfo,
162 }
163
164 impl Migration {
165 pub fn new(origin: MigrationOrigin, info: MigrationInfo) -> Self {
166 Self { origin, info }
167 }
168 }
169
170 #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, Default)]
171 pub struct Migrations(Vec<Migration>);
172 impl FromIterator<Migration> for Migrations {
173 fn from_iter<T: IntoIterator<Item = Migration>>(iter: T) -> Self {
174 Migrations::from(iter.into_iter().collect::<Vec<_>>())
175 }
176 }
177
178 impl Migrations {
179 pub fn new() -> Self {
180 Self(Vec::new())
181 }
182
183 pub fn inner(self) -> Vec<Migration> {
184 self.0
185 }
186
187 pub fn push(&mut self, migration: Migration) {
188 self.0.push(migration)
189 }
190
191 pub fn from(migrations: Vec<Migration>) -> Self {
192 Self(migrations)
193 }
194
195 pub fn contains(&self, migration: &Migration) -> bool {
196 self.0.contains(migration)
197 }
198
199 pub fn len(&self) -> usize {
200 self.0.len()
201 }
202
203 pub fn is_empty(&self) -> bool {
204 self.0.is_empty()
205 }
206
207 pub fn origins(&self) -> Vec<MigrationOrigin> {
208 self.0.iter().map(|migration| migration.origin.clone()).collect()
209 }
210
211 pub fn infos(&self) -> Vec<MigrationInfo> {
212 self.0.iter().map(|migration| migration.info.clone()).collect()
213 }
214
215 pub fn total_ct_amount(&self) -> u128 {
216 self.0.iter().map(|migration| migration.info.contribution_token_amount).sum()
217 }
218
219 pub fn biggest_vesting_time(&self) -> u64 {
220 self.0.iter().map(|migration| migration.info.vesting_time).max().unwrap_or(0)
221 }
222 }
223}
224
225pub const USD_DECIMALS: u8 = 6;
226pub const USD_UNIT: u128 = 10u128.pow(USD_DECIMALS as u32);
227pub const PLMC_DECIMALS: u8 = 10;
228pub const PLMC_FOREIGN_ID: u32 = 3344;
229
230pub trait ProvideAssetPrice {
231 type AssetId;
232 type Price: FixedPointNumber;
233 fn get_price(asset_id: Self::AssetId) -> Option<Self::Price>;
237
238 fn calculate_decimals_aware_price(
244 original_price: Self::Price,
245 usd_decimals: u8,
246 asset_decimals: u8,
247 ) -> Option<Self::Price> {
248 let usd_unit = 10u128.checked_pow(usd_decimals.into())?;
249 let usd_price_with_decimals = original_price.checked_mul_int(usd_unit)?;
250 let asset_unit = 10u128.checked_pow(asset_decimals.into())?;
251
252 Self::Price::checked_from_rational(usd_price_with_decimals, asset_unit)
253 }
254
255 fn convert_back_to_normal_price(
256 decimals_aware_price: Self::Price,
257 usd_decimals: u8,
258 asset_decimals: u8,
259 ) -> Option<Self::Price> {
260 let abs_diff: u32 = asset_decimals.abs_diff(usd_decimals).into();
261 let abs_diff_unit = 10u128.checked_pow(abs_diff)?;
262 let abs_diff_fixed = Self::Price::checked_from_rational(abs_diff_unit, 1)?;
264 if usd_decimals > asset_decimals {
265 decimals_aware_price.checked_div(&abs_diff_fixed)
266 } else {
267 decimals_aware_price.checked_mul(&abs_diff_fixed)
268 }
269 }
270
271 fn get_decimals_aware_price(asset_id: Self::AssetId, usd_decimals: u8, asset_decimals: u8) -> Option<Self::Price> {
272 let original_price = Self::get_price(asset_id)?;
273 Self::calculate_decimals_aware_price(original_price, usd_decimals, asset_decimals)
274 }
275}