Stride's Technical Architecture

A technical overview of how Stride enabled interchain liquid staking.

High-Level System Design

Users stake their tokens on Stride from any Cosmos chain. Rewards accumulate in real time. No minimum. They will receive staked tokens immediately when they liquid stake. These staked tokens can be freely traded, and can be redeemed with Stride at any time to receive your original tokens plus staking rewards.

On the backend, Stride permissionlessly stakes these tokens on the host chain and compounds user rewards. Stride lets users use your staked assets to compound their yields. Continue to earn staking yield, and earn additional yield by lending, LPing, and more. They can set their own risk tolerance in Cosmos DeFi.

Users can always redeem from Stride. When they select "redeeem" on the Stride website, Stride will initiate unbonding on the host zone. Once the unbonding period elapses, the users will receive native tokens in their wallets.

In-Depth System Design

*Written by the wonderful Informal Systems audit team.

In Stride, staking occurs every 6 hours and it goes through 3 epochs:

  • epoch n: New deposit record (DR) which tracks all deposits in a given epoch for a given host zone is created with 0 tokens. LiquidStake is called then and stTokens are minted, but the actual staking of the user’s tokens is addressed in epoch n+2.
  • epoch n+1: All tokens on DR from epoch n are IBC transferred from Stride’s module account to Delegation ICA. Whenever a change to delegation happens all of the rewards are withdrawn (to Withdraw ICA). • epoch n+2: Tokens on the DR are staked (by weight) across all (30 at the moment) Stride validators.

Reinvestment executes automatically and the rewards are auto-compounded on every epoch (6h). 90% of the rewards are being sent to Delegation ICA (reinvestment) and 10% to the Fee ICA (the only place where Stride charges the fees)

  • epoch n: Queries Interchain Query (ICQ) to check balances of Withdraw ICA and creates a new record for those tokens.
  • epoch n+1: Transfers tokens to Delegation ICA from the Withdraw ICA.
  • epoch n+2: Stakes the tokens.

Unstaking executes every day. Only 7 concurrent unbondings are allowed (a constraint on Cosmos) on host zones for a delegator and validator pair.

  • epoch n: EpochUnbondingRecord is created. It stores many HostZoneUnbondings (HZU), with one HZU per host zone (e.g. for CosmosHub we have 1 HZU per epoch). When the user sends 1stAtom to Stride (Stride now custodies this 1 stAtom) and specifies an address on the Cosmos Hub that the tokens should be sent, HZU is updated and UserRedemptionRecord (URR) is created (claim on user’s tokens that they can trigger later once the tokens have unbonded).
  • epoch n+m (m = unbondingPeriodOnHostZone / 7 + 1): For CosmosHub it happens every 4 days (unbPeriod = 21 days). MsgUndelegate is triggered and all of the pulled unbonding tokens are undelegated (MsgUndelegate ICAs are triggered across the validators Stride has delegated to). HZUs are updated with unbonding time.
  • epoch n+unbonding time: Tokens are transferred to Redemption ICA account. The URR is updated so that the tokens are claimable and anyone can transfer tokens to the already specified address which is stored on URR. So this is an ICA call that transfers tokens back to the end user’s account.

In Stride, staking occurs every 6 hours and it goes through 3 epochs:

  • epoch n: New deposit record (DR) which tracks all deposits in a given epoch for a given host zone is created with 0 tokens. LiquidStake is called then and stTokens are minted, but the actual staking of the user’s tokens is addressed in epoch n+2.
  • epoch n+1: All tokens on DR from epoch n are IBC transferred from Stride’s module account to Delegation ICA. Whenever a change to delegation happens all of the rewards are withdrawn (to Withdraw ICA). • epoch n+2: Tokens on the DR are staked (by weight) across all (30 at the moment) Stride validators.

Reinvestment executes automatically and the rewards are auto-compounded on every epoch (6h). 90% of the rewards are being sent to Delegation ICA (reinvestment) and 10% to the Fee ICA (the only place where Stride charges the fees)

  • epoch n: Queries Interchain Query (ICQ) to check balances of Withdraw ICA and creates a new record for those tokens.
  • epoch n+1: Transfers tokens to Delegation ICA from the Withdraw ICA.
  • epoch n+2: Stakes the tokens.

