Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: r/gov/dao v2 + r/sys/vars #2581

Open
wants to merge 70 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
4dd7c58
Add standard govdao types
zivkovicmilos Jul 13, 2024
aad859b
Initial simpledao implementation
zivkovicmilos Jul 13, 2024
81396bf
Add membstore tests
zivkovicmilos Jul 13, 2024
dd5d64b
Add propstore tests
zivkovicmilos Jul 13, 2024
c342741
Add testing stubs
zivkovicmilos Jul 13, 2024
3d16ba6
Update examples/gno.land/p/gov/dao/types.gno
zivkovicmilos Jul 14, 2024
8f2e3ac
Move /nt/simpledao -> /demo/simpledao
zivkovicmilos Jul 14, 2024
ae0639a
Move DAO API to dao.gno
zivkovicmilos Jul 14, 2024
3a7ba00
Move vote API to vote.gno
zivkovicmilos Jul 14, 2024
00056a5
Add GetVoteByMember to proposal API
zivkovicmilos Jul 14, 2024
209ee06
Drop name from dao.Member
zivkovicmilos Jul 14, 2024
54c555d
Add limit to number of proposals returned
zivkovicmilos Jul 14, 2024
84aeb30
Add Size() to MembStore API
zivkovicmilos Jul 14, 2024
322bc8c
Apply Yoda wisdom to MemberStore API
zivkovicmilos Jul 14, 2024
794a353
Add execution failed ProposalStatus
zivkovicmilos Jul 14, 2024
8c45039
Drop unused PropStore method
zivkovicmilos Jul 14, 2024
268021d
Drop RemoveMember, add removal in UpdateMember
zivkovicmilos Jul 14, 2024
43e5c66
Add Size() to PropStore API
zivkovicmilos Jul 14, 2024
e1cd617
Add pagination to MembStore
zivkovicmilos Jul 14, 2024
67e4535
Rename proposal.gno -> proposals.gno
zivkovicmilos Jul 14, 2024
8fbbd5e
Start cleaning up r/gov/dao
zivkovicmilos Jul 14, 2024
fd13d02
Clean house for the govdao executor
zivkovicmilos Jul 14, 2024
ebfd3d2
Add remaining simpledao tests
zivkovicmilos Jul 15, 2024
b3856c7
Remove unsafe ctx executor usage
zivkovicmilos Jul 15, 2024
4d3d94e
Fixup prop filetests
zivkovicmilos Jul 15, 2024
1f1d5d7
Fixup prop filetests
zivkovicmilos Jul 15, 2024
7fa7530
Remove faulty test
zivkovicmilos Jul 15, 2024
ef299bd
Add pagination for vote fetching
zivkovicmilos Jul 15, 2024
3d0c056
Tidy mod
zivkovicmilos Jul 15, 2024
4396c48
Update update method
zivkovicmilos Jul 15, 2024
24e0b08
Add r/sys/vars
zivkovicmilos Jul 15, 2024
4827d0b
Version packages, add unit tests
zivkovicmilos Jul 15, 2024
26f21d8
Start integrating r/sys/vars into EndBlocker
zivkovicmilos Jul 15, 2024
71e8644
Add moniker to r/gnoland/valopers
zivkovicmilos Jul 16, 2024
93d2b70
Resolve freaky import
zivkovicmilos Jul 16, 2024
efe35cc
Restore Endblocker
zivkovicmilos Jul 16, 2024
63cf23f
Make tidy
zivkovicmilos Jul 16, 2024
4520593
Make tidy
zivkovicmilos Jul 16, 2024
af65764
Fixup vars tests
zivkovicmilos Jul 16, 2024
f567394
Merge branch 'master' into dev/zivkovicmilos/govdao
zivkovicmilos Jul 16, 2024
4ec9f3b
Export errors
zivkovicmilos Jul 16, 2024
30ac5ff
Update comment
zivkovicmilos Jul 16, 2024
c05289f
Drop useless voters methods
zivkovicmilos Jul 16, 2024
d8e5b7d
Drop excess field
zivkovicmilos Jul 16, 2024
d320493
Update voting power
zivkovicmilos Jul 16, 2024
d33b0df
Please the standards police
zivkovicmilos Jul 16, 2024
79be838
Slim down the DAO API
zivkovicmilos Jul 16, 2024
8c52893
Revert "Slim down the DAO API"
zivkovicmilos Jul 16, 2024
74948fc
Add Executor to proposal
zivkovicmilos Jul 16, 2024
c9b4cc4
Fix up old API
zivkovicmilos Jul 17, 2024
93ca6a8
Mod tidy
zivkovicmilos Jul 17, 2024
ee77102
Make the PropStore part of the DAO API
zivkovicmilos Jul 17, 2024
dbf0966
Fixup prop test
zivkovicmilos Jul 17, 2024
2f337a6
Move out memberstore
zivkovicmilos Jul 17, 2024
31d265f
Add Render to proposal
zivkovicmilos Jul 18, 2024
908b949
Add govdao executor wrapper
zivkovicmilos Jul 18, 2024
6b3b81c
Remove unused code
zivkovicmilos Jul 18, 2024
480a287
Add new prop test
zivkovicmilos Jul 18, 2024
06fe4fe
Move out memberstore
zivkovicmilos Jul 20, 2024
dba0379
Move p/gov/dao -> p/demo/dao
zivkovicmilos Jul 20, 2024
873ba9b
Drop voting fetchers from the propstore
zivkovicmilos Jul 21, 2024
6ceb72a
Rename VotingStats -> Stats
zivkovicmilos Jul 21, 2024
8198c7f
Add key name to r/sys/vars emit event
zivkovicmilos Jul 21, 2024
8a51452
Merge branch 'master' into dev/zivkovicmilos/govdao
zivkovicmilos Jul 22, 2024
b0fa6b7
Merge branch 'master' into dev/zivkovicmilos/govdao
zivkovicmilos Aug 15, 2024
676b202
Make the executor not panic due to wrong caller
zivkovicmilos Aug 15, 2024
852291c
isCallerGOVDAO -> isCallerDAORealm
zivkovicmilos Aug 15, 2024
ae650d2
Resolve nitpick
zivkovicmilos Aug 15, 2024
aa4f9ef
Fix faulty test assert
zivkovicmilos Aug 15, 2024
33343d7
Merge branch 'master' into dev/zivkovicmilos/govdao
zivkovicmilos Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/gno.land/p/gov/dao/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module gno.land/p/gov/dao

