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;
}
TallyResult
This query returns the vote tally result for a proposal. It returns the already calculated result if the proposal state is final; otherwise it calculates the result at the moment and returns the result.
message QueryTallyResultRequest {
string asset = 1;
uint64 proposal = 2;
}
message QueryTallyResultResponse {
repeated cosmos.gov.v1.WeightedVoteOption options = 1 [(gogoproto.nullable) = false];
}
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.