Skip to main content
Version: 0.9.0

ICStaking Module

Overview

The ICStaking module facilitates liquid staking of native tokens for Cosmos-based chains. Users can mint LSD tokens ( cAsset) by staking their assets. This module batches staked assets from users and delegates them at defined intervals. It also compounds staking rewards automatically, staking these compounded rewards to maximize earnings. The ICStaking module depends on the Cosmos interchain accounts feature to execute staking-related operations on supported chains.

Concepts

Host Chain

The Host Chain is a chain where native tokens earn rewards through staking. It holds the necessary data for staking assets and retrieving related information. The HostChain structure includes these elements:

  • Id: A unique identifier provided by the host chain creator (governance). Once established, this id cannot be changed. It's used in the cToken denom, where a host chain with an id "uatom" would be "c:uatom". Typically, the id value matches the BaseDenom field.
  • ConnectionType: Identifies the type of interchain connection with the host chain. Currently, only the ICA type is supported. The connectionType and connectionId fields are unique together.
  • ConnectionId: The connection identifier. For an ICA connection type, this is the ID for the IBC connection between PRYZM and the host chain. The connectionId and connectionType fields are unique together.
  • BaseDenom: The bond denomination of the host chain.
  • TransferChannels: A list of supported transfer channels for token transfers between the host chain and PRYZM. Each channel includes:
    • Type: The transfer channel protocol. Currently, only IBC token transfer (ICS-20) is supported.
    • Id: The channel identifier. For IBC channel type, this is the ICS-20 channel ID.
    • WrappedDenom: An optional property representing the token name on the receiving chain. Useful when a bridge is used and the underlying asset is wrapped on the bridge, like axlWETH when the channel end is the Axelar network.
    • DestinationChain: An optional property specifying the target chain's name. Useful when a bridge is used and the host chain differs from the receiving chain.
  • Params: The staking parameters for the host chain. The ICStaking module defines default values for these parameters in the module params. These default values are overridden by the provided parameters for the host chain. The properties for this field are detailed in the Staking Parameters section.
  • Validators: A list of whitelisted validators to whom PRYZM delegates the staked tokens. The tokens are distributed based on each validator's weight. Each validator has these properties:
  • Address: The validator's address on the host chain.
  • Weight: The weight of delegation to the validator. This is a decimal number between 0 and 1. The total weight of all validators per host chain must equal 1.

Host Chain State

The HostChainState is a data structure storing a portion of the host chain's state, necessary for PRYZM's operation. Each host chain has one HostChainState, created during the host chain's registration. The HostChainState structure contains the following information:

  • HostChainId: The ID of the host chain.
  • HostAccounts: Details about interchain accounts registered on the host chain by PRYZM. Each account includes the account's address, balance (reported by an oracle), and registration status on the host chain (NOT_REGISTERED, REGISTERING, REGISTERED). The following accounts are registered on the host chain:
  • Delegation: Initially receives transferred staked tokens. Interchain messages for delegation, undelegation, and redelegation are sent from this account.
  • Reward: Collects staking reward tokens. A portion of the rewards is transferred to the sweep account as protocol fee, with the remainder going to the delegation account for delegation. To collect rewards, the reward account must be set as the withdrawal account for the delegation account. The status of setting the reward account for accruing rewards is stored in the HostAccounts struct.
  • Sweep: Collects tokens to be transferred back to PRYZM, including protocol fee tokens and undelegated tokens. PRYZM sends appropriate IBC token transfer messages to the host chain on behalf of this account.
  • ValidatorStates: A list of validators and their state, including the delegation amount to each validator.
  • AmountToBeDelegated: Assets in the delegation interchain account ready for delegation. The sum of this value and UndelegatedAmountToCollect should equal the balance of the delegation interchain account unless there are accidental token transfers.
  • UndelegatedAmountToCollect: Undelegated assets in the delegation account awaiting collection. The sum of this value and AmountToBeDelegated should equal the balance of the delegation interchain account unless there are accidental token transfers.
  • ExchangeRate: Current cToken/Token exchange rate, calculated from oracle-reported data using the formula in the Oracle section.
  • State: Current state of the interchain operations state machine. Interchain operations require certain blocks to execute, and simultaneous operations may cause calculation conflicts. Operations begin only if no other operation is in progress. Possible state values include:
    • INITIALIZING: The host chain is initializing.
    • IDLE: No interchain operation is active; the host chain is ready for new operations.
    • TRANSFERRING: The host chain has sent a token transfer packet to the host chain and awaits acknowledgment.
    • DELEGATING: The host chain has sent delegation interchain messages and awaits acknowledgment.
    • UNDELEGATING: The host chain has sent undelegation interchain messages and awaits acknowledgment.
    • REDELEGATING: The host chain has sent redelegation interchain messages and awaits acknowledgment.
    • COMPOUNDING: The host chain has sent compounding interchain messages and awaits acknowledgment.
    • COLLECTING: The host chain has sent undelegation collection interchain messages and awaits acknowledgment.
    • SWEEPING: The host chain has sent sweeping interchain messages and awaits acknowledgment.
  • LastIdleStateHostHeight: The host chain's block height when the State changed to IDLE. The state becomes IDLE upon receiving an acknowledgment packet (or timeout) for an interchain message. This height is used when handling oracle reports to ensure that the oracle's data is more recent than the updates from the host chain's interchain message acknowledgments.

