Skip to content

Commit

Permalink
feat: add index token deposit range (#336)
Browse files Browse the repository at this point in the history
* feat: add deposit range

* feat: remove dot contribution limits
  • Loading branch information
mattsse committed Sep 6, 2021
1 parent 8558a11 commit 252f339
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 27 deletions.
23 changes: 22 additions & 1 deletion pallets/asset-index/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use frame_benchmarking::{benchmarks, vec};
use frame_support::{
assert_ok,
dispatch::UnfilteredDispatchable,
sp_runtime::{traits::AccountIdConversion, FixedPointNumber},
sp_runtime::{
traits::{AccountIdConversion, Bounded, One},
FixedPointNumber,
},
traits::{EnsureOrigin, Get},
};
use orml_traits::MultiCurrency;
Expand All @@ -18,6 +21,7 @@ use xcm::v0::MultiLocation;
use crate::Pallet as AssetIndex;

use super::*;
use crate::types::DepositRange;

benchmarks! {
add_asset {
Expand Down Expand Up @@ -185,6 +189,16 @@ benchmarks! {
assert_eq!(metadata.decimals, decimals);
}

set_deposit_range {
let origin = T::AdminOrigin::successful_origin();
let range = DepositRange {minimum : T::Balance::one(), maximum: T::Balance::max_value()};
let call = Call::<T>::set_deposit_range(
range.clone()
);
}: { call.dispatch_bypass_filter(origin)? } verify {
assert_eq!(range, IndexTokenDepositRange::<T>::get());
}

withdraw {
let asset_id :T::AssetId = T::try_convert(2u8).unwrap();
let units = 100_u32.into();
Expand Down Expand Up @@ -273,6 +287,13 @@ mod tests {
});
}

#[test]
fn set_deposit_range() {
new_test_ext().execute_with(|| {
assert_ok!(Pallet::<Test>::test_benchmark_set_deposit_range());
});
}

#[test]
fn deposit() {
new_test_ext().execute_with(|| {
Expand Down
70 changes: 60 additions & 10 deletions pallets/asset-index/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ pub mod pallet {
AssetAvailability, AssetProportion, AssetProportions, Ratio,
};

use crate::types::{AssetMetadata, AssetRedemption, AssetWithdrawal, IndexTokenLock, PendingRedemption};
use crate::types::{
AssetMetadata, AssetRedemption, AssetWithdrawal, DepositRange, IndexTokenLock, PendingRedemption,
};
use primitives::traits::MaybeAssetIdConvert;

type AccountIdFor<T> = <T as frame_system::Config>::AccountId;
Expand Down Expand Up @@ -93,9 +95,6 @@ pub mod pallet {
/// and being able to withdraw the awarded assets
#[pallet::constant]
type WithdrawalPeriod: Get<Self::BlockNumber>;
/// The maximum amount of DOT that can exist in the index
#[pallet::constant]
type DOTContributionLimit: Get<Self::Balance>;
/// Type that handles cross chain transfers
type RemoteAssetManager: RemoteAssetManager<Self::AccountId, Self::AssetId, Self::Balance>;
/// Type used to identify assets
Expand Down Expand Up @@ -195,6 +194,13 @@ pub mod pallet {
#[pallet::getter(fn locked_index_tokens)]
pub type LockedIndexToken<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, T::Balance, ValueQuery>;

/// The range of the index token equivalent a deposit must be in in order to be allowed.
///
/// A valid deposit lies within `[deposit_bounds.minimum, deposit_bounds.maximum]`.
#[pallet::storage]
#[pallet::getter(fn deposit_bounds)]
pub type IndexTokenDepositRange<T: Config> = StorageValue<_, DepositRange<T::Balance>, ValueQuery>;

/// Metadata of an asset ( for reversed usage now ).
#[pallet::storage]
#[pallet::getter(fn asset_metadata)]
Expand All @@ -210,6 +216,8 @@ pub mod pallet {

#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
/// The range that determines valid deposits.
pub deposit_range: DepositRange<T::Balance>,
/// All the liquid assets together with their parachain id known at
/// genesis
pub liquid_assets: Vec<(T::AssetId, polkadot_parachain::primitives::Id)>,
Expand All @@ -220,7 +228,11 @@ pub mod pallet {
#[cfg(feature = "std")]
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self { liquid_assets: Default::default(), saft_assets: Default::default() }
Self {
deposit_range: Default::default(),
liquid_assets: Default::default(),
saft_assets: Default::default(),
}
}
}

Expand All @@ -232,9 +244,12 @@ pub mod pallet {
let availability = AssetAvailability::Liquid((Junction::Parent, Junction::Parachain(id.into())).into());
Assets::<T>::insert(asset, availability)
}

for asset in self.saft_assets.iter().cloned() {
Assets::<T>::insert(asset, AssetAvailability::Saft)
}

IndexTokenDepositRange::<T>::put(self.deposit_range.clone());
}
}

Expand Down Expand Up @@ -267,6 +282,8 @@ pub mod pallet {
/// New metadata has been set for an asset. \[asset_id, name, symbol,
/// decimals\]
MetadataSet(T::AssetId, Vec<u8>, Vec<u8>, u8),
/// The index token deposit range was updated.\[new_range\]
IndexTokenDepositRangeUpdated(DepositRange<T::Balance>),
}

#[pallet::error]
Expand Down Expand Up @@ -315,6 +332,12 @@ pub mod pallet {
InsufficientIndexTokens,
/// Thrown if deposits reach limit
TooManyDeposits,
/// Thrown when the given DepositRange is invalid
InvalidDepositRange,
/// The deposited amount is below the minimum value required.
DepositAmountBelowMinimum,
/// The deposited amount exceeded the cap allowed.
DepositExceedsMaximum,
}

#[pallet::hooks]
Expand All @@ -340,7 +363,7 @@ pub mod pallet {
location: MultiLocation,
amount: T::Balance,
) -> DispatchResultWithPostInfo {
let caller = T::AdminOrigin::ensure_origin(origin.clone())?;
let caller = T::AdminOrigin::ensure_origin(origin)?;
if units.is_zero() {
return Ok(().into());
}
Expand Down Expand Up @@ -423,6 +446,21 @@ pub mod pallet {
Ok(())
}

/// Updates the range for how much a deposit must be worth in index token in order to be
/// accpedted. Requires `T::AdminOrigin`
///
/// Parameters:
/// - `new_range`: The new valid range for deposits.
#[pallet::weight(T::WeightInfo::set_deposit_range())]
pub fn set_deposit_range(origin: OriginFor<T>, new_range: DepositRange<T::Balance>) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?;
ensure!(!new_range.minimum.is_zero(), Error::<T>::InvalidDepositRange);
ensure!(new_range.maximum > new_range.minimum, Error::<T>::InvalidDepositRange);
IndexTokenDepositRange::<T>::put(new_range.clone());
Self::deposit_event(Event::<T>::IndexTokenDepositRangeUpdated(new_range));
Ok(())
}

/// Force the metadata for an asset to some value.
///
/// Origin must be ForceOrigin.
Expand Down Expand Up @@ -469,7 +507,7 @@ pub mod pallet {
/// available price pairs
#[pallet::weight(T::WeightInfo::deposit())]
pub fn deposit(origin: OriginFor<T>, asset_id: T::AssetId, units: T::Balance) -> DispatchResult {
let caller = T::AdminOrigin::ensure_origin(origin.clone())?;
let caller = T::AdminOrigin::ensure_origin(origin)?;
if units.is_zero() {
return Ok(());
}
Expand All @@ -486,9 +524,9 @@ pub mod pallet {

// the amount of index token the given units of the liquid assets are worth
let index_tokens = Self::index_token_equivalent(asset_id, units)?;
if index_tokens.is_zero() {
return Err(Error::<T>::InsufficientDeposit.into());
}

// ensure the index token equivalent worth is within the set bounds
Self::ensure_deposit_in_bounds(index_tokens)?;

// transfer from the caller's sovereign account into the treasury's account
T::Currency::transfer(asset_id, &caller, &Self::treasury_account(), units)?;
Expand Down Expand Up @@ -931,6 +969,14 @@ pub mod pallet {
}
all_withdrawn
}

/// Ensures the given lies within the configured deposit range
pub fn ensure_deposit_in_bounds(amount: T::Balance) -> DispatchResult {
let bounds = IndexTokenDepositRange::<T>::get();
ensure!(amount >= bounds.minimum, Error::<T>::DepositAmountBelowMinimum);
ensure!(amount <= bounds.maximum, Error::<T>::DepositExceedsMaximum);
Ok(())
}
}

impl<T: Config> AssetRecorder<T::AccountId, T::AssetId, T::Balance> for Pallet<T> {
Expand Down Expand Up @@ -1284,6 +1330,7 @@ pub mod pallet {
fn unlock() -> Weight;
fn withdraw() -> Weight;
fn set_metadata() -> Weight;
fn set_deposit_range() -> Weight;
}

/// For backwards compatibility and tests
Expand Down Expand Up @@ -1319,5 +1366,8 @@ pub mod pallet {
fn withdraw() -> Weight {
Default::default()
}
fn set_deposit_range() -> Weight {
Default::default()
}
}
}
2 changes: 0 additions & 2 deletions pallets/asset-index/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ parameter_types! {
pub LockupPeriod: <Test as system::Config>::BlockNumber = 10;
pub MinimumRedemption: u32 = 3;
pub WithdrawalPeriod: <Test as system::Config>::BlockNumber = 10;
pub DOTContributionLimit: Balance = 999;
pub TreasuryPalletId: PalletId = PalletId(*b"12345678");
pub IndexTokenLockIdentifier: LockIdentifier = *b"pintlock";
pub StringLimit: u32 = 4;
Expand Down Expand Up @@ -180,7 +179,6 @@ impl pallet_asset_index::Config for Test {
type IndexTokenLockIdentifier = IndexTokenLockIdentifier;
type MinimumRedemption = MinimumRedemption;
type WithdrawalPeriod = WithdrawalPeriod;
type DOTContributionLimit = DOTContributionLimit;
type RemoteAssetManager = ();
type AssetId = AssetId;
type SelfAssetId = PINTAssetId;
Expand Down
85 changes: 80 additions & 5 deletions pallets/asset-index/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
// Copyright 2021 ChainSafe Systems
// SPDX-License-Identifier: LGPL-3.0-only

use crate as pallet;
use crate::mock::*;
use frame_support::{assert_noop, assert_ok, traits::Currency as _};
use orml_traits::MultiCurrency;
use rand::Rng;
use sp_runtime::{traits::Zero, FixedPointNumber};
use xcm::v0::MultiLocation;

use pallet_price_feed::PriceFeed;
use primitives::{
traits::{AssetRecorder, NavProvider},
AssetAvailability, Price,
};
use rand::Rng;
use sp_runtime::{traits::Zero, FixedPointNumber};
use xcm::v0::MultiLocation;

use crate as pallet;
use crate::{mock::*, types::DepositRange};

#[test]
fn can_add_asset() {
Expand Down Expand Up @@ -347,6 +349,78 @@ fn deposit_fails_on_missing_assets() {
})
}

#[test]
fn ensure_valid_deposit_range() {
new_test_ext().execute_with(|| {
assert_ok!(AssetIndex::set_deposit_range(
Origin::signed(ACCOUNT_ID),
DepositRange { minimum: 1, maximum: Balance::MAX }
));

assert_noop!(
AssetIndex::set_deposit_range(
Origin::signed(ACCOUNT_ID),
DepositRange { minimum: Balance::MAX, maximum: Balance::MAX }
),
pallet::Error::<Test>::InvalidDepositRange
);

assert_noop!(
AssetIndex::set_deposit_range(
Origin::signed(ACCOUNT_ID),
DepositRange { minimum: Balance::MAX, maximum: Balance::MAX - 1 }
),
pallet::Error::<Test>::InvalidDepositRange
);

assert_noop!(
AssetIndex::set_deposit_range(
Origin::signed(ACCOUNT_ID),
DepositRange { minimum: 0, maximum: Balance::MAX }
),
pallet::Error::<Test>::InvalidDepositRange
);
});
}

#[test]
fn deposit_fails_on_invalid_deposit_range() {
let deposit = 1_000;
let initial_units = 1_000;

new_test_ext().execute_with(|| {
assert_ok!(AssetIndex::add_asset(
Origin::signed(ACCOUNT_ID),
ASSET_A_ID,
100,
MultiLocation::Null,
initial_units
));

assert_ok!(Currency::deposit(ASSET_A_ID, &ASHLEY, 100_000));

// fund account to cover index deposits
assert_ok!(Currency::deposit(ASSET_A_ID, &ASHLEY, deposit));

assert_ok!(AssetIndex::deposit(Origin::signed(ASHLEY), ASSET_A_ID, deposit));

assert_ok!(AssetIndex::set_deposit_range(
Origin::signed(ACCOUNT_ID),
DepositRange { minimum: Balance::MAX - 1, maximum: Balance::MAX }
));
assert_noop!(
AssetIndex::deposit(Origin::signed(ASHLEY), ASSET_A_ID, deposit),
pallet::Error::<Test>::DepositAmountBelowMinimum
);

assert_ok!(AssetIndex::set_deposit_range(Origin::signed(ACCOUNT_ID), DepositRange { minimum: 1, maximum: 2 }));
assert_noop!(
AssetIndex::deposit(Origin::signed(ASHLEY), ASSET_A_ID, deposit),
pallet::Error::<Test>::DepositExceedsMaximum
);
})
}

#[test]
fn deposit_fails_on_exceeding_limit() {
let deposit = 1_000;
Expand All @@ -361,6 +435,7 @@ fn deposit_fails_on_exceeding_limit() {
initial_units
));

// fund account to cover index deposits
assert_ok!(Currency::deposit(ASSET_A_ID, &ASHLEY, deposit));

for _ in 0..50 {
Expand Down
22 changes: 21 additions & 1 deletion pallets/asset-index/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

use codec::{Decode, Encode};
use frame_support::{
sp_runtime::{traits::Zero, RuntimeDebug},
sp_runtime::{
traits::{AtLeast32BitUnsigned, Zero},
RuntimeDebug,
},
sp_std::vec::Vec,
};

Expand Down Expand Up @@ -62,3 +65,20 @@ impl<AssetId, Balance: Zero> Default for AssetRedemption<AssetId, Balance> {
Self { asset_amounts: Vec::new(), redeemed_index_tokens: Balance::zero() }
}
}

/// Limits the amount of deposits
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct DepositRange<Balance> {
/// Minimum amount of index tokens a deposit must be worth
pub minimum: Balance,
/// Maximum amount of index tokens a deposit must be worth
pub maximum: Balance,
}

// Default implementation for bounds [0, MAX]
impl<Balance: AtLeast32BitUnsigned> Default for DepositRange<Balance> {
fn default() -> Self {
Self { minimum: Balance::one(), maximum: Balance::max_value() }
}
}
Loading

0 comments on commit 252f339

Please sign in to comment.