Liquid Staking

Overview

As Polkadot uses NPoS (Nominated Proof-of-Stake), DOT holders stake their DOT tokens to nominate validators to earn yields. Stakers may want to expand the utility of their staked DOT.
Parallel finance comes out a solution to unlock the liquidity of staked DOT without compromising the security of the network.
Staking
When the user deposits DOT to the staking pool, he will receive xDOT based on the exchange rate. The staking pool compounds the reward automatically which incentivizes the user to stake early and longer.
exchangeRate=StakingPoolxDOTtotalSupplyexchangeRate = \frac{StakingPool}{xDOT totalSupply}
After a user receives xDOT, he could use XDOT in the following scenario:
    Supply xDOT to the money market to earn double interest
    Use xDOT as collateral to borrow other assets
    Trade xDOT in open markets
    use xDOT as payment
    Rf: risk-free rate (Polkadot net staking rewards)
    t: number of timer periods
Parallel finance uses auto-selected validators recommended by the Polkadot network for now, and we will implement features that allow xDOT holders to vote for specific validators.
Unstaking
There is a 28-day cool-down period for xDOT holders who want to redeem DOT. We will improve features to reduce the cool-down period and create an xDOT/DOT market for users who need early redemption.
If a nominated validator is slashed during the staking period, Parallel finance will use its reserve fund to cover the loss.

Staking Process

1.1 User transfer KSM from relaychain account to parachain account
1.2 Through XCM from relaychain to parachain
1.3 User receives KSM in our parachain
2.1 User stake KSM in our parachain pool, this pool is controlled by multi-sig account
2.2 Our parachain mint xKSM to user
3.1 Multi-sig via off-chain stake client, for XCM transfer from parachain to relaychain
3.2 Transfer KSM to our relychain multi-sig account(XCM)
4.1 Multi-sig via off-chain stake client, for relaychain operations, include bond\bond_extra, etc.
4.2 Execute operations in relaychain( bond\unbond\bond_extra...)
5.1 Off-chain stake client will monitor the status of both relaychain and parachain relaychain monitor: stake reward\slash...parachain monitor: user stake\unstake, record_rewards
5.2 DB record off-chain database will record multi-sig sequence, and some necessary info

Stake-Client Architecture

View Data Storage

ExchangeRate

The exchange rate converts staking native token to voucher.
Code
View Method
1
pub type ExchangeRate<T: Config> = StorageValue<_, Rate, ValueQuery>;
Copied!
1
ExchangeRate::<T>::get()
2
Self::exchange_rate()
Copied!

TotalStakingAsset

The total amount of a staking asset.
Code
View Method
1
pub type TotalStakingAsset<T: Config> = StorageValue<_, Balance, ValueQuery>;
Copied!
1
TotalStakingAsset::<T>::get()
2
Self::total_staking()
Copied!

TotalVoucher

The total amount of staking voucher.
Code
View Method
1
pub type TotalVoucher<T: Config> = StorageValue<_, Balance, ValueQuery>;
Copied!
1
TotalVoucher::<T>::get()
2
Self::total_voucher()
Copied!

AccountPendingUnstake

The queue stores all the pending unstaking requests. Key is the owner of assets.
Code
View Method
1
pub type AccountPendingUnstake<T: Config> =
2
StorageMap<_, Blake2_128Concat, T::AccountId, UnstakeInfo<T::BlockNumber>>;
Copied!
1
AccountPendingUnstake::T::get(account_id: &T::AccountId)
2
Self::account_pending_unstake(account_id: &T::AccountId)
Copied!
Key Name
Type
Description
account_id
T::AccountId
User's account Id

AccountProcessingUnstake

The queue stores all the unstaking requests in the process. Key1 is the mutilsig agent in relaychain, key2 is the owner of assets.
Code
View Method
1
pub type AccountProcessingUnstake<T: Config> = StorageDoubleMap<
2
_,
3
Blake2_128Concat,
4
T::AccountId,
5
Blake2_128Concat,
6
T::AccountId,
7
BoundedVec<UnstakeInfo<T::BlockNumber>, T::MaxAccountProcessingUnstake>,
8
>;
Copied!
1
AccountProcessingUnstake::<T>::get(agent: &T::AccountId, owner: &T::AccountId)
2
Self::unstaking_processing_queue(agent: &T::AccountId, owner: &T::AccountId)
Copied!
Key Name
Type
Description
agent
T::AccountId
The mutil-sig agent in relaychain
owner
T::AccountId
The owner of assets

View Methods

Stake()

Put assets under staking, the native assets will be transferred to the account owned by the pallet, the user receives the voucher in return, such vouchers can be further used as collateral for lending.
1
pub fn stake(origin: OriginFor<T>, amount: Balance) -> DispatchResultWithPostInfo
Copied!
Name
Substrate Config
Runtime Type
Description
origin
T::Origin
RawOrigin
The account signed this transaction
amount
T::Balance
u128
The amount for staking
RETURN: Returns Ok() when staking succeeds, otherwise return a substrate error type.
Tip: Follow the link to know more about `RawOrigin`.

Withdraw()