Undelegation

Users can unstake their assets by submitting their cToken. PRYZM then batches these requests and processes them periodically, each processing time referred to as an epoch. After the host chain's unbonding period, the undelegated assets are ready to be claimed. To claim these unbonded assets, they must be redeemed and transferred back to PRYZM.

Undelegating assets involves multiple steps and interchain messages. This process's state control requires information about the state stored on the chain, for which we use Undelegation records. Each host chain epoch has one Undelegation record. We also need ChannelUndelegation, which stores undelegation state information for each channel.

The Undelegation structure includes:

  • HostChain: Host chain ID.
  • Epoch: Undelegation epoch number.
  • ExchangeRate: The cToken/Token exchange rate at the undelegation epoch end.
  • Started: Indicates if the undelegation request has been launched on the host chain.
  • Completed: Indicates if the unbonding period has passed and the undelegated assets are available.
  • CompletionTime: The time when the undelegation completes and unbonded assets transfer to the delegation account.

The ChannelUndelegation structure includes:

  • HostChain: Host chain ID.
  • Epoch: Undelegation epoch number.
  • TransferChannel: The channel through which the undelegated assets should be received.
  • TotalCAmount: The total amount of cTokens to be undelegated.
  • UndelegatedCAmount: The total amount of cTokens that have been sent for undelegation.
  • ReceivedAmount: The amount of assets already undelegated and transferred to the redeem account.
  • PendingAmount: The amount of assets awaiting receipt.
  • PendingCAmount: The cToken equivalent of assets awaiting receipt.
  • Swept: Indicates if the IBC transfer messages for sweeping assets to PRYZM have been sent successfully.
  • Received: Indicates if all the undelegations have been fully received. If received is true, PendingAmount must be zero.
  • ClaimedUAmount: The amount of unstaked tokens redeemed by users. A channel undelegation record is deleted when ClaimedUAmount equals TotalCAmount.

Staking

Users can stake their base denom tokens from the host chain (transferred to PRYZM using IBC) and mint cTokens based on the cAsset/Asset exchange rate. The provided assets are stored in the delegation queue module account for future delegation. Users must also specify the channel through which the assets have been transferred to ensure proper unwinding when transferred back to the host chain on the same channel.

cAmount = amount / hostChainState.ExchangeRate
SendCoinsFromAccountToModule(userAddress, DelegationQueueAccount, amount)
MintCoinsForAccount(userAddress, hostChain.CDenom(), cAmount)

The delegation queue is processed periodically (refer to DelegationInterval), and the collected assets are transferred to the host chain via the delegation transfer bridge.

Unstaking

Users can request to unstake their cAssets. In return, they receive an unstaking token of the underlying token for the epoch they submitted their requests, maintaining a 1:1 ratio with the cAssets. For example, a user unstaking 100 c:uatom on channel 0 in the 10th epoch will receive 100 u:uatom:channel-0:10.

