PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/r/gnoswap/gov/governance/v1

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v1
Namespace
gnoswap / gov / governance
Files
23 (README)(gnomod.toml)
Exported functions
n/a — Gnoland RPC unavailable (functions): rpc abci_query: HTTP 500
Module
gno.land/r/gnoswap/gov/governance/v1
gno
0.9

Files (23)

  • README.mdmarkdown
  • gnomod.tomltoml
  • assert.gnogno
  • config.gnogno
  • consts.gnogno
  • doc.gnogno
  • errors.gnogno
  • getter_utils.gnogno
  • getter.gnogno
  • governance_execute.gnogno
  • governance_propose.gnogno
  • governance_vote.gnogno
  • init.gnogno
  • instance.gnogno
  • parameter_registry.gnogno
  • proposal_action_status.gnogno
  • proposal_data.gnogno
  • proposal_schedule_status.gnogno
  • proposal_status.gnogno
  • proposal_vote_status.gnogno
  • proposal.gnogno
  • render.gnogno
  • utils.gnogno
  • governance_propose.gnogno
    1package governance23import (4	"chain"5	"chain/runtime"6	"errors"7	"time"8

    Functions

    Gnoland RPC unavailable (functions): rpc abci_query: HTTP 500

    Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.

    Rendered

    RenderedRawgnoweb ↗
    This realm does not provide renderable output (vm/qrender → NoRenderDeclError). View on gnoweb ↗

    vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.

    9 "gno.land/r/gnoswap/access/v1"
    10
    11 gnsmath "gno.land/p/gnoswap/gnsmath/v1"
    12 "gno.land/p/gnoswap/utils/v1"
    13
    14 "gno.land/r/gnoswap/gov/xgns"
    15 "gno.land/r/gnoswap/halt/v1"
    16
    17 "gno.land/r/gnoswap/gov/governance"
    18)
    19
    20// ProposeText creates a text proposal for community discussion.
    21//
    22// Signal proposals for non-binding community sentiment.
    23// Used for policy discussions, roadmap planning, and community feedback.
    24// No on-chain execution, serves as formal governance record.
    25//
    26// Parameters:
    27//
    28// - _: Internal call discriminator; pass 0.
    29//
    30// - rlm: Propagated realm context; validated as current before proposal state writes.
    31//
    32// - title: Short, descriptive proposal title (up to 255 characters; 100 is a UX recommendation)
    33//
    34// - description: Full proposal content with rationale and context
    35//
    36// Requirements:
    37// - Caller must hold at least the configured ProposalCreationThreshold amount in xGNS
    38// - No other active proposal from same address
    39// - Title and description must be non-empty
    40//
    41// Process:
    42// - Configured delay before voting starts (1 day by default)
    43// - Configured voting period (7 days by default)
    44// - Quorum and a strict yes-over-no majority determine the outcome
    45// - No execution phase (signal only)
    46//
    47// Returns:
    48// - newProposalId: ID of the newly created text proposal
    49func (gv *governanceV1) ProposeText(
    50 _ int, rlm realm,
    51 title string,
    52 description string,
    53) (newProposalId int64) {
    54 access.AssertIsRlmCurrent(0, rlm)
    55
    56 halt.AssertIsNotHaltedGovernance()
    57
    58 prev := rlm.Previous()
    59 callerAddress := prev.Address()
    60
    61 createdAt := time.Now().Unix()
    62 createdHeight := runtime.ChainHeight()
    63 xgnsBalance := xgns.BalanceOf(callerAddress)
    64
    65 config, ok := gv.getCurrentConfig()
    66 if !ok {
    67 panic(errors.New(errDataNotFound))
    68 }
    69
    70 // Clean up inactive user proposals before checking if caller already has an active proposal
    71 err := gv.removeInactiveUserProposals(0, rlm, callerAddress, createdAt)
    72 if err != nil {
    73 panic(err)
    74 }
    75
    76 // Check if caller already has an active proposal (one proposal per address)
    77 if gv.hasActiveProposal(callerAddress) {
    78 panic(errors.New(errAlreadyActiveProposal))
    79 }
    80
    81 // Get timestamp anchor and total voting weight for proposal creation
    82 maxVotingWeight, snapshotTime, err := gv.getVotingWeightSnapshot(
    83 createdAt,
    84 config.VotingWeightSmoothingDuration,
    85 )
    86 if err != nil {
    87 panic(err)
    88 }
    89 quorumWeight := gv.stakerAccessor.GetTotalxGnsSupply()
    90
    91 // Create the text proposal with metadata
    92 proposal, err := gv.createProposal(
    93 0, rlm,
    94 governance.Text,
    95 config,
    96 maxVotingWeight,
    97 quorumWeight,
    98 snapshotTime,
    99 governance.NewProposalMetadata(title, description),
    100 NewProposalTextData(),
    101 callerAddress,
    102 xgnsBalance,
    103 createdAt,
    104 createdHeight,
    105 )
    106 if err != nil {
    107 panic(err)
    108 }
    109
    110 // Initialize empty voting info tree for this proposal (votes will be added as users vote)
    111 err = gv.updateProposalUserVotes(0, rlm, proposal, governance.NewProposalUserVotingInfoTree())
    112 if err != nil {
    113 panic(err)
    114 }
    115
    116 // Emit proposal creation event for indexing and tracking
    117 chain.Emit(
    118 "ProposeText",
    119 "prevAddr", prev.Address().String(),
    120 "prevRealm", prev.PkgPath(),
    121 "title", title,
    122 "proposalId", utils.FormatInt(proposal.ID()),
    123 "quorumAmount", utils.FormatInt(proposal.VotingQuorumAmount()),
    124 "maxVotingWeight", utils.FormatInt(proposal.VotingMaxWeight()),
    125 "configVersion", utils.FormatInt(proposal.ConfigVersion()),
    126 "createdAt", utils.FormatInt(proposal.CreatedAt()),
    127 )
    128
    129 return proposal.ID()
    130}
    131
    132// ProposeCommunityPoolSpend creates a treasury disbursement proposal.
    133//
    134// Allocates community pool funds for approved purposes.
    135// Supports grants, development funding, and protocol incentives.
    136// The transfer is attempted when the approved proposal executes.
    137//
    138// Parameters:
    139//
    140// - _: Internal call discriminator; pass 0.
    141//
    142// - rlm: Propagated realm context; validated as current before proposal state writes.
    143//
    144// - title: Proposal title describing purpose
    145//
    146// - description: Detailed justification and budget breakdown
    147//
    148// - to: Recipient address for funds
    149//
    150// - tokenPath: Registered token contract path (e.g., "gno.land/r/gnoswap/gns")
    151//
    152// - amount: Strictly positive amount to transfer (in smallest unit)
    153//
    154// Requirements:
    155// - Caller must hold at least the configured ProposalCreationThreshold amount in xGNS
    156// - Valid recipient address
    157// - Registered token path
    158// - Community-pool balance is checked at execution, not proposal creation
    159//
    160// Security:
    161// - Enforces timelock after approval
    162// - Single transfer per proposal
    163// - Tracks all disbursements on-chain
    164//
    165// Returns:
    166// - newProposalId: ID of the newly created community-pool-spend proposal
    167func (gv *governanceV1) ProposeCommunityPoolSpend(
    168 _ int, rlm realm,
    169 title string,
    170 description string,
    171 to address,
    172 tokenPath string,
    173 amount int64,
    174) (newProposalId int64) {
    175 access.AssertIsRlmCurrent(0, rlm)
    176
    177 halt.AssertIsNotHaltedGovernance()
    178
    179 assertIsValidToken(tokenPath)
    180
    181 createdAt := time.Now().Unix()
    182 createdHeight := runtime.ChainHeight()
    183
    184 prev := rlm.Previous()
    185 callerAddress := prev.Address()
    186 xgnsBalance := xgns.BalanceOf(callerAddress)
    187
    188 config, ok := gv.getCurrentConfig()
    189 if !ok {
    190 panic(errors.New(errDataNotFound))
    191 }
    192
    193 // Clean up inactive user proposals before checking if caller already has an active proposal
    194 err := gv.removeInactiveUserProposals(0, rlm, callerAddress, createdAt)
    195 if err != nil {
    196 panic(err)
    197 }
    198
    199 // Check if caller already has an active proposal (one proposal per address)
    200 if gv.hasActiveProposal(callerAddress) {
    201 panic(errors.New(errAlreadyActiveProposal))
    202 }
    203
    204 // Get timestamp anchor and total voting weight for proposal creation
    205 maxVotingWeight, snapshotTime, err := gv.getVotingWeightSnapshot(
    206 createdAt,
    207 config.VotingWeightSmoothingDuration,
    208 )
    209 if err != nil {
    210 panic(err)
    211 }
    212 quorumWeight := gv.stakerAccessor.GetTotalxGnsSupply()
    213
    214 // Create the community pool spend proposal with execution data
    215 proposal, err := gv.createProposal(
    216 0, rlm,
    217 governance.CommunityPoolSpend,
    218 config,
    219 maxVotingWeight,
    220 quorumWeight,
    221 snapshotTime,
    222 governance.NewProposalMetadata(title, description),
    223 NewProposalCommunityPoolSpendData(tokenPath, to, amount, COMMUNITY_POOL_PATH),
    224 callerAddress,
    225 xgnsBalance,
    226 createdAt,
    227 createdHeight,
    228 )
    229 if err != nil {
    230 panic(err)
    231 }
    232
    233 // Initialize empty voting info tree for this proposal (votes will be added as users vote)
    234 err = gv.updateProposalUserVotes(0, rlm, proposal, governance.NewProposalUserVotingInfoTree())
    235 if err != nil {
    236 panic(err)
    237 }
    238
    239 // Emit proposal creation event for indexing and tracking
    240 chain.Emit(
    241 "ProposeCommunityPoolSpend",
    242 "prevAddr", prev.Address().String(),
    243 "prevRealm", prev.PkgPath(),
    244 "title", title,
    245 "to", to.String(),
    246 "tokenPath", tokenPath,
    247 "amount", utils.FormatInt(amount),
    248 "proposalId", utils.FormatInt(proposal.ID()),
    249 "quorumAmount", utils.FormatInt(proposal.VotingQuorumAmount()),
    250 "maxVotingWeight", utils.FormatInt(proposal.VotingMaxWeight()),
    251 "configVersion", utils.FormatInt(proposal.ConfigVersion()),
    252 "createdAt", utils.FormatInt(proposal.CreatedAt()),
    253 )
    254
    255 return proposal.ID()
    256}
    257
    258// ProposeParameterChange creates a protocol parameter update proposal.
    259//
    260// Modifies system parameters through governance.
    261// Supports multiple parameter changes in single proposal.
    262// Changes apply atomically on execution.
    263//
    264// Parameters:
    265//
    266// - _: Internal call discriminator; pass 0.
    267//
    268// - rlm: Propagated realm context; validated as current before proposal state writes.
    269//
    270// - title: Clear description of changes
    271//
    272// - description: Rationale and impact analysis
    273//
    274// - numToExecute: Number of parameter changes
    275//
    276// - executions: Raw execution string encoded as messages separated by *GOV*.
    277// Each message is formatted as <pkgPath>*EXE*<function>*EXE*<params>.
    278//
    279// Example execution format (one registered handler; all seven arguments are required):
    280//
    281// gno.land/r/gnoswap/gov/governance*EXE*Reconfigure*EXE*86400,604800,86400,50,1000000000,86400,2592000
    282//
    283// Requirements:
    284// - Caller must hold at least the configured ProposalCreationThreshold amount in xGNS
    285// - Encoded execution messages must match numToExecute
    286// - Target handlers must exist in the parameter registry
    287// - Parameters must match registered function signatures
    288//
    289// Returns:
    290// - newProposalId: ID of the newly created parameter-change proposal
    291func (gv *governanceV1) ProposeParameterChange(
    292 _ int, rlm realm,
    293 title string,
    294 description string,
    295 numToExecute int64,
    296 executions string,
    297) (newProposalId int64) {
    298 access.AssertIsRlmCurrent(0, rlm)
    299
    300 proposalData := NewProposalExecutionData(numToExecute, executions)
    301 assertIsNotHaltedGovernanceForProposalData(proposalData)
    302
    303 prev := rlm.Previous()
    304 callerAddress := prev.Address()
    305
    306 createdAt := time.Now().Unix()
    307 createdHeight := runtime.ChainHeight()
    308 xgnsBalance := xgns.BalanceOf(callerAddress)
    309
    310 config, ok := gv.getCurrentConfig()
    311 if !ok {
    312 panic(errors.New(errDataNotFound))
    313 }
    314
    315 // Clean up inactive user proposals before checking if caller already has an active proposal
    316 err := gv.removeInactiveUserProposals(0, rlm, callerAddress, createdAt)
    317 if err != nil {
    318 panic(err)
    319 }
    320
    321 // Check if caller already has an active proposal (one proposal per address)
    322 if gv.hasActiveProposal(callerAddress) {
    323 panic(errors.New(errAlreadyActiveProposal))
    324 }
    325
    326 // Get timestamp anchor and total voting weight for proposal creation
    327 maxVotingWeight, snapshotTime, err := gv.getVotingWeightSnapshot(
    328 createdAt,
    329 config.VotingWeightSmoothingDuration,
    330 )
    331 if err != nil {
    332 panic(err)
    333 }
    334 quorumWeight := gv.stakerAccessor.GetTotalxGnsSupply()
    335
    336 // Create the parameter change proposal with execution data
    337 proposal, err := gv.createProposal(
    338 0, rlm,
    339 governance.ParameterChange,
    340 config,
    341 maxVotingWeight,
    342 quorumWeight,
    343 snapshotTime,
    344 governance.NewProposalMetadata(title, description),
    345 proposalData,
    346 callerAddress,
    347 xgnsBalance,
    348 createdAt,
    349 createdHeight,
    350 )
    351 if err != nil {
    352 panic(err)
    353 }
    354
    355 // Initialize empty voting info tree for this proposal (votes will be added as users vote)
    356 err = gv.updateProposalUserVotes(0, rlm, proposal, governance.NewProposalUserVotingInfoTree())
    357 if err != nil {
    358 panic(err)
    359 }
    360
    361 // Emit proposal creation event for indexing and tracking
    362 chain.Emit(
    363 "ProposeParameterChange",
    364 "prevAddr", prev.Address().String(),
    365 "prevRealm", prev.PkgPath(),
    366 "title", title,
    367 "numToExecute", utils.FormatInt(numToExecute),
    368 "proposalId", utils.FormatInt(proposal.ID()),
    369 "quorumAmount", utils.FormatInt(proposal.VotingQuorumAmount()),
    370 "maxVotingWeight", utils.FormatInt(proposal.VotingMaxWeight()),
    371 "configVersion", utils.FormatInt(proposal.ConfigVersion()),
    372 "createdAt", utils.FormatInt(proposal.CreatedAt()),
    373 )
    374
    375 return proposal.ID()
    376}
    377
    378// createProposal handles proposal creation logic.
    379// Validates input data, checks proposer eligibility, and creates proposal object.
    380func (gv *governanceV1) createProposal(
    381 _ int, rlm realm,
    382 proposalType governance.ProposalType,
    383 config governance.Config,
    384 maxVotingWeight int64,
    385 quorumWeight int64,
    386 snapshotTime int64,
    387 proposalMetadata *governance.ProposalMetadata,
    388 proposalData *governance.ProposalData,
    389 proposerAddress address,
    390 proposerXGnsBalance int64,
    391 createdAt int64,
    392 createdHeight int64,
    393) (*governance.Proposal, error) {
    394 // Validate proposal metadata (title and description)
    395 metadataResolver := NewProposalMetadataResolver(proposalMetadata)
    396 err := metadataResolver.Validate()
    397 if err != nil {
    398 return nil, err
    399 }
    400
    401 // Validate proposal data (type-specific validation)
    402 dataResolver := NewProposalDataResolver(proposalData)
    403 err = dataResolver.Validate()
    404 if err != nil {
    405 return nil, err
    406 }
    407
    408 // Check if proposer has enough xGNS balance to create proposal
    409 if proposerXGnsBalance < config.ProposalCreationThreshold {
    410 return nil, errors.New(errNotEnoughBalance)
    411 }
    412
    413 // Generate unique proposal ID
    414 proposalID := gv.nextProposalID(0, rlm)
    415
    416 // Create proposal status with voting schedule and requirements
    417 proposalStatus := NewProposalStatus(
    418 config,
    419 maxVotingWeight,
    420 proposalType.IsExecutable(),
    421 createdAt,
    422 quorumWeight,
    423 )
    424
    425 // Get current configuration version for tracking
    426 configVersion := gv.getCurrentConfigVersion()
    427
    428 // Create the proposal object with a timestamp anchor for lazy voting-weight lookup
    429 proposal := governance.NewProposal(
    430 proposalID,
    431 proposalStatus,
    432 proposalMetadata,
    433 proposalData,
    434 proposerAddress,
    435 configVersion,
    436 snapshotTime,
    437 createdHeight,
    438 )
    439
    440 // Store the proposal in state
    441 success := gv.addProposal(0, rlm, proposal)
    442 if !success {
    443 return nil, errors.New(errDataNotFound)
    444 }
    445
    446 return proposal, nil
    447}
    448
    449// getVotingWeightSnapshot retrieves the averaged total voting weight for proposal creation.
    450// It uses two snapshots (current and current - smoothingPeriod) and averages them.
    451func (gv *governanceV1) getVotingWeightSnapshot(
    452 current,
    453 smoothingPeriod int64,
    454) (int64, int64, error) {
    455 // Calculate the earlier timestamp by subtracting the smoothing duration
    456 snapshotTime := current - smoothingPeriod
    457 if snapshotTime < 0 {
    458 snapshotTime = 0
    459 }
    460
    461 // Get total delegation at the earlier timestamp from staker history
    462 totalAtSnapshot, ok := gv.stakerAccessor.GetTotalDelegationAmountAtSnapshot(snapshotTime)
    463 if !ok {
    464 totalAtSnapshot = 0
    465 }
    466
    467 totalAtCurrent, ok := gv.stakerAccessor.GetTotalDelegationAmountAtSnapshot(current)
    468 if !ok {
    469 totalAtCurrent = 0
    470 }
    471
    472 totalVotingWeight := gnsmath.SafeAddInt64(totalAtSnapshot, totalAtCurrent) / 2
    473 if totalVotingWeight <= 0 {
    474 return 0, snapshotTime, errors.New(errNotEnoughVotingWeight)
    475 }
    476
    477 return totalVotingWeight, snapshotTime, nil
    478}
    479