Superfluid Staking
Abstract
Superfluid Staking provides the consensus layer more security with a sort of "Proof of Useful Stake". Each person gets an amount of Osmo representative of the value of their share of liquidity pool tokens staked and delegated to validators, resulting in the security guarantee of the consensus layer to also be based on GAMM LP shares. The OSMO token is minted and burned in the context of Superfluid Staking. Throughout all of this, OSMO's supply is preserved in queries to the bank module.
The process
All of the below methods are found under the Superfluid modules.
- The
SuperfluidDelegatemethod stores your share of bonded liquidity pool tokens, withvalidateLockas a verifier for lockup time. GetSuperfluidOsmomints OSMO tokens each day for delegation as a representative of the value of your pool share. This amount is minted because the staking module at the moment requires staked tokens to be in OSMO. This amount is burned each day and re-minted to keep the representative amount of the value of your pool share accurate. The lockup duration is guaranteed from the underlying lockup module.GetExpectedDelegationAmountiterates over each (denom, delegate) pair and checks for how much OSMO we have delegated. The difference from the current balance to what is expected is burned / minted to match with the expected.- A
messageServermethod executes the Superfluid delegate message. syntheticLockupis used to index bond holders and tracking their addresses for reward distribution or potentially slashing purposes. These track whether if your Superfluid stake is currently bonding or unbonding.- An
IntermediaryAccountis mostly used for the actual reward distribution or slashing events, and are responsible for establishing the connection between each superfluid staked lock and their delegation to the validator. These work by transferring the superfluid OSMO to their respective delegators. Rewards are linearly scaled based on how much you have locked for a given (validator, denom) pair. Rewards are first moved to the incentive gauges, then distributed from the gauges. In this way, we're using the existing gauge reward system for paying out superfluid staking rewards and tracking the amount you have superfluidly staked using the lockup module. - Rewards are distributed per epoch, which is currently a day.
abci.gochecks whether or not the current block is at the beginning of the epoch usingBeginBlock. - Superfluid staking will continue to expand to other Osmosis pools based on governance proposals and vote turnouts.
Example
If Alice has 500 GAMM tokens bonded to the ATOM <> OSMO, she will have the equivalent value of OSMO minted, delegated to her chosen staker, and burned for her each day with Superfluid staking. On the user side, all she has to know is who she wants to delegate her tokens to. In order to switch delegation, she has to unbond her tokens from the pool first and then redeposit. Bob, who has a share of the same liquidity pool before Superfluid Staking went live, also has to re-deposit into the pool for the above process to kickstart.
Why mint Osmo? How is this method safe and accurate?
Superfluid staking requires the minting of OSMO because in order to stake on the Osmosis chain, OSMO tokens are required as the chosen collateral. Synthetic Osmo is minted here as a representative of the value of each superfluid staker's liquidity pool tokens.
The pool tokens are acquired by the user from normally staking in a liquidity pool. They get minted an amount of OSMO equivalent to the value of their GAMM pool tokens. This method is accurate because querying the value OSMO every day allows for burning and minting according to the difference in value of OSMO relative to the expected delegation amount (as seen with GetExpectedDelegationAmount). It's like having a price oracle for fairly calculating the amount the user has superfluidly staked.
On epoch (start of every day), we read from the lockup module how much
GAMM tokens we have locked which acts as an oracle for the
representative price of the GAMM token shares. The superfluid module has
"hooks" messages to refresh delegation amounts
(RefreshIntermediaryDelegationAmounts) and to increase delegation on
lockup (IncreaseSuperfluidDelegation). Then, we see whether or not the
superfluid OSMO currently delegated is worth more or less than this
expected delegation amount. If the OSMO is worth more, we do
instant undelegations and immediately burn the OSMO. If less, we mint
OSMO and update the amount delegated. A simplified diagram of this whole
process is found below:
This minting is safe because we strict constrain the permissions of Bank
(the module that burns and mints OSMO) to do what it's designed to do.
The authority is mediated through mintOsmoTokensAndDelegate and
forceUndelegateAndBurnOsmoTokens keeper methods called by the
SuperfluidDelegate and SuperfluidUndelegate message handlers for the
tokens. The hooks above that increase delegation and refresh delegation
amounts also call this keeper method.
The delegation is then verified to not already be associated with an intermediary account (to prevent double-staking), and is always delegated or withdrawn taking into account various multipliers for synthetic OSMO value (its worth with respect to the liquidity pool, and a risk modifier) to prevent mint inaccuracies. Before minting, we also check that the message sender is the owner of the locked funds; that the lock is not unlocking; is locked for at least the unbonding period, and is bonded to a single asset. We also check to see if the lock isn't already in superfluid and that the same lock isn't currently being unbonded.
On the end of each epoch, we iterate through all intermediary accounts to withdraw delegation rewards they may have received and put it all into the perpetual gauges corresponding to each account for reward delegation.
Bonding, unbonding, slashing
Here, we describe how token bonding and unbonding works, and what happens to your superfluid tokens in the case of a slashing event.
Bonding
When bonding, your input tokens are locked up and you are given GAMM pool tokens in exchange. These GAMM pool tokens represent a share of the total liquidity pool, and allows you to get transaction fees or participate in external incentive gauge token distributions. When bonding, on top of the regular bonding transaction there will also be a selection of validators. As stated above, OSMO is also minted and burned each day and superfluidly staked to whoever you have chosen to be your validator. You gain additional APR as a reward for bolstering the Osmosis chain's consensus integrity by delegating.
Unbonding
When unbonding, superfluid tokens get un-delegated. After making sure that the unbond message sender is the owner of their corresponding locked funds, the existing synthetic lockup is deleted and replaced with a new synthetic lockup for unbonding purposes. The undelegated OSMO is then instantly withdrawn from the intermediate account and validator using the InstantUndelegate function. The OSMO that was originally used for representing your LP shares are burnt. Moves the tracker for unbonding, allows the underlying lock to start unlocking if desired
Concepts
SyntheticLockups
SyntheticLockups are synthetic forms of PeriodLocks, but different in the sense that they store suffix, which is a combination of bonding/unbonding status + validator address. This is mainly used to track whether an individual lock that has been superfluid staked has an bonding status or an unbonding status from the staking delegations.
Intermediary Account
Intermediary Accounts establish the connections between the superfluid staked locks and delegations to the validator. Intermediary accounts exist for every denom + validator combination, so that it would group locks with the same denom + validator selection. Superfluid staking a lock would mint equivalent amount of OSMO of the lock and send it to the intermediary account and the intermediary accounts would be delegating to the specified validator.
Intermediary Account Connection
Intermediary Accounts Connection serves the role of tracking the locks that an Intermediary Account is dedicated to.
State
Superfluid Asset
A superfluid asset is an alternative asset (non-OSMO) that is allowed by governance to be used for staking.
It can only be updated by governance proposals. We validate at proposal creation time that the denom + pool exists. (Are we going to ignore edge cases around a reference pool getting deleted it)
Intermediary Accounts
Lots of questions to be answered here
Dedicated Gauges
Each intermediary account has a dedicated gauge where it sends the delegation rewards to. Gauges are distributing the rewards to end users at the end of the epoch.
Synthetic Lockups created
At the moment, one lock can only be fully bonded to one validator.
Osmo Equivalent Multipliers
The Osmo Equivalent Multiplier for an asset is the multiplier it has for its value relative to OSMO.
Different types of assets can have different functions for calculating their multiplier. We currently support two asset types.
- Native Token
The multiplier for OSMO is always 1.
- Gamm LP Shares
Currently we use the spot price for an asset based on a designated osmo-basepair pool of an asset. The multiplier is set once per epoch, at the beginning of the epoch. In the future, we will switch this out to use a TWAP instead.
State changes
The state of superfluid module state modifiers are classified into below categories.
Messages
Superfluid Delegate
Owners of superfluid asset locks can submit MsgSuperfluidDelegate
transactions to delegate the Osmo in their locks to a selected
validator.
type MsgSuperfluidDelegate struct {
Sender string
LockId uint64
ValAddr string
}
State Modifications:
- Safety Checks that are being done before running superfluid logic:
- Check that
Senderis the owner oflock - Check that
lockcorresponds to a single locked asset - Check that
lockis not unlocking - Check that
lockis locked for at least the unbonding period - Check that this
LockIDis not already superfluided - Check that the same lock isn't being unbonded
- Check that
- Get the
IntermediaryAccountfor this lock'sDenomandValAddrpair.- Create it + a new gauge for the synthetic denom, if it does not yet exist.
- Create a SyntheticLockup.
- Calculate
Osmoto delegate on behalf of thislock, asOsmo Equivalent Multiplier*# LP Shares*Risk Adjustment Factor- If this amount is less than 0.000001
Osmo(1 uosmo) reject the transaction, as it would be delegating0 uosmo
- If this amount is less than 0.000001
- Mint
Osmoto match this amount and send toIntermediaryAccount - Create a delegation from
IntermediaryAccounttoValidator - Create a new perpetual
Gaugefor distributing staking payouts to locks of a synethic asset based on thisValidator/Denompair. - Create a connection between this
lockIDand thisIntermediaryAccount
Superfluid Undelegate
type MsgSuperfluidUndelegate struct {
Sender string
LockId uint64
}
State Modifications:
- Lookup
lockbyLockID - Check that
Senderis the owner oflock - Get the
IntermediaryAccountfor thislockID - Delete the
SyntheticLockupassociated to thislockID+ValAddrpair - Create a new
SyntheticLockupwhich is unbonding - Calculate the amount of
Osmodelegated on behalf of thislockasOsmo Equivalent Multiplier*# LP Shares*Risk Adjustment Factor- If this amount is less than 0.000001
Osmo, there is no delegatedOsmoto undelegate and burn
- If this amount is less than 0.000001
- Use
InstantUndelegateto instantly remove delegation fromIntermediaryAccounttoValidator - Immediately burn undelegated
Osmo - Delete the connection between
lockIDandIntermediaryAccount
Lock and Superfluid Delegate
type MsgLockAndSuperfluidDelegate struct {
Sender string
Coins sdk.Coins
ValAddr string
}
This is effectively a multimsg tx of lockup's MsgLockTokens and
superfluid's MsgSuperfluidDelegate, but it is implemented as a single
msg, because currently we don't have a way of passing the lockid
outputted by MsgLockTokens as an input into the
MsgSuperfluidDelegate prior to execution.
State Modifications:
- Ensures that Coins has a length of only 1 (we use sdk.Coins instead of sdk.Coin in order to allow more flexibility in the future)
- Creates a lockup with Coins of a lock duration equivalent to the
unstaking period from the staking module
- Uses the lockup module's MsgServer
- Gets the lock id of the created lock, and uses it generate and
execute a MsgSuperfluidDelegate message
- Uses the SuperfluidDelegate function on this msg server
Superfluid Unbond Lock
type MsgSuperfluidUnbondLock struct {
Sender string
LockId uint64
}
This message does all the functionality of MsgSuperfluidUndelegate but
also starts unbonding the underlying lock as well, allowing both the
unstaking and unlocking to complete at the same time. Without using this
function, a user will not be able to start unbonding their underlying
lock until after the unstaking has finished.
State Modifications:
- This runs the functionality of
MsgSuperfluidUndelegate - It then triggers a force unbond of the underlying lock id
Epochs
Overall Epoch sequence
- Epoch N ends, during AfterEpochEnd:
- Distribute gauge rewards for all non-superfluid gauges
- Mint new tokens
- Issue new Osmo, and send to various modules (distribution, incentives, etc.)
- 25% currently goes to
x/distributionwhich fundsStakingandSuperfluidrewards - Rewards for
Superfluidare based on the just updated delegation amounts, and queued for payout in the next epoch
- BeginBlock for Distribution
- Distribute staking rewards to all of the 'lazy accounting' accumulators. (F1)
- Epoch N ends, during BeginBlock for superfluid After
AfterEpochEnd:
- Claim staking rewards for every
Intermediary Account, put them into gauges. - Distribute Superfluid staking rewards from gauges to bonded Synthetic Lock owners
- Update
Osmo Equivalent Multipliervalue for each LP token- (Currently spot price at epoch)
- Refresh delegation amounts for all
Intermediary Accounts- Calculate the expected delegation for this account as
Osmo Equivalent Multiplier# LP SharesRisk adjustment- If this is less than 0.000001
Osmoit will be rounded to 0
- If this is less than 0.000001
- Lookup current delegation amount for
Intermediary Account- If there is no delegation, treat the current delegation as 0
- If expected amount > current delegation:
- Mint new
OsmoandDelegatetoValidator
- Mint new
- If expected amount < current delegation:
- Use
InstantUndelegateand burn the receivedOsmo
- Use
- Calculate the expected delegation for this account as
- Claim staking rewards for every