Withdraw assets from liquid staking pool for off-chain relay chain nomination. May only be called from T::WithdrawOrigin.
1
pub fn withdraw(origin: OriginFor<T>, agent: T::AccountId, amount: Balance,) -> DispatchResultWithPostInfo
Copied!
Name
Substrate Config
Runtime Type
Description
origin
T::Origin
RawOrigin
The account signed this transaction
agent
T::AccountId
AccountId
the multi-sig account of relay chain
amount
T::Balance
u128
the number of requested assets
RETURN: Returns Ok() when withdraw succeeds, otherwise return a substrate error type.
Tip: Follow the link to know more about `AccountId`.

Record_rewards()

Record the staking rewards, no real transfer. May only be called from T::WithdrawOrigin.
1
pub fn record_rewards(origin: OriginFor<T>, agent: T::AccountId, amount: Balance,) -> DispatchResultWithPostInfo
Copied!
Name
Substrate Config
Runtime Type
Description
origin
T::Origin
RawOrigin
The account signed this transaction
agent
T::AccountId
AccountId
the multi-sig account of relay chain
amount
T::Balance
u128
the number of rewarded assets
RETURN: Returns Ok() when record_rewards succeeds, otherwise return a substrate error type.

Record_slash()

Record the staking slash event, no real transfer happened. May only be called from T::WithdrawOrigin.
1
pub fn record_slash(origin: OriginFor<T>, agent: T::AccountId, amount: Balance,) -> DispatchResultWithPostInfo
Copied!
Name
Substrate Config
Runtime Type
Description
origin
T::Origin
RawOrigin
The account signed this transaction
agent
T::AccountId
AccountId
the multi-sig account of relay chain
amount
T::Balance
u128
the number of slashed assets
RETURN: Returns Ok() when record_slash succeeds, otherwise return a substrate error type.

Unstake()

Unstake by exchange voucher for assets, the assets will not be avaliable immediately. Instead, the request is recorded and pending for the nomination accounts in relaychain to do the unbond operation.
1
pub fn unstake(origin: OriginFor<T>, amount: Balance) -> DispatchResultWithPostInfo
Copied!
Name
Substrate Config
Runtime Type
Description
origin
T::Origin
RawOrigin
The account signed this transaction
amount
T::Balance
u128
The amount for staking
RETURN: Returns Ok() when unstake succeeds, otherwise return a substrate error type.

Process_pending_unstake()

Relay chain accounts process the pending unstake by unbonding assets. May only be called from T::WithdrawOrigin.
1
pub fn process_pending_unstake(origin: OriginFor<T>, agent: T::AccountId, owner: T::AccountId, amount: Balance,) -> DispatchResultWithPostInfo
Copied!
Name
Substrate Config
Runtime Type
Description
origin
T::Origin
RawOrigin
The account signed this transaction
agent
T::AccountId
AccountId
The multi-sig account of relay chain
owner
T::AccountId
AccountId
The account which performs unstake operation
amount
T::Balance
u128
Assets can unbond for the owner's unstake request
RETURN: Returns Ok() when process_pending_unstake succeeds, otherwise return a substrate error type.

Finish_processed_unstake()

The unbond waiting period is finished, relay chain accounts transfer the free assets to Parallel, and finish the owner's unstake operation by transfer assets back to the owner. May only be called from T::WithdrawOrigin.
1
pub fn finish_processed_unstake(origin: OriginFor<T>, agent: T::AccountId, owner: T::AccountId, amount: Balance,) -> DispatchResultWithPostInfo
Copied!
Name
Substrate Config
Runtime Type
Description
origin
T::Origin
RawOrigin
The account signed this transaction
agent
T::AccountId
AccountId
The multi-sig account of relay chain
owner
T::AccountId
AccountId
The account which performs unstake operation
amount
T::Balance
u128
Assets already unbond for the owner's unstake request
RETURN: Returns Ok() when finish_processed_unstake succeeds, otherwise return a substrate error type.

View Event

Staked

The assets get staked successfully
Staked(T::AccountId, Balance)

Unstaked

The voucher gets unstaked successfully
Unstaked(T::AccountId, Balance)

WithdrawSuccess

The withdrawal request is successful
WithdrawSuccess(T::AccountId, Balance)

RewardsRecorded

The rewards are recorded
RewardsRecorded(T::AccountId, Balance)

SlashRecorded

The slash is recorded
SlashRecorded(T::AccountId, Balance)

UnstakeProcessed

The unstake request is processed
UnstakeProcessed(T::AccountId, T::AccountId, Balance)

UnstakeProcessing

The unstake reuqest is under processing by multisig account
UnstakeProcessing(T::AccountId, T::AccountId, Balance)

View Error

Name
Description
InvalidExchangeRate
ExchangeRate is invalid
Overflow
Calculation overflow
Underflow
Calculation underflow
ExcessWithdraw
The withdraw assets exceed the threshold
NoPendingUnstake
The account doesn't have any pending unstake
InvalidUnstakeAmount
The agent process an invalid amount of unstake asset
NoProcessingUnstake
There is no unstake in progress
InvalidProcessedUnstakeAmount
There is no unstake in progress with the input amount
MaxAccountProcessingUnstakeExceeded
The maximum account processing unstake request exceeded
Last modified 3mo ago