#![cfg_attr(not(feature = "std"), no_std)]
use cfg_primitives::Balance;
use cfg_types::{
fee_keys::FeeKey,
pools::PoolNav,
tokens::{CurrencyId, StakingCurrency},
};
use orml_traits::GetByKey;
use pallet_loans::entities::input::PriceCollectionInput;
use pallet_pool_system::Nav;
use sp_core::parameter_types;
use sp_runtime::{
traits::{Get, Zero},
DispatchError,
};
use sp_std::marker::PhantomData;
pub mod account_conversion;
pub mod apis;
pub mod changes;
pub mod evm;
pub mod fees;
pub mod gateway;
pub mod migrations;
pub mod oracle;
pub mod origins;
pub mod permissions;
pub mod pool;
pub mod remarks;
pub mod routing;
pub mod transfer_filter;
pub mod xcm;
pub mod instances {
pub type BlockRewards = pallet_rewards::Instance1;
pub type TechnicalCollective = pallet_collective::Instance2;
pub type TechnicalMembership = pallet_membership::Instance1;
pub type CouncilCollective = pallet_collective::Instance1;
}
parameter_types! {
pub const NativeCurrency: CurrencyId = CurrencyId::Native;
}
pub struct AllowanceDeposit<T>(sp_std::marker::PhantomData<T>);
impl<T: cfg_traits::fees::Fees<Balance = Balance, FeeKey = FeeKey>> Get<Balance>
for AllowanceDeposit<T>
{
fn get() -> Balance {
T::fee_value(FeeKey::AllowanceCreation)
}
}
#[macro_export]
macro_rules! production_or_benchmark {
($production:expr, $benchmark:expr) => {{
if cfg!(feature = "runtime-benchmarks") {
$benchmark
} else {
$production
}
}};
}
pub struct CurrencyED<T>(PhantomData<T>);
impl<T> GetByKey<CurrencyId, Balance> for CurrencyED<T>
where
T: pallet_balances::Config<Balance = Balance>
+ orml_asset_registry::module::Config<AssetId = CurrencyId, Balance = Balance>,
{
fn get(currency_id: &CurrencyId) -> Balance {
match currency_id {
CurrencyId::Native => T::ExistentialDeposit::get(),
CurrencyId::Staking(StakingCurrency::BlockRewards) => T::ExistentialDeposit::get(),
currency_id => orml_asset_registry::module::Pallet::<T>::metadata(currency_id)
.map(|metadata| metadata.existential_deposit)
.unwrap_or_default(),
}
}
}
pub fn update_nav<T>(
pool_id: <T as pallet_pool_system::Config>::PoolId,
) -> Result<PoolNav<<T as pallet_pool_system::Config>::Balance>, DispatchError>
where
T: pallet_loans::Config<
PoolId = <T as pallet_pool_system::Config>::PoolId,
Balance = <T as pallet_pool_system::Config>::Balance,
> + pallet_pool_system::Config
+ pallet_pool_fees::Config<
PoolId = <T as pallet_pool_system::Config>::PoolId,
Balance = <T as pallet_pool_system::Config>::Balance,
>,
{
let input_prices: PriceCollectionInput<T> =
match pallet_loans::Pallet::<T>::registered_prices(pool_id) {
Ok(_) => PriceCollectionInput::FromRegistry,
Err(_) => PriceCollectionInput::Empty,
};
update_nav_with_input::<T>(pool_id, input_prices)
}
pub fn update_nav_with_input<T>(
pool_id: <T as pallet_pool_system::Config>::PoolId,
price_input: PriceCollectionInput<T>,
) -> Result<PoolNav<<T as pallet_pool_system::Config>::Balance>, DispatchError>
where
T: pallet_loans::Config<
PoolId = <T as pallet_pool_system::Config>::PoolId,
Balance = <T as pallet_pool_system::Config>::Balance,
> + pallet_pool_system::Config
+ pallet_pool_fees::Config<
PoolId = <T as pallet_pool_system::Config>::PoolId,
Balance = <T as pallet_pool_system::Config>::Balance,
>,
{
let mut pool = pallet_pool_system::Pool::<T>::get(pool_id)
.ok_or(pallet_pool_system::Error::<T>::NoSuchPool)?;
let prev_nav_loans = pallet_loans::Pallet::<T>::portfolio_valuation(pool_id).value();
let nav_loans =
pallet_loans::Pallet::<T>::update_portfolio_valuation_for_pool(pool_id, price_input)
.map(|(nav_loans, _)| nav_loans)
.unwrap_or(prev_nav_loans);
let prev_fees_loans = pallet_pool_fees::Pallet::<T>::portfolio_valuation(pool_id).value();
let nav_fees = pallet_pool_fees::Pallet::<T>::update_portfolio_valuation_for_pool(
pool_id,
&mut pool.reserve.total,
)
.map(|(nav_fees, _)| nav_fees)
.unwrap_or(prev_fees_loans);
let nav = Nav::new(nav_loans, nav_fees);
let total = nav
.total(pool.reserve.total)
.unwrap_or(<T as pallet_pool_system::Config>::Balance::zero());
Ok(PoolNav {
nav_aum: nav.nav_aum,
nav_fees: nav.nav_fees,
reserve: pool.reserve.total,
total,
})
}
pub mod xcm_fees {
use cfg_primitives::{constants::currency_decimals, types::Balance};
use frame_support::weights::constants::{ExtrinsicBaseWeight, WEIGHT_REF_TIME_PER_SECOND};
pub fn native_per_second() -> Balance {
default_per_second(currency_decimals::NATIVE)
}
pub fn ksm_per_second() -> Balance {
default_per_second(currency_decimals::KSM) / 50
}
pub fn default_per_second(decimals: u32) -> Balance {
let base_weight = Balance::from(ExtrinsicBaseWeight::get().ref_time());
let default_per_second = WEIGHT_REF_TIME_PER_SECOND as u128 / base_weight;
default_per_second * base_fee(decimals)
}
fn base_fee(decimals: u32) -> Balance {
dollar(decimals)
.saturating_div(100)
.saturating_div(10)
}
pub fn dollar(decimals: u32) -> Balance {
10u128.saturating_pow(decimals)
}
}
pub mod asset_registry {
use cfg_primitives::types::AccountId;
use cfg_types::tokens::{AssetMetadata, CurrencyId};
use frame_support::{
dispatch::RawOrigin,
traits::{EnsureOrigin, EnsureOriginWithArg},
};
use orml_traits::asset_registry::AssetProcessor;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_runtime::DispatchError;
use sp_std::marker::PhantomData;
#[derive(
Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen,
)]
pub struct CustomAssetProcessor;
impl AssetProcessor<CurrencyId, AssetMetadata> for CustomAssetProcessor {
fn pre_register(
id: Option<CurrencyId>,
metadata: AssetMetadata,
) -> Result<(CurrencyId, AssetMetadata), DispatchError> {
match id {
Some(id) => Ok((id, metadata)),
None => Err(DispatchError::Other("asset-registry: AssetId is required")),
}
}
fn post_register(
_id: CurrencyId,
_asset_metadata: AssetMetadata,
) -> Result<(), DispatchError> {
Ok(())
}
}
pub struct AuthorityOrigin<
Origin,
DefaultEnsureOrigin,
>(PhantomData<(Origin, DefaultEnsureOrigin)>);
impl<
Origin: Into<Result<RawOrigin<AccountId>, Origin>> + From<RawOrigin<AccountId>>,
DefaultEnsureOrigin: EnsureOrigin<Origin>,
> EnsureOriginWithArg<Origin, Option<CurrencyId>>
for AuthorityOrigin<Origin, DefaultEnsureOrigin>
{
type Success = ();
fn try_origin(
origin: Origin,
asset_id: &Option<CurrencyId>,
) -> Result<Self::Success, Origin> {
match asset_id {
Some(CurrencyId::Tranche(_, _)) => Err(origin),
_ => DefaultEnsureOrigin::try_origin(origin).map(|_| ()),
}
}
#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin(_asset_id: &Option<CurrencyId>) -> Result<Origin, ()> {
Err(())
}
}
}
pub mod investment_portfolios {
use cfg_primitives::{AccountId, Balance, InvestmentId, PoolId};
use cfg_traits::{investments::InvestmentCollector, PoolInspect};
use cfg_types::{investments::InvestmentPortfolio, tokens::CurrencyId};
use frame_support::traits::{
fungibles,
tokens::{Fortitude, Preservation},
};
use fungibles::{Inspect, InspectHold};
use sp_runtime::DispatchError;
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
#[allow(clippy::type_complexity)]
pub fn get_account_portfolio<T>(
investor: AccountId,
) -> Result<Vec<(InvestmentId, InvestmentPortfolio<Balance, CurrencyId>)>, DispatchError>
where
T: frame_system::Config<AccountId = AccountId>
+ pallet_investments::Config<InvestmentId = InvestmentId, Amount = Balance>
+ orml_tokens::Config<Balance = Balance, CurrencyId = CurrencyId>
+ pallet_restricted_tokens::Config<Balance = Balance, CurrencyId = CurrencyId>
+ pallet_pool_system::Config<PoolId = PoolId, CurrencyId = CurrencyId>,
{
let mut portfolio =
BTreeMap::<InvestmentId, InvestmentPortfolio<Balance, CurrencyId>>::new();
for currency in orml_tokens::Accounts::<T>::iter_key_prefix(&investor) {
if let CurrencyId::Tranche(pool_id, tranche_id) = currency {
let pool_currency = pallet_pool_system::Pallet::<T>::currency_for(pool_id)
.ok_or(DispatchError::Other("Pool must exist; qed"))?;
let free_balance = pallet_restricted_tokens::Pallet::<T>::reducible_balance(
currency,
&investor,
Preservation::Preserve,
Fortitude::Polite,
);
let reserved_balance = pallet_restricted_tokens::Pallet::<T>::balance_on_hold(
currency,
&(),
&investor,
);
portfolio
.entry((pool_id, tranche_id))
.and_modify(|p| {
p.free_tranche_tokens = free_balance;
p.reserved_tranche_tokens = reserved_balance;
})
.or_insert(
InvestmentPortfolio::<Balance, CurrencyId>::new(pool_currency)
.with_free_tranche_tokens(free_balance)
.with_reserved_tranche_tokens(reserved_balance),
);
}
}
for investment_id in pallet_investments::InvestOrders::<T>::iter_key_prefix(&investor) {
let pool_currency = pallet_pool_system::Pallet::<T>::currency_for(investment_id.0)
.ok_or(DispatchError::Other("Pool must exist; qed"))?;
let _ = pallet_investments::Pallet::<T>::collect_investment(
investor.clone(),
investment_id,
);
let amount = pallet_investments::InvestOrders::<T>::get(&investor, investment_id)
.map(|order| order.amount())
.unwrap_or_default();
let free_tranche_tokens_new = pallet_restricted_tokens::Pallet::<T>::reducible_balance(
investment_id.into(),
&investor,
Preservation::Preserve,
Fortitude::Polite,
);
portfolio
.entry(investment_id)
.and_modify(|p| {
p.pending_invest_currency = amount;
if p.free_tranche_tokens < free_tranche_tokens_new {
p.claimable_tranche_tokens =
free_tranche_tokens_new.saturating_sub(p.free_tranche_tokens);
}
})
.or_insert(
InvestmentPortfolio::<Balance, CurrencyId>::new(pool_currency)
.with_pending_invest_currency(amount)
.with_claimable_tranche_tokens(free_tranche_tokens_new),
);
}
for investment_id in pallet_investments::RedeemOrders::<T>::iter_key_prefix(&investor) {
let pool_currency = pallet_pool_system::Pallet::<T>::currency_for(investment_id.0)
.ok_or(DispatchError::Other("Pool must exist; qed"))?;
let balance_before = pallet_restricted_tokens::Pallet::<T>::reducible_balance(
pool_currency,
&investor,
Preservation::Preserve,
Fortitude::Polite,
);
let _ = pallet_investments::Pallet::<T>::collect_redemption(
investor.clone(),
investment_id,
);
let amount = pallet_investments::RedeemOrders::<T>::get(&investor, investment_id)
.map(|order| order.amount())
.unwrap_or_default();
let balance_after = pallet_restricted_tokens::Pallet::<T>::reducible_balance(
pool_currency,
&investor,
Preservation::Preserve,
Fortitude::Polite,
);
portfolio
.entry(investment_id)
.and_modify(|p| {
p.pending_redeem_tranche_tokens = amount;
if balance_before < balance_after {
p.claimable_currency = balance_after.saturating_sub(balance_before);
}
})
.or_insert(
InvestmentPortfolio::<Balance, CurrencyId>::new(pool_currency)
.with_pending_redeem_tranche_tokens(amount)
.with_claimable_currency(balance_after),
);
}
Ok(portfolio.into_iter().collect())
}
}
pub mod xcm_transactor {
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_std::{vec, vec::Vec};
use xcm_primitives::{UtilityAvailableCalls, UtilityEncodeCall, XcmTransact};
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
pub struct NullTransactor {}
impl UtilityEncodeCall for NullTransactor {
fn encode_call(self, _call: UtilityAvailableCalls) -> Vec<u8> {
vec![]
}
}
impl XcmTransact for NullTransactor {
fn destination(self) -> staging_xcm::latest::Location {
Default::default()
}
}
}
pub mod foreign_investments {
use cfg_primitives::{conversion::convert_balance_decimals, Balance};
use cfg_traits::IdentityCurrencyConversion;
use cfg_types::tokens::CurrencyId;
use frame_support::pallet_prelude::PhantomData;
use orml_traits::asset_registry::Inspect;
use sp_runtime::DispatchError;
pub struct IdentityPoolCurrencyConverter<AssetRegistry>(PhantomData<AssetRegistry>);
impl<AssetRegistry> IdentityCurrencyConversion for IdentityPoolCurrencyConverter<AssetRegistry>
where
AssetRegistry: Inspect<
AssetId = CurrencyId,
Balance = Balance,
CustomMetadata = cfg_types::tokens::CustomMetadata,
>,
{
type Balance = Balance;
type Currency = CurrencyId;
type Error = DispatchError;
fn stable_to_stable(
currency_in: Self::Currency,
currency_out: Self::Currency,
amount_out: Self::Balance,
) -> Result<Self::Balance, Self::Error> {
match (currency_out, currency_in) {
(from, to) if from == to => Ok(amount_out),
(CurrencyId::ForeignAsset(_), CurrencyId::ForeignAsset(_)) => {
let from_metadata = AssetRegistry::metadata(¤cy_out)
.ok_or(DispatchError::CannotLookup)?;
let to_metadata =
AssetRegistry::metadata(¤cy_in).ok_or(DispatchError::CannotLookup)?;
frame_support::ensure!(
from_metadata.additional.pool_currency
|| to_metadata.additional.pool_currency,
DispatchError::Token(sp_runtime::TokenError::Unsupported)
);
convert_balance_decimals(
from_metadata.decimals,
to_metadata.decimals,
amount_out,
)
.map_err(DispatchError::from)
}
_ => Err(DispatchError::Token(sp_runtime::TokenError::Unsupported)),
}
}
}
}
pub mod origin {
use cfg_primitives::AccountId;
use frame_support::traits::{EitherOfDiverse, SortedMembers};
use frame_system::{EnsureRoot, EnsureSignedBy};
use sp_core::Get;
pub type EnsureAccountOrRoot<Account> =
EitherOfDiverse<EnsureSignedBy<AdminOnly<Account>, AccountId>, EnsureRoot<AccountId>>;
pub type EnsureAccountOrRootOr<Account, O> = EitherOfDiverse<EnsureAccountOrRoot<Account>, O>;
pub struct AdminOnly<Account>(sp_std::marker::PhantomData<Account>);
impl<Account> SortedMembers<AccountId> for AdminOnly<Account>
where
Account: Get<AccountId>,
{
fn sorted_members() -> sp_std::vec::Vec<AccountId> {
sp_std::vec![Account::get()]
}
}
#[cfg(test)]
mod test {
use frame_support::traits::EnsureOrigin;
use sp_core::{crypto::AccountId32, parameter_types};
use super::*;
parameter_types! {
pub Admin: AccountId = AccountId::new([0u8;32]);
}
#[derive(Clone)]
enum OuterOrigin {
Raw(frame_system::RawOrigin<AccountId>),
Council(pallet_collective::RawOrigin<AccountId, pallet_collective::Instance1>),
Dummy,
}
impl Into<Result<frame_system::RawOrigin<AccountId>, OuterOrigin>> for OuterOrigin {
fn into(self) -> Result<frame_system::RawOrigin<AccountId>, OuterOrigin> {
match self {
Self::Raw(raw) => Ok(raw),
_ => Err(self),
}
}
}
impl
Into<
Result<
pallet_collective::RawOrigin<
sp_runtime::AccountId32,
pallet_collective::Instance1,
>,
OuterOrigin,
>,
> for OuterOrigin
{
fn into(
self,
) -> Result<
pallet_collective::RawOrigin<AccountId32, pallet_collective::Instance1>,
OuterOrigin,
> {
match self {
Self::Council(raw) => Ok(raw),
_ => Err(self),
}
}
}
impl From<frame_system::RawOrigin<AccountId>> for OuterOrigin {
fn from(value: frame_system::RawOrigin<AccountId>) -> Self {
Self::Raw(value)
}
}
impl From<pallet_collective::RawOrigin<AccountId, pallet_collective::Instance1>> for OuterOrigin {
fn from(
value: pallet_collective::RawOrigin<AccountId, pallet_collective::Instance1>,
) -> Self {
Self::Council(value)
}
}
mod ensure_account_or_root_or {
use super::*;
use crate::origins::gov::types::HalfOfCouncil;
#[test]
fn works_with_account() {
let origin = OuterOrigin::Raw(frame_system::RawOrigin::Signed(Admin::get()));
assert!(
EnsureAccountOrRootOr::<Admin, HalfOfCouncil>::ensure_origin(origin).is_ok()
)
}
#[test]
fn fails_with_non_admin_account() {
let origin =
OuterOrigin::Raw(frame_system::RawOrigin::Signed(AccountId::from([1u8; 32])));
assert!(
EnsureAccountOrRootOr::<Admin, HalfOfCouncil>::ensure_origin(origin).is_err()
)
}
#[test]
fn works_with_half_of_council() {
let origin = OuterOrigin::Council(pallet_collective::RawOrigin::Members(5, 9));
assert!(
EnsureAccountOrRootOr::<Admin, HalfOfCouncil>::ensure_origin(origin).is_ok()
)
}
#[test]
fn fails_with_less_than_half_of_council() {
let origin = OuterOrigin::Council(pallet_collective::RawOrigin::Members(4, 9));
assert!(
EnsureAccountOrRootOr::<Admin, HalfOfCouncil>::ensure_origin(origin).is_err()
)
}
#[test]
fn works_with_root() {
let origin = OuterOrigin::Raw(frame_system::RawOrigin::Root);
assert!(
EnsureAccountOrRootOr::<Admin, HalfOfCouncil>::ensure_origin(origin).is_ok()
)
}
#[test]
fn fails_with_none() {
let origin = OuterOrigin::Raw(frame_system::RawOrigin::None);
assert!(
EnsureAccountOrRootOr::<Admin, HalfOfCouncil>::ensure_origin(origin).is_err()
)
}
#[test]
fn fails_with_dummy() {
let origin = OuterOrigin::Dummy;
assert!(
EnsureAccountOrRootOr::<Admin, HalfOfCouncil>::ensure_origin(origin).is_err()
)
}
}
mod ensure_account_or_root {
use super::*;
#[test]
fn works_with_account() {
let origin = OuterOrigin::Raw(frame_system::RawOrigin::Signed(Admin::get()));
assert!(EnsureAccountOrRoot::<Admin>::ensure_origin(origin).is_ok())
}
#[test]
fn fails_with_non_admin_account() {
let origin =
OuterOrigin::Raw(frame_system::RawOrigin::Signed(AccountId::from([1u8; 32])));
assert!(EnsureAccountOrRoot::<Admin>::ensure_origin(origin).is_err())
}
#[test]
fn works_with_root() {
let origin = OuterOrigin::Raw(frame_system::RawOrigin::Root);
assert!(EnsureAccountOrRoot::<Admin>::ensure_origin(origin).is_ok())
}
#[test]
fn fails_with_none() {
let origin = OuterOrigin::Raw(frame_system::RawOrigin::None);
assert!(EnsureAccountOrRoot::<Admin>::ensure_origin(origin).is_err())
}
#[test]
fn fails_with_dummy() {
let origin = OuterOrigin::Dummy;
assert!(EnsureAccountOrRoot::<Admin>::ensure_origin(origin).is_err())
}
}
}
}
pub mod rewards {
frame_support::parameter_types! {
#[derive(scale_info::TypeInfo)]
pub const SingleCurrencyMovement: u32 = 1;
}
}
pub mod message_queue {
use cumulus_primitives_core::{AggregateMessageOrigin, ParaId};
use frame_support::traits::{QueueFootprint, QueuePausedQuery};
use pallet_message_queue::OnQueueChanged;
use sp_std::marker::PhantomData;
pub struct NarrowOriginToSibling<Inner>(PhantomData<Inner>);
impl<Inner: QueuePausedQuery<ParaId>> QueuePausedQuery<AggregateMessageOrigin>
for NarrowOriginToSibling<Inner>
{
fn is_paused(origin: &AggregateMessageOrigin) -> bool {
match origin {
AggregateMessageOrigin::Sibling(id) => Inner::is_paused(id),
_ => false,
}
}
}
impl<Inner: OnQueueChanged<ParaId>> OnQueueChanged<AggregateMessageOrigin>
for NarrowOriginToSibling<Inner>
{
fn on_queue_changed(origin: AggregateMessageOrigin, fp: QueueFootprint) {
if let AggregateMessageOrigin::Sibling(id) = origin {
Inner::on_queue_changed(id, fp)
}
}
}
pub struct ParaIdToSibling;
impl sp_runtime::traits::Convert<ParaId, AggregateMessageOrigin> for ParaIdToSibling {
fn convert(para_id: ParaId) -> AggregateMessageOrigin {
AggregateMessageOrigin::Sibling(para_id)
}
}
}