Skip to main content
Version: v0.18

pGov Module

Overview

The pGov module allows ICStaked asset holders to participate in the governance of the asset's native chain. Upon staking their assets in the ICStaking module, users delegate their voting power to the delegation interchain account. The pGov module's purpose is to facilitate user participation in the governance of the asset's native chain by voting on proposals using their cAssets and pAssets on the PRYZM chain. Proposals from supported asset chains are mirrored on PRYZM, voted on within a shorter period, and the results are subsequently submitted via an interchain message on the native chain.

Concepts

Staking pASSETs

Holders of cAssets can refract their assets into y and p assets, then stake pAssets in the pGov module to participate in native chain voting. The staked pAssets are escrowed in the pGov module account. The amount of staked assets contributes to the holder's voting power. The assets can be redeemed by the owner at any time. However, the amount of staked pAssets at the time of vote tallying (after the voting period ends) is calculated as part of the voting power.

Although staking isn't logically necessary due to the absence of an unbonding period, it's used here as we don't have access to the total number of pAssets an account holds due to multiple maturities of pAssets.

message StakedPAsset {
string account = 1;
string p_asset = 2;
string amount = 3; // sdk.Int
}

Mirroring Proposals on PRYZM

Before a vote on a proposal can occur, the proposal must be submitted to PRYZM's pgov module. If the host chain's connection type is loop-back (the asset is the native Pryzm token), the proposal is replicated by using gov module's hooks, which allows to take action when a proposal is submitted. For the ICA and Multi-Sig connected host chains, the submission process involves a specialized type of interchain query. Anyone can submit a proposal from the target host chain to PRYZM, accompanied by a proof of proposal existence and the height at which the proof was retrieved. PRYZM uses the IBC light client state to verify the proof and stores the verified proposals with a defined voting end time.

message Proposal {
uint64 proposal_id = 1;
string asset = 2;
google.protobuf.Timestamp start_time = 3;
google.protobuf.Timestamp end_time = 4; // voting end time
cosmos.gov.v1.TallyResult final_vote = 5; // this is set after voting ended
ProposalStatus status = 6;
}

enum ProposalStatus {
ACTIVE = 0; // still active
SUBMITTING = 1; // submitting the final vote
SUBMITTED = 2; // final vote is successfully submitted
FAILED = 3; // failed to submit the final vote
}

Proposal Voting

PRYZM users, holding cAssets or staked pAssets, can participate in voting. All users are eligible to vote on proposals, but their voting power is calculated at the time of vote tallying. The voting power is determined using the following formula:

voting_power = totalStakePAssets + (cAssetBalance * assetExchnageRate)

The pGov module's vote structure is weighted and similar to the cosmos gov module (MsgVoteWeighted).

