Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for rebasing tokens #1063

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
164 changes: 101 additions & 63 deletions crates/dex-stable/src/base_pool.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/dex-stable/src/base_pool_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ fn create_pool_should_work() {
checked_pow(10, (POOL_TOKEN_COMMON_DECIMALS - TOKEN4_DECIMAL) as usize).unwrap(),
]),
balances: BoundedVec::truncate_from(vec![Zero::zero(); 4]),
rebased_balances: BoundedVec::truncate_from(vec![Zero::zero(); 4]),
fee: SWAP_FEE,
admin_fee: ADMIN_FEE,
initial_a: INITIAL_A_VALUE * (A_PRECISION as Balance),
Expand Down
41 changes: 38 additions & 3 deletions crates/dex-stable/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ mod base_pool;
mod default_weights;
mod meta_pool;
mod primitives;
mod rebase;
mod utils;

use frame_support::{
Expand All @@ -64,6 +65,7 @@ use sp_std::{ops::Sub, vec, vec::Vec};
pub use default_weights::WeightInfo;
pub use pallet::*;
use primitives::*;
pub use rebase::TryConvertBalance;
use traits::{StablePoolLpCurrencyIdGenerate, ValidateCurrency};

#[allow(type_alias_bounds)]
Expand Down Expand Up @@ -108,6 +110,10 @@ pub mod pallet {

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;

/// Convert supported currencies to target asset.
/// NOTE: the price should only ever increase
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a hard requirement?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think theoretically the price can also decrease if a slashing event happens (LSD) or a default without sufficient collateral to cover the debt (qToken).

type RebaseConvert: TryConvertBalance<Balance, Balance, AssetId = Self::CurrencyId>;
}

#[pallet::pallet]
Expand All @@ -134,6 +140,10 @@ pub mod pallet {
#[pallet::getter(fn lp_currencies)]
pub type LpCurrencies<T: Config> = StorageMap<_, Blake2_128Concat, T::CurrencyId, T::PoolId>;

// quote -> base
#[pallet::storage]
pub type RebaseTokens<T: Config> = StorageMap<_, Blake2_128Concat, T::CurrencyId, T::CurrencyId, OptionQuery>;

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
Expand Down Expand Up @@ -1105,6 +1115,23 @@ pub mod pallet {
Ok(())
})
}

/// Add rebasing token with dynamically adjusted price.
///
/// Only callable by admin.
///
/// # Argument
///
/// - `from`: The asset to rebase (e.g. LDOT).
/// - `to`: The target asset (e.g. DOT).
#[pallet::call_index(19)]
#[pallet::weight(1_000_000)]
#[transactional]
pub fn insert_rebase_token(origin: OriginFor<T>, from: T::CurrencyId, to: T::CurrencyId) -> DispatchResult {
ensure_root(origin)?;
RebaseTokens::<T>::insert(from, to);
Ok(())
}
}
}

Expand All @@ -1124,6 +1151,7 @@ impl<T: Config> Pallet<T> {
) -> Result<Balance, DispatchError> {
Pools::<T>::try_mutate_exists(pool_id, |optioned_pool| -> Result<Balance, DispatchError> {
let pool = optioned_pool.as_mut().ok_or(Error::<T>::InvalidPoolId)?;
Self::inner_collect_yield(pool)?;
match pool {
Pool::Base(bp) => Self::base_pool_add_liquidity(who, pool_id, bp, amounts, min_mint_amount, to),
Pool::Meta(mp) => Self::meta_pool_add_liquidity(who, pool_id, mp, amounts, min_mint_amount, to),
Expand All @@ -1144,6 +1172,7 @@ impl<T: Config> Pallet<T> {

Pools::<T>::try_mutate_exists(pool_id, |optioned_pool| -> Result<Balance, DispatchError> {
let pool = optioned_pool.as_mut().ok_or(Error::<T>::InvalidPoolId)?;
Self::inner_collect_yield(pool)?;
match pool {
Pool::Base(bp) => Self::base_pool_swap(who, pool_id, bp, i, j, in_amount, out_min_amount, to),
Pool::Meta(mp) => Self::meta_pool_swap(who, pool_id, mp, i, j, in_amount, out_min_amount, to),
Expand All @@ -1161,6 +1190,7 @@ impl<T: Config> Pallet<T> {
Pools::<T>::try_mutate_exists(pool_id, |optioned_pool| -> DispatchResult {
ensure!(!lp_amount.is_zero(), Error::<T>::InvalidTransaction);
let global_pool = optioned_pool.as_mut().ok_or(Error::<T>::InvalidPoolId)?;
Self::inner_collect_yield(global_pool)?;
let pool = match global_pool {
Pool::Base(bp) => bp,
Pool::Meta(mp) => &mut mp.info,
Expand All @@ -1174,13 +1204,17 @@ impl<T: Config> Pallet<T> {
let min_amounts_length = min_amounts.len();
ensure!(currencies_length == min_amounts_length, Error::<T>::MismatchParameter);

// fees are not applied on withdrawal as this method
// does not change the imbalance of the pool in any way
let fees: Vec<Balance> = vec![Zero::zero(); currencies_length];
let amounts = Self::calculate_base_remove_liquidity(pool, lp_amount).ok_or(Error::<T>::Arithmetic)?;

for (i, amount) in amounts.iter().enumerate() {
ensure!(*amount >= min_amounts[i], Error::<T>::AmountSlippage);
pool.balances[i] = pool.balances[i].checked_sub(*amount).ok_or(Error::<T>::Arithmetic)?;
T::MultiCurrency::transfer(pool.currency_ids[i], &pool.account, to, *amount)?;
pool.rebased_balances[i] = pool.rebased_balances[i]
.checked_sub(*amount)
.ok_or(Error::<T>::Arithmetic)?;
Self::do_convert_back_and_transfer_out(pool, i, &to, *amount)?;
}

T::MultiCurrency::withdraw(pool.lp_currency_id, who, lp_amount)?;
Expand All @@ -1207,6 +1241,7 @@ impl<T: Config> Pallet<T> {
Pools::<T>::try_mutate_exists(pool_id, |optioned_pool| -> Result<Balance, DispatchError> {
ensure!(!lp_amount.is_zero(), Error::<T>::InvalidTransaction);
let pool = optioned_pool.as_mut().ok_or(Error::<T>::InvalidPoolId)?;
Self::inner_collect_yield(pool)?;
match pool {
Pool::Base(bp) => {
Self::base_pool_remove_liquidity_one_currency(pool_id, bp, who, lp_amount, index, min_amount, to)
Expand All @@ -1227,6 +1262,7 @@ impl<T: Config> Pallet<T> {
) -> DispatchResult {
Pools::<T>::try_mutate_exists(pool_id, |optioned_pool| -> DispatchResult {
let pool = optioned_pool.as_mut().ok_or(Error::<T>::InvalidPoolId)?;
Self::inner_collect_yield(pool)?;
match pool {
Pool::Base(bp) => {
Self::base_pool_remove_liquidity_imbalance(who, pool_id, bp, amounts, max_burn_amount, to)
Expand Down Expand Up @@ -1486,7 +1522,6 @@ impl<T: Config> Pallet<T> {
}

let balance = T::MultiCurrency::free_balance(pool.currency_ids[currency_index], &pool.account);

balance.checked_sub(pool.balances[currency_index])
} else {
None
Expand Down
Loading