SendCoinsFromAccountToModule(userAddress, UndelegationQueueAccount, cAmount)
undelegation = getCurrentChannelUndelegation(hostChainId, transferChannel)
MintCoinsForAccount(userAddress, hostChain.UDenom(undelegation), cAmount)
undelegation.TotalCAmount += cAmount

Following the conclusion of each epoch, a message encompassing all undelegated amounts is dispatched to the host chain, utilizing the exchange rate at the epoch's end time. Once the unbonding period on the host chain expires, the unstaked assets are transferred from the delegation account back to PRYZM, to a module account named the redeem account. Subsequently, users can redeem their unstaking tokens for the asset. The redemption rate, representing the exchange rate of the redeem, is derived from the amount received from the host chain.

undelegation := GetChannelUndelegation(hostChainId, epoch, transferChannel)
...[check that undelegation.Received is true]...
redemptionRate = undelegation.ReceivedAmount / undelegation.TotalCAmount
amount = uAmount * redemptionRate
SendCoinsFromModuleToAccount(RedeemAccount, userAddress, amount)
BurnCoins(userAddress, hostChain.UDenom(undelegation), uAmount)
undelegation.ClaimedUAmount += uAmount

Instant Unstaking

Users have the option to unstake their tokens instantly by exchanging their cAssets for assets in the delegation queue, incurring a fee. The delegation queue may be depleted or insufficient. Users can specify the range of assets they expect to receive: minCAmount and maxCAmount. If the delegation queue's available assets fall short of the minCAmount equivalent, the operation fails. The user can redeem any other amount up to the maxCAmount equivalent.

minAmount = minCAmount * hostChainState.ExchangeRate
maxAmount = maxCAmount * hostChainState.ExchangeRate
balance = getDelegationQueueBalance(hostChain, transferChannel)
amount = min(maxAmount, balance)
if amount > minAmount {
SendCoinsFromModuleToAccount(DelegationQueueAccount, userAddress, amount)
}

Netting-Off

Netting-off involves offsetting the delegation queue's asset amount with the asset amount requested for undelegation. This process streamlines asset flow between PRYZM and the host chain and facilitates users to redeem part of their unstaking tokens without the unbonding period wait. As the protocol expands and staking requests outnumber unstaking requests, undelegation never happens, allowing users to redeem their assets immediately after netting-off.

Each epoch concludes with a match between the delegation and undelegation queues. The queue holding fewer assets is emptied, and the opposite queue has the same amount canceled. The following code exemplifies this process:

// for each transfer channel
delegatingAmount = getDelegationQueueBalance(hostChain, transferChannel)
undelegation = getCurrentChannelUndelegation(hostChainId, transferChannel)
undelegatingCAmount = undelegation.TotalCAmount - undelegation.UndelegatedCAmount
undelegatingAmount := undelegatingCAmount * hostChainState.ExchangeRate

if undelegatingAmount >= delegatingAmount {
// if the undelegating amount is greater or equal than delegation,
// we want to truncate the delegation account and send it to redeem account
delegationSubtraction = delegatingAmount

// and then burn the corresponding cASSET amount from undelegation account
undelegationSubtraction = delegatingAmount / hostChainState.ExchangeRate
} else {
// if the undelegating amount is lower than delegation,
// we want to send undelegating amount from delegation account to redeem account
delegationSubtraction = undelegatingAmount

// and then burn all the assets in undelegation account
undelegationSubtraction = undelegatingCAmount
}

// send delegationSubtraction amount from delegation queue to redeem account to be redeemed by users
SendCoinsFromModuleToModule(DelegationQueueAccount, RedeemAccount, delegationSubtraction)

// burn undelegationSubtraction amount of cAssets from undelegation queue
BurnCoins(types.UndelegationQueueAccount, undelegationSubtraction)

// update channel undelegation record with the delegation subtraction as received amount of assets
undelegation.UndelegatedCAmount += undelegationSubtraction
undelegation.ReceivedAmount += delegationSubtraction