require gno.land/p/gov/proposal v0.0.0-latest
34 changes: 34 additions & 0 deletions examples/gno.land/p/gov/dao/members.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dao

import (
"std"
)

// MemberStore defines the member storage abstraction
type MemberStore interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deserves Its own package.

A DAO is not necessarily based on members. It could be composed of other DAOs, among other things.

Let's create a memberstore package for DAOs that require a memberstore, as well as other non-DAO entities that need a memberstore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved it out into a separate package:

2f337a6

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move these two interfaces in p/demo/membstore,so that Dao.membstore does not depend on gov//dao?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so that Dao.membstore does not depend on gov//dao

What do you mean by this?

The dao.MemberStore defines the interface that is now implemented in p/demo/membstore

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced we should be moving the memberstore interface outside p/gov/dao, since this should be a shared interface that implementations fulfill

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until we have a way to to make anything addressable, including gno object, we should remove the member store from p/demo/dao.

A Dao isn’t necessarily about addressable users. And if for instance, we want to make interchain DAOs, we definitely don’t care at all about their members, we just do care about being able to propose things, and read proposals’ state.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make interchain DAOs

I had to pause for a second to grasp this concept. Powerful stuff

I've kept this commit stashed in case I got clarity on it, which I have now (dao.DAO is not the defactor API for DAOs yet, but a suggestion that should be moved out of p/gov/dao)

Moved:
06fe4fe

// GetMembers returns all members in the store
GetMembers() []Member
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Size()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added:

84aeb30

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also added for the PropStore:

43e5c66

moul marked this conversation as resolved.
Show resolved Hide resolved

// IsMember returns a flag indicating if the given address
// belongs to a member
IsMember(address std.Address) bool

// GetMember returns the requested member
GetMember(address std.Address) (Member, error)

// AddMember attempts to add a member to the store
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"attempts"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed, have I

322bc8c

AddMember(member Member) error

// RemoveMember attempts to remove a member from the store
RemoveMember(address std.Address) error

