PathrockNetwork Gno Explorer
HomeBlocksTransactionsRealmsPackagesValidatorsAnalytics

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/gov/dao/impl/v0

Realm
Open in gnoweb ↗

Overview

Kind
Realm (renderable)
Name
v0
Namespace
gov / dao / impl
Files
25 (gnomod.toml)
Exported functions
17
Module
gno.land/r/gov/dao/impl/v0
gno
0.9

Files (25)

  • gnomod.tomltoml
  • clamp.gnogno
  • filter.gnogno
  • govdao.gnogno
  • impl.gnogno
  • prop_requests.gnogno
  • render.gnogno
  • types.gnogno
  • clamp_test.gnogno
  • denied_reason_test.gnogno
  • executor_disclosure_filetest.gnogno
  • executor_invocation_authority_filetest.gnogno
  • govdao_execute_proposal_00_filetest.gnogno
  • govdao_execute_proposal_01_filetest.gnogno
  • govdao_execute_proposal_02_filetest.gnogno
  • govdao_execute_proposal_03_filetest.gnogno
  • govdao_test.gnogno
  • render_error_paths_filetest.gnogno
  • stringify_proposal_00_filetest.gnogno
  • title_clamp_filetest.gnogno
  • z_addr_validation_test.gnogno
  • z_denied_reason_store_test.gnogno
  • z_treasury_payment_request_test.gnogno
  • z_unknown_proposal_test.gnogno
  • z_upgrade_request_test.gnogno
  • prop_requests.gnogno
    1package impl23import (4	"chain/runtime/unsafe"5	"strings"67	"gno.land/p/aeddi/panictoerr/v0"8

    Functions

    • AddMember(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string)

    • GetInstance(int, rlm interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}) *gno.land/r/gov/dao/impl/v0.GovDAO

    • NewAddMemberRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, tier string, portfolio string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewChangeLawRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newLaw struct{Supermajority float64}) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewFilterByTier(tier string) struct{Tier string}

    • NewGovDAO() *gno.land/r/gov/dao/impl/v0.GovDAO

    • NewLaw(supermajority float64) struct{Supermajority float64}

    • NewPromoteMemberRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, fromTier string, toTier string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewProposalsStatuses() struct{BPTree *gno.land/p/nt/bptree/v0.BPTree}

    • NewRender(d *gno.land/r/gov/dao/impl/v0.GovDAO) *gno.land/r/gov/dao/impl/v0.render

    • NewTreasuryGRC20TokensUpdate(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newTokenKeys []string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewTreasuryPaymentRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, payment interface {BankerID func() string; String func() string}, reason string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewUpgradeDaoImplRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, newDao interface {ExecuteProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalID, gno.land/r/gov/dao.Executor) .uverse.error; PostCreateProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalRequest, gno.land/r/gov/dao.ProposalID); PreCreateProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalRequest) (.uverse.address, .uverse.error); PreExecuteProposal func(int, .uverse.realm, gno.land/r/gov/dao.ProposalID) (bool, .uverse.error); Render func(.uverse.realm, string, string) string; VoteOnProposal func(int, .uverse.realm, gno.land/r/gov/dao.VoteRequest) .uverse.error}, realmPkg string, reason string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • NewWithdrawMemberRequest(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, addr string, reason string) struct{title string; description string; executor gno.land/r/gov/dao.Executor; filter gno.land/r/gov/dao.Filter}

    • Render(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, in string) string

    • StringifyProposal(p *gno.land/r/gov/dao.Proposal) string

    • StringifyVotes(int, rlm interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, ps *gno.land/r/gov/dao/impl/v0.proposalStatus) string

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

    Rendered

    RenderedRawgnoweb ↗

    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.

    "gno.land/p/moul/md/v0"
    9 "gno.land/p/nt/markdown/sanitize/v0"
    10 trs_pkg "gno.land/p/nt/treasury/v0"
    11 "gno.land/p/nt/ufmt/v0"
    12
    13 "gno.land/r/gov/dao"
    14 "gno.land/r/gov/dao/memberstore/v0"
    15 "gno.land/r/gov/dao/treasury/v0"
    16)
    17
    18func NewChangeLawRequest(cur realm, newLaw Law) dao.ProposalRequest {
    19 member, _ := memberstore.Get(0, cur).GetMember(unsafe.OriginCaller())
    20 if member == nil {
    21 panic("proposer is not a member")
    22 }
    23
    24 cb := func(cur realm) error {
    25 law = &newLaw
    26 return nil
    27 }
    28
    29 e := dao.NewSimpleExecutor(0, cur, cb, ufmt.Sprintf("A new Law is proposed:\n %v", newLaw))
    30
    31 return dao.NewProposalRequest("Change Law Proposal", "This proposal is looking to change the actual govDAO Law", e)
    32}
    33
    34func NewUpgradeDaoImplRequest(cur realm, newDao dao.DAO, realmPkg, reason string) dao.ProposalRequest {
    35 // Rejected here as well as in UpdateImpl so the mistake surfaces when the
    36 // proposal is written, not when it executes. An empty realmPkg would be
    37 // stored as an allowlist entry matching every user realm's empty
    38 // PkgPath(), and would render as a blank name in the grant sentence below.
    39 if strings.TrimSpace(realmPkg) == "" {
    40 panic("realmPkg must be the realm path being granted govDAO authority")
    41 }
    42 // Padding is rejected for the same reason UpdateImpl rejects it: the entry
    43 // is stored as given and matched whole, so a padded path grants nobody
    44 // anything. Caught here so the author sees it, not the voters.
    45 if realmPkg != strings.TrimSpace(realmPkg) {
    46 panic("realmPkg must not have leading or trailing spaces")
    47 }
    48
    49 member, _ := memberstore.Get(0, cur).GetMember(unsafe.OriginCaller())
    50 if member == nil {
    51 panic("proposer is not a member")
    52 }
    53
    54 cb := func(cur realm) error {
    55 // dao.UpdateImpl() must be cross-called from v0/impl but
    56 // what calls this cb function is r/gov/dao.
    57 // therefore we must cross back into v0/impl and then
    58 // cross call dao.UpdateRequest().
    59 dao.UpdateImpl(cross(cur), dao.NewUpdateRequest(newDao, []string{"gno.land/r/gov/dao/impl/v0", realmPkg}))
    60 return nil
    61 }
    62
    63 // State the grant: this executor rewrites AllowedDAOs, and previously
    64 // rendered nothing at all. realmPkg is caller-supplied and lands in a code
    65 // span, so it is wrapped with sanitize.InlineCode — which emits its own
    66 // fence, so do not add backticks around it. (md.EscapeText is wrong here:
    67 // CommonMark 6.1 does not process backslash escapes inside code spans.)
    68 // Nothing ties realmPkg to newDao, which is why the text says so.
    69 e := dao.NewSimpleExecutor(0, cur, cb, ufmt.Sprintf(
    70 "Replaces the govDAO implementation with the DAO carried by this proposal.\n\n"+
    71 "After execution, only `gno.land/r/gov/dao/impl/v0` and %s may replace the "+
    72 "implementation, mutate the member store, or move treasury funds.\n\n"+
    73 "This grant is stated by the proposer: the incoming implementation is passed "+
    74 "as a value and is not verified to belong to the realm named above.",
    75 sanitize.InlineCode(realmPkg)))
    76
    77 return dao.NewProposalRequest("Change DAO implementation", "This proposal is looking to change the actual govDAO implementation. Reason: "+reason, e)
    78}
    79
    80func NewAddMemberRequest(cur realm, addr address, tier string, portfolio string) dao.ProposalRequest {
    81 // Reject a non-bech32 address at proposal-build time (see AddMember): keeps
    82 // an unauthenticable / injection-bearing key out of the member store.
    83 if !addr.IsValid() {
    84 panic("invalid member address: " + addr.String())
    85 }
    86 _, ok := memberstore.GetTier(tier)
    87 if !ok {
    88 panic("provided tier does not exists")
    89 }
    90
    91 if tier != memberstore.T1 && tier != memberstore.T2 {
    92 panic("Only T1 and T2 members can be added by proposal. To add a T3 member use AddMember function directly.")
    93 }
    94
    95 if portfolio == "" {
    96 panic("A portfolio for the proposed member is required")
    97 }
    98
    99 member, _ := memberstore.Get(0, cur).GetMember(unsafe.OriginCaller())
    100 if member == nil {
    101 panic("proposer is not a member")
    102 }
    103
    104 if member.InvitationPoints <= 0 {
    105 panic("proposer does not have enough invitation points for inviting new people to the board")
    106 }
    107
    108 cb := func(cur realm) error {
    109 // Add the member first, spend the proposer's invitation point second.
    110 //
    111 // SetMember RETURNS an error when the address is already a member --
    112 // easy to reach, since AddMember enrols T3 members directly. A returned
    113 // executor error does not revert: ExecuteOrRejectProposal marks the
    114 // proposal rejected and the transaction still commits. So with the old
    115 // order the point was spent and the member was not added.
    116 //
    117 // RemoveInvitationPoint can still fail, despite the build-time check
    118 // above: it panics at zero, and AddMember spends points from this same
    119 // captured member, so a proposer can drain their own between creation
    120 // and execution. That panics and reverts, which loses nothing.
    121 if err := memberstore.Get(0, cur).SetMember(tier, addr, memberByTier(tier)); err != nil {
    122 return err
    123 }
    124 member.RemoveInvitationPoint()
    125
    126 return nil
    127 }
    128
    129 e := dao.NewSimpleExecutor(0, cur, cb, ufmt.Sprintf("A new member with address %v is proposed to be on tier %v. Provided Portfolio information:\n\n%v", addr, tier, portfolio))
    130
    131 name := tryResolveAddr(addr)
    132 return dao.NewProposalRequestWithFilter(
    133 ufmt.Sprintf("New %s Member Proposal", tier),
    134 ufmt.Sprintf("This is a proposal to add `%s` to **%s**.\n#### `%s`'s Portfolio:\n\n%s\n", name, tier, name, portfolio),
    135 e,
    136 FilterByTier{Tier: tier},
    137 )
    138}
    139
    140func NewWithdrawMemberRequest(cur realm, addr address, reason string) dao.ProposalRequest {
    141 member, tier := memberstore.Get(0, cur).GetMember(addr)
    142 if member == nil {
    143 panic("user we want to remove not found")
    144 }
    145
    146 reason = strings.TrimSpace(reason)
    147 if tier == memberstore.T1 && reason == "" {
    148 panic("T1 user removals must contains a reason.")
    149 }
    150
    151 cb := func(cur realm) error {
    152 memberstore.Get(0, cur).RemoveMember(addr)
    153 return nil
    154 }
    155
    156 e := dao.NewSimpleExecutor(0, cur, cb, ufmt.Sprintf("Member with address %v will be withdrawn.\n\n REASON: %v.", addr, reason))
    157
    158 return dao.NewProposalRequest(
    159 "Member Withdrawal Proposal",
    160 ufmt.Sprintf("This is a proposal to remove %s from the GovDAO", tryResolveAddr(addr)),
    161 e,
    162 )
    163}
    164
    165func NewPromoteMemberRequest(cur realm, addr address, fromTier string, toTier string) dao.ProposalRequest {
    166 cb := func(cur realm) error {
    167 // Fail on an unknown destination tier before touching the member.
    168 //
    169 // Everything that can be checked before a write is checked here, and
    170 // RETURNS. A returned executor error rejects the proposal and closes it,
    171 // and since nothing has been mutated yet that commits nothing.
    172 //
    173 // After RemoveMember the policy inverts: a return would commit a member
    174 // who was removed and not re-added, so everything below panics instead,
    175 // which reverts the whole transaction.
    176 // A tier dropped from the global table. NewChangeTiersRequest replaces
    177 // that table wholesale, so a proposal listing only T1 and T2 removes T3
    178 // for good. memberByTier would not notice: it switches on the constant
    179 // and ignores GetTier's ok, so the promotion would succeed and hand the
    180 // member zero invitation points. Untested -- reaching it needs a passed
    181 // tier-change proposal, which rewrites state every other test shares.
    182 if _, ok := memberstore.GetTier(toTier); !ok {
    183 return ufmt.Errorf("unknown destination tier: %s", toTier)
    184 }
    185
    186 mbt := memberstore.Get(0, cur)
    187
    188 // SetMember consults the store's own index, not the global tier table
    189 // above: DeleteAll empties the buckets and leaves the definitions.
    190 if !mbt.Has(toTier) {
    191 return ufmt.Errorf("destination tier is missing from the member store: %s", toTier)
    192 }
    193
    194 prevTier := mbt.RemoveMember(addr)
    195 if prevTier == "" {
    196 panic("member not found, so cannot be promoted")
    197 }
    198
    199 if prevTier != fromTier {
    200 panic("previous tier changed from the one indicated in the proposal")
    201 }
    202
    203 if err := mbt.SetMember(toTier, addr, memberByTier(toTier)); err != nil {
    204 // Unreachable: both of SetMember's failures are ruled out above.
    205 // Panicking is what makes it safe to be wrong about that.
    206 panic("promotion failed after removal: " + err.Error())
    207 }
    208
    209 return nil
    210 }
    211
    212 e := dao.NewSimpleExecutor(0, cur, cb, ufmt.Sprintf("A new member with address %v will be promoted from tier %v to tier %v.", addr, fromTier, toTier))
    213
    214 return dao.NewProposalRequestWithFilter(
    215 "Member Promotion Proposal",
    216 ufmt.Sprintf("This is a proposal to promote %s from **%s** to **%s**.", tryResolveAddr(addr), fromTier, toTier),
    217 e,
    218 FilterByTier{Tier: toTier},
    219 )
    220}
    221
    222func NewTreasuryPaymentRequest(cur realm, payment trs_pkg.Payment, reason string) dao.ProposalRequest {
    223 // A foreign impl renders a Payment line no Banker will process, so it would
    224 // fail only at execution, after the vote. Rejected here so the proposer
    225 // sees it, not the voters. See IsCanonicalPayment.
    226 if !trs_pkg.IsCanonicalPayment(payment) {
    227 panic("payment must be built by treasury.NewCoinsPayment or treasury.NewGRC20Payment")
    228 }
    229 if !treasury.HasBanker(payment.BankerID()) {
    230 panic("banker not registered in treasury with ID: " + payment.BankerID())
    231 }
    232
    233 reason = strings.TrimSpace(reason)
    234 if reason == "" {
    235 panic("treasury payment request requires a reason")
    236 }
    237
    238 cb := func(cur realm) error {
    239 return panictoerr.PanicToError(func() {
    240 treasury.Send(cross(cur), payment)
    241 })
    242 }
    243
    244 // Both are caller-supplied and land in the same body as the Payment line a
    245 // member votes on, which render.gno emits raw. InlineText folds newlines to
    246 // spaces, so neither can start a line of its own and forge that label.
    247 // Clamp before escaping, per clamp.gno.
    248 safeReason := sanitize.InlineText(clampField(reason, maxRenderedReason))
    249 safePayment := sanitize.InlineText(clampField(payment.String(), maxRenderedPayment))
    250
    251 e := dao.NewSimpleExecutor(0, cur,
    252 cb,
    253 ufmt.Sprintf(
    254 "A payment will be sent by the GovDAO treasury.\n\nReason: %s\n\nPayment: %s.",
    255 safeReason,
    256 safePayment,
    257 ),
    258 )
    259
    260 return dao.NewProposalRequest(
    261 "Treasury Payment",
    262 ufmt.Sprintf(
    263 "This proposal is looking to send a payment using the treasury.\n\nReason: %s\n\nPayment: %s",
    264 safeReason,
    265 safePayment,
    266 ),
    267 e,
    268 )
    269}
    270
    271// NewTreasuryGRC20TokensUpdate creates a proposal request to update the list of GRC20 tokens registry
    272// keys used by the treasury. The new list, if voted and accepted, will overwrite the current one.
    273func NewTreasuryGRC20TokensUpdate(cur realm, newTokenKeys []string) dao.ProposalRequest {
    274 if len(newTokenKeys) == 0 {
    275 panic("the list of new tokens is empty")
    276 }
    277
    278 // Copied for the same reason NewCoinsPayment copies its coins: the bullet
    279 // list below is rendered once at creation, but the executor closure
    280 // re-reads the slice at execution, so the caller could otherwise install a
    281 // token set the board never saw.
    282 keys := make([]string, len(newTokenKeys))
    283 copy(keys, newTokenKeys)
    284
    285 cb := func(cur realm) error {
    286 return panictoerr.PanicToError(func() {
    287 // NOTE:: Consider checking if the keys are already registered
    288 // in the grc20reg before updating the treasury tokens keys.
    289 treasury.SetTokenKeys(cross(cur), keys)
    290 })
    291 }
    292
    293 bulletList := md.BulletList(keys)
    294
    295 e := dao.NewSimpleExecutor(0, cur,
    296 cb,
    297 ufmt.Sprintf(
    298 "The list of GRC20 tokens used by the treasury will be updated.\n\nNew Token Keys:\n%s.\n",
    299 bulletList,
    300 ),
    301 )
    302
    303 return dao.NewProposalRequest(
    304 "Treasury GRC20 Tokens Update",
    305 ufmt.Sprintf(
    306 "This proposal is looking to update the list of GRC20 tokens used by the treasury.\n\nNew Token Keys:\n%s",
    307 bulletList,
    308 ),
    309 e,
    310 )
    311}
    312
    313func memberByTier(tier string) *memberstore.Member {
    314 switch tier {
    315 case memberstore.T1:
    316 t, _ := memberstore.GetTier(memberstore.T1)
    317 return memberstore.NewMember(t.InvitationPoints)
    318 case memberstore.T2:
    319 t, _ := memberstore.GetTier(memberstore.T2)
    320 return memberstore.NewMember(t.InvitationPoints)
    321 case memberstore.T3:
    322 t, _ := memberstore.GetTier(memberstore.T3)
    323 return memberstore.NewMember(t.InvitationPoints)
    324 default:
    325 panic("member not found by the specified tier")
    326 }
    327}
    328