Bridge

Bridges implement a universal interface for interaction with host chains. They manage replies, errors, and communication timeouts from the host chain. Each bridge simplifies the communication process for the rest of the logic, enabling message sending to the host chain.

Every bridge has a unique ID for routing message execution results. Bridge implementers must set a ReplyData in the store to access message execution results. The ReplyData object includes the following attributes:

  • BridgeId: The ID of the bridge receiving the reply.
  • PacketId: The ID of the packet linked with the reply data, including the port ID, channel ID, and sequence of IBC messages.
  • HostChainId: The ID of the host chain expected to reply.
  • Data: The marshalled data for the message reply, storing the context of asynchronous message execution.

The module defines these bridge interfaces:

type MessageBridge interface {
GetID() string
MsgReply(ctx sdk.Context, replyData types.ReplyData, txMsgData *sdk.TxMsgData) error
MsgError(ctx sdk.Context, replyData types.ReplyData, error string) error
MsgTimeout(ctx sdk.Context, replyData types.ReplyData) error
}

type TransferBridge interface {
GetID() string
TransferReply(ctx sdk.Context, replyData types.ReplyData) error
TransferError(ctx sdk.Context, replyData types.ReplyData, error string) error
TransferTimeout(ctx sdk.Context, replyData types.ReplyData) error
}

Upon receiving an IBC packet transfer result, the icstaking handler checks for expected replies and directs them to the correct bridge method. If the host chain acknowledgment is successful, the MsgReply (or TransferReply) method is called with the reply data and response message. If the acknowledgment contains an error, the MsgError ( or TransferError) method is invoked with the error message. If no acknowledgment arrives before timeout, MsgTimeout (or TransferTimeout) handles it.

Bridge implementers can use the following abstract structs to leverage utility methods:

(b AbstractBridge) SetReplyData(
ctx sdk.Context,
packetID types.PacketId,
hostChain types.HostChain,
data proto.Marshaler
) error

(b AbstractMsgBridge) SendMsg(
ctx sdk.Context,
hostChain types.HostChain,
portID string,
msgs []sdk.Msg
) (pId types.PacketId, err error)

(b AbstractTransferBridge) SendTransferMsg(
ctx sdk.Context,
hostChain types.HostChain,
channel types.TransferChannel,
from string,
to string,
coin sdk.Coin,
) (pID types.PacketId, err error)

The icstaking module permits other modules to register their bridges and interact with host chains. For example, the pgov module uses this feature to transmit proposal votes to the host chain.

The bridges in the icstaking module include:

  • Delegation Transfer Bridge
  • Delegation Bridge
  • Undelegation Bridge
  • Compound Bridge
  • Collect Undelegated Bridge
  • Sweep Bridge
  • Redelegation Bridge
  • Set Withdraw Address Bridge

Oracle

PRYZM employs the oracle module to access external information. The ICStaking module uses the oracle to refresh the host chain state. The oracle reports the following data to this module for each host chain:

  • BlockHeight: The oracle-reported block height for the host chain.
  • ValidatorStates: A list of validators and their states, including delegation amounts.
  • DelegationAccountBalance: The balance of the delegation interchain account.
  • RewardAccountBalance: The balance of the reward interchain account.
  • SweepAccountBalance: The balance of the sweep interchain account.
  • LastCompletedUndelegationEpoch: The highest undelegation epoch number for which the undelegation is complete and ready for sweeping to PRYZM.

Upon receiving this data from the oracle, PRYZM updates the host chain state when:

  1. The host chain state is IDLE to prevent conflict as the bridge functions are concurrently updating the state.
  2. The host chain block height, as reported by the oracle, is more recent than PRYZM's last known idle state block height (hostChainState.LastIdleStateHostHeight). This condition ensures the oracle's data is current, considering the oracle module's latency due to voting delay.

When these conditions are satisfied, the host chain state and cAsset/Asset exchange rate are updated using the formula:

ER=totalDelegation+delegationQueue+AmountToBeDelegated+rewardAmounttotalCSupplyER = \frac{totalDelegation + delegationQueue + AmountToBeDelegated + rewardAmount}{totalCSupply}