// UpdateMember attempts to update the member in the store
UpdateMember(address std.Address, member Member) error
}

// Member holds the relevant member information
type Member struct {
Address std.Address // bech32 gno address of the member (unique)
Name string // name associated with the member
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Name string // name associated with the member

we'll use r/users for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped:

209ee06

VotingPower uint64 // the voting power of the member
}
47 changes: 47 additions & 0 deletions examples/gno.land/p/gov/dao/proposal.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package dao

import "std"

type Status string

// ACTIVE -> ACCEPTED -> EXECUTED
// ACTIVE -> NOT ACCEPTED
var (
Active Status = "active" // proposal is still active
Accepted Status = "accepted" // proposal gathered quorum
NotAccepted Status = "not accepted" // proposal failed to gather quorum
Executed Status = "executed" // proposal is executed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timedout

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Freeze?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry, just noticed that it was the "DAO Status"

please rename the type DAOStatus

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh no, not sorry; please, keep timedout, and freeze, but rename ProposalStatus

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • add ExecutedError

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed, and added:

794a353

)

func (s Status) String() string {
return string(s)
}

// PropStore defines the proposal storage abstraction
type PropStore interface {
// GetProposals returns the given paginated proposals
GetProposals(offset, count uint64) []Proposal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add a filter like active=true

or a dedicated helper

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, we should not return a slice with all the proposals, but helper to get a specific one by ID, to know the size, and eventually to paginate, but by default, the API should discourage listing more than the last 10 ones or getting a specific one

it's a bad gas optimization and a way to hit memory limit to let the current listing for such potentially-infinite objects.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we shouldn't expose this kind of infinite logic, especially on an open API like this (anyone can get the PropStore)

I've limited it to a sane default (10):

54c555d


// GetProposalByID returns the proposal associated with
// the given ID, if any
GetProposalByID(id uint64) (Proposal, error)

// GetProposalsByAddress returns the proposals associated
// with the given proposer address
GetProposalsByAddress(address std.Address) []Proposal
moul marked this conversation as resolved.
Show resolved Hide resolved
}

// Proposal is the single proposal abstraction
type Proposal interface {
// GetAuthor returns the author of the proposal
GetAuthor() std.Address

// GetDescription returns the description of the proposal
GetDescription() string

// GetStatus returns the status of the proposal
GetStatus() Status
moul marked this conversation as resolved.
Show resolved Hide resolved

// GetVotes returns the votes of the proposal
GetVotes() []Vote
moul marked this conversation as resolved.
Show resolved Hide resolved
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetVoteByAuthor() Vote

-> for the member portfolio.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, along with unit tests:

00056a5

43 changes: 43 additions & 0 deletions examples/gno.land/p/gov/dao/types.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dao

import (
"std"

"gno.land/p/gov/proposal"
)

type VoteOption string

const (
YesVote VoteOption = "YES"
NoVote VoteOption = "NO"
zivkovicmilos marked this conversation as resolved.
Show resolved Hide resolved
)

func (v VoteOption) String() string {
return string(v)
}

// Vote encompasses a single GOVDAO vote
type Vote struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to vote.gno

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved:

3a7ba00

Address std.Address // the address of the voter
Option VoteOption // the voting option
}

// DAO defines the DAO abstraction
type DAO interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to dao.gno

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved:

ae0639a

// Propose adds a new proposal to the executor-based GOVDAO.
// Returns the generated proposal ID
Propose(comment string, executor proposal.Executor) (uint64, error)

// VoteOnProposal adds a vote to the given proposal ID
VoteOnProposal(id uint64, option VoteOption) error

// ExecuteProposal executes the proposal with the given ID
ExecuteProposal(id uint64) error

// GetMembStore returns the member store associated with the DAO
GetMembStore() MemberStore

// GetPropStore returns the proposal store associated with the DAO
GetPropStore() PropStore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not an idea to have independent interfaces since they are tightly coupled to this top-level interface. I understand your preference for splitting things, but in this case, it's about making the DAO parameters unavailable from the MembStore and PrepStore, which is unfortunate because most of your helpers in the stores are checking if the caller is the DAO.

I suggest embedding (or flattening) the interfaces in the main interface instead of returning an independent interface. This way, you can have a single struct implementing everything or decide to use embedded standalone MemberStore/PropStore in the implementation struct.

