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: DAO SDK #18

Open
wants to merge 111 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
f677f1c
feat: DA0-DA0 port
n0izn0iz Sep 18, 2023
135287a
feat: social feed and moderation dao by @hthieu1110
n0izn0iz Sep 18, 2023
6cc1280
chore: remove duplicates and unversion packages
n0izn0iz Sep 18, 2023
ce2465a
chore: reset gnovm changes
n0izn0iz Sep 18, 2023
d375b61
fix: make social feed and moderation work with latest dao packages
n0izn0iz Sep 18, 2023
d8800be
chore: move daodao contracts under teritori dir
n0izn0iz Sep 19, 2023
0fa396f
chore: move ujson under teritori dir
n0izn0iz Sep 19, 2023
86d4fcc
chore: move binutils under teritori dir
n0izn0iz Sep 19, 2023
fd7a5b3
chore: move flags_index under teritori dir
n0izn0iz Sep 19, 2023
9c89420
chore: move havl under teritori dir
n0izn0iz Sep 19, 2023
06d722f
chore: move markdown_utils under teritori dir
n0izn0iz Sep 19, 2023
f9f376d
chore: move utf16 under teritori dir
n0izn0iz Sep 19, 2023
3532d57
chore: update modboards golden test
n0izn0iz Sep 19, 2023
380f7e7
chore: move dao_realm under teritori dir
n0izn0iz Sep 19, 2023
3d12ce1
chore: move dao_registry under teritori dir
n0izn0iz Sep 19, 2023
6a8e185
chore: fork groups into teritori dir
n0izn0iz Sep 19, 2023
6ba2ba2
chore: move modboards under teritori dir
n0izn0iz Sep 19, 2023
3ab1751
chore: move social_feeds under teritori dir
n0izn0iz Sep 19, 2023
13b1337
chore: move social_feeds_dao under teritori dir
n0izn0iz Sep 19, 2023
b416f44
chore: move tori under teritori dir
n0izn0iz Sep 19, 2023
f320770
chore: update modboards golden test
n0izn0iz Sep 19, 2023
85302e4
chore: reset gnovm changes
n0izn0iz Sep 19, 2023
69dd461
chore: reset .gitignore changes
n0izn0iz Sep 19, 2023
507039d
feat: escrow contract by @go7066
n0izn0iz Sep 19, 2023
5a77310
chore: move escrow under teritori dir
n0izn0iz Sep 19, 2023
0c01047
feat: cosmos-style voting DAO by @go7066
n0izn0iz Sep 19, 2023
9e826d7
chore: move gnodaos under teritori dir
n0izn0iz Sep 19, 2023
911d788
chore: add disclaimer in gnodaos
n0izn0iz Sep 19, 2023
ea09b63
feat: justicedao and vrf by @go7066
n0izn0iz Sep 19, 2023
225aa19
chore: move justicedao and vrf under teritori dir
n0izn0iz Sep 19, 2023
93ac190
chore: disable test targeting test3 avl
n0izn0iz Sep 19, 2023
34d62bb
chore: move daodao contracts at teritori root
n0izn0iz Sep 19, 2023
f5f82eb
chore: add teritori contracts overview
n0izn0iz Sep 19, 2023
94e02fd
feat: abstract modules config query
n0izn0iz Sep 19, 2023
28fd066
fix: add missing imports in gno.mods
n0izn0iz Sep 19, 2023
66ad360
feat: add tutorial to create a DAO
n0izn0iz Sep 20, 2023
a73db3d
chore: clean dao core
n0izn0iz Sep 21, 2023
10345cd
chore: add moderation DAO documentation
n0izn0iz Sep 26, 2023
54f55a2
feat(ujson): add union support
n0izn0iz Sep 26, 2023
12692b4
chore: use ujson union
n0izn0iz Sep 26, 2023
97ac41f
feat: add voting durations
n0izn0iz Sep 26, 2023
f0a1b10
chore: clearer error messages
n0izn0iz Sep 26, 2023
0281ad3
fix: pass examples CI tests
n0izn0iz Sep 27, 2023
2d7a15d
feat: check expiration in vote procedure
n0izn0iz Sep 27, 2023
5c227f7
chore: typo in doc
n0izn0iz Sep 27, 2023
29bdb6d
chore: fmt
n0izn0iz Sep 28, 2023
c2a5707
feat: add social follow system (#15)
omarsy Feb 18, 2024
9cce73e
chore: tidy utf16
n0izn0iz Mar 11, 2024
f1fdc46
chore: clean ujson
n0izn0iz Mar 11, 2024
91c8303
chore: clean flags_index
n0izn0iz Mar 11, 2024
eef83fe
chore: clean dao_interfaces
n0izn0iz Mar 11, 2024
af10b75
chore: clean social_feeds
n0izn0iz Mar 11, 2024
1a729f2
chore: clean havl
n0izn0iz Mar 11, 2024
98631b5
chore: clean havl groups
n0izn0iz Mar 11, 2024
f3ed282
chore: clean markdown_utils
n0izn0iz Mar 11, 2024
397d156
chore: clean dao_core
n0izn0iz Mar 11, 2024
d92a828
chore: clean dao_utils
n0izn0iz Mar 11, 2024
76d7200
chore: clean dao_proposal_single
n0izn0iz Mar 11, 2024
6f67bef
chore: clean dao_voting_group
n0izn0iz Mar 11, 2024
f22248d
chore: clean dao_registry
n0izn0iz Mar 11, 2024
3b9983e
fix: disable broken registry in dao
n0izn0iz Mar 11, 2024
9e37291
chore: clean modboard
n0izn0iz Mar 13, 2024
215068d
chore: clean tori
n0izn0iz Mar 13, 2024
3915cb3
chore: clean dao_realm
n0izn0iz Mar 13, 2024
19e50eb
chore: clean escrow package
n0izn0iz Mar 13, 2024
dc1474c
chore: rename escrow -> projects_manager
n0izn0iz Mar 13, 2024
852a499
chore: remove vrf and justicedao
n0izn0iz Apr 10, 2024
eaf6792
chore: remove unused package
n0izn0iz Apr 10, 2024
2157ff3
chore: remove unrelated packages
n0izn0iz Apr 10, 2024
5a1307e
chore: update golden tests
n0izn0iz Apr 10, 2024
865c6d3
chore: fmt imports
n0izn0iz Apr 10, 2024
4dc53b5
chore: remove duplicate utf16
n0izn0iz Apr 10, 2024
b35749e
chore: replace ujson with official json
n0izn0iz Apr 11, 2024
6195695
chore: remove moderation stuff
n0izn0iz Apr 11, 2024
af3a8ef
feat: improve render
n0izn0iz Apr 11, 2024
c471f46
chore: remove markdown_utils
n0izn0iz Apr 11, 2024
70b461e
feat: render supported messages
n0izn0iz Apr 11, 2024
2629c68
fix: pass gno2go check
n0izn0iz Apr 11, 2024
27e4829
fix: update test
n0izn0iz Apr 11, 2024
9235af4
chore: remove obsolete comment
n0izn0iz Apr 11, 2024
1f0f2b6
chore: move docs
n0izn0iz Apr 11, 2024
d88b16d
Merge remote-tracking branch 'teritori/master' into daosdk
n0izn0iz Apr 11, 2024
67126bf
chore: improve docs
n0izn0iz Apr 11, 2024
346ca7a
chore: remove default proposal in exemple
n0izn0iz Apr 11, 2024
73f84dc
chore: precise todo
n0izn0iz Apr 11, 2024
b5a5150
chore: remove leftovers
n0izn0iz Apr 11, 2024
ef96580
feat: allow to change tori admin
n0izn0iz Apr 12, 2024
893907c
chore: remove obsolete comment
n0izn0iz Apr 12, 2024
1555f35
feat: improve dao doc
n0izn0iz Apr 12, 2024
7d4e448
Merge branch 'master' into daosdk
n0izn0iz Apr 12, 2024
a6689df
chore: fix test
n0izn0iz Apr 12, 2024
165c1a6
chore: use strings.Builder instead of concatenation
n0izn0iz Apr 13, 2024
05433bd
chore: update doc
n0izn0iz Apr 13, 2024
2fd99f5
Merge branch 'master' into daosdk
n0izn0iz Apr 14, 2024
3501f28
Merge branch 'master' into daosdk
n0izn0iz Apr 15, 2024
d7cdd9c
Merge branch 'master' into daosdk
n0izn0iz Apr 17, 2024
c1c4d97
Merge branch 'master' into daosdk
n0izn0iz Apr 22, 2024
f863190
Merge remote-tracking branch 'origin/master' into daosdk
n0izn0iz May 4, 2024
a3251f1
chore: add space after blocks
n0izn0iz May 4, 2024
e701f83
chore: improve doc
n0izn0iz May 4, 2024
66a4854
chore: improve docs
n0izn0iz May 4, 2024
5eaf156
chore: improve docs
n0izn0iz May 4, 2024
f51719c
chore: better comment
n0izn0iz May 6, 2024
7956e63
chore: rename teritori -> dao-maker
n0izn0iz May 6, 2024
9c4463d
Merge remote-tracking branch 'origin/master' into daosdk
n0izn0iz May 6, 2024
00b499f
chore: mod tidy
n0izn0iz May 6, 2024
58a8385
chore: rename dao-maker -> dao_maker because - is not supported in re…
n0izn0iz May 6, 2024
1b78823
Merge branch 'master' into daosdk
n0izn0iz May 14, 2024
f4f54a2
Merge branch 'master' into daosdk
n0izn0iz May 22, 2024
db663c7
chore: update docs
n0izn0iz Jun 17, 2024
63e3de2
chore: update docs
n0izn0iz Jun 17, 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
344 changes: 344 additions & 0 deletions docs/how-to-guides/creating-dao.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,344 @@
---
id: creating-dao
---

# How to Create a DAO

## Overview

This guide will show you how to write a simple [**DAO**](https://en.wikipedia.org/wiki/Decentralized_autonomous_organization) [realm](../concepts/realms.md) in [Gno](../concepts/gno-language.md). For actually deploying the realm, please see the
[deployment](deploy.md) guide.

We'll cover the core components that make up a DAO, walk you through the process of creating your first DAO, and provide code examples to help you get started.

## Theoretical Foundations

### Core Interface

The `IDAOCore` interface ties all the other components together and offers the main entry points for interacting with the DAO.

**Interface Definition:**

```go
type ActivableProposalModule struct {
Enabled bool
Module IProposalModule
}

type IDAOCore interface {
Render(path string) string

VotingModule() IVotingModule
ProposalModules() []ActivableProposalModule
ActiveProposalModuleCount() int
Registry() *MessagesRegistry

UpdateVotingModule(newVotingModule IVotingModule)
UpdateProposalModules(toAdd []IProposalModule, toDisable []int)
}
```

A default implementation is provided in the package `gno.land/p/demo/dao_maker/dao_core`, and custom implementations are generally not required.

### Voting Module

The `gno.land/p/demo/dao_maker/dao_interfaces.IVotingModule` interface defines how voting power is allocated to addresses within the DAO.

**Interface Definition:**

```go
type IVotingModule interface {
Info() ModuleInfo
ConfigJSON() string
Render(path string) string
VotingPowerAtHeight(address std.Address, height int64) (power uint64)
TotalPowerAtHeight(height int64) uint64
}
```

There is only one implementation currently, `gno.land/p/demo/dao_maker/dao_voting_group`, providing a membership-based voting power definition.

### Proposal Modules

A proposal module (`gno.land/p/demo/dao_maker/dao_interfaces.IProposalModule`) is responsible for:
- Receiving proposals, the proposal type is defined by the module
- Managing the proposals lifecycle
- Tallying votes, the vote type is defined by the module and the associated voting power is queried from the voting module
- Executing proposals once they are passed

**Interface Definition:**
```go
type IProposalModule interface {
Core() IDAOCore
Info() ModuleInfo
ConfigJSON() string
Render(path string) string
Execute(proposalID int)
VoteJSON(proposalID int, voteJSON string)
ProposeJSON(proposalJSON string) int
ProposalsJSON(limit int, startAfter string, reverse bool) string
ProposalJSON(proposalID int) string
}
```

There is only one implementation currently, `gno.land/p/demo/dao_maker/dao_proposal_single`, providing a yes/no/abstain vote model with quorum and threshold.

### Message handlers

Proposal actions are encoded as objects implementing `ExecutableMessage` found under `gno.land/p/demo/dao_maker/dao_interfaces`.
```go
type ExecutableMessage interface {
ToJSON() *json.Node
FromJSON(ast *json.Node)

String() string
Type() string
}
```

They are unmarshalled and executed by message handlers implementing `gno.land/p/demo/dao_maker/dao_interfaces.MessageHandler`.
```go
type MessageHandler interface {
Execute(message ExecutableMessage)
Instantiate() ExecutableMessage
Type() string
}
```

Message handlers are registered at core creation and new message handlers can be registered via proposals to extend the DAO capabilities.

## Practical Implementation
In this section, we will showcase how to implement your own DAO using the `dao_maker` package suite.

### Setting Up Your Workspace

#### Setup the Tooling

To setup your tooling, see [Getting Started: Local Setup](../getting-started/local-setup.md).

#### Create a new Gno module

- Create a new directory and move into it: `mkdir my-gno-dao && cd my-gno-dao`
- Initialize the gno module: `gno mod init gno.land/r/<your_namespace>/my_dao`

### Creating the Voting Module

We will start by instantiating a voting module.

1. **Initialize the Factory**

Modules instantiation uses the factory pattern in case the module needs to access the core.

`my_dao.gno`
```go
package my_dao

import (
"gno.land/p/demo/dao_maker/dao_interfaces"
)

func init() {
votingModuleFactory := func(core dao_interfaces.IDAOCore) {

}
}
```

2. **Instantiate the module**

`my_dao.gno`
```go
package my_dao

import (
"gno.land/p/demo/dao_maker/dao_interfaces"
"gno.land/p/demo/dao_maker/dao_voting_group" // <- new
)

func init() {
var group *dao_voting_group.VotingGroup // <- new

votingModuleFactory := func(core dao_interfaces.IDAOCore) {
group = dao_voting_group.NewVotingGroup() // <- new
}
}
```

We need to keep a reference to the module to instantiate its message handlers later.

3. **Add Initial Members and return the module**

`my_dao.gno`
```go
func init() {
votingModuleFactory := func(core dao_interfaces.IDAOCore) {
group = dao_voting_group.NewVotingGroup()
group.SetMemberPower("your-address", 1) // <- new
// repeat for any other initial members you want in the DAO
return group // <- new
}
}
```

Now let's create a proposal module.

### Creating the proposal module

1. **Initialize the Factory**

`my_dao.gno`
```go
func init() {
votingModuleFactory := func(core dao_interfaces.IDAOCore) {
// ...
}
proposalModuleFactories := []dao_interfaces.ProposalModuleFactory{
func(core dao_interfaces.IDAOCore) dao_interfaces.IProposalModule {

},
}
}
```

2. **Configure and instantiate the Proposal Module**

`my_dao.gno`
```go
package my_dao

import (
"gno.land/p/demo/dao_maker/dao_interfaces"
"gno.land/p/demo/dao_maker/dao_voting_group"
"gno.land/p/demo/dao_maker/dao_proposal_single" // <- new
)

func init() {
// ...
var proposalModule *dao_proposal_single.DAOProposalSingle
proposalModuleFactories := []dao_interfaces.ProposalModuleFactory{
func(core dao_interfaces.IDAOCore) dao_interfaces.IProposalModule {
tt := dao_proposal_single.PercentageThresholdPercent(100) // 1% threshold
tq := dao_proposal_single.PercentageThresholdPercent(100) // 1% quorum
proposalModule = dao_proposal_single.NewDAOProposalSingle(core, &dao_proposal_single.DAOProposalSingleOpts{
MaxVotingPeriod: time.Hour * 24 * 42,
Threshold: &dao_proposal_single.ThresholdThresholdQuorum{
Threshold: &tt,
Quorum: &tq,
},
})
return proposalModule
},
}
}
```

We also need to keep a reference to the module to instantiate it's message handlers later.

### Registering Message Handlers

Add message handlers to allow your DAO to perform specific actions when proposals are executed.

`my_dao.gno`
```go
package my_dao

import (
"gno.land/p/demo/dao_maker/dao_interfaces"
"gno.land/p/demo/dao_maker/dao_voting_group"
"gno.land/p/demo/dao_maker/dao_proposal_single"
)

func init() {
// ...
messageHandlersFactories := []dao_interfaces.MessageHandlerFactory{
// Allow to manage the voting group
func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler {
return group.UpdateMembersHandler()
},
// Allow to update the proposal module settings
func(core dao_interfaces.IDAOCore) dao_interfaces.MessageHandler {
return dao_proposal_single.NewUpdateSettingsHandler(proposalModule)
},
}
}
```

### Creating the DAO Core

Now we can create the actual DAO.

```go
package my_dao

import (
"gno.land/p/demo/dao_maker/dao_interfaces"
"gno.land/p/demo/dao_maker/dao_voting_group"
"gno.land/p/demo/dao_maker/dao_proposal_single"
"gno.land/p/demo/dao_maker/dao_core" // <- new
)

var (
daoCore dao_interfaces.IDAOCore // <- new
)

func init() {
// ...
messageHandlersFactories := []dao_interfaces.MessageHandlerFactory{
// ...
}
daoCore = dao_core.NewDAOCore(votingModuleFactory, proposalModuleFactories, messageHandlersFactories) // <- new
}
```

We also need to expose the DAO methods in the realm.

```go
func init() {
// ...
}

func Render(path string) string {
return daoCore.Render(path)
}

func VoteJSON(moduleIndex int, proposalID int, voteJSON string) {
module := dao_core.GetProposalModule(daoCore, moduleIndex)
if !module.Enabled {
panic("proposal module is not enabled")
}

module.Module.VoteJSON(proposalID, voteJSON)
}

func Execute(moduleIndex int, proposalID int) {
module := dao_core.GetProposalModule(daoCore, moduleIndex)
if !module.Enabled {
panic("proposal module is not enabled")
}

module.Module.Execute(proposalID)
}

func ProposeJSON(moduleIndex int, proposalJSON string) int {
module := dao_core.GetProposalModule(daoCore, moduleIndex)
if !module.Enabled {
panic("proposal module is not enabled")
}

return module.Module.ProposeJSON(proposalJSON)
}

func getProposalsJSON(moduleIndex int, limit int, startAfter string, reverse bool) string {
module := dao_core.GetProposalModule(daoCore, moduleIndex)
return module.Module.ProposalsJSON(limit, startAfter, reverse)
}

func getProposalJSON(moduleIndex int, proposalIndex int) string {
module := dao_core.GetProposalModule(daoCore, moduleIndex)
return module.Module.ProposalJSON(proposalIndex)
}
```

## Conclusion

That's it! You've successfully created your first DAO using the Gno DAO framework. To expand its capabilities, you can register additional message handlers or even create new modules if you feel bold.
Loading
Loading