Here, totalDelegationtotalDelegation and rewardAmountrewardAmount are reported by oracle, delgationQueuedelgationQueue represents the amount of staked assets awaiting delegation, and totalCSupplytotalCSupply is the total supply of the cAsset.

Following the state update, if the reward account balance is positive, the compounding operation is executed. If the oracle indicates that some undelegation records are complete and redeemable, the state of the completed undelegations is updated, and the sweeping operation is performed.

Messages

MsgUpdateParams

This message updates the module parameters. Only the governance can execute this message.

message MsgUpdateParams {
string authority = 1;
Params params = 2 [(gogoproto.nullable) = false];
}

MsgRegisterHostChain

This message allows to register a new host chain on PRYZM. It can be executed by the governance module address (using a governance proposal) or one of the admin addresses defined in the module's parameters.

message MsgRegisterHostChain {
string creator = 1;
HostChain host_chain = 2 [(gogoproto.nullable) = false];
}

MsgUpdateHostChain

This message allows to update the whitelisted validators and staking parameters of a registered host chain. It can be executed by the governance module address(using a governance proposal) or one of the admin addresses defined in the module's parameters.

message MsgUpdateHostChain {
string creator = 1;
string host_chain_id = 2;
repeated Validator validators = 3;
StakingParams params = 4;
}

MsgStake

This message allows users to stake transferred assets, minting cTokens at the current host chain exchange rate. The response includes the amount of minted cTokens and the fee deducted from the assets.

message MsgStake {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
string amount = 4; // sdk.Int
}
message MsgStakeResponse {
cosmos.base.v1beta1.Coin c_amount = 1 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin fee = 2 [(gogoproto.nullable) = false];
}

MsgUnstake

This message enables users to request unstaking of their cTokens and retrieval of unstaking tokens, specifying the channel for retrieving their unstaked assets after the unbonding period. The response includes the amount of minted unstaking tokens.

message MsgUnstake {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
string c_amount = 4; // sdk.Int
}
message MsgUnstakeResponse {
cosmos.base.v1beta1.Coin u_amount = 1 [(gogoproto.nullable) = false];
}

MsgRedeemUnstaked

This message allows users to submit their unstaking tokens upon completion of the token's epoch undelegation, and redeem their assets. The response includes the amount of redeemed tokens and the deducted fee.

message MsgRedeemUnstaked {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
string u_amount = 4; // sdk.Int
uint64 epoch = 5;
}
message MsgRedeemUnstakedResponse {
cosmos.base.v1beta1.Coin amount = 1 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin fee = 2 [(gogoproto.nullable) = false];
}

MsgInstantUnstake

This message allows users to instantly redeem their assets for given cTokens, within a specified acceptable range for the redeemed amount. The response includes the amount of redeemed assets and the deducted fee.

message MsgInstantUnstake {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
string min_c_amount = 4; // sdk.Int
string max_c_amount = 5; // sdk.Int
}
message MsgInstantUnstakeResponse {
cosmos.base.v1beta1.Coin amount = 1 [(gogoproto.nullable) = false];
cosmos.base.v1beta1.Coin fee = 2 [(gogoproto.nullable) = false];
}

MsgRebalanceDelegations

This message allows any user to request a rebalancing of delegations for host chain validators. The process of rebalancing is executed only if a specific duration has passed since the last rebalancing and the deviation from the expected weights of delegations is above a certain threshold.

message MsgRebalanceDelegations {
string creator = 1;
string host_chain = 2;
}

MsgRegisterInterchainAccount

This message permits users to register interchain accounts, particularly in instances of registration failures or when a timeout has occurred, resulting in closed ICA channels.

Request

message MsgRegisterInterchainAccount {
string creator = 1;
string host_chain = 2;
ICARegistrationType registration_type = 3;
}

enum ICARegistrationType {
DELEGATION = 0; // Register delegation interchain account
REWARD = 1; // Register reward interchain account
SWEEP = 2; // Register sweep interchain account
REWARD_CLAIMING = 3; // Register reward account as the withdraw address on the host chain
}