Copy link
Member Author

@zivkovicmilos zivkovicmilos Jul 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is tight coupling with what the top level DAO API does, I agree 🙁

I wanted to preserve the shallow API for the DAO, but also not limit the functionality so we can create wrapper Realms to do much more sophisticated and complicated queries (ex. return JSON objects...)

I've definitely thought about embedding the interfaces in DAO, but I'm struggling to understand how this is a better option than having 2 getters, if we want to use a DAO interface in /r/gov/dao. What if there is a DAO implementation that doesn't publicly expose changing the member set (ex. returns nil for GetMembStore())?

Do we and pipe relevant methods, and let the caller do whatever they want with the DAO through a fetcher?
What I'm essentially asking is, does our r/gov/dao realm look like this?

package dao

var d dao.DAO

func init() {
	d := simpledao.New(...)
}

func Propose(comment string, executor proposal.Executor) (uint64, error) {
	return d.Propose(comment, executor)
}

func VoteOnProposal(id uint64, option VoteOption) error {
	return d.VoteOnProposal(id, option)
}

func ExecuteProposal(id uint64) error {
	return d.ExecuteProposal(id)
}

func GetDAO() dao.DAO {
	return d
}

func Render(path string) string {
	// ...
}

Copy link
Member Author

@zivkovicmilos zivkovicmilos Jul 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Through this API, r/gov/dao is minimal, and we don't have a gazillion methods in our public API, but let the caller wrap them through another Realm that calls GetDAO

I just need clarify whether we need this:

What if there is a DAO implementation that doesn't publicly expose changing the member set (ex. returns nil for GetMembStore())?

  • If the answer is yes, this is possible, I'd prefer to keep the GetXXX on the DAO interface, and not embed
  • If the answer is no, I see no reason for not embedding

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re right.

Let’s remove the notion of member from p/gov/dao so that a DAO is an object that:

  • handles proposals
  • have assets

member management is something that can be made generic but it’s not because of a Dao, it’s just for anything that wants members.

r/gov/dao should implements p/gov/dao (the proposal framework) AND p/something/membership; while relying on the proposal framework (injecting it?) to manage the members through proposals.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've applied this change, but can't find the exact commit 🤦‍♂️

}
206 changes: 206 additions & 0 deletions examples/gno.land/p/nt/simpledao/dao.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package simpledao

import (
"errors"
"std"

"gno.land/p/demo/ufmt"
"gno.land/p/gov/dao"
pproposal "gno.land/p/gov/proposal"
)

var (
errInvalidExecutor = errors.New("invalid executor provided")
errInsufficientProposalFunds = errors.New("insufficient funds for proposal")
errInsufficientExecuteFunds = errors.New("insufficient funds for executing proposal")
errProposalExecuted = errors.New("proposal already executed")
errProposalInactive = errors.New("proposal is inactive")
errProposalExpired = errors.New("proposal is expired")
errProposalNotAccepted = errors.New("proposal is not accepted")

errNotGovDAO = errors.New("caller not correct govdao instance")
)

var (
minProposalFeeValue int64 = 100 * 1_000_000 // minimum gnot required for a govdao proposal (100 GNOT)
minExecuteFeeValue int64 = 500 * 1_000_000 // minimum gnot required for a govdao proposal (500 GNOT)

minProposalFee = std.NewCoin("ugnot", minProposalFeeValue)
minExecuteFee = std.NewCoin("ugnot", minExecuteFeeValue)
)

// SimpleDAO is a simple DAO implementation
type SimpleDAO struct {
membStore *MembStore
propStore *PropStore
}

// NewSimpleDAO creates a new instance of the simpledao DAO
func NewSimpleDAO(membStore *MembStore, propStore *PropStore) *SimpleDAO {
moul marked this conversation as resolved.
Show resolved Hide resolved
return &SimpleDAO{
membStore: membStore,
propStore: propStore,
}
}

