parallel-finance / parallel

A decentralized lending & staking protocol built on top of the Polkadot ecosystem.
https://parallel.fi
GNU General Public License v3.0
120 stars 35 forks source link

LiquidStaking: fast unstake based on matching pool #1834

Closed mclyk closed 2 years ago

mclyk commented 2 years ago

Currently, instant unstake fee based on Loans charged high, now design instant unstake based on matching pool charged less, and not instantly. called it fast unstake

mclyk commented 2 years ago

Considering:

Q1: what if in one era, user unstake through both of RelayChain/MatchingPool

Q2: if user unstake through MatchingPool, shoud add more Storage type?

Q3: if add new storage type, how to deal it with current Unlockings/UnlockChunk, how to deal with it if currently era, total_stake_amount < total_unstake_amount

mclyk commented 2 years ago

1. add MatchingPool option

pub enum UnstakeProvider {
    RelayChain = 0,
    Loans = 1,
    MatchingPool = 2,
}

2. add config type

/// MatchingPool fast unstake fee
#[pallet::constant]
type MatchingPoolFastUnstakeFee: Get<Rate>;

3. add dispatch call

pub fn fast_match_unstake(origin: OriginFor<T>, redeemer_list: Vec<T::AccountId>)-> DispatchResult {
        let _ = ensure_signed(origin)?;
    for redeemer in redeemer_list {
        Self::do_fast_match_unstake(&redeemer)?;
    }
    Ok(())
}

4. add implementation function

fn do_fast_match_unstake(redeemer: &T::AccountId){
    //TODO: check MatchingPool, total_stake_amount and total_unstake_amount
    //TODO: decrease storage `FastUnstakeRequests`
    //TODO: decrease storage `Unlockings`
    //TODO: charge fee and transfer to redeemer
}

5. add storage

// storage the total fast unstake amount and its accountId

#[pallet::storage]
#[pallet::getter(fn fast_unstake_requests)]
pub type FastUnstakeRequests<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId,BalanceOf<T>>;
mclyk commented 2 years ago

Logic Process

  1. user call unstake(MatchingPool) store unstake amount in Unlockings (no change), besides, store it into FastUnstakeRequests
  2. offchain call fast_match_unstake offchain client listening storage FastUnstakeRequests, calculate and using its accounts calling fast_match_unstake
    • decrease user.Unlocking.UnlockingChunks
    • decrease user.FastUnstakeRequests.amount
    • charge fee
    • decrease matchingpool(total_stake and total_unstake)
    • transfer to the user.
  3. clear FastUnstakeRequests Once advance new era, clear FastUnstakeRequests storage, only deal with fast unstake requests in one era.
mclyk commented 2 years ago

Option2: Avoid unmatched sDOT waiting 28 eras

Logic Process

  1. user call unstake(MatchingPool)

    • ensure unstake amount is smaller than the user's liquid balance
    • only store unstake amount into FastUnstakeRequests (in liquid currency), no other storage change(Unlocking/Matching pool).
    • user can still transfer liquid balance to other account
  2. offchain call fast_match_unstake on-chain logic:

    • use the smaller amount between the user's current actual liquid balance and the amount in FastUnstakeRequests
    • burn user's liquid balance (sDOT)
    • decrease user.FastUnstakeRequests.amount
    • charge fee
    • decrease matching pool(only total_stake)
    • transfer DOT to the user.
  3. clear FastUnstakeRequests