forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5871be5
commit 56df907
Showing
28 changed files
with
2,323 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package core | ||
|
||
import ( | ||
"std" | ||
"strings" | ||
|
||
dao_interfaces "gno.land/p/demo/daodao/interfaces_v3" | ||
"gno.land/p/demo/markdown_utils" | ||
) | ||
|
||
// TODO: add wrapper message handler to handle multiple proposal modules messages | ||
|
||
type IDAOCore interface { | ||
AddProposalModule(proposalMod dao_interfaces.IProposalModule) | ||
|
||
VotingModule() dao_interfaces.IVotingModule | ||
ProposalModules() []dao_interfaces.IProposalModule | ||
|
||
Render(path string) string | ||
} | ||
|
||
type daoCore struct { | ||
IDAOCore | ||
|
||
votingModule dao_interfaces.IVotingModule | ||
proposalModules []dao_interfaces.IProposalModule | ||
} | ||
|
||
func NewDAOCore( | ||
votingModule dao_interfaces.IVotingModule, | ||
proposalModules []dao_interfaces.IProposalModule, | ||
) IDAOCore { | ||
return &daoCore{ | ||
votingModule: votingModule, | ||
proposalModules: proposalModules, | ||
} | ||
} | ||
|
||
func (d *daoCore) VotingModule() dao_interfaces.IVotingModule { | ||
return d.votingModule | ||
} | ||
|
||
func (d *daoCore) ProposalModules() []dao_interfaces.IProposalModule { | ||
return d.proposalModules | ||
} | ||
|
||
func (d *daoCore) AddProposalModule(proposalMod dao_interfaces.IProposalModule) { | ||
d.proposalModules = append(d.proposalModules, proposalMod) | ||
} | ||
|
||
func (d *daoCore) Render(path string) string { | ||
s := "# DAO Core\n" | ||
s += "This is a port of [DA0-DA0 contracts](https://github.com/DA0-DA0/dao-contracts)\n" | ||
s += markdown_utils.Indent(d.votingModule.Render(path)) + "\n" | ||
for _, propMod := range d.proposalModules { | ||
s += markdown_utils.Indent(propMod.Render(path)) + "\n" | ||
} | ||
return s | ||
} | ||
|
||
func GetProposalModule(core IDAOCore, moduleIndex int) dao_interfaces.IProposalModule { | ||
if moduleIndex < 0 { | ||
panic("Module index must be >= 0") | ||
} | ||
mods := core.ProposalModules() | ||
if moduleIndex >= len(mods) { | ||
panic("invalid module index") | ||
} | ||
return mods[moduleIndex] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
module gno.land/p/demo/daodao/core_v3 | ||
|
||
require ( | ||
"gno.land/p/demo/daodao/interfaces_v3" v0.0.0-latest | ||
"gno.land/p/demo/markdown_utils" v0.0.0-latest | ||
) |
172 changes: 172 additions & 0 deletions
172
examples/gno.land/p/demo/daodao/interfaces_v3/dao_interfaces.gno
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
package dao_interfaces | ||
|
||
import ( | ||
"std" | ||
"strconv" | ||
|
||
"gno.land/p/demo/avl" | ||
"gno.land/p/demo/jsonutil_v2" | ||
) | ||
|
||
type IVotingModule interface { | ||
VotingPower(addr std.Address) uint64 | ||
TotalPower() uint64 | ||
Render(path string) string | ||
} | ||
|
||
type Ballot struct { | ||
Power uint64 | ||
Vote Vote | ||
Rationale string | ||
} | ||
|
||
func (b Ballot) ToJSON() string { | ||
return jsonutil.FormatObject([]jsonutil.KeyValue{ | ||
{Key: "power", Value: b.Power}, | ||
{Key: "vote", Value: b.Vote}, | ||
{Key: "rationale", Value: b.Rationale}, | ||
}) | ||
} | ||
|
||
type Votes struct { | ||
Yes uint64 | ||
No uint64 | ||
Abstain uint64 | ||
} | ||
|
||
func (v *Votes) Add(vote Vote, power uint64) { | ||
switch vote { | ||
case VoteYes: | ||
v.Yes += power | ||
case VoteNo: | ||
v.No += power | ||
case VoteAbstain: | ||
v.Abstain += power | ||
default: | ||
panic("unknown vote kind") | ||
} | ||
} | ||
|
||
func (v *Votes) Remove(vote Vote, power uint64) { | ||
switch vote { | ||
case VoteYes: | ||
v.Yes -= power | ||
case VoteNo: | ||
v.No -= power | ||
case VoteAbstain: | ||
v.Abstain -= power | ||
default: | ||
panic("unknown vote kind") | ||
} | ||
} | ||
|
||
func (v *Votes) Total() uint64 { | ||
return v.Yes + v.No + v.Abstain | ||
} | ||
|
||
func (v Votes) ToJSON() string { | ||
return jsonutil.FormatObject([]jsonutil.KeyValue{ | ||
{Key: "yes", Value: v.Yes}, | ||
{Key: "no", Value: v.No}, | ||
{Key: "abstain", Value: v.Abstain}, | ||
}) | ||
} | ||
|
||
type Proposal struct { | ||
ID int | ||
Title string | ||
Description string | ||
Proposer std.Address | ||
Messages []ExecutableMessage | ||
Ballots *avl.Tree // dev | ||
// Ballots *avl.MutTree // test3 | ||
Votes Votes | ||
Status ProposalStatus | ||
} | ||
|
||
var _ jsonutil.JSONAble = (*Proposal)(nil) | ||
|
||
func (p Proposal) ToJSON() string { | ||
return jsonutil.FormatObject([]jsonutil.KeyValue{ | ||
{Key: "id", Value: p.ID}, | ||
{Key: "title", Value: p.Title}, | ||
{Key: "description", Value: p.Description}, | ||
{Key: "proposer", Value: p.Proposer}, | ||
{Key: "messages", Value: jsonutil.FormatSlice(p.Messages), Raw: true}, | ||
{Key: "ballots", Value: p.Ballots}, | ||
{Key: "votes", Value: p.Votes}, | ||
{Key: "status", Value: p.Status}, | ||
}) | ||
} | ||
|
||
type ProposalStatus int | ||
|
||
const ( | ||
ProposalStatusOpen ProposalStatus = iota | ||
ProposalStatusPassed | ||
ProposalStatusExecuted | ||
) | ||
|
||
func (p ProposalStatus) ToJSON() string { | ||
return jsonutil.FormatString(p.String()) | ||
} | ||
|
||
func (p ProposalStatus) String() string { | ||
switch p { | ||
case ProposalStatusOpen: | ||
return "Open" | ||
case ProposalStatusPassed: | ||
return "Passed" | ||
case ProposalStatusExecuted: | ||
return "Executed" | ||
default: | ||
return "Unknown(" + strconv.Itoa(int(p)) + ")" | ||
} | ||
} | ||
|
||
type Vote int | ||
|
||
const ( | ||
VoteYes Vote = iota | ||
VoteNo | ||
VoteAbstain | ||
) | ||
|
||
func (v Vote) ToJSON() string { | ||
return jsonutil.FormatString(v.String()) | ||
} | ||
|
||
func (v Vote) String() string { | ||
switch v { | ||
case VoteYes: | ||
return "Yes" | ||
case VoteNo: | ||
return "No" | ||
case VoteAbstain: | ||
return "Abstain" | ||
default: | ||
return "Unknown(" + strconv.Itoa(int(v)) + ")" | ||
} | ||
} | ||
|
||
type IProposalModule interface { | ||
Propose( | ||
title string, | ||
description string, | ||
actions []ExecutableMessage, | ||
) | ||
Vote(proposalId int, vote Vote, rationale string) | ||
Execute(proposalId int) | ||
Threshold() Threshold | ||
|
||
Proposals() []Proposal | ||
GetBallot(proposalId int, addr std.Address) Ballot | ||
|
||
Render(path string) string | ||
} | ||
|
||
type ExecutableMessage interface { | ||
String() string | ||
Binary() []byte | ||
Type() string | ||
} |
66 changes: 66 additions & 0 deletions
66
examples/gno.land/p/demo/daodao/interfaces_v3/dao_messages.gno
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package dao_interfaces | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/binary" | ||
"strings" | ||
|
||
"gno.land/p/demo/avl" | ||
) | ||
|
||
type MessageHandler interface { | ||
Execute(message ExecutableMessage) | ||
FromBinary(b []byte) ExecutableMessage | ||
Type() string | ||
} | ||
|
||
type MessagesRegistry struct { | ||
handlers *avl.Tree | ||
} | ||
|
||
func NewMessagesRegistry() *MessagesRegistry { | ||
return &MessagesRegistry{handlers: avl.NewTree()} | ||
} | ||
|
||
func (r *MessagesRegistry) Register(handler MessageHandler) { | ||
r.handlers.Set(handler.Type(), handler) | ||
} | ||
|
||
func (r *MessagesRegistry) FromBinary(b []byte) ExecutableMessage { | ||
if len(b) < 2 { | ||
panic("invalid ExecutableMessage: invalid length") | ||
} | ||
l := binary.BigEndian.Uint16(b[:2]) | ||
if len(b) < int(l+2) { | ||
panic("invalid ExecutableMessage: invalid length") | ||
} | ||
t := string(b[2 : l+2]) | ||
|
||
h, ok := r.handlers.Get(t) | ||
if !ok { | ||
panic("invalid ExecutableMessage: invalid message type") | ||
} | ||
return h.(MessageHandler).FromBinary(b) | ||
} | ||
|
||
func (r *MessagesRegistry) FromBase64String(s string) ExecutableMessage { | ||
b, err := base64.RawURLEncoding.DecodeString(s) | ||
if err != nil { | ||
panic("invalid ExecutableMessage: invalid base64 string") | ||
} | ||
return r.FromBinary(b) | ||
} | ||
|
||
func (r *MessagesRegistry) Execute(msg ExecutableMessage) { | ||
h, ok := r.handlers.Get(msg.Type()) | ||
if !ok { | ||
panic("invalid ExecutableMessage: invalid message type") | ||
} | ||
return h.(MessageHandler).Execute(msg) | ||
} | ||
|
||
func (r *MessagesRegistry) ExecuteMessages(msgs []ExecutableMessage) { | ||
for _, msg := range msgs { | ||
r.Execute(msg) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/p/demo/daodao/interfaces_v3 |
59 changes: 59 additions & 0 deletions
59
examples/gno.land/p/demo/daodao/interfaces_v3/proposal_test.gno
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package dao_interfaces | ||
|
||
import ( | ||
"testing" | ||
|
||
"gno.land/p/demo/avl" | ||
"gno.land/p/demo/jsonutil_v2" | ||
) | ||
|
||
type NoopMessage struct{} | ||
|
||
var _ ExecutableMessage = (*NoopMessage)(nil) | ||
|
||
func (m NoopMessage) String() string { | ||
return "noop" | ||
} | ||
|
||
func (m NoopMessage) Binary() []byte { | ||
return nil | ||
} | ||
|
||
func (m NoopMessage) Type() string { | ||
return "noop-type" | ||
} | ||
|
||
func (m NoopMessage) ToJSON() string { | ||
return jsonutil.FormatString(m.String()) | ||
} | ||
|
||
func TestProposalJSON(t *testing.T) { | ||
props := []Proposal{ | ||
{ | ||
ID: 0, | ||
Title: "Prop #0", | ||
Description: "Wolol0\n\t\r", | ||
Proposer: "0x1234567890", | ||
Votes: Votes{ | ||
Yes: 7, | ||
No: 21, | ||
Abstain: 42, | ||
}, | ||
Ballots: avl.NewTree(), | ||
}, | ||
{ | ||
ID: 1, | ||
Title: "Prop #1", | ||
Description: `Wolol1\"`, | ||
Proposer: "0x1234567890", | ||
Status: ProposalStatusExecuted, | ||
Messages: []ExecutableMessage{NoopMessage{}, NoopMessage{}, NoopMessage{}}, | ||
}, | ||
} | ||
props[0].Ballots.Set("0x1234567890", Ballot{Power: 1, Vote: VoteYes, Rationale: "test"}) | ||
str := jsonutil.FormatSlice(props) | ||
expected := `[{"id":0,"title":"Prop #0","description":"Wolol0\n\t\r","proposer":"0x1234567890","messages":[],"ballots":{"0x1234567890":{"power":1,"vote":"Yes","rationale":"test"}},"votes":{"yes":7,"no":21,"abstain":42},"status":"Open"},{"id":1,"title":"Prop #1","description":"Wolol1\\\"","proposer":"0x1234567890","messages":["noop","noop","noop"],"ballots":{},"votes":{"yes":0,"no":0,"abstain":0},"status":"Executed"}]` | ||
if expected != str { | ||
t.Fatalf("JSON does not match, expected %s, got %s", expected, str) | ||
} | ||
} |
Oops, something went wrong.