Queries

Params

This query fetches the parameters of the module.

message QueryParamsRequest {}

message QueryParamsResponse {
Params params = 1 [(gogoproto.nullable) = false];
}

HostChain

This query retrieves a host chain using its id.

message QueryGetHostChainRequest {
string host_chain_id = 1;
}

message QueryGetHostChainResponse {
HostChain host_chain = 1;
}

HostChainAll

This query fetches all host chains.

message QueryAllHostChainRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}

message QueryAllHostChainResponse {
repeated HostChain host_chain = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

HostChainState

This query retrieves the state of a host chain using its id.

message QueryGetHostChainStateRequest {
string host_chain_id = 1;
}

message QueryGetHostChainStateResponse {
HostChainState host_chain_state = 1 [(gogoproto.nullable) = false];
}

HostChainStateAll

This query fetches the state of all host chains.

message QueryAllHostChainStateRequest {
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}

message QueryAllHostChainStateResponse {
repeated HostChainState host_chain_state = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

Undelegation

This query retrieves an Undelegation using the host chain id and epoch number.

message QueryGetUndelegationRequest {
string host_chain = 1;
uint64 epoch = 2;
}

message QueryGetUndelegationResponse {
Undelegation undelegation = 1 [(gogoproto.nullable) = false];
}

UndelegationAll

This query fetches all Undelegation records, filtered by the host chain.

message QueryAllUndelegationRequest {
string host_chain = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryAllUndelegationResponse {
repeated Undelegation undelegation = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

IncompleteUndelegationAll

Fetches a list of incomplete undelegations, filtered by the provided host chain and sorted by completion time.

message QueryIncompleteUndelegationRequest {
string host_chain = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryIncompleteUndelegationResponse {
repeated Undelegation undelegation = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

ChannelUndelegation

This query retrieves a ChannelUndelegation using the host chain id, epoch number and the transfer channel.

message QueryGetChannelUndelegationRequest {
string host_chain = 1;
uint64 epoch = 2;
string transfer_channel = 3;
}

message QueryGetChannelUndelegationResponse {
ChannelUndelegation channel_undelegation = 1 [(gogoproto.nullable) = false];
}

ChannelUndelegationAll

This query fetches all ChannelUndelegation records, filtered by host chain and epoch number.

message QueryAllChannelUndelegationRequest {
string host_chain = 1;
uint64 epoch = 2;
cosmos.base.query.v1beta1.PageRequest pagination = 3 [(gogoproto.nullable) = true];
}

message QueryAllChannelUndelegationResponse {
repeated ChannelUndelegation channel_undelegation = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2 [(gogoproto.nullable) = true];
}

DelegationQueueBalance

Fetches the balance of a delegation queue for a specific host chain on a particular transfer channel.

message QueryDelegationQueueBalanceRequest {
string host_chain = 1;
string transfer_channel = 2;
}

message QueryDelegationQueueBalanceResponse {
cosmos.base.v1beta1.Coin balance = 1 [(gogoproto.nullable) = false];
}

EpochInfo

Fetches the last delegation time, last undelegation time, and the current undelegation epoch number

message QueryEpochInfoRequest {
string host_chain = 1;
}

message QueryEpochInfoResponse {
google.protobuf.Timestamp last_delegation_time = 1;
google.protobuf.Timestamp last_undelegation_time = 2;
uint64 current_undelegation_epoch = 3;
}

Events

EventSetHostChain

Emitted when a host chain is set. It includes the host chain information.

message EventSetHostChain {
HostChain host_chain = 1 [(gogoproto.nullable) = false];
}

EventSetHostChainState

Emitted when the host chain's state is set. It includes the host chain state details.

message EventSetHostChainState {
HostChainState host_chain_state = 1 [(gogoproto.nullable) = false];
}

EventSetParams

Emitted when parameters are set. It contains the module parameters.

message EventSetParams {
Params params = 1 [(gogoproto.nullable) = false];
}

EventSetUndelegation

Emitted when an undelegation record is set. Includes the undelegation record details.

message EventSetUndelegation {
Undelegation undelegation = 1 [(gogoproto.nullable) = false];
}

EventSetChannelUndelegation

Emitted when a channel undelegation record is set. Includes the channel undelegation record details.

message EventSetChannelUndelegation {
ChannelUndelegation channel_undelegation = 1 [(gogoproto.nullable) = false];
}

EventStake

Emitted when a user stakes assets. Contains details related to the stake transaction.

message EventStake {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
string amount = 4; // sdk.Int
cosmos.base.v1beta1.Coin fee = 5;
cosmos.base.v1beta1.Coin c_amount = 6;
}

EventUnstake

Emitted when a user requests to unstake assets. Contains details related to the unstake transaction.

message EventUnstake {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
string c_amount = 4; // sdk.Int
cosmos.base.v1beta1.Coin u_amount = 5;
}

EventRedeemUnstaked

This event triggers when a user redeems unstaked assets, encapsulating the details of the redemption transaction.

message EventRedeemUnstaked {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
uint64 epoch = 4;
string u_amount = 5; // sdk.Int
cosmos.base.v1beta1.Coin amount = 6;
cosmos.base.v1beta1.Coin fee = 7;
}

EventInstantUnstake

This event triggers when a user requests instant asset unstaking, encapsulating the details of the instant unstake transaction.

message EventInstantUnstake {
string creator = 1;
string host_chain = 2;
string transfer_channel = 3;
string min_c_amount = 4; // sdk.Int
string max_c_amount = 5; // sdk.Int
cosmos.base.v1beta1.Coin amount = 6;
cosmos.base.v1beta1.Coin fee = 7;
}

State

Params

Module parameters are stored under the following store key:

([]byte("v1/params/")) -> ProtoBuf(Params)

HostChain

Host chains are stored using their IDs as store keys:

(
[]byte("v1/host-chain/") |
[]byte(id)
) -> ProtoBuf(HostChain)

Host chains are also indexed with the connection type and ID to guarantee unique connection IDs:

(
[]byte("v1/host-chain-by-connection/") |
[]byte(connectionType) | // as uint32 in 4 bytes
[]byte(connectionId)
) -> []byte(hostChainId)

The state of host chains is stored using the host chain ID as the store key:

(
[]byte("v1/host-chain-state/") |
[]byte(hostChainId)
) -> ProtoBuf(HostChainState)

Epoch Trackers

PRYZM stores the last delegation time, last undelegation time, and the current undelegation number to periodically process delegation and undelegation batches.

(
[]byte("v1/last-delegation-time/") |
[]byte(hostChainId)
) -> []byte(lastDelegationTime) // marshalled time
(
[]byte("v1/last-undelegation-time/") |
[]byte(hostChainId)
) -> []byte(lastUndelegationTime) // marshalled time
(
[]byte("v1/undelegation-epoch/") |
[]byte(hostChainId)
) -> []byte(epoch) // marshalled uint64

Undelegation

Undelegations are stored using the host chain ID and epoch number as the store key:

(
[]byte("v1/undelegation/") |
[]byte(lengthPrefixedHostChainId) |
[]byte(epoch) // represented as an 8-byte uint 64
) -> ProtoBuf(Undelegation)

Incomplete undelegations, sorted by completion time for post-completion handling, are stored using the host chain ID, completion time, and epoch number as the key.

(
[]byte("v1/incomplete-undelegation/") |
[]byte(lengthPrefixedHostChainId) |
[]byte(completionTime) | // formatted as a sortable string
[]byte(epoch) // represented as an 8-byte uint 64
) -> undelegationKey

Undelegations are indexed regardless of whether they are initiated or completed:

(
[]byte("v1/unstarted-undelegation/") |
[]byte(lengthPrefixedHostChainId) |
[]byte(epoch) // represented as an 8-byte uint 64
) -> []byte{1}
(
[]byte("v1/completed-undelegation/") |
[]byte(lengthPrefixedHostChainId) |
[]byte(epoch) // represented as an 8-byte uint 64
) -> []byte{1}

Channel Undelegation

Channel undelegations are stored with the host chain id, epoch number, and transfer channel id as their store key:

(
[]byte("v1/channel-undelegation/") |
[]byte(lengthPrefixedHostChainId) |
[]byte(epoch) | // represented as an 8-byte uint 64
[]byte(transferChannel)
) -> ProtoBuf(ChannelUndelegation)

Channel undelegations are also indexed if they haven't been swept:

(
[]byte("v1/unswept-channel-undelegation/") |
[]byte(lengthPrefixedHostChainId) |
[]byte(epoch) | // represented as an 8-byte uint 64
[]byte(transferChannel)
) -> []byte{1}

Last Redelegation Time

The last time a redelegation has been executed on the host chain, stored with the following structure:

(
[]byte("v1/last-redelegation-time/") |
[]byte(hostChainId)
) -> []byte{time}

IBC Ports

PRYZM is the ICA controller in the connection with the host chain and opens one port for each interchain account responsible for sending packets to control the account on the host chain. Therefore, for every defined host chain on PRYZM, it opens three ports with the following names:

  • Delegation account: icacontroller-delegation-{hostChainId}
  • Reward account: icacontroller-reward-{hostChainId}
  • Sweep account: icacontroller-sweep-{hostChainId}

Parameters

Module Params

StakingParams

This parameter serves as the default staking parameters for all host chains. Each host chain can override each staking parameter's default value by specifying a value in the HostChain object.

Admins

This parameter is a list containing admin addresses, authorized to register and update host chains without gov proposal.

Staking Params

The staking parameters include the following properties:

FeeRatios

The fee ratios for each operation supported by the module are:

  • Yield: The portion of the asset staking reward taken as the protocol fee.

    • Type: sdk.Dec
    • Range: [0, 1]
    • Default Value: 0.1
  • Staking: The portion of the asset amount a user is staking that is taken as the fee.

    • Type: sdk.Dec
    • Range: [0, 1]
    • Default Value: 0
  • Unstaking: Fee taken as a ratio of the asset amount a user redeems.

    • Type: sdk.Dec
    • Range: [0, 1]
    • Default Value: 0
  • InstantUnstaking: Fee taken as a ratio of the instant asset amount a user redeems.

    • Type: sdk.Dec
    • Range: [0, 1]
    • Default Value: 0.05

DelegationInterval

Interval for PRYZM to send delegation messages to the host chain.

  • Type: time.Duration
  • Range: [0, 24h]
  • Default Value: 6h

UndelegationInterval

Interval for PRYZM to send undelegation messages to the host chain. Max value depends on the host chain's UnbondingTime / MaxEntries.

  • Type: time.Duration
  • Range: [0, 120h]
  • Default Value: 72h

IBCTransferTimeout

Timeout duration when sending IBC transfer messages.

  • Type: time.Duration
  • Range: [0, 1h)
  • Default Value: 30m

ICATimeout

Timeout duration when sending ICA messages.

  • Type: time.Duration
  • Range: [0, 1h)
  • Default Value: 30m

RebalanceParams

Parameters for re-balancing delegation between whitelisted validators.

  • MaxMsgs: Maximum number of redelegation messages in each re-balance operation.

    • Type: int32
    • Range: [1, 10]
    • Default Value: 3
  • RebalanceThreshold: Minimum divergence a validator delegation weight must reach to trigger a re-balance operation.

    • Type: sdk.Dec
    • Range: [0, 1]
    • Default Value: 0.1
  • MinRebalanceAmount: Minimum asset amount for each redelegation message.

    • Type: sdk.Int
    • Range: [0, inf)
    • Default Value: 1_000_000_000
  • MinRebalanceInterval: Minimum interval between two re-balance operations.

    • Type: time.Duration
    • Range: [1h, inf)
    • Default Value: 21d

Genesis

The genesis state of this module includes:

  • Params: Parameters for the icstaking module.
  • port_id: Bound port used by the blockchain network.
  • host_chain_list: List of all host chains.
  • host_chain_state_list: List of all host chain states.
  • undelegation_list: List of all undelegation records.
  • channel_undelegation_list: List of all channel undelegation records.