Unstaking executes every day. Only 7 concurrent unbondings are allowed (a constraint on Cosmos) on host zones for a delegator and validator pair.

  • epoch n: EpochUnbondingRecord is created. It stores many HostZoneUnbondings (HZU), with one HZU per host zone (e.g. for CosmosHub we have 1 HZU per epoch). When the user sends 1stAtom to Stride (Stride now custodies this 1 stAtom) and specifies an address on the Cosmos Hub that the tokens should be sent, HZU is updated and UserRedemptionRecord (URR) is created (claim on user’s tokens that they can trigger later once the tokens have unbonded).
  • epoch n+m (m = unbondingPeriodOnHostZone / 7 + 1): For CosmosHub it happens every 4 days (unbPeriod = 21 days). MsgUndelegate is triggered and all of the pulled unbonding tokens are undelegated (MsgUndelegate ICAs are triggered across the validators Stride has delegated to). HZUs are updated with unbonding time.
  • epoch n+unbonding time: Tokens are transferred to Redemption ICA account. The URR is updated so that the tokens are claimable and anyone can transfer tokens to the already specified address which is stored on URR. So this is an ICA call that transfers tokens back to the end user’s account.

Deposit & Liquid Staking

Withdrawal and deposit belong to regular bank transfers (outside of Stride). After transferring native tokens to Stride, liquid staking can be processed and it includes:

  1. Sending IBC/Tokens to stakeibc account
  2. Minting stTokens to stakeibc account
  3. Sending stTokens from stakeibc account to user account on Stride

Staking & Reinvesment

Staking and reinvestment steps:

  1. Sweeps the deposit record (DR) marked TRANSFER_QUEUE from previous epochs. Under the hub, it con structs IBC MsgTransfer with 30min timeout. TransferCallback is also created which is been called OnAcknowledgementPacket or OnTimeoutPacket. In the case of nill ack or ack_error DR’s status is set back to TRANSFER_QUEUE otherwise it becomes a candidate for delegation with DELEGATION_QUEUE flag.
  2. Delegates DRs with status DELEGATION_QUEUE. It creates a set of MsgDelegate msgs (delegation to every validator from that host zone whose relative amount is positive). Each validator gets targetAmount=valWeight*depRecordAmount / totalValWeight. Also, DelegateCallback is defined. In the case of the happy ibc path, the zone’s staked balance is increased by a delegated amount to each validator whose delegationAmt is updated and finally DR is removed.
  3. Rewards are automatically sent to the Withdrawal ICA.
  4. WithdrawalBalanceCallback executes ICA SendTx with MsgSend to Delegation ICA (90% reward) and Fee ICA (10% reward). It also adds ReinvestCallback which triggers as previous ones, and in the happy path, it creates a new DR with DELEGATION_QUEUE status (using WITHDRAWAL_ICA source this time, not STRIDE source) while the sad path is ignored.

Unbonding

Unbonding goes as follows:

  1. User sends RedeemStake msg which creates UserRedemptionRecord (URR), calculates the amount of native tokens to get back: nativeAmount=stAmount*redemptionRate, updates HostZoneUnbonding (HZU) record and sends numStAtoms to module’s account where they will eventually be burned after unbonding.
  2. On every epoch (a day) initiates unbondings for all host zones with ICA SendTx containing MsgUndelegate (from Delegation ICA to all validators) and sets HZU status to UNBONDING_IN_PROGRESS (note: HZU is created at RegisterHostZone with initial status set to UNBONDING_QUEUE). If ICA SendTx fails, UndelegateCallback will roll back the HZU status to UNBONDING_QUEUE, otherwise it burns escrowed stTokens, updates: HZU status to EXIT_TRANSFER_QUEUE, HostZone staked balance and validator amounts.
  3. The funds are in escrow for the unbonding period and then get automatically transferred to back to the Delegation ICA.
  4. Once per epoch if the unbonding period has elapsed, all HZU’s tokens with EXIT_TRANSFER_QUEUE are swept as a batch to Redemption ICA. If everything goes well, HZU status is set to CLAIMABLE, otherwise it’s reset to EXIT_TRANSFER_QUEUE during RedemptionCallback execution.
  5. Users can claim their unbonded tokes via ClaimUndelegatedToken which also sets claim pending to TRUE (to avoid double claims). ICA SendTx is constructed with MsgSend inside (from Redemption ICA to user address on host) with 10min timeout. If no ack errors, URR is removed and HZU’s native token amount is decremented by the claimed value. Else, if timeot or ack failure, claim pending is set to false.

Interchain Accounts

It can be seen that many paths lead to ICA’s SendTx, including both staking and unstaking, while all paths (except direct Msgs) have the same root - the BeforeEpochStart function.

There are only two scenarios for ICA’s RegisterInterchainAccount to be called. The first one is when registering the host zone, 4 ICAs are created. The second one, in the case of timeout, if a channel closes, the controller chain must be able to regain access to registered interchain accounts by simply opening a new channel which is done through RestoreInterchainAccount. IBCTransfer is called at one place only from record’s module when sweeping existing deposits from Stride to the Host Zones.