func (s *SimpleDAO) Propose(description string, executor pproposal.Executor) (uint64, error) {
// Make sure the executor is set
if executor == nil {
return 0, errInvalidExecutor
}

var (
caller = getDAOCaller()
sentCoins = std.GetOrigSend() // Get the sent coins, if any
canCoverFee = sentCoins.AmountOf("ugnot") >= minProposalFee.Amount
)

// Check if the proposal is valid
if !s.membStore.IsMember(caller) && !canCoverFee {
return 0, errInsufficientProposalFunds
}

// Create the wrapped proposal
prop := &proposal{
description: description,
executor: executor,
author: caller,
votes: newVotes(),
}

// Add the proposal
id, err := s.propStore.addProposal(prop)
if err != nil {
return 0, ufmt.Errorf("unable to add proposal, %s", err)
}

return id, nil
}

func (s *SimpleDAO) VoteOnProposal(id uint64, option dao.VoteOption) error {
// Verify the GOVDAO member
member, err := s.membStore.GetMember(getDAOCaller())
if err != nil {
return ufmt.Errorf("unable to get govdao member, %s", err)
}

// Check if the proposal exists
propRaw, err := s.propStore.GetProposalByID(id)
if err != nil {
return ufmt.Errorf("unable to get proposal %d, %s", id, err)
}

prop := propRaw.(*proposal)

// Check the proposal status
if prop.GetStatus() == dao.Executed {
// Proposal was already executed, nothing to vote on anymore.
//
// In fact, the proposal should stop accepting
// votes as soon as a 2/3+ majority is reached
// on either option, but leaving the ability to vote still,
// even if a proposal is accepted, or not accepted,
// leaves room for "principle" vote decisions to be recorded
return errProposalInactive
}

// Check if the proposal executor is expired
if prop.executor.IsExpired() {
return errProposalExpired
}

// Cast the vote
if err = prop.votes.castVote(member, option); err != nil {
return ufmt.Errorf("unable to vote on proposal %d, %s", id, err)
}

// Check the votes to see if quorum is reached
var (
majorityPower = s.membStore.getMajorityPower()
yays, nays = prop.votes.getTally()
)

switch {
case yays > majorityPower:
prop.status = dao.Accepted
case nays > majorityPower:
prop.status = dao.NotAccepted
default:
// Quorum not reached
}

return nil
}

func (s *SimpleDAO) ExecuteProposal(id uint64) error {
var (
caller = getDAOCaller()
sentCoins = std.GetOrigSend() // Get the sent coins, if any
canCoverFee = sentCoins.AmountOf("ugnot") >= minExecuteFee.Amount
)

// Check if the non-DAO member can cover the execute fee
if !s.membStore.IsMember(caller) && !canCoverFee {
return errInsufficientExecuteFunds
}

// Check if the proposal exists
propRaw, err := s.propStore.GetProposalByID(id)
if err != nil {
return ufmt.Errorf("unable to get proposal %d, %s", id, err)
}

prop := propRaw.(*proposal)

// Check the proposal status
if prop.GetStatus() != dao.Accepted {
// Proposal is not accepted, cannot be executed
return errProposalNotAccepted
}

// Check if the proposal is executed
if prop.GetStatus() == dao.Executed {
// Proposal is already executed
return errProposalExecuted
}

// Check if proposal is expired
if prop.executor.IsExpired() {
return errProposalExpired
}

// Update the proposal status
prop.status = dao.Executed

// Attempt to execute the proposal
if err = prop.executor.Execute(); err != nil {
return ufmt.Errorf("error during proposal %d execution, %s", id, err)
}

return nil
}

func (s *SimpleDAO) GetMembStore() dao.MemberStore {
return s.membStore
}

func (s *SimpleDAO) GetPropStore() dao.PropStore {
return s.propStore
}

// getDAOCaller returns the DAO caller.
// XXX: This is not a great way to determine the caller, and it is very unsafe.
// However, the current MsgRun context does not persist escaping the main() scope.
// Until a better solution is developed, this enables proposals to be made through a package deployment + init()
func getDAOCaller() std.Address {
return std.GetOrigCaller()
}

// TODO change to r/sys/vars
const daoPkgPath = "gno.land/r/gov/dao"

// isCallerGOVDAO returns a flag indicating if the
// current caller context is the active GOVDAO
func isCallerGOVDAO() bool {
moul marked this conversation as resolved.
Show resolved Hide resolved
return std.CurrentRealm().PkgPath() == daoPkgPath
}
Loading
Loading