message Vote {
string asset = 1;
uint64 proposal = 2;
string voter = 3;
repeated cosmos.gov.v1.WeightedVoteOption options = 4;
}
func Tally(proposal: Proposal) {
results := make(map[v1.VoteOption]sdk.Dec)
votes := getVotesForAsset(proposal.asset)
for vote in votes {
votingPower := calculateVotingPower(voter)
for _, option := range vote.Options {
weight, _ := sdk.NewDecFromStr(option.Weight)
subPower := votingPower.Mul(weight)
results[option.Option] = results[option.Option].Add(subPower)
}
}

func EndBlocker(ctx) {
proposals := getActiveProposalsTill(ctx.BlockTime());
for proposal in proposals {
Tally(proposal)
setProposalResult(proposal, results) // proposal.result = results, proposal.status = INACTIVE
deleteVotes(proposal)

submitResultViaICA(proposal)
}
}

Submitting Voting Results

PRYZM stakes assets on the native chain using ICA and the delegation account. PRYZM's voting results must be sent to the host chain to execute the voting process, and this can be securely triggered using ICA.

After the voting period ends, PRYZM processes the votes in the EndBlocker and sends the results through the icstaking bridges using MsgVoteWeighted.

Messages

MsgUpdateParams

This message updates the parameters of the module. It can only be executed by the gov module and is not directly usable.

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

MsgStakePAssets

This message stakes a specified amount of pAssets, using them as voting power. The amount is an array of coins, allowing the sender to stake tokens with the same underlying asset but with multiple maturities in a single transaction. However, the amount of coins cannot represent principal tokens of different assets.

message MsgStakePAssets {
string creator = 1;
repeated cosmos.base.v1beta1.Coin amount = 2; // sdk.Coins
}

Upon successful transaction, the response will be structured as follows, containing the creator’s total staked pAssets.

message MsgStakePAssetsResponse {
string total_staked_p_assets = 1; // sdk.Int
}

MsgUnstakePAssets

This message unstakes a specified amount of pAssets, reducing the creator’s voting power. Like MsgStakePAssets, the amount of coins cannot represent tokens of different underlying assets.

message MsgUnstakePAssets {
string creator = 1;
repeated cosmos.base.v1beta1.Coin amount = 2; // sdk.Coins
}

Upon successful transaction, the response will be structured as follows, containing the creator’s total remaining staked pAssets.

message MsgUnstakePAssetsResponse {
string total_staked_p_assets = 1; // sdk.Int
}

MsgSubmitProposal

This message proposes a specific host chain on PRYZM, specifying the underlying asset of the chain, proposal id on the host chain, proto-marshalled value of the proposal on the host chain, and a proof alongside the proof height. The latter can be used to verify the proposal's existence on the host chain.

message MsgSubmitProposal {
string creator = 1;
string asset = 2;
bytes proposal = 3;
ibc.core.client.v1.Height height = 4 [(gogoproto.nullable) = false];
bytes proof = 5;
}
message MsgSubmitProposalResponse {
Proposal proposal = 1 [(gogoproto.nullable) = false];
}

MsgSubmitVote

This message submits a voter’s votes on a proposal on a specific host chain. The vote is stored and utilized after the proposal voting period has ended, based on the voter’s voting power. The vote options are weighted vote options available in the cosmos-sdk’s gov module.

message MsgSubmitVote {
string voter = 1;
string asset = 2;
uint64 proposal = 3;
repeated cosmos.gov.v1.WeightedVoteOption options = 4;
}

MsgRetryVoteTransmit

This message is used to retry the transmission of an ICA message to submit the final tally results of proposal voting on PRYZM to the host chain. It can only be executed if the final tally result is available and the result packet has previously failed to be sent.

message MsgRetryVoteTransmit {
string creator = 1;
string asset = 2;
uint64 proposal = 3;
}

Queries

Params

This query fetches the parameters of the module.

message QueryParamsRequest {}

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

StakedPAsset

This query retrieves the staked value of a specified pAsset denom for a given account.

message QueryGetStakedPAssetRequest {
string account = 1;
string p_asset = 2;
}

message QueryGetStakedPAssetResponse {
StakedPAsset staked_p_asset = 1 [(gogoproto.nullable) = false];
}

StakedPAssetAll

This query retrieves the list of all staked pAssets, allowing to filter by asset owner account.

message QueryAllStakedPAssetRequest {
string account = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2;
}

message QueryAllStakedPAssetResponse {
repeated StakedPAsset staked_p_asset = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

TotalStakedPAsset

This query retrieves the total amount of an account’s staked pAssets for a specific asset.

message QueryGetTotalStakedPAssetRequest {
string account = 1;
string asset = 2;
}

message QueryGetTotalStakedPAssetResponse {
TotalStakedPAsset total_staked_p_asset = 1 [(gogoproto.nullable) = false];
}

TotalStakedPAssetAll

This query retrieves a list of total amount of staked pAssets, allowing to filter by account address.

message QueryAllTotalStakedPAssetRequest {
string account = 1;
cosmos.base.query.v1beta1.PageRequest pagination = 2 [(gogoproto.nullable) = true];
}

message QueryAllTotalStakedPAssetResponse {
repeated TotalStakedPAsset total_staked_p_asset = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2 [(gogoproto.nullable) = true];
}

Proposal

This query retrieves the proposal with a specific id for the host chain of the asset.

message QueryGetProposalRequest {
string asset = 1;
uint64 proposal_id = 2;
}

message QueryGetProposalResponse {
Proposal proposal = 1 [(gogoproto.nullable) = false];
}

ProposalAll

This query retrieves the list of all mirrored proposals on PRYZM.

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

message QueryAllProposalResponse {
repeated Proposal proposal = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

Vote

This retrieves a voter's vote on a specified proposal on the asset's host chain.

message QueryGetVoteRequest {
string asset = 1;
uint64 proposal = 2;
string voter = 3;
}

message QueryGetVoteResponse {
Vote vote = 1 [(gogoproto.nullable) = false];
}

VoteAll

This retrieves all uncalculated votes still present on the chain for a specific proposal.

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

message QueryAllVoteResponse {
repeated Vote vote = 1 [(gogoproto.nullable) = false];
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

Events

EventSetParams

Emitted when module parameters are updated and contains the updated Params object.

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

EventSetProposal

Emitted when a proposal is updated and contains the updated Proposal object.

message EventSetProposal {
Proposal proposal = 1 [(gogoproto.nullable) = false];
}

EventPAssetStake

Emitted when a user stakes pAssets. It contains the user's address, the staked asset, the staked amount, and the total amount already staked for that asset.

message EventPAssetStake {
string address = 1;
string asset = 2;
repeated cosmos.base.v1beta1.Coin amount = 3; // sdk.Coins
string total_staked_p_asset = 4; // sdk.Int
}

EventPAssetUnstake

Emitted when a user unstakes pAssets. It contains the user's address, the unstaked asset, the unstaked amount, and the remaining staked amount.

message EventPAssetUnstake {
string address = 1;
string asset = 2;
repeated cosmos.base.v1beta1.Coin amount = 3; // sdk.Coins
string total_staked_p_asset = 4; // sdk.Int
}

EventVoteSubmit

Emitted when a user submits a vote. The message contains the submitted Vote.

message EventVoteSubmit {
Vote vote = 1;
}

EventProposalEnd

Emitted when a proposal ends. The message contains the ended Proposal.

message EventProposalEnd {
Proposal proposal = 1;
}

EventVoteTransmit

Emitted when a proposal's final tally is sent to the host chain. It contains the proposal ID and asset ID.

message EventVoteTransmit {
uint64 proposal_id = 1;
string asset = 2;
}

EventVoteTransmitFailure

This event fires when the transmission of the final tally for a proposal to the host chain fails. It includes the proposal ID, asset ID, and error message.

message EventVoteTransmitFailure {
uint64 proposal_id = 1;
string asset = 2;
string error = 3;
}

EventVoteAckSuccess

This event fires when the host chain acknowledges the successful transmission of a proposal's final tally. It includes the proposal ID and asset ID.

message EventVoteAckSuccess {
uint64 proposal_id = 1;
string asset = 2;
}

EventVoteAckFailure

This event fires when the host chain returns an error as a response to the final tally transmission of a proposal. It includes the proposal ID, asset ID, and error message.

message EventVoteAckFailure {
uint64 proposal_id = 1;
string asset = 2;
string error = 3;
}

EventVoteTimeout

This event fires when the voting period for a proposal ends. It includes the proposal ID and the asset ID.

message EventVoteTimeout {
uint64 proposal_id = 1;
string asset = 2;
}

State

StakedPAssets

The staked pAssets are stored using the owner's account and pASSET denomination as the key.

(
[]byte("v1/staked-p-asset/") |
[]byte(lengthPrefixedAccount) |
[]byte(lengthPrefixedPAssetDenom)
) -> ProtoBuf(StakedPAsset)

The total amount of staked pAssets, of different maturities but the same underlying asset, is stored as follows:

(
[]byte("v1/total-staked-p-asset/") |
[]byte(lengthPrefixedAccount) |
[]byte(lengthPrefixedAssetDenom)
) -> []byte(total) // total is of type sdk.Int

Proposal

Proposals are stored using the asset ID and proposal ID as the key.

(
[]byte("v1/proposal/") |
[]byte(asset) |
[]byte(proposalId)
) -> ProtoBuf(Proposal)

To access active proposals sorted by their end time (for tallying votes when a proposal ends), the keys of active proposals are stored using their end time, asset, and proposal ID.

(
[]byte("v1/active-proposal/") |
[]byte(proposalEndTime) | // in sortable string format
[]byte(asset) |
[]byte(proposalId)
) -> []byte(proposalKey)

Vote

Submitted votes are stored using the asset ID, proposal ID, and voter address as the key.

(
[]byte("v1/vote/") |
[]byte(lengthPrefixedAsset) |
[]byte(proposalId) |
[]byte(lengthPrefixedVoter)
) -> ProtoBuf(Vote)

Parameters

voting_result_submission_window

This parameter specifies the duration between the end time of the replicated proposal.

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

Genesis

This module's genesis state encompasses the following properties:

  • Params outlines the pGov module parameters.
  • StakedPAssetList comprises all staked pAssets.
  • ProposalList includes all host chain proposals on PRYZM.
  • VoteList embodies all proposal votes.