Building on the functionality of Sputnik V1, Sputnik DAO V2 offers even more features and enhanced configuration ability. Sputnik V1 is archived because it can no longer be extended. Its newer version, Sputnik V2, aims to be more flexible in this regard and it provides new features that can be opt-in by the users. Code Review video with Trevor of CronCat.
Name | Description |
---|---|
Setup | Step-by-step guide to deploy a DAO factory and DAO contracts. |
Roles & Permissions | Setup roles and define permissions for each role. |
Proposals | Each action on the DAO is done by creating and approving a proposal. |
Voting | Configure policies, setup governance tokens, and vote on proposals. |
Bounties | Add and configure bounties. |
Blob Storage | Store large data blobs and content and index them by the data's hash. |
Upgradability | Upgrade the DAO to different contract code versions. |
1. Install Rustup: ``` curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` [_(Taken from official installation guide)_](https://www.rust-lang.org/tools/install) 2. Configure your current shell: ``` source $HOME/.cargo/env ``` 3. Add Wasm target to your toolchain: ``` rustup target add wasm32-unknown-unknown ```
Using [`near-cli`](https://docs.near.org/docs/tools/near-cli#near-login), login to your account which will save your credentials locally: ```bash near login ```
```bash git clone https://github.com/near-daos/sputnik-dao-contract ```
```bash cd sputnik-dao-contract/sputnikdao-factory2 && ./build.sh ```
- Create an env variable replacing `YOUR_ACCOUNT.testnet` with the name of the account you logged in with earlier: ```bash export CONTRACT_ID=YOUR_ACCOUNT.testnet ``` - Deploy factory contract by running the following command from your current directory _(`sputnik-dao-contract/sputnikdao-factory2`)_: ```bash near deploy $CONTRACT_ID --wasmFile=res/sputnikdao_factory2.wasm --accountId $CONTRACT_ID ```
```bash near call $CONTRACT_ID new --accountId $CONTRACT_ID --gas 100000000000000 ```
- Define the council of your DAO: ```bash export COUNCIL='["council-member.testnet", "YOUR_ACCOUNT.testnet"]' ``` - Configure the name, purpose, and initial council members of the DAO and convert the arguments in base64: ```bash export ARGS=`echo '{"config": {"name": "genesis", "purpose": "Genesis DAO", "metadata":""}, "policy": '$COUNCIL'}' | base64` ``` - Create the new DAO!: ```bash near call $CONTRACT_ID create "{\"name\": \"genesis\", \"args\": \"$ARGS\"}" --accountId $CONTRACT_ID --amount 10 --gas 150000000000000 ``` **Example Response:** ```bash Scheduling a call: sputnik-v2.testnet.create({"name": "genesis", "args": "eyJjb25maWciOiB7Im5hbWUiOiAiZ2VuZXNpcyIsICJwdXJwb3NlIjogIkdlbmVzaXMgREFPIiwgIm1ldGFkYXRhIjoiIn0sICJwb2xpY3kiOiBbImNvdW5jaWwtbWVtYmVyLnRlc3RuZXQiLCAiWU9VUl9BQ0NPVU5ULnRlc3RuZXQiXX0K"}) with attached 5 NEAR Transaction Id 5beqy8ZMkzpzw7bTLPMv6qswukqqowfzYXZnMAitRVS7 To see the transaction in the transaction explorer, please open this url in your browser https://explorer.testnet.near.org/transactions/5beqy8ZMkzpzw7bTLPMv6qswukqqowfzYXZnMAitRVS7 true ``` **Note:** If you see `false` at the bottom (after the transaction link) something went wrong. Check your arguments passed and target contracts and re-deploy.
The DAO deployment will create a new [sub-account](https://docs.near.org/docs/concepts/account#subaccounts) ( `genesis.YOUR_ACCOUNT.testnet` ) and deploy a Sputnik v2 DAO contract to it. - Setup another env variable for your DAO contract: ```bash export SPUTNIK_ID=genesis.$CONTRACT_ID ``` - Now call `get_policy` on this contract using [`near view`](https://docs.near.org/docs/tools/near-cli#near-view) ```bash near view $SPUTNIK_ID get_policy ``` - Verify that the name, purpose, metadata, and council are all configured correctly. Also note the following default values: ```json { "roles": [ { "name": "all", "kind": "Everyone", "permissions": ["*:AddProposal"], "vote_policy": {} }, { "name": "council", "kind": { "Group": ["council-member.testnet", "YOUR_ACCOUNT.testnet"] }, "permissions": [ "*:Finalize", "*:AddProposal", "*:VoteApprove", "*:VoteReject", "*:VoteRemove" ], "vote_policy": {} } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "0", "threshold": [1, 2] }, "proposal_bond": "1000000000000000000000000", "proposal_period": "604800000000000", "bounty_bond": "1000000000000000000000000", "bounty_forgiveness_period": "86400000000000" } ```
The DAO can have several roles, each of which allows for permission configuring. These permissions are a combination of
proposal_kind
andVotingAction
. Due to this combination these permissions can be scoped to be very specific or you can use wildcards to grant greater access.
Examples:
A role with: ["transfer:VoteReject","transfer:VoteRemove"]
means they can only vote to reject or remove a transfer
proposal but they can't vote to approve.
A role with: ["transfer:*"]
can perform any vote action on transfer
proposals.
A role with: ["*:*"]
has unlimited permission. Normally, the council
role has *:*
as its permission so they can perform any vote action on any kind of proposal.
Here is a list of actions:
AddProposal
- Adds given proposal to the DAO (this is the primary mechanism for getting things done).RemoveProposal
- Removes given proposal (this is used for immediate deletion in special cases).VoteApprove
- Votes to approve given proposal or bounty.VoteReject
- Votes to reject given proposal or bounty.VoteRemove
- Votes to remove given proposal or bounty (this may be because the proposal is spam or otherwise invalid).Finalize
- Finalizes proposal which is cancelled when proposal has expired (this action also returns funds).MoveToHub
- Moves a proposal to the hub (this is used to move a proposal into another DAO).Proposals are the main way to interact with the DAO. Each action on the DAO is performed by creating and approving a proposal.
Contents |
---|
Proposal types |
Add proposal |
View proposal |
View multiple proposals |
Approve proposal |
Each kind of proposal represents an operation the DAO can perform. Here are the kinds of proposals:
ProposalKind::ChangeConfig { .. },
ProposalKind::ChangePolicy { .. },
ProposalKind::AddMemberToRole { .. },
ProposalKind::RemoveMemberFromRole { .. },
ProposalKind::FunctionCall { .. },
ProposalKind::UpgradeSelf { .. },
ProposalKind::UpgradeRemote { .. },
ProposalKind::Transfer { .. },
ProposalKind::SetStakingContract { .. },
ProposalKind::AddBounty { .. },
ProposalKind::BountyDone { .. },
ProposalKind::Vote,
ProposalKind::FactoryInfoUpdate { .. },
ProposalKind::ChangePolicyAddOrUpdateRole { .. },
ProposalKind::ChangePolicyRemoveRole { .. },
ProposalKind::ChangePolicyUpdateDefaultVotePolicy { .. },
ProposalKind::ChangePolicyUpdateParameters { .. },
NEAR
and any NEP-141
token that this DAO has.FactoryInfoUpdate
.Adds a proposal to the DAO contract and returns the index number of the proposal or "proposal ID". By default, anyone can add a proposal but it requires a minimum 1 Ⓝ bond (attached deposit).
add_proposal
proposal
description
kind
```json { "proposal": { "description": "Add New Council", "kind": { "AddMemberToRole": { "member_id": "council_member_3.testnet", "role": "council" } } } } ```
```bash near call genesis.sputnik-v2.testnet add_proposal \ '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' \ --accountId proposer.testnet \ --amount 1 ```
```bash Transaction Id HbJdK9AnZrvjuuoys2z1PojdkyFiuWBvrDbXsAf5ndvu To see the transaction in the transaction explorer, please open this url in your browser https://explorer.testnet.near.org/transactions/HbJdK9AnZrvjuuoys2z1PojdkyFiuWBvrDbXsAf5ndvu 0 ``` **Note:** The number under the transaction link is the proposal ID.
Returns proposal details by passing the ID or index of a given proposal.
get_proposal
id
```bash near view genesis.sputnik-v2.testnet get_proposal '{"id": 0}' ```
```json { "id": 0, "proposer": "near-example.testnet", "description": "Add New Council", "kind": { "AddMemberToRole": { "member_id": "council_member_3.testnet", "role": "council" } }, "status": "InProgress", "vote_counts": {}, "votes": {}, "submission_time": "1624947631810665051" } ```
Returns multiple proposal details by passing the index ("ID") starting point and a limit of how many records you would like returned.
get_proposals
from_index
limit
```bash near view genesis.sputnik-v2.testnet get_proposals '{"from_index": 1, "limit": 2}' ```
```js [ { id: 1, proposer: 'near-example.testnet', description: 'Add New Council', kind: { AddMemberToRole: { member_id: 'council_member_4.testnet', role: 'council' } }, status: 'InProgress', vote_counts: {}, votes: {}, submission_time: '1624947785010147691' }, { id: 2, proposer: 'near-example.testnet', description: 'Add New Council', kind: { AddMemberToRole: { member_id: 'council_member_5.testnet', role: 'council' } }, status: 'InProgress', vote_counts: {}, votes: {}, submission_time: '1624947838518330827' } ] ```
Approves proposal by ID. Only council members can approve a proposal
act_proposal
id
action
```bash near call genesis.sputnik-v2.testnet act_proposal '{"id": 0, "action": "VoteApprove"}' \ --accountId council_member_1.testnet ```
```bash Receipts: 3mkSgRaHsd46FHkf9AtTcPbNXkYkxMCzPfJFHsHk8NPm, GjJ6hmoAhxt2a7si4hVPYZiL9CWeM5fmSEzMTpC7URxV Log [genesis.sputnik-v2.testnet]: ["council"] Transaction Id BZPHxNoBpyMG4seCojzeNrKpr685vWPynDMTdg1JACa7 To see the transaction in the transaction explorer, please open this url in your browser https://explorer.testnet.near.org/transactions/BZPHxNoBpyMG4seCojzeNrKpr685vWPynDMTdg1JACa7 '' ```
Only council members are allowed to vote on a proposal.
You can set a different vote policy for each one of the proposal kinds.
Vote policy can be: TokenWeight
, meaning members vote with tokens, or RoleWeight(role)
where all users with such role (e.g."council") can vote.
Also a vote policy has a "threshold". The threshold could be a ratio. e.g. threshold:[1,2]
=> 1/2 or 50% of the votes approve the proposal, or the threshold could be a fixed number (weight), so you can say that you need 3 votes to approve a proposal disregarding the amount of people in the role, and you can say that you need 1m tokens to approve a proposal disregarding total token supply.
When vote policy is TokenWeight
, vote % is measured against total toke supply, and each member vote weight is based on tokens owned. So if threshold is 1/2 you need half the token supply to vote "yes" to pass a proposal.
When vote policy is RoleWeight(role)
, vote % is measured against the count of people with that role, and each member has one vote. So if threshold is 1/2 you need half the members with the role to vote "yes" to pass a proposal.
DAO votes to select some token to become voting token (only can be done once, can't change later).
User flow to vote with selected token:
Add and configure bounties using
AddBounty
proposal.
The lifecycle of a bounty is the next:
AddBounty
which contains the bounty information including token
to pay the reward in and amount
to pay it out.id
in the bounty list which can be queried via get_bounties
.bounty_claim(id, deadline)
up to repeat
times which was specified in the bounty. This allows to have repetitive bounties or multiple working collaboratively.deadline
specifies how long it will take the sender to complete the bounty.bounty_giveup(id)
, and within forgiveness_period
their claim bond will be returned. After this period, their bond is forfeited and is kept in the DAO.bounty_done(id)
, which will add a proposal BountyDone
that, when voted, will pay to whoever completed the bounty.DAO supports storing larger blobs of data and content indexing them by hash of the data. This is done to allow upgrading the DAO itself and other contracts.
Blob lifecycle:
Blob can be removed only by the original storer.
Allow the DAO to be upgraded to different contract code versions. This allows the DAO to use a newer, more stable and faster version of the contract code. New versions usually include new features, bug fixes and improvements in performance. Downgrade to an older version is also possible.
There are two major ways to upgrade the DAO:
upgrade(code)
.DAOs can explicitly vote to disable factory auto upgrades and can pull the upgrade